I am trying to understand the concept of dynamic memory allocation. I wondered what is the change in size after allocating memory.
My code is below.
#include <stdio.h>
#include <stdlib.h>
int main(){
char *s;
printf("Size before malloc: %d\n", sizeof(s));
s = (char*)malloc(1024 * sizeof(char));
printf("Size after malloc: %d\n", sizeof(s));
printf("Size actual: %d\n", sizeof(s) * 1024);
s[0] = 'a';
s[1] = 'b';
s[2] = 'c';
s[3] = 'd';
printf("Size by calculation: %d", sizeof(s) / sizeof(char));
return 0;
}
When I execute this code I get output as follows.
Size before malloc: 8
Size after malloc: 8
Size actual: 8192
Size by calculation: 8
So why not returns 8192 as size after allocating memory. Further when I added chars into s
why not getting as 4
?
CodePudding user response:
The size of all variables is constant through its entire lifetime. No function call will change the size of a variable.
So why not returns 8192 as size after allocating memory.
Because the variable is an object of type char *s
and the size of that type is not 8192 on your system.
CodePudding user response:
sizeof pointer
gives the size of the pointer itself, not the size of the referenced object. So on modern PC machines it will be 4 (32bit system/build) or 8 (64 bits system/build) bytes.
There is no portable way in C to get the size of the dynamically allocated memory block.
Some remarks.
sizeof
gives size_t
type.
printf("Size by calculation: %d", sizeof(s) / sizeof(char));
invokes undefined behaviour. You need to use the correct format:
printf("Size by calculation: %zu", sizeof(s) / sizeof(char));
CodePudding user response:
I always find that pictures help in a situation like this.
The declaration
char *s;
creates a single object of type char *
:
char *
---
| | s
---
with an indeterminate value (not necessarily 0 or any particular value, may be different each time the program is run).
The statement
s = (char*)malloc(1024 * sizeof(char));
allocates space for 1024 char
objects somewhere else in memory, and assigns the address of the first object in that space to s
:
char * char
--- ---
| | s ----> | | s[0]
--- ---
| | s[1]
---
| | s[2]
---
...
The size of the s
variable never changes, only its value. In this case, its value is the address of s[0]
.
So why not returns 8192 as size after allocating memory. Further when I added chars into s why not getting as 4 ?
sizeof(s)
gives you the size of the pointer variable itself, not the size of the object to which it is pointing. You want to write that call as
printf("Size actual: %d\n", sizeof *s * 1024);
The type of s
is char *
, which is going to be greater than 1 on any real world system (4 or 8, depending). The type of the expression *s
is char
, which is what you want to use here.
Further when I added chars into s why not getting as 4 ?
You haven't added anything to s
(or more properly, the buffer it points to) - you've only assigned new values to things that were already there. You allocated space for 1024 char
objects, and that size doesn't change just by assigning elements. Critically, something like
s[1026] = some_value;
doesn't automatically extend the allocated memory - you're just writing outside of the bounds of that memory. You have to use the realloc
library call to increase the size of the allocated block.
Additionally,
printf("Size by calculation: %d", sizeof(s) / sizeof(char));
doesn't do what you're expecting - Again, sizeof(s)
gives you the size of the pointer variable itself, not the size of the thing to which it is pointing. sizeof(*s)
won't work either, because that gives you the size of a single char
object, not the entire allocated space. You have to keep track of how much space you allocated in a separate variable:
size_t num_elements = 1024;
char *s = malloc( sizeof *s * num_elements );
Like I said above, if you need to extend that space later on, you need to use the realloc
function:
/**
* Double the size of the allocated block.
*/
char *tmp = realloc( s, sizeof *s * (num_elements * 2) );
if ( tmp )
{
s = tmp;
num_elements *= 2;
}
If realloc
cannot successfully extend the buffer, it will return NULL
and leave the original buffer in place. Because of this, we want to assign the result to a temporary variable - we don't want to risk s
being set to NULL
and thus losing our only reference to that memory. Similarly, we don't want to update the size until we know the realloc
call was successful.
Some style notes -
As of C89, the cast on malloc
(and calloc
and realloc
) is unnecessary and generally considered bad practice. The more common practice is
T *p = malloc( sizeof *p * N ); // for any type T
Since the expression *p
has type T
, sizeof *p
is equivalent to sizeof (T)
. This way you're not repeating type information multiple times in a single malloc
call, which eases the maintenance burden (fewer things to update if T ever changes). This is not true in C and a cast is still required on malloc
, calloc
, and realloc
, but if you're writing C you shouldn't be using C-style memory management in the first place.
Also, remember that sizeof
is an operator, not a function - parentheses are only necessary if the operand is a type name or is an expression containing arithmetic or other operators of lower precedence. For example,
sizeof x y
is parsed as
(sizeof x) y
If you want the size of the expression x y
, then you need to write
sizeof (x y)