4

In C, why do X and Y work but not Z?

//X     
int num = 5;
int *ptr;
ptr = #

//Y
int *mptr = (int *)malloc(sizeof(int *));       
*mptr = 5;       

//Z
int* myptr;
*myptr = 5;

In Z, I declare a pointer and try to make it point to a number, but I get a segmentation fault. But it seems to me like I'm doing the same thing in Z as X and Y. In X I just use the variable num instead of the number 5 directly, and in Y I just use the heap to hold the variables instead of the stack. So how come X and Y work but Z doesn't?

Vilhelm Gray
  • 11,516
  • 10
  • 61
  • 114
glen4096
  • 674
  • 1
  • 9
  • 19
  • because pointers just points to a memory address. You need to alloc memory to store data. – Damián Montenegro Jun 01 '15 at 18:57
  • 1
    `*x = y` *doesn't* make x point at y; It assigns y to whatever x is pointing at at the moment. Which is garbage in your case, because you didn't initialize x. – Cubic Jun 01 '15 at 18:59
  • Pointers are called pointers because they point to chunks of memory (not to numbers, numbers are stored in memory). – Matthias Jun 01 '15 at 19:06
  • 3
    You have an error here as well. `mptr` is declared as an integer pointer. When you `malloc` you should be calling `malloc(sizeof(int))` not `malloc(sizeof(int *))`. This may accidentally be working because the pointer and integer sizes may be the same, but it is still incorrect. You also should never cast the return value of malloc since it's already `void *`, otherwise you can inadvertently conceal other errors. Finally, – David Hoelzer Jun 01 '15 at 19:09
  • Finally, David Hoelzer ? – Quentin Jun 01 '15 at 19:21

11 Answers11

9
//X     
int num = 5;
int *ptr;
ptr = #

For the above, the int value "num" is allocated on stack, or in program data segment so it has an address. Lets pretend it was assigned by the compiler the address 0x12345678. You create an int* ptr. This also has address lets say 0x20000000. The address currently point to random data. We want to make the pointer at 0x20000000 point to the data value at 0x12345678, so that we can read the contents of 0x12345678 and get back the value 5... so we place 0x12345678 inside the storage space at 0x20000000 (we set ptr = &num).

//Z
int* myptr;
*myptr = 5;

For the second example, we only have 0x20000000 (myptr). Its a pointer and it currently pointing nowhere or anywhere. When we do *myptr = 5, we look at the address stored at 0x20000000. Its random so it may be 0xffff0000 lets use that example. It will then try and write the value 5 to this address (0xffff0000) which does not exist and causes the segfault.

So in your last example, the pointer exists, but it does not point anywhere valid, so when you try to write where it points, you either corrupt valid memory or cause a segment fault.

Chris
  • 2,655
  • 2
  • 18
  • 22
2

You didn't specify what myptr is pointing to. You only declared it as a pointer (i.e. variable used to point somewhere), but you didn't initialize it. In X and Y you indeed do initialize the pointers:

//X     
int num = 5;
int *ptr;       /* Declaration */
ptr = #     /* Initialization */

//Y
int *mptr = (int *)malloc(sizeof(int));   /* Declaration + Initialization*/ 
*mptr = 5;      /* De-referencing*/ 

//Z
int* myptr;  /* Declaration only */
*myptr = 5;  /* De-referencing before initialization */

In Z there's only the declaration: int* myptr. You're attempting to de-reference the pointer in the following line, i.e. *myptr = 5;. This leads to a seg-fault as myptr hasn't been initialized yet.

Edit

Note that, as pointed out by David Hoelzer, when you call malloc you probably wanted to write malloc(sizeof(int)) rather than malloc(sizeof(int*)).

banach-space
  • 1,781
  • 1
  • 12
  • 27
  • You overlook that he is calling `malloc` with the wrong argument and that he is casting the return of `malloc`. – David Hoelzer Jun 01 '15 at 19:09
  • I'm not the OP, I can't accept. You are also still casting the return value of malloc which should never be done since it conceals errors. See http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – David Hoelzer Jun 02 '15 at 08:51
  • Not sure what happened there, I was convinced that there was a comment from OP. I was wrong and so I deleted mine as it made no sense. Casting pointers is valid `C` and it doesn't feel that relevant here. I decided not to mention it. Thank you for your remark though. The link that you pasted provides enough quality discussion on the matter. – banach-space Jun 02 '15 at 09:21
2

Pointers must point to memory locations. (Unless of course the pointer is null or invalid, but in that case you cannot write through it anyway).

The expression 5 doesn't indicate a memory location. There isn't some memory sitting out there with 5 in it waiting to be pointed at. We say that 5 is a value (or sometimes r-value) for this reason.

Expressions that do indicate memory locations are called l-values . (You can think of the l as standing for location). If you want to point to a location that contains 5 , you will have to include some code that reserves an area of memory. This code will use an l-value to refer to that memory location. To be clear, the term l-value means the expression, not the memory location.

In your code:

  • X: int num = 5; reserves a location, names it num, and stores 5 in it. The l-value is num.
  • Y: malloc(sizeof(int) (sic) reserves a location, and *mptr = 5; stores a value in it. The l-value is *mptr .
  • Z: *myptr is an l-value, however it does not designate a memory location because you have not made myptr point to any memory. So this code compiles but it causes undefined behaviour: an l-value must designate a valid memory location at the point it is evaluated.

NB. l-values and r-values are usually explained poorly by tutorials so be careful when googling. (I couldn't find any good pages that were C-only).

M.M
  • 138,810
  • 21
  • 208
  • 365
1

Pointers are variables to hold memory address of other spaces.When a pointer is declared, it does not have any memory, it has to be explicitly initialized by assigning it to some already existing variable or memory be allocated by using the malloc() C library function.

In your X case, ptr is made to point to the address of num, and hence it reflects the value of num once deferenced.

Similarly in case Y, memory is allocated explicitly and the pointer mptr is made to point to it using

int *mptr = (int *)malloc(sizeof(int *)); 

But in the Z case, the pointer is just declared, but it is not assigned to or allocated any memory location. So the pointer points to some wild location which is beyond the scope of the executing program, and hence ends up giving segmentation fault, which generally means out of scope memory access.

Rndp13
  • 1,094
  • 1
  • 21
  • 35
0

You have to assign a variable a value before you use that value. In Z, you are using the value of myptr before you assign it one. Neither of your other two examples do that.

//X     
int num = 5; // Assign num a value
int *ptr;
ptr = # // Assign ptr a value

//Y
int *mptr = (int *)malloc(sizeof(int *));     // Assign mptr a value
*mptr = 5;       // Use the value

//Z
int* myptr; // Doesn't assign myptr a value
*myptr = 5; // Uses myptr's value
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
0
int num = 5; // name a memory location "num" and store 5 in it
int *ptr;    // name a memory location "ptr"
ptr = #  // store address of "num" in "ptr"

//Y
int *mptr = (int *)malloc(sizeof(int *)); // name a memory location mptr
// then (wrongly, should have been sizeof(int)) allocate enough memory to store
// a pointer and store the address of that allocated memory in "mptr"
*mptr = 5;  // write value 5 in that anonymous memory location allocated by malloc
// that mptr is pointing at

//Z
int* myptr; // name a memory location "myptr", don't write anything to it
*myptr = 5; // try to write value of 5 into memory pointed by myptr -- 
//except myptr is not pointed anywhere yet, get undefined behavior
MK.
  • 33,605
  • 18
  • 74
  • 111
0

What's stopping you isn't necessarily the C language, but rather your operating system.

  • In case X, your memory requirements can be deduced from your code and memory is allocated before your program is executed.
  • In case Y, you request memory to be dynamically allocated and the operating system attempts to fulfill that request.
  • In case Z, the memory requirements cannot be deduced from the code, nor is there a request made to the operating system to dynamically allocate memory; thus, no memory has been allocated: you get a segmentation fault attempting to deference the unallocated memory.

The segmentation fault is issued by the operating system protecting you from dereferencing unallocated memory; it's a safety feature in modern operating systems designed to prevent your program from clobbering data being used elsewhere in the system.

In case Z, your pointer may be set to a random address. If you deference this address and store a 5 at that location, you may be overwriting another program's data in memory.

Imagine the chaos that would arise from programs interfering with each other's data in memory: you couldn't trust your variables anymore since another program could have messed with them, or worse -- a small bug in your program could overwrite your operating system in memory and crash your whole machine; this is why the modern operating system prevents you from doing that.

If you were programming on the bare metal (i.e. no operating system), you could deference all the random memory addresses you desire since nothing would be around to stop you from shooting yourself in the foot.

Vilhelm Gray
  • 11,516
  • 10
  • 61
  • 114
0

In fact, syntaxically speaking you can do it by using explicit cast:

//Z
int* myptr;
myptr = (void *)5;

So it can be (dangerously) used as an int but if you try to dereference it you will face a segmentation fault since myptr point at address 0x5 that is invalid.

Naam
  • 1
  • 3
0
  1. Pointers are called "pointers" for a reason. Data pointers can only point to lvalues, i.e. to objects that have a location in storage (in memory).

    "A number" does not have location in memory. "A number" is not an lvalue. It is not possible to point to something that does not have a location.

  2. Your example labeled Z does not even remotely look like an attempt to make a pointer to point to a number. Your examples labeled X and Y clearly show that in order to make a pointer ptr to point somewhere, you have to assign a value directly to ptr, not to *ptr. In you Z example you never assign any value to myptr.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

Your failing code

//Z
int* myptr;
*myptr = 5;

does the following (assuming 32-bit addresses and 32-bit int):

  1. int* myptr; declare myptr to be a pointer to int. Which is to say that it holds the address of an int. Since you have not initialized myptr, myptr holds an unidentified value. It could be the absolute address 0x00000000 or some seemingly random value (the bits from whatever last occupied that location in memory most likely).

  2. *myptr = 5; is an instruction to *save the integer value 5 (0x00000005) in the 4 contiguous bytes of memory located at the address contained in myptr. Whether or not you can write to that location in memory is entirely dependent on what that address is. If you are lucky, your program will fault and die. If you are unlucky, it will corrupt memory by tromp on something that matters (your call stack linkage, perhaps, or something worse).

Then, your program will run just fine...until it crashes in strange and mysterious ways. And you will rip your hair out trying to figure out what's going on. Don't ask me how I know this.

Now, you could say something like

myptr = 5 ;

which does something different: it assigns myptr the absolute address 0x00000005. Then if you say something like:

*myptr = 3*5 ;

It will try to store the integer value 15 (0x0000000F) in the 4 bytes located at absolute address 0x00000005. It will still (almost certainly) crash, but at least it will do so in a consistent and reproducible way.

For the sake of argument, my example addresses are assuming a 32-bit address space.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
-1

You cant do that since that in this declaration, Z is only a pointer and does not hold any memory. you need to "point" with the Z to a new memory

you can do that in 3 ways:

int* myptr;
myptr(int *)malloc(sizeof(int));

myptr = 5;

OR

int* myptr = new int;

OR

int A;
int* myptr = A;
xsss
  • 81
  • 1
  • 2
  • 9
  • 1
    Not a single one of those is both C and works as advertised (or even compiles for that matter). That's the sort of content for which Stack Overflow implemented the feature, where an author can freely delete their own proposed answers. – IInspectable Oct 22 '16 at 15:30