Compatibility of data size while using malloc() - c

I recently studied about malloc() in C with declaration as follows:
void *malloc(size_t size)
where size_t is unsigned int and size defines the no. of bytes to be reserved.
Question is that on my system float values occupy 4bytes of memory. So if i make memory pointer(of float type) using malloc of 2bytes,
float *p;
p = (float *)malloc(2);
then how come it does not give any error? Because what i think is that float data required 4 bytes so if i issue only 2 bytes to it then it may lead to some data loss.
or is it that i m understanding malloc() incorrrectly?

In the example you give, if you only allocate 2 bytes for a float * and then attempt to write to that location by dereferencing the pointer, you'll be writing to memory that hasn't been allocated. This results in undefined behavior. That means it might work, it might core dump, or it might behave in unpredictable ways.
If you want to allocate memory for one or more floats, you would do it like this:
// allocates space for an array of 5 floats
// don't cast the result of malloc
int arrayLen = 5;
float *f = malloc(sizeof(float) * arrayLen);

You're encountering an implementation-specific result of the requirements of the C Standard:
7.22.3 Memory management functions
The order and contiguity of storage allocated by successive calls to
the aligned_alloc , calloc , malloc , and realloc functions
is unspecified. The pointer returned if the allocation succeeds is
suitably aligned so that it may be assigned to a pointer to any type
of object with a fundamental alignment requirement and then used to
access such an object or an array of such objects in the space
allocated (until the space is explicitly deallocated).
In order to provide storage "suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement", an implementation has to return memory from malloc() et al at specific offsets that are multiples of the most restrictive alignment requirement for the system. That's usually something like 8 or 16 bytes.
Given that each and every block returned has to be aligned that way, most implementations internally create blocks of memory in multiples of the alignment requirement.
So if your system has an 8-byte alignment requirement, your malloc() implementation is likely to actually give you an 8-byte block of memory even though you requested two bytes. Likewise, ask for 19 bytes and you'll likely get something like 24 in reality.
It's still undefined behavior to go beyond what you asked for, though. And undefined behavior does unfortunately include "works just fine".

This can be problematic if you try to use that pointer -actually the pointer is fine, it's the allocated memory it points to that's too small-, the reason the compiler doesn't recognize it as an error is the fact that the pointer isn't "aware" of what it points to , actually pointers are variables that contain memory addresses, so basically they're just a number, and in most cases ( as user694733 pointed out) the size of the pointer is the same whether it points to a short or a float.
what the compiler sees is a cast from (void*) to (float*) and to the compiler it's a totally valid cast.

Your question has actually nothing to do with the malloc, but rather to the data casting. In this case you have casted the bytes located starting from the address returned by malloc to float. Thus, if you later say *p = 0.0f; you will actually write 4 bytes to the mentioned memory area, but only 2 bytes are ligal to you to use since you have allocated only 2 bytes. Therefore, your code will compile and run with memory corruption (this either will result in crash, or in an unexpected runtime behavior later)

Related

Allocating memory with calloc for an int pointer

Hey guys given the example below in C when operating on a 64bit system as i understand, a pointer is 8 byte. Wouldn't the calloc here allocate too little memory as it takes the sizeof(int) which is 4 bytes? Thing is, this still works. Does it overwrite the memory? Would love some clarity on this.
Bonus question: if i remove the type casting (int*) i sometimes get a warning "invalid conversion from 'void*' to 'int*', does this mean it still works considering the warning?
int *ptr;
ptr = (int*)calloc(4, sizeof(int));
calloc is allocating the amount of memory you asked for on the heap. The pointer is allocated by your compiler either in registers or on the stack. In this case, calloc is actually allocating enough memory for 4 ints on the heap (which on most systems is going to be 16 bytes, but for the arduino uno it would be 8 because the sizeof(int) is 2), then storing the pointer to that allocated memory in your register/stack location.
For the bonus question: Arduino uses C++ instead of C, and that means that it uses C++'s stronger type system. void * and int * are different types, so it's complaining. You should cast the return value of malloc when using C++.

Does sizeof returns the amount of memory allocated?

I read that:
sizeof operator returns the size of the data type, not the amount of memory allocated to the variable.
Isn't the amount of memory allocated depends on the size of the data type? I mean that sizeof will return 4 (architecture-dependent) when I pass int to it.
Am I missing something here?
sizeof returns the number of bytes that a variable or stack allocated array occupies.
Examples:
sizeof(char)=1 (in most configurations)
But sizeof(char*)=8 (depending on the platform)
If you dynamically allocate memory with malloc, you will receive a pointer to that block of memory. If use the sizeof on it, you will just get the size of the pointer.
However, sizeof() a stack allocated array like when you write int a[10] is the size of the allocated memory (so 4*10)
The size of the pointer doesn't depend on the size of the datatype it represents. (On 32 bit platforms, a pointer is 32bit)
The text you quote is technically incorrect. sizeof variable_name does return the size of memory that the variable called variable_name occupies.
The text makes a common mistake of conflating a pointer with the memory it points to. Those are two separate things. If a pointer points to an allocated block, then that block is not allocated to the pointer. (Nor are the contents of the block stored in the pointer -- another common mistake).
The allocation exists in its own right, the pointer variable exists elsewhere, and the pointer variable points to the allocation. The pointer variable could be changed to point elsewhere without disturbing the allocation.
sizeof returns the number of bytes
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type.
but the size of each byte is not guaranteed to be 8. So you don't obtain directly the amount of memory allocated.
A byte is composed of a contiguous sequence of bits, the number of which is implementation-defined
anyway you can deduce the amount of memory allocated using the CHAR_BIT constant, which contains the number of bit is a byte.
"Memory allocation" in C typically refers to explicit allocation (i.e: on the heap - malloc() and friends), or implicit allocation (i.e: on the stack).
As you've defined, sizeof() returns the size of the data type:
sizeof(char) - a single char
sizeof(void *) - an void pointer
If you call malloc(sizeof(int)), you're requesting "enough memory to hold the data for an int", which may be 4 bytes on your system... you may find that more memory than you requested is allocated (though this will typically be hidden from you, see canaries).
Additionally, if you call int *x = malloc(1024), and sizeof(*x), you might get 4, because an int happens to be 4 bytes... even though the memory you've allocated is 1 KiB. If you were to incorrectly call sizeof(x), then you'll get the size of a pointer returned, not the size of the type it points to. Neither of these (sizeof(*x) or sizeof(x)) will return 1024.

Is this explicit heap or stack dynamic?

Given the following code snippet in C:
int* x;
x = (int *) malloc(40);
We know that this is an explicit heap dynamic allocation.
If I change the code to this though:
int* x = (int *) malloc(40);
is it still an explicit heap dynamic? My friend thinks it's a stack dynamic, but I think its an explicit heap dynamic because we're allocating memory from the heap.
Explicit heap dynamic is defined as variables that are allocated and deallocated by explicit run-time instructions written by the programmer. Wouldn't that imply that any malloc/calloc call would be explicit heap?
Edit: I spoke to my professor and she clarified some stuff for me.
When we declare something like
char * str = (char *) malloc(15);
We say that str is of data type char pointer, and has a stack dynamic storage binding. However, when we are referring to the object referenced by str, we say that it is explicit heap dynamic.
First off, if you are going to store the results of malloc in a variable, then declare it properly as a pointer, not an int.
The problem with storing the malloc results in an int, is that you could possibly truncate the pointer and blow up when you dereference it later. For instance if you ran that expression on a 64 bit system, the malloc would come back with a 8 byte pointer. But since you are assigning it to a 4 byte int, it gets truncated. Not good.
Your code should be like this:
int* x = (int*)malloc(bla);
Anyways, the int pointer x itself is stored on the stack. But don't get the two confused. X itself is a pointer on the stack, but it points to memory allocated on the heap.
Note:
32 bit applications (usually) have 4 byte pointers.
64 bit applications (usually) have 8 byte pointers.
Yes, malloc allocates requested memory on the heap. Heap memory is used for dynamic data structures that grow and shrink.
From standard 6.3.2.3
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
This claims why the type of the pointer should be int*. Also the casting is not needed - void* to int* conversion will be implicitly done over here.
Also why do you think declaring a variable with initializer or without it would affect the storage duration of the memory allocated by *alloc and it's friend functions. It is not.
Interesting thing x has automatic storage duration (usually this is realized using stack) but the memory it contains (yes type of x would be int*) - it will be of allocated storage duration. The thing is heap/stack are not something specified or mentioned by C standard. Most implementations usually realize allocated storage duration using heap.

How to limit the size allocated memory?

While studying pointers I found that if I allocate memory for a 2 digit int I can give that int a higher value. Can someone explain me why it works like that and how to work around it?
Here is the code:
int *p;
p = (int *) malloc(2*sizeof(int));
p = 123456;
First of all, Please see this discussion on why not to cast the return value of malloc() and family in C..
That said, here, you're just overwriting the pointer returned by malloc(). Don't do that. It will cause memory leak. Also, if you try to dereference this pointer later, you may face undefined behavior, as there is no guarantee that this pointer points to a valid memory location. Accessing invalid memory leads to UB.
Finally, to address
I allocate memory for a 2 digit int [...]
let me tell you, you are allocating memory to hold two integers, not a two digit integer. Also, to store the integer values into the memory area pointed by the pointer, you need to dereference the pointer, like *p and store (assign) the value there, like *p=12345;.
malloc() allocates memory of the size of bytes passed as it's argument. Check the sizeof(int) (and 2*sizeof(int), if you want) to make it more clear.
Regarding this, quoting C11, chapter
void *malloc(size_t size);
The malloc function allocates space for an object whose size is specified by size [...]
So, here, malloc() returns a pointer with the size of two integers (i.e., capable of holding two integer values), not two digits or bytes.
Also, it's always a good practice to check the success of malloc() before using the returned pointer to avoid the possible UB by dereferencing NULL in case of malloc() failure.
if i allocate memory for a 2 digit int i can give that int a higher value.
No, you cannot. You have allocated memory for two ints, with full int range. Standard guarantees at least five digits (215-1), but on most modern systems you are safe with eight digits (231-1).
Back to your question, there is no way in C to ensure that your program does not write past the end of the memory that it allocates. It is the responsibility of your program to not do that, including any checks it must perform in order to catch potentially unsafe access.
This is called undefined behavior. The system may crash when you access memory past the end of the allocated area, but it does not have to.
allocate memory for a 2 digit int
malloc(2*sizeof(int)) allocates memory for two integers, not for a single two-digit integer.
you do not need the cast in the first place
You are allocated memory for two integers - typically 32 bits each
You are giving a pointer the value 123456
Perhaps reread the book

realloc in C — exact behaviour and uses

Reading some literature, I was able to grasp that realloc takes in a void pointer and a size variable and re-allocates the memory of the block the void pointer points to.
What will happen if realloc is called on an integer pointer (int *)
with a size of character? And vice versa.
What can be a possible application of this? (An example would definitely help.)
The realloc() function is the all-in-one memory management system.
If called with a null pointer and a non-zero size, it allocates memory.
If called with a valid pointer and a zero size, it frees memory.
If called with a valid pointer and a non-zero size, it changes the size of the allocated memory.
If you call realloc() with an invalid pointer — one which was not obtained from malloc(), calloc() or realloc() — then you get undefined behaviour.
You could pass realloc() an integer pointer to an allocated space of sizeof(char) bytes (1 byte), but you'd be in danger of invoking undefined behaviour. The problem is not with realloc(); it is with the code that was given an unusable integer pointer. Since only 1 byte was allocated but sizeof(int) is greater than 1 (on essentially all systems; there could be exceptions, but not for someone asking this question), there is no safe way to use that pointer except by passing it to free() or realloc().
Given:
int *pointer = malloc(sizeof(char));
you cannot do *pointer = 0; because there isn't enough space allocated (formally) for it to write to. You cannot do int x = *pointer; because there isn't enough space allocated (formally) for it to read from. The word 'formally' is there because in practice, the memory allocators allocate a minimum size chunk, which is often 8 or 16 bytes, so there actually is space after the one byte. However, your are stepping outside the bounds of what the standard guarantees, and it is possible to conceive of memory allocators that would hand you exactly one byte. So, don't risk it. An integer pointer to a single byte of allocated memory is unusable except as an argument to the memory allocation functions.
The first argument to realloc() is a void *. Since you're going to have a prototype in scope (#include <stdlib.h>), the compiler will convert the int * to a void * (if there's anything to do for such a cast), and as long as the space pointed at was allocated, everything will be fine; realloc() will change the allocation size, possibly returning the same pointer or possibly returning a different pointer, or it will release the space if the new size is zero bytes.
There is a vitally important requirement to non-NULL pointers that you pass to realloc: they must themselves come from a call to malloc, calloc or realloc, otherwise the behavior is undefined.
If you allocate a chunk of memory sufficient to store an int and then realloc for a char, you will always get back the same pointer, because sizeof(char) is less than or equal to the sizeof(int):
int* intPtr = malloc(sizeof(int));
int* otherPtr = realloc(intPtr, sizeof(char));
// intPtr == otherPtr
If you try it the other way around, you will almost certainly get back the same pointer as well, because memory allocators rarely, if ever, parcel the memory to chunks smaller than sizeof(int). However, the result is implementation-dependent, so theoretically you may get back a different address.
As far as the utility of any of the above exercises goes, it is not useful: realloc has been designed with the intention to help you manage variable-sized arrays, simplifying the code for growing the size of such arrays, and potentially reducing the number of allocations and copying. I do not see a reason for realloc-ing a scalar.
The type of the first parameter of realloc is void *, as you said yourself. So the function argument that you pass is converted to a void pointer, which is an implicit and safe conversion.
It's the same as if you called a function with a long int parameter with an int argument, essentially.
Realloc takes the size in bytes. If you do
int* a= malloc(sizeof(int));
and then
a=realloc(a,1);
of course a will now not be big enough for an int type and writing an int in it will give you odd behavior.
As stated by dasblinkenlight, realloc doesn't make sense for a scalar.
In your example:
int * a = malloc(sizeof(int));
int * b = realloc(a,sizeof(char));
Would result in a == b, simply because sizeof(char) < sizeof(int) and there is no reason for moving the data to a new location.
The difference is, that after the realloc you are writing your int to unallocated space, as you decreased the allocated space using realloc. Here this is only of theoretical relevance. Because of alignment it is very unlikely, that the os is reusing the freed memory. But you shouldn't rely on that. This is undefined behavior.
It could become relevant, if you resize the space of a long int to an int. This depends on your architecture.
Writing to unallocated space is like laying a time bomb and not knowing when it will explode. You should only read from / write to allocated space.

Resources