I'm very new in C programming and I was playing around with malloc(), free() and Pointer Assignment in order to get a better grasp of it.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
void
array_fill(int * const arr, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
arr[i] = i;
}
void
array_print(const int * const arr, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("\n");
}
int
main(int argc, char ** argv)
{
int * p1, * p2;
p1 = (int *) malloc(SIZE * sizeof(int));
p2 = p1;
array_fill(p1, SIZE);
array_print(p1, SIZE);
array_print(p2, SIZE);
printf("\nFREE(p1)\n");
free(p1);
array_print(p2, SIZE);
return (0);
}
Compiling it with gcc test.c -o test and running it with ./test:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
FREE(p1)
0 0 2 3 4 5 6 7 8 9
p2 = p1, does it mean that p2 points to the same value as p1?
After freeing p1 why I can still print p2 (Value of index 1 is different)? Am I causing any memory leak or pointer dangling?
Is this normal to be able to print p2 even p1 is freed?
p2 = p1, does it mean that p2 points to the same value as p1?
Yes, after the assignment both pointers point to the same region of memory.
After freeing p1 why I can still print p2 (Value of index 1 is different)? Am I causing any memory leak or pointer dangling?
Yes, once you free p1, the p2 pointer becomes dangling. Accessing anything through it is undefined behavior.
Is this normal to be able to print p2 even p1 is freed?
No, it is undefined behavior.
Don't let the fact that you see numbers that look like ones that you have previously confuse you: any resemblance with the numbers that were there before you called free is a complete coincidence. Unfortunately, coincidences like that make problems with dangling pointers extremely hard to find. To aid with this problem, memory profiler programs take over the free-d region, and deliberately write some garbage values into it. This makes detection faster, but it is not bulletproof.
1) Yes
2) You are accessing freed memory which is a bad thing.
3) The behaviour is undefined. It could work, it could crash, it could print garbage, it could suck us all into a black hole (though unlikely)
Yes, p2 points to same area as p1.
Apparently, memory is freed, but it wasn't reused yet (though, one value was already overwritten). After freeing memory, you're not supposed to access it via another pointer.
It could lead to undefined behavior. In your case, it printed corrupted array. It also could've crashed with segmentation fault (if memory page no longer belonged to your application). Behavior could change depending on OS, compiler and other stuff, so it's better to avoid such practices.
1) The values stored in pointers are memory addresses. Which means, two pointers with the same value points to the same address, which means the same memory region.
2) Freeing the pointer p1 only sets the value of p1 to NULL and says that the memory pointed to by p1 is free to use and it's no longer reserved. But it doesn't erase the memory. It still holds it's value. But accessing it by another pointer that still has the address is an undefined behavior as it can be reserved for another thing.
3) Yes it's normal as it's already explained in (2); the memory region is not erased or set to 0s and p2 still points to the address which means it still prints the same value
Note that if the memory region is reserved by later by malloc, printing p2 may print another value if the memory region is modified.
Related
My concept of the way malloc()/calloc() create memory has always been that once an item is created, the address of the object stays the same. But a function I often use to create an array of strings, and one that seems to have always worked well, recently caused me to question my understanding, that is, memory addresses of objects can be (and are) moved simply by calling calloc/malloc.
To illustrate, here is the function I have used to create memory for an array of strings - char **:
char ** CreateArrayOfStrings(char **a, int numWords, int maxWordLen)
{
int i;
a = calloc(numWords, sizeof(char *)); //create array of pointers
if(!a) return a; //required caller to check for NULL
for(i=0;i<numWords;i++)
{
a[i] = calloc(maxWordLen + 1, 1); //create memory for each string
}
return a;
}
On my system, (Win7, 32bit compile, ANSI C) The line:
a = calloc(numWords, sizeof(char *)); //create array of pointers
Creates a block of contiguous memory, sized for numWords char *, in this case 7, yielding 28 bytes:
Memory spans from address 0x03260080 + 1C (0x0326009C)
Or:
a[0] is at 0x3200260080
a[1] is at 0x3200260084
a[2] is at 0x3200260088
a[3] is at 0x320026008C
a[4] is at 0x3200260090
a[5] is at 0x3200260094
a[6] is at 0x3200260098
Then, I create memory for each of numWords (7) strings
for(i=0;i<numWords;i++)
{
a[i] = calloc(maxWordLen + 1, 1); //maxWordLen == 5 in this example
}
Which results in the following:
This shows that the memory locations of the pointers a[1] - a[6] have been changed.
Can someone explain how/why this happens in malloc()/calloc()?
It appears that you are comparing apples to oranges:
When you print a[i] is at ... pointers, you show the addresses of elements inside the array a
However, when you shoe the memory layout, you show the values at these addresses, which are themselves pointers, so the whole picture looks confusing.
If you print the values at a[i] before assigning calloc results to them, you should get all zeros, because calloc NULLs out the memory. After the assignments, you see pointers to 6-byte blocks at each a[i], which makes perfect sense.
To summarize, your initial understanding of what happens when you allocate memory with malloc and calloc is correct: once a chunk of memory is allocated, its address* remains the same.
* On systems with virtual memory, I should say "its virtual address".
The memory of those addresses has not been changed. You are creating a 28-byte large block of space (a) and then at each element, dynamically allocating a second 6-byte block of space with its own address.
In other words, at a[1] (memory address 0x03260084), the value that is stored there is a pointer to memory address 0x32600D0.
To check the memory locations and the values at each one, try this:
for ( i = 0; i < 8; i++ )
{
printf("a[%d] %p %p\n",i,&(a[i]),a[i]);
}
When you call calloc(numWords, sizeof(char *)) you ask the operating system to allocate numWords pointers of size ``4 bytes each, on your system'', which is why the resulting block is 4 * numWords bytes, and it returns the address to the first one of them. So now you can store the addresses of the pointers that will hold the actual data. The nth call to calloc(maxWordLen + 1, 1) will then return the address to a block of size maxWordLen and store it at the memory location pointed to by a[n] which is simply the address returned by the first call to calloc plus n * sizeof(char *).
Consider the following code:
int *foo (int x) {
int a[2];
a[0] = x;
a[1] = x + 1;
return a;
}
…
int *p1 = foo(2);
int *p2 = foo(3);
At the conclusion of this code snippet, what are the values of each of the following? (answer is given)
p1[0] = 3
p1[1] = 4
p2[0] = 3
p2[1] = 4
Since a is allocated on the stack, the memory does not remain allocated when the call returns and may be reused by something else. In this case, since foo(3) is called immediately after foo(2) at the same call depth (i.e. they use the same stack space), they will both return the same pointer– i.e. p1 is equal to p.
I did not understand the above explanation. What does it really mean? Why we have exact same values for p2 and p1? I know that you cannot return a pointer to a local variable in C..but I do not understand why p2 and p1 has same values ....
Using a pointer to automatic storage variables (variables scoped to functions) outside its scope is undefined, which is exactly why you should never do it, and never depend on its behavior.
However, if you want to peel back the cover on the compiler/machine/os, the reason is that the automatic storage happens to be allocated at the same address for the two function calls.
An example...
#include "stdio.h"
int* foo(int x) {
int a[2];
printf("&a[0] = %p\n", &a[0]);
printf("&a[1] = %p\n\n", &a[1]);
a[0] = x;
a[1] = x + 1;
return a;
}
int main(int argc, char* argv[]) {
printf("foo(2)\n");
int* p1 = foo(2);
printf("foo(3)\n");
int* p2 = foo(3);
printf("p1[0] = %i\n", p1[0]);
printf("p1[1] = %i\n\n", p1[1]);
printf("p2[0] = %i\n", p2[0]);
printf("p2[1] = %i\n", p2[1]);
return 0;
}
outputs...
foo(2)
&a[0] = 0x7fff4dd0f054
&a[1] = 0x7fff4dd0f058
foo(3)
&a[0] = 0x7fff4dd0f054
&a[1] = 0x7fff4dd0f058
p1[0] = 3
p1[1] = 4
p2[0] = 3
p2[1] = 4
So, &a[0] and &a[1] have the same addresses in both foo(2) and foo(3).
What's happening is that when you enter a function, it generally creates a stack frame (by decrementing the stack pointer on x86). This allocates memory on the stack for automatic storage variables. When you leave the function, the stack frame is destroyed (and the stack pointer returned to its original value). So if you enter the same function again, you generally use the same memory for the stack frame (stack pointer is decremented to same value as last call to function).
it is undefined behavior, your program might crash too.
The local variable is stored on the stack and its life time is only with in the scope of the function.
In your case, the program is reusing the same location the next time , so your values are overwritten, but it is not always going to be same and never return address of local variable.
After each function call memory is allocated on stack for function variables and stack pointer is moved forward. After function execution stack memory is not erased for efficiency reasons and all data stays there. So if you call same function second time and leave some variables uninitialized you can find some funny values from last function call. Every array in C is stored as a big chunk of memory where elements are found by shifting pointer.
As for you question: foo returns pointer to integer, which is actually a memory address.
After foo(2) p1 will store some address for example 0x00. Adding braces [] with index to a integer pointer means to add an integer size * index to a memory address. We may add any random index to pointer and try to get data from there. If we get lucky and memory is readable - we will have some garbage data. After first function call
p1 points to a stack array and values are:
p1[0] == 2;
p2[1] == 3;
p1 == 0x00; (for example)
Function is executed and it returns stack pointer back. Next function call foo(3) gets same memory chunk on stack. Second call rewrites variables with new values. After second call we get:
p2[0] == 3;
p2[1] == 4;
p2 == 0x00; (same memory address)
The problem is that p1 points to same memory address on stack. If you call any other function - both p1 and p2 will be changed again, because same region of stack will be reused again.
Stack unwind while returning from the function call will release the memory allocated for the array. That memory is no longer available. The behavior is undefined.
In your function foo() you have a local variable and your are trying to return the address of the local variable. Don't your compiler throw the below error:
warning: function returns address of local variable
If you have to get the answer you have posted then you need to make the array int a[2] global.
Else we have an undefined behavior here.
Pointers are a really tricky thing in C. For a lot of people is hard to understand it, so for a good understanding I wrote following code:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int *p; // pointer -> will be dynamic allocated
int *a; // array -> will be dynamic allocated
// print before allocate memory (1)
printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
printf("&a: %p\ta: %p\t*a: %d\n", &a, a, *a);
printf("\n");
// allocate memory (2)
p = (int *)malloc(sizeof(int));
a = (int *)malloc(sizeof(int) * 10);
// print after allocate, but before give a value to poinetrs (3)
printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
printf("&a: %p\ta: %p\t*a: %d\n", &a, a, *a);
printf("\n");
// give a value to poinetrs (4)
*p = 1;
for (int i = 0; i < 10; i++) { a[i] = i; }
// print after we gave a value to pointers (5)
printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
printf("&a: %p\ta: %p\t*a: ", &a, a);
// because a is an array we must use a loop for print
for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
printf("\n");
printf("\n");
// free pointers (6)
free(p);
free(a);
// print pointers after free (7)
printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
printf("&a: %p\ta: %p\t*a: ", &a, a);
// because a is an array we must use a loop for print
for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
printf("\n");
printf("\n");
// try to change values after free (8)
*p = 12;
for (int i = 0; i < 10; i++) { a[i] = 3; }
// print after (8)
printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
printf("&a: %p\ta: %p\t*a: ", &a, a);
// because a is an array we must use a loop for print
for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
printf("\n");
printf("\n");
return 0;
}
Output:
&p: 0xbfe5db64 p: 0xbfe5dc24 *p: -1075452506
&a: 0xbfe5db68 a: 0xbfe5dc2c *a: -1075452502
&p: 0xbfe5db64 p: 0x8716008 *p: 0
&a: 0xbfe5db68 a: 0x8716018 *a: 0
&p: 0xbfe5db64 p: 0x8716008 *p: 1
&a: 0xbfe5db68 a: 0x8716018 *a: 0 1 2 3 4 5 6 7 8 9
&p: 0xbfe5db64 p: 0x8716008 *p: 0
&a: 0xbfe5db68 a: 0x8716018 *a: 0 1 2 3 4 5 6 7 8 9
&p: 0xbfe5db64 p: 0x8716008 *p: 12
&a: 0xbfe5db68 a: 0x8716018 *a: 3 3 3 3 3 3 3 3 3 3
Now, questions and observations:
When I print pointers before give memory for it, why pointer have a random value and a random address to point to it and why it isn't NULL?
After we use malloc, we can see the address where pointer points to changed and its value is NULL, so what malloc really does?
After we give a value to it and print it, we free it and print it again, but values and address are same as behind for array, but not for the integer, why? So what free really does?
After we freed space, we can continue to change values of array and integer, why is this possible after free space? We don't need to reuse malloc?
Because the language specification says so. The value of the pointer (i.e. the address it points to) is indeterminate. It can point anywhere, just like an int could hold any value. Reading those values, (as you do with *p and *a in the first printfs) is actually undefined behaviour.
If you mean the data it points to is 0, that is by chance. The memory allocated does not have to be zeroed out. For example, it could be part of a block previously allocated with malloc and then freed (free doesn't zero out the memory, see point 3. below.)
That is also by chance. When you free the memory, it is not zeroed out, and it does not have to be used immediately. It can keep storing old values until it is used for something else (for instance, by another allocation)
That is also undefined behaviour. You are writing to memory you no longer own. Anything can happen. The program could have crashed. By chance, it seems like you can successfully write to the array, probable because the memory is still not used by anything else that would cause a more apparent run time error.
1.When I print pointers before give memory for it, why pointer have a random value and a random address to point to it and why it isn't NULL?
You didn't make the pointer NULL. you are just declaring it. After declaring the pointer, it may have any value.
To make NULL-
int *p = NULL;
int *a = NULL;
2.After we use malloc, we can see the address where pointer points to changed and its value is NULL, so what malloc really does?
Man Page says-
void *malloc(size_t size);
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
If your allocated memory have 0 means that is by a chance only! The memory allocated by malloc doesn't freed out. But calloc does!
3.After we give a value to it and print it, we free it and print it again, but values and address are same as behind for array, but not for the integer, why? So what free really does?
free does not mean that it will actually delete the memory! It will inform to the OS that I don't want this memory any more, use it for some other process!
You can certainly continue to use memory a after calling free(...) and nothing will stop you. However the results will be completely undefined and unpredictable. It works by luck only. This is a common programming error called "use after free" which works in many programs for literally years without "problems" -- until it causes a problem.
4.After we freed space, we can continue to change values of array and integer, why is this possible after free space? We don't need to reuse malloc?
This is totally Undefined behavior! After freeing the memory also the pointer still points to the same memory location. It is called Dangling Pointer.
To avoid dangling pointer, Make pointer to null after free!
But after freeing the memory, you need to use memory means use malloc to allocate memory and use it!
What are pointers
Pointer declarations look much like other declarations: but don't be misled. When pointers are declared, the keyword at the beginning declares the type of variable that the pointer will point to. The pointer itself is not of that type, it is of type pointer to that type. A given pointer only points to one particular type, not to all possible types. In practice, all pointers are treated as integers, but the compiler will most likely complain about an assignment between incompatible types.
int *p;
The pointer p hasn't been assigned an address, so it still contains whatever random value was in the memory it occupies (whatever value was there before it was used for p). So, provided that a pointer holds the address of something, the notation *p is equivalent to giving the name of the something directly. What benefit do we get from all this? Well, straight away it gets round the call-by-value restriction of functions. Imagine a function that has to return, say, two integers representing a month and a day within that month.
Summary
Arrays always index from zero—end of story.
There are no multidimensional arrays; you use arrays of arrays instead.
Pointers point to things; pointers to different types are themselves different types.
They have nothing in common with each other or any other types in C; there are no automatic conversions between pointers and other types.
Pointers can be used to simulate ‘call by reference’ to functions, but it takes a little work to do it.
Incrementing or adding something to a pointer can be used to step along arrays.
To facilitate array access by incrementing pointers, the Standard guarantees that in an n element array, although element n does not exist, use of its address is not an error.
Pointer arithmetic
Not only can you add an integral value to a pointer, but you can also compare or subtract two pointers of the same type. They must both point into the same array, or the result is undefined. The difference between two pointers is defined to be the number of array elements separating them; the type of this difference is implementation defined and will be one of short, int, or long. This next example shows how the difference can be calculated and used, but before you read it, you need to know an important point.
In an expression the name of an array is converted to a pointer to the first element of the array. The only places where that is not true are when an array name is used in conjunction with sizeof, when a string is used to initialize an array or when the array name is the subject of the address-of operator (unary &). We haven't seen any of those cases yet, they will be discussed later. Here's the example.
#include <stdio.h>
#include <stdlib.h>
#define ARSZ 10
main(){
float fa[ARSZ], *fp1, *fp2;
fp1 = fp2 = fa; /* address of first element */
while(fp2 != &fa[ARSZ]){
printf("Difference: %d\n", (int)(fp2-fp1));
fp2++;
}
exit(EXIT_SUCCESS);
}
freed Pointers
Calling free() on a pointer doesn't change it, only marks memory as free. Your pointer will still point to the same location which will contain the same value, but that vluae can now get overwritten at any time, so you should never use a pointer after it is freed. To ensure that it is a good idea to always set the pointer to NULL after free'ing it.
Regarding 1), reading an uninitialised value is undefined behaviour. Undefined behaviour means that the C standard does not specify what the compiler should do with your program when you attempt to read an uninitialised value, and therefore the compiler writer is free to pretend that uninitialised values will never be read from - and aren't required to account for that happening in a program.
Within the context of your code, you are presumably using GCC or Clang to compile your program. Your two pointer variables are local variables, and local variables are stored on the stack in GCC and Clang C code. The stack may contain 'random' bytes in it; a pointer on 64-bit computers is generally a 64-bit (8-byte) number that corresponds to an address of memory.
For each of the two pointer variables, these randomly initialised 8 bytes are interpreted as a memory address. You can attempt to read from this random memory address, and if you do this (and your program is allowed to access that particular memory address), you will read some garbage values that will be interpreted as the datatype that your pointer points to. For example, in your case, a regular int.
Regarding 4), not to add to what the accepted answer has already said - I'm curious whether, if you malloc a number of objects and you free one of them and you make no further memory allocations, you are not creating new malloc metadata since you have no new allocations. It would be reasonable that the free'd block does not have its contents mutated, right?
To my knowledge, free does not actually return memory to the operating system. Therefore, one would expect that the answer to the above be yes - the block is not mutated.
I have a question in regards to creating a dynamic array.
int *p;
p = malloc( 3 * sizeof( int ) );
// initializes elements in the array
for ( int i = 0; i < 3; ++i ) {
*p++ = i * 4;
}
how can i free the memory i just allocated? for some reason, i find much easier deallocating a two dimensional array than one LOL. It's been along time since the last time i used C.
if i do the following:
free( p ); // will probably get an error.
Another thing in regards to pointers. I tried this:
int * p = malloc( sizeof( int ) );
*p = 4;
printf( "%d\n", *p ) // prints 4 as expected
free( p );
printf( "%d\n", *p ) // still prints the number 4!!!
the free function should release the block of memory that p points to. how is it that printf stills prints 4 then?
Malloc() returns a pointer to the allocated block. Keep it for future use with free().
Index your array with integer OR walk through it with a pointer, but keep the original address stored somewhere.
You can do like this
int *p = malloc(3 * sizeof(int));
for( int i = 0; i < 3; i++)
p[i] = 4*i;
// .....
free(p);
or
int *p = malloc(3 * sizeof(int));
int *q = p;
for( int i = 0; i < 3; i++)
*q++ = 4*i;
// .....
free(p);
In the first case, you just write free(p) as you only allocated one block of memory.
What you are doing in the second case is undefined behaviour. It might print 4, it might crash, it could do literally anything. The chances are that the implementation is just marking that location as reusable, but not actually clearing it out (why should it, it's a waste of time)
free(p) de allocates memory pointed by p. But p still having memory address which is de allocated by free(p). De-allocation means that block of memory added to list of free memories which is maintained by memory allocation module. When you print data pointed by p still prints value at address because that memory is added to free list and not removed.
how can i free the memory i just allocated?
You could do something like this
int *p;
p = malloc( 3 * sizeof( int ) );
// initializes elements in the array
for ( int i = 0; i < 3; ++i ) {
p[i] = i * 4;
}
this would not change p, so you could use free(p) to free it.
If you need to change p, you should remember its original value in another variable, and call free() on that original value, such as
int *op;
op = p;
/* do something that changes `p` */
free(op);
how is it that printf stills prints 4
What you did, access a dynamically allocated memory region after you freed it, will lead to undefined behavior. It could print 4, it also could crash or do something really wild.
Malloc ==> Only allocate the memory (not changing memory data to NULL )
Free ==> Only It will release the allocated resources on the pointer,(not changing memory data to NULL )
In both cases user has the responsibly to set appropriate value if required.
In first case you can still free the memory by taking the pointer back to the first element's address and then calling free(p) like:
p = p-3;
free(p);
Reason : malloc store some information about how much memory need to be freed once allocated dynamically that's why you need to point it back to the start so that free can read that information and free exactly 3 int memory allocated by malloc.
In your second case it is still printing is not universal result, output can be anything even program may crash.
Reason : free do not do anything special with the memory it only add it back to the FREE LIST it maintains for the memory available for dynamic allocation.
It is only up-to the programmer that they do not dereference the after calling free on it.
how can i free the memory i just allocated?
When we want to free a memory chunk previously allocated by malloc(), we use the free function. This function accepts a char pointer to a previously allocated memory chunk, and frees it - that is, adds it to the list of free memory chunks, that may be re-allocated. Use free(p).
how is it that printf stills prints 4 then?
Usage Of free():
Several notes about free():
The size of the chunk was stored by malloc() previously in its memory map, and that is how free() knows how many bytes to free.
The freed memory is not being cleared or erased in any manner. This is why accessing memory that was just freed often does not cause a crash - any data in it is still the same as before calling free().
The free() function cannot nullify pointers to the given memory chunk that might still exist in our program. After we call free(), it is up to us (the programmers) not to try and dereference pointers that still point to that memory chunk. Such pointers are known as 'dangling pointers' - they point to memory that was already freed, and thus they should NOT be dereferenced again, unless they are assigned the address of a different (not-freed) memory chunk.
I came across this program on HSW:
int *p;
int i;
p = (int *)malloc(sizeof(int[10]));
for (i=0; i<10; i++)
*(p+i) = 0;
free(p);
I don't understand the loop fully.
Assuming the memory is byte addressable, and each integer takes up 4 bytes of memory, and say we allocate 40 bytes of memory to the pointer p from address 0 to 39.
Now, from what I understand, the pointer p initially contains value 0, i.e. the address of first memory location. In the loop, a displacement is added to the pointer to access the subsequent integers.
I cannot understand how the memory addresses uptil 39 are accessed with a displacement value of only 0 to 9. I checked and found that the pointer is incremented in multiples of 4. How does this happen? I'm guessing it's because of the integer type pointer, and each pointer is supposedly incremented by the size of it's datatype. Is this true?
But what if I actually want to point to memory location 2 using an integer pointer. So, I do this: p = 2. Then, when I try to de-reference this pointer, should I expect a segmentation fault?
Now, from what I understand, the pointer p initially contains value 0
No, the pointer p would not hold the value 0 in case malloc returns successfully.
At the point of declaring it, the pointer is uninitialized and most probably holds a garbage value. Once you assign it to the pointer returned by malloc, the pointer points to a region of dynamically allocated memory that the allocator sees as unoccupied.
I cannot understand how the memory addresses uptil 39 are accessed
with a displacement value of only 0 to 9
The actual displacement values are 0, 4, 8, 12 ... 36. Because the pointer p has a type, in that case int *, this indicates that the applied offset in pointer arithmetics is sizeof(int), in your case 4. In other words, the displacement multiplier is always based on the size of the type that your pointer points to.
But what if I actually want to point to memory location 2 using an
integer pointer. So, I do this: p = 2. Then, when I try to
de-reference this pointer, should I expect a segmentation fault?
The exact location 2 will most probably be unavailable in the address space of your process because that part would either be reserved by the operating system, or will be protected in another form. So in that sense, yes, you will get a segmentation fault.
The general problem, however, with accessing a data type at locations not evenly divisible by its size is breaking the alignment requirements. Many architectures would insist that ints are accessed on a 4-byte boundary, and in that case your code will trigger an unaligned memory access which is technically undefined behaviour.
Now, from what I understand, the pointer p initially contains value 0
No, it contains the address to the first integer in an array of 10. (Assuming that malloc was successful.)
In the loop, a displacement is added to the pointer to access the subsequent integers.
Umm no. I'm not sure what you mean but that is not what the code does.
I checked and found that the pointer is incremented in multiples of 4. How does this happen?
Pointer arithmetic, that is using + - ++ -- etc operators on a pointer, are smart enough to know the type. If you have an int pointer a write p++, then the address that is stored in p will get increased by sizeof(int) bytes.
But what if I actually want to point to memory location 2 using an integer pointer. So, I do this: p = 2.
No, don't do that, it doesn't make any sense. It sets the pointer to point at address 0x00000002 in memory.
Explanation of the code:
int *p; is a pointer to integer. By writing *p = something you change the contents of what p points to. By writing p = something you change the address of where p points.
p = (int *)malloc(sizeof(int[10])); was written by a confused programmer. It doesn't make any sense to cast the result of malloc in, you can find extensive information about that topic on this site.
Writing sizeof(int[10]) is the same as writing 10*sizeof(int).
*(p+i) = 0; is the very same as writing p[i] = 0;
I would fix the code as follows:
int *p = malloc(sizeof(int[10]));
if(p == NULL) { /* error handling */ }
for (int i=0; i<10; i++)
{
p[i] = 0;
}
free(p);
Since you have a typed pointer, when you perform common operations on it (addition or subtraction), it automatically adjusts the alignment for your type. Here, since on your computer sizeof (int) is 4, p + i will result in the address p + sizeof (int) * i, or p + 4*i in your case.
And you seem to misunderstand the statement *(p+i) = 0. This statement is equivalent to p[i] = 0. Obviously, your malloc() call won't return you 0, except if it fails to actually allocate the memory you asked.
Then, I assume that your last question means "If I shift my malloc-ated address by exactly two bytes, what will occur?".
The answer depends on what you do next and on the endianness of your system. For example:
/*
* Suppose our pointer p is well declared
* And points towards a zeroed 40 bytes area.
* (here, I assume sizeof (int) = 4)
*/
int *p1 = (int *)((char *)p + 2);
*p1 = 0x01020304;
printf("p[0] = %x, p[1] = %x.\n", p[0], p[1]);
Will output
p[0] = 102, p[1] = 3040000.
On a big endian system, and
p[0] = 3040000, p[1] = 102
On a little endian system.
EDIT : To answer to your comment, if you try to dereference a randomly assigned pointer, here is what can happen:
You are lucky : the address you type correspond to a memory area which has been allocated for your program. Thus, it is a valid virtual address. You won't get a segfault, but if you modify it, it might corrupt the behavior of your program (and it surely will ...)
You are luckier : the address is invalid, you get a nice segfault that prevents your program from totally screwing things up.
It is called pointer arithmetic. Add an integer n to a pointer of type t* moves the pointer by n * sizeof(t) elements. Therefore, if sizeof(int) is 4 bytes:
p + 1 (C) == p + 1 * sizeof(int) == p + 1 * 4 == p + 4
Then it is easier to index your array:
*(p+i) is the i-th integer in the array p.
I don't know if by "memory location 2" you mean your example memory address 2 or if you mean the 2nd value in your array. If you mean the 2nd value, that would be memory address 1. To get a pointer to this location you would do int *ptr = &p[1]; or equivalently int *ptr = p + 1;, then you can print this value with printf("%d\n", *ptr);. If you mean the memory address 2 (your example address), that would be the 3rd value in the array, then you'd want p[2] or p + 2. Note that memory addresses are usually in hex and wouldn't actually start at 0. It would be something like 0x092ef000, 0x092ef004, 0x092ef008, . . .. All of the other answers aren't understanding that you are using memory addresses 0 . . . 39 just as example addresses. I don't think you honestly are referring to the physical locations starting at address 0x00000000 and if you are then what everyone else is saying is right.