Variable Memory allocation in c

Thread Starter

King2

Joined Jul 17, 2022
80
Hello everyone

This is all about learning the operation of the C language. I understand that to store any variable we have to allocate memory.

C:
int x;
Compiler will allocate memory for variable x when it compiles this line in program. This is a static memory allocation and the address of the variable x will not change.

C:
int * p;
Compiler will allocate memory for variable p when it compiles this line in program. This is a static memory allocation and the address of the variable will not change.

C:
int *m = malloc(sizeof(int));
I read that the memory for m will be allocated at run time. I don't understand what happens if this line will compile only.

Will the memory be allocated for the variable m when the compiler compiles this line or memory will be allocated for variable m when code will compile and run on the target system?
 

WBahn

Joined Mar 31, 2012
27,394
In general, int x results in an "automatic" memory allocation that DOES change. It is normally allocated as part of that function's stack frame when the function is called. It is automatically allocated and deallocated with each call to the function, hence the name. If you want it to have static allocation, then you usually need to add the 'static' modifier to the declaration.

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

This does NOT allocate memory for the variable m. The variable m is a pointer whose memory is allocated the same way that as for your earlier variable p. Nothing has changed in that regard.

What malloc() does, at run time, is request the allocation of heap memory from the memory manager and it returns the address of the first byte of this block. This is then used to initialize the value of m, not it's address.

In the early days, declaring variables and initializing them with executable code was not allowed and all variable declarations (within a function) had to occur prior to the first line containing executable code. So this statement would have been written something like:

int *m;
...
m = malloc(sizeof(int));

or, better (at least per lots of people):

int *m;
...
m = (int *) malloc(sizeof(int));

The language standard has evolved over time to allow some degree of mixing of variable declarations and executable code, but the compiler has to be able to essentially split things up along the lines of separate tasks.
 

Thread Starter

King2

Joined Jul 17, 2022
80
declaration.

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

This does NOT allocate memory for the variable m. The variable m is a pointer whose memory is allocated the same way that as for your earlier variable p. Nothing has changed in that regard.

What malloc() does, at run time, is request the allocation of heap memory from the memory manager and it returns the address of the first byte of this block. This is then used to initialize the value of m, not it's address.
It means memory is allocated for m at compile time whereas malloc allocates memory at run time. Dynamic memory doesn't exist at compile time, whereas static memory exist at compile and run time

As a programmer it is up to me whether I want to allocate static memory or allocate dynamic memory for variables.
 

WBahn

Joined Mar 31, 2012
27,394
It means memory is allocated for m at compile time whereas malloc allocates memory at run time. Dynamic memory doesn't exist at compile time, whereas static memory exist at compile and run time

As a programmer it is up to me whether I want to allocate static memory or allocate dynamic memory for variables.
You are confusing the memory for the variable 'm' with the memory for what 'm' points to.

When you say:

int a = 5;

You are allocating memory for 'a' (albeit as an offset to a stack frame pointer) and initializing the value stored at that location to the value 5.

It's the same thing if you do:

float *m = (float *) malloc(sizeof(float));

Let's say that you are targeting a 64-bit machine such that pointers require 8 bytes of memory. The "float *m" part results in 8-bytes of memory being allocated. Let's say that it is at address 0x0000000000001234. The value stored there, however, at this point is undefined and it likely just whatever garbage was there previously.

Now malloc executes and you have requested that it return the address of a block of memory that can hold at least four bytes (the size of a float). Let's say that that address is at 0x000000000000ABCD.

The VALUE stored in 'm' is now equal to 0x000000000000ABCD, but the address of 'm' has not changed and is still 0x0000000000001234.
 

Thread Starter

King2

Joined Jul 17, 2022
80
You are confusing the memory for the variable 'm' with the memory for what 'm' points to.
In this code the variable m points to the static memory.
C:
int x = 12;
int *m = &x;
This line changes the value of x now x will become 20
C:
*m = 20;
In this code the variable m points to the dynamic memory. 5 is stored in dynamic memory that m point
C:
int *m = malloc(sizeof(int));
*m = 5;
 

WBahn

Joined Mar 31, 2012
27,394
In this code the variable m points to the static memory.
C:
int x = 12;
int *m = &x;
Again, in nearly all contexts, this memory is NOT static memory, it is automatic. If you want it to be static, you need to make it static by doing

static int x = 12;

The only variables that, by default, are statically allocated are global variables, which well-written programs use sparingly, if at all.

This line changes the value of x now x will become 20
C:
*m = 20;
So? I'm not seeing the point you are trying to make.

Assuming this code is inside some function (including main), the variables x and m are both automatic (what you insist on calling static). In every C implementation I'm aware of (though the language standard does not dictate this) they are allocated on the stack when the function is entered and remain allocated on the stack until the function exists, at which point they are deallocated.

In this code the variable m points to the dynamic memory. 5 is stored in dynamic memory that m point
C:
int *m = malloc(sizeof(int));
*m = 5;
Again, what point are you trying to make. The variable 'm' is STILL automatically allocated exactly as it was before.

In the first case, &x evaluates to a number and that number is stored in the automatically allocated variable 'm'. It has NO affect on where 'm' or 'x' are stored. That ship has sailed and both of them are sitting in memory at the locations where they were automatically allocated.

In the second case, malloc(sizeof(int)) evaluates to a number and that number is stored in the automatically allocated variable 'm'. It has NO affect on where 'm' or 'x' are stored. That ship has sailed and both of them are sitting in memory at the locations where they were automatically allocated.

When you dereference a number (generally by dereferencing a variable of pointer type that happens to contain the desired number), you are changing the contents of memory stored at the address whose value is being dereferenced. You are NOT changing where any variable is located.
 

Thread Starter

King2

Joined Jul 17, 2022
80
Again, in nearly all contexts, this memory is NOT static memory, it is automatic. If you want it to be static, you need to make it static by doing
You are absolutely correct. C language has different storage classes for different purposes.

int *m;
...
m = (int *) malloc(sizeof(int));
what is the advantage of type casting i see some people use it and some people don't
 
Last edited:

WBahn

Joined Mar 31, 2012
27,394
You are absolutely correct. C language has different storage classes for different purposes.



what is the advantage of type casting i see some people use it and some people don't
There are definitely different schools of thought on this. The school I belong to has the general guiding principle of not letting the compiler do my thinking for me. Instead, I want to force it to do what I want. As a consequence, I am very sparing in which type casts I am willing to let it make implicitly. For instance, if I am doing division I consider whether I want to do integer division or floating point division and if either operand is the wrong time, I use an explicit type cast to the correct type, even if I know that the compiler will do it for me in the case of promoting an integer type to a floating point type if the other operand is a floating point type.

On a number of occasions I have seen very hard to track down bugs get introduced because they did something like the following:

int count;
double sum;
double average;
...
average = sum / count;

Because 'sum' is a floating point type, the division will be done using floating point division.

But then, for whatever reason, it is realized that, for this particular problem, they want sum to be an integer. So they just change it to

int count;
int sum;
double average;
...
average = sum / count;

Now the division uses integer division instead and the value stored in average is not what was desired.

So I would write the first version as:

int count;
double sum;
double average;
...
average = sum / (double) count;

Now if I change sum from a double to an int, the average calculation will still be done correctly, even if I miss typecasting the numerator to a double in the line of code.

The malloc() function returns a void pointer and C will implicitly cast a void pointer to any other type as needed and will do so without issuing a warning. It will also cast one type of pointer to another, even if they are not compatible, but it will generally issue a warning that it did so. Warnings are red flags that should always be investigated and cleared (unless the reason for the warning is understood and it is acceptable). But I have seen so many people (myself included) get tripped up and mistakenly use a pointer to one thing where they needed a pointer to another thing. So whenever I have a void pointer and I use it as a pointer to a specific type, I cast it as a pointer to that type. In many instances, this let's the compiler do a better job type checking. The downside is that that it is basing it's type checking not on what the types really are, but based on what I told them I want them to be. So there is the potential for me to introduce incorrect information. I'll accept that risk because I accept the responsibility for ensuring that the information I inject is correct.

I also find, in general, that doing explicit type casts, particularly of void pointers, makes the code much more self-documenting.
 

Thread Starter

King2

Joined Jul 17, 2022
80
When I declare array to hold 4 characters. I see the compiler actually allocates five bytes. I understand that the last byte is for the null terminator, which the compiler allocates.
C:
unsigned char Name [] = {"ROCK"}; // string of 4 characters
Do compilers add null terminator after character 'K'? or the last byte will be allocated for the null terminator in below line?
C:
unsigned char Name [10] = {"ROCK"}; // string of 4 characters
 

WBahn

Joined Mar 31, 2012
27,394
Just like 'K' is an expression that evaluates to a number, "ROCK" is (effectively) an expression that evaluates to an five-element array consisting of the codes for the explicit characters in the string and the NUL character at the end. The NUL character at the end is not so much "added" by the compiler, it is simply part of the string literal itself.

With the second designation you gave, you have provided an initializer that defines the first five values of your ten-element array. Per the language standard, the remaining elements are initialized to zero.

C:
#include <stdio.h>

int main(void)
{
    unsigned char Name [10] = {"ROCK"};
   
    for (int i = 0; i < 10; i++)
        printf("%2i %3i [%c]\n", i, Name[i], Name[i]);
   
    return 0;
}
 

Thread Starter

King2

Joined Jul 17, 2022
80
So I would write the first version as:

int count;
double sum;
double average;
...
average = sum / (double) count;
I can understand when to use int, char and float data type but double data type I can't figure out when to use it.

In my system float occupies 4 byte of memory while double occupies 8 byte of memory.

It seems to me that double data type is used when we have to store more floating point than the range of float data type. But I can't say with full confidence that I'm right
 

WBahn

Joined Mar 31, 2012
27,394
I can understand when to use int, char and float data type but double data type I can't figure out when to use it.

In my system float occupies 4 byte of memory while double occupies 8 byte of memory.

It seems to me that double data type is used when we have to store more floating point than the range of float data type. But I can't say with full confidence that I'm right
Unless you are targeting a pretty severely resource-starved processor or have to for some specific reason, you should use type double as your default floating point representation. The float data type only gives you about seven sig figs of resolution. It's very easy to end up with accumulated round-off errors that become significant enough to have an impact. The double data type has about fifteen sig figs, making it a lot harder (but far from impossible) for this to happen.
 

Thread Starter

King2

Joined Jul 17, 2022
80
Unless you are targeting a pretty severely resource-starved processor or have to for some specific reason, you should use type double as your default floating point representation.
Do you agree with my statement?

It seems to me that double data type is used when we have to store more floating point than the range of float data type. But I can't say with full confidence that I'm right
 

WBahn

Joined Mar 31, 2012
27,394
Do you agree with my statement?
No, I do not agree with it. The range of a float is from from values as small as 1.2x10^-38 to as large as 3.4x10^38 (both positive and negative). There are extremely few applications that require values that don't fit within that range. But many, many applications that need more than seven sig figs in order for the computations to be done with sufficient precision to avoid round-off errors that affect the results even to four sig figs.
 

Thread Starter

King2

Joined Jul 17, 2022
80
No, I do not agree with it. The range of a float is from from values as small as 1.2x10^-38 to as large as 3.4x10^38 (both positive and negative).
sorry but i still don't understand when to use double data type in c language.

Could you please offer an example that clearly explains the difference between float and double data types?
 

WBahn

Joined Mar 31, 2012
27,394
sorry but i still don't understand when to use double data type in c language.

Could you please offer an example that clearly explains the difference between float and double data types?
The primary difference (for the overwhelming majority of applications) is that the float data type provides about seven to eight significant figures of resolution while the double type provides about fifteen to sixteen sig figs.


The IEEE-754 Floating Point Standard defined several floating point representations. At the time, processors were very limited compared to today and memory was much more expensive, so the single-precision standard defined a representation that only used 32 bits and was a compromise between what was needed for many applications and what the computers at the time were capable of. This is the standard that is used by the 'float' data types in C and many other languages. They also defined the double-precision standard which is a 64-bit representation that is used by the 'double' data types. People with applications that were beyond what the 'float' type could handle used the 'double', which left very few applications that couldn't be supported. The standard also provided even larger applications for those very few applications and has been extended to provide a shorter (16-bit) version that is used for things like graphic cards in which speed and compactness is more important the resolution.

As an example in which the 'float' isn't good enough, image you have an investment that is worth $1,234,567 and it grows by 1%. How much is it worth?

This is something that we can do easily by hand. The growth is 1% of the investment, or $12,345.67. Now we just add these together to get $1,246912.67.

Now run the following program:

C:
#include <stdio.h>

int main(void)
{
    float investment, roi, growth, final_value;
    
    investment = 1234567;
    roi = 0.01;
    
    growth = investment * roi;
    final_value = investment + growth;
    
    printf("$ %13.4f\n", investment);
    printf("+ %13.4f\n", growth);
    printf("---------------\n");
    printf("$ %13.4f\n", final_value);
    
    return 0;
}
You will likely get
Code:
$  1234567.0000
+    12345.6699
---------------
$  1246912.6250
Notice how there is an error picked up just in multiplying the investment by 0.01 and that the result is clearly not the sum of the two numbers above it.

To further drive home this point, consider the following program that finds the smallest about by which you can increase the investment by and actually represent the increased value:

C:
#include <stdio.h>

int main(void)
{
    float investment, cents, next_larger;
    
    investment = 1234567;

    cents = 0.00;
    do
    {
        cents += 0.01;
        next_larger = investment + cents;
    } while (next_larger <= investment);
    
    printf("$ %13.4f\n", investment);
    printf("+ %13.4f\n", cents);
    printf("---------------\n");
    printf("$ %13.4f\n", next_larger);
    
    return 0;
}
Code:
$  1234567.0000
+        0.0700
---------------
$  1234567.1250
So if you try to increase the value by 6 cents, you get the same value, while if you increase the value by 7 cents you end up increasing it by 12.5 cents.

If you use doubles instead, you get what you would expect:

Code:
$  1234567.0000
+        0.0100
---------------
$  1234567.0100
The same resolution problem exists with doubles (or any other fixed-width representation), but the problem is pushed much further into the noise.

Code:
$  1234567.00000000000
+        0.00000000012
----------------------
$  1234567.00000000020
This makes it much less likely that errors due to round-off will affect the results in any significant way.

So, unless you really need to use a 'float' representation, don't. Use a 'double' instead. If you do need to use floats, then you really need to consider the implications of limited-resolution very carefully.
 
Top