How to make static array point to NULL - arrays

Is there a possible way to make static array of structs points to NULL since I can't delete arrays and I want to clean the memory?
Suppose we have the following code:
struct_x defaultStructX[6];
struct_x requiredStructX [6];
gettingDefaultX(defaultStructX, 6);
for (uint8_t i = 0; i <6; i++)
{
setStructX(requiredStructX[i].index, requiredStructX[i].icirate, requiredStructX[i].icis, requiredStructX[i].iei);
//error handle case
if (status == STATUS_SUCCESS)
{
writeResponse.writeStatus = status_ok; /*This is another struct not important at this point*/
} else
{
errorHandleQciFlowMeter(defaultStructX, 6);
writeResponse.writeStatus = status_nok;
break;
}
}
/*here I want to write code line to clean the defaultStructX from memory. Is it possible? I have tried *defaultStructX[i]= NULL and ##((void*)defaultQciFlowMeter) = NULL; ## and many other methods but it didn't work*/

Is there a possible way to make static array of stucts points to NULL since I can't delete arrays and I want to clean the memory?
No. Arrays are not pointers, nor are the elements of your particular arrays pointers, either. Pointer values cannot be assigned to either the arrays themselves or to the elements, and in particular, NULL cannot be assigned to them.
You can overwrite the memory occupied by the array with, say,
memset(defaultStructX, 0, sizeof(defaultStructX));
That will replace the data previously stored within,* which might be useful if those data were sensitive. The application would then need to assign new, valid values to the array elements before using them again.
Any way around, however, you cannot free the memory of an object with static storage duration, which is any object declared at file scope, outside all functions, or with the static qualifier inside a function. The entire point of static storage duration is for objects' lifetimes to be the entire duration of the program's run. If you want to be able to release the memory then you should allocate space for your arrays dynamically, or, if it works for your particular application, automatically (as a local variable of a well-chosen function).
* In principle. As #chux noted in comments, it may be the case that the compiler chooses to optimize out such an overwrite, which it might do if it could determine that the zeroed out data were never read. If this is a concern, then the best mitigation would probably be to declare the arrays volatile.

struct_x defaultStructX[6];
struct_x requiredStructX [6];
With the above statements, you have reserved memory for the two arrays of structs. This memory will remain allocated to you during the entire lifetime of your program and cannot be de-allocated until your program exits.
However, what you want to store in this memory is completely under your control. What is your definition of 'cleaning from memory'?
Do you haves some sensitive data that you want to erase from memory? You can always memset them to zero (or any other value) with:
memset(defaultStructX, 0x00, sizeof(defaultStructX));
Do you want to physically de-allocate the memory? If you want to have control over allocation and deallocation of chunks of memory, you should do so with malloc and free.
EDIT: Apparently the memset() solution can be derailed by compiler optimizations.
Here is a useful description of this issue from the SEI CERT C page.

You cannot manually deallocate anything that wasn’t allocated with malloc, calloc, or realloc. If you declared your arrays at file scope (outside of any function) or with the static keyword, then their memory won’t be released until the program terminates. Otherwise, their memory will be released when the function in which they were declared exits.
You can overwrite elements not currently in use with zeros or some other “not a value” value, but you cannot free the memory they occupy.
Arrays are not pointers. Expressions of array type are converted to pointers as necessary, but the array object itself is not a pointer.

Is there a possible way to make static array of structs points to NULL since I can't delete arrays and I want to clean the memory?
No. Arrays do not point. The array could be cleared.
I want to write code line to clean the defaultStructX from memory.
Simply assigned zeros to it.
memset() can get optimized out if the compiler sees the data was not subsequently read.
Alternative: use a loop with a pointer to volatile data to avoid loop from being optimized out.
volatile unsigned char *vp = defaultStructX;
for (size_t i = 0; i<sizeof defaultStructX; i++ {
vp[i] = 0;
}

Hmm... On one hand I love the detailed answer offered by John Bollinger and I believe he gave the correct answer for your use case... however...
There is a way to make the statically allocated array appear to point to NULL.
Obviously you can't assign NULL to an array. However, you can make a statically allocated "Array" seem to point to NULL by hiding it behind a pointer.
It might look like this:
#define STRUCT_X_ARRAY_LENGTH 6
struct_x defaultStructX___[STRUCT_X_ARRAY_LENGTH];
struct_x requiredStructX___[STRUCT_X_ARRAY_LENGTH];
struct_x * defaultStructX = defaultStructX___;
struct_x * requiredStructX = requiredStructX___;
gettingDefaultX(defaultStructX, STRUCT_X_ARRAY_LENGTH);
Now you could set the (no longer) "Array" to NULL using:
defaultStructX = NULL;
requiredStructX = NULL;
You could also reset the state (zero out the memory and reassign the correct memory address) using:
defaultStructX = defaultStructX___;
requiredStructX = requiredStructX___;
memset(defaultStructX___, 0 , sizeof(defaultStructX___));
memset(defaultStructX___, 0 , sizeof(requiredStructX___));
Not that this fits your use case, but it's a good enough technique that others might want to know about it.

Related

Dynamically allocating memory without using malloc

I have been writing a program where I have a 2d array that changes size if the user wants, as follows:
#include <stdlib.h>
#include <stdio.h>
int max_length = 1024;
int memory_length = 16;
int block_length = 64;
void process_input(int memory[memory_length][block_length], char* user_input) {
...
}
int main(void) {
printf("Not sure what to do? Enter 'help'\n");
while (0 == 0) {
int memory[memory_length][block_length];
char user_input[max_length];
printf(">> ");
fgets(user_input, max_length, stdin);
printf("\n");
process_input(memory, user_input);
if (user_input[0] == 'e' && user_input[1] == 'n' && user_input[2] == 'd') {
break;
}
printf("\n");
}
return 0;
}
NOTE: The process_input() function that I made allows the user to play around with the values inside the array 'memory' as well as change the value of memory_length or block_length, hence then changing the length of the array. After the user is done the cycle repeats with a fresh array.
I can use the 2d array perfectly fine, parsing it to any function. However one day I discover that there are functions such as malloc() that allow you to dynamically allocate memory through a pointer. This made me then question:
Should I re-write my whole very complicated program to use malloc and other 'memory functions', or is it okay to keep it this way?
Also as a side question that might be answered by answering the main question:
Every time I declare the 2d array, does the previous contents of the array get free, or do I keep filling up my memory like an amateur?
Finally if there is anything else that you may notice in the code or in my writing please let me know.
Thanks.
Should I re-write my whole very complicated program to use malloc and other 'memory functions', or is it okay to keep it this way?
Probably rewrite it indeed. int memory[memory_length][block_length]; in main() is a variable-length array (VLA). It is allocated with automatic storage and gets the size of those size variables at the point where its declaration is encountered, then it can't be resized from there.
For some reason a lot of beginners seem to think you can resize the VLA by changing the variables originally used to determine it's size, but no such magic relation between the VLA and those variables exists. How to declare variable-length arrays correctly?
The only kind of array in C that allows run-time resizing is one which was allocated dynamically. The only alternative to that is to allocate an array "large enough" and then keep track of how much of the array you actively are using - but it will sit there in memory (and that is likely no big deal).
However, it is not recommended to allocate huge arrays with automatic storage, since those usually end up on the stack and can cause stack overflows. Use either static storage duration or allocated storage (with malloc etc).
Every time I declare the 2d array, does the previous contents of the array get free, or do I keep filling up my memory like an amateur?
You can only declare it once. In case you do so inside a local scope, with automatic storage duration, it does indeed get cleared up every time you leave the scope which it was declared. But that also means that it can't be used outside that scope.
Finally if there is anything else that you may notice in the code or in my writing please let me know.
Yes, get rid of the global variables. There is no reason to use them in this example, avoid them like the plague. For example a function using an already allocated array might pass the sizes along, like in this example:
void process_input (size_t memory_length,
size_t block_length,
int memory[memory_length][block_length],
char* user_input)
In C, local variables, i.e. variables declared within a function, are allocated on the stack. They are only allocated once when the function is first called. The fact that you can declare variables within a while loop can lead to some confusion. The loop does not somehow allocate the memory again and again.
The memory allocated for all local variables is released when the function return.
The main reason that you might want declare a variable inside a loop (besides convenience) is to limit the scope of the variable. In your code above, you cannot access the "memory" variable outside of the while loop. You can easily check this for yourself. Your compiler should raise an error.
Whether the stack or the heap contains more memory depends on your computer architecture. In an embedded system you can often specify whether to allocate more or less memory to the heap or the stack. On a computer with virtual memory, such as a PC, the size of the heap and the stack are only limited by the size of your hard drive and the address space.
Allocating arrays on the heap is not as simple as it might seem. Single dimensional arrays work just as you might imagine, but things get more complicated with multidimensional arrays, so it is probably better to stick with either a locally or statically declared array in your case.

How use local array in function - ANSI C?

I want to add something to the end of the array passed to the function.
Which is better, declaring a new larger array or using alloc ()?
1.
void array_append(int *block, size_t size)
{
int new_block[size + 2];
memcpy(new_block, block, size);
(...append)
}
void array_append(int *block, size_t size)
{
int *new_block = calloc(1, sizeof(int) + 2);
memcpy(new_block, block, size);
(...append)
free(new_block);
}
I am not returning the newly created array anywhere.
I only use new_block inside functions.
Does not modify the original array in the function.
Declaring new_block as static is omitted.
I know how calloc() / malloc() works, I know that this operation has to be validated.
new_block is only meant to live in a function.
I just wonder which solution is better and why ...
regards
You should dynamically allocate an array instead of using a variable length array because in general in the last case the code can be unsafe due to a comparatively big size of the array that can lead to the stack overflow.
I want to add something to the end of the array
But you cannot really. Unless with realloc(). This is how your ...append trick can be done, whatever it means.
If you need a temporary array to work with and then copy into your array (but not at the end!), then all methods for allocation are allowed - it really depends on how often and with which sizes.
If it is called very often with limited sizes, it could be a static array.
There is no easy solution for growing arrays (or for memory management in general). At the extreme you allocate every element individually and link them together: a linked list.
--> avoid reaching the end of your arrays. Define a higher maximum or then implement a linked list.
In certain situations realloc() also makes sense (big changes in size, but not often). Problem is sometimes the whole array has to be memcopied to keep the larger array contiguous: "realloc", not "append". So it is "expensive".
I am not returning the newly created array anywhere.
That is part of the problem. You actually seem to be doing half of what realloc() does: allocate the new space, memcpy() the old contents...and then free the old and return the new array(-pointer) to the caller.
First version can not return the array pointer, because end of function is also end of local auto arrays, VLA or not.
If the append can be done to the existing array (which it can if the caller expects this and the memory of the array has room), you can merely append to the existing array.
Otherwise, you need a new array. In this case, the array must be returned to the caller. You can do this by returning a pointer to its first element or by having the caller pass a pointer to a pointer, and you modify the pointed-to pointer to point to the first element of the new array.
When you provide a new array, you must allocate memory for it with malloc or a similar routine. You should not use an array defined inside your function without static, as the memory for such an array is reserved only until execution of the function ends. When your function returns to the caller, that memory is released for other uses. (Generally, you also should not use an array declared with static, but for reasons involving good design, reducing bugs, and multiple serial or parallel calls to the function.)

Why do variables declared with the same name in different scopes get assigned the same memory addresses?

I know that declaring a char[] variable in a while loop is scoped, having seen this post: Redeclaring variables in C.
Going through a tutorial on creating a simple web server in C, I'm finding that I have to manually clear memory assigned to responseData in the example below, otherwise the contents of index.html are just continuously appended to the response and the response contains duplicated contents from index.html:
while (1)
{
int clientSocket = accept(serverSocket, NULL, NULL);
char httpResponse[8000] = "HTTP/1.1 200 OK\r\n\n";
FILE *htmlData = fopen("index.html", "r");
char line[100];
char responseData[8000];
while(fgets(line, 100, htmlData) != 0)
{
strcat(responseData, line);
}
strcat(httpResponse, responseData);
send(clientSocket, httpResponse, sizeof(httpResponse), 0);
close(clientSocket);
}
Correct by:
while (1)
{
...
char responseData[8000];
memset(responseData, 0, strlen(responseData));
...
}
Coming from JavaScript, this was surprising. Why would I want to declare a variable and have access to the memory contents of a variable declared in a different scope with the same name? Why wouldn't C just reset that memory behind the scenes?
Also... Why is it that variables of the same name declared in different scopes get assigned the same memory addresses?
According to this question: Variable declared interchangebly has the same pattern of memory address that ISN'T the case. However, I'm finding that this is occurring pretty reliably.
Not completely correct. You don't need to clear the whole responseData array - clearing its first byte is just enough:
responseData[0] = 0;
As Gabriel Pellegrino notes in the comment, a more idiomatic expression is
responseData[0] = '\0';
It explicitly defines a character via its code point of zero value, while the former uses an int constant zero. In both cases the right-side argument has type int which is implicitly converted (truncated) to char type for assignment. (Paragraph fixed thx to the pmg's comment.)
You could know that from the strcat documentation: the function appends its second argument string to the first one. If you need the very first chunk to get stored into the buffer, you want to append it to an empty string, so you need to ensure the string in the buffer is empty. That is, it consists of the terminating NUL character only. memset-ting the whole array is an overkill, hence a waste of time.
Additionally, using a strlen on the array is asking for troubles. You can't know what the actual contents of the memory block allocated for the array is. If it was not used yet or was overwritten with some other data since your last use, it may contain no NUL character. Then strlen will run out of the array causing Undefined Behavior. And even if it returns successfuly, it will give you the string's length bigger than the size of the array. As a result memset will run out of the array, possibly overwriting some vital data!
Use sizeof whenever you memset an array!
memset(responseData, 0, sizeof(responseData));
EDIT
In the above I tried to explain how to fix the issue with your code, but I didn't answer your questions. Here they are:
Why do variables (...) in different scopes get assigned the same memory addresses?
In regard of execution each iteration of the while(1) { ... } loop indeed creates a new scope. However, each scope terminates before the new one is created, so the compiler reserves appropriate block of memory on the stack and the loop re-uses it in every iteration. That also simplifies a compiled code: every iteration is executed by exactly the same code, which simply jumps at the end to the beginning. All instructions within the loop that access local variables use exactly the same addressing (relative to the stack) in each iteration. So, each variable in the next iteration has precisely the same location in memory as in all previous iterations.
I'm finding that I have to manually clear memory
Yes, automatic variables, allocated on the stack, are not initialized in C by default. We always need to explicitly assign an initial value before we use it – otherwise the value is undefined and may be incorrect (for example, a floating-point variable can appear not-a-number, a character array may appear not terminated, an enum variable may have a value out of the enum's definition, a pointer variable may not point at a valid, accessible location, etc.).
otherwise the contents (...) are just continuously appended
This one was answered above.
Coming from JavaScript, this was surprising
Yes, JavaScript apparently creates new variables at the new scope, hence each time you get a brand new array – and it is empty. In C you just get the same area of a previously allocated memory for an automatic variable, and it's your responsibility to initialize it.
Additionally, consider two consecutive loops:
void test()
{
int i;
for (i=0; i<5; i++) {
char buf1[10];
sprintf(buf1, "%d", i);
}
for (i=0; i<1; i++) {
char buf2[10];
printf("%s\n", buf2);
}
}
The first one prints a single-digit, character representation of five numbers into the character array, overwriting it each time - hence the last value of buf1[] (as a string) is "4".
What output do you expect from the second loop? Generally speaking, we can't know what buf2[] will contain, and printf-ing it causes UB. However we may suppose the same set of variables (namely a single 10-items character array) from both disjoint scopes will get allocated the same way in the same part of a stack. If this is the case, we'll get a digit 4 as an output from a (formally uninitialized) array.
This result depends on the compiler construction and should be considered a coincidence. Do not rely on it as this is UB!
Why wouldn't C just reset that memory behind the scenes?
Because it's not told to. The language was created to compile to effective, compact code. It does as little 'behind the scenes' as possible. Among others things it does not do is not initializing automatic variables unless it's told to. Which means you need to add an explicit initializer to a local variable declaration or add an initializing instruction (e.g. an assignment) before the first use. (This does not apply to global, module-scope variables; those are initialized to zeros by default.)
In higher-level languages some or all variables are initialized on creation, but not in C. That's its feature and we must live with it – or just not use this language.
With this line:
char responseData[8000];
You are saying to your compiler: Hey big C, give me a 8000 bytes chunk and name it responseData.
In runtime, if you don't specify, no one will ever clean or give you a "brand-new" chunk of memory. That means that the 8000 bytes chunk you get in every single execution can hold all the possible permutations of bits in this 8000 bytes. Something extraordinary that can happens, is that you're getting in every execution the same memory region and thus, the same bits in this 8000 bytes your big C gave to you in the first time. So, if you don't clean, you have the impression that you're using the same variable, but you're not! You're just using the same (never cleaned) memory region.
I'd add that it's part of the programmer's responsibilities to clean, if you need to, the memory you're allocating, in dynamic or static way.
Why would I want to declare a variable and have access to the memory contents of a variable declared in a different scope with the same name? Why wouldn't C just reset that memory behind the scenes?
Objects with auto storage duration (i.e., block-scope variables) are not automatically initialized - their initial contents are indeterminate. Remember that C is a product of the early 1970s, and errs on the side of runtime speed over convenience. The C philosophy is that the programmer is in the best position to know whether something should be initialized to a known value or not, and is smart enough to do it themselves if needed.
While you're logically creating and destroying a new instance of responseData on each loop iteration, it turns out the same memory location is being reused each time through. We like to think that space is allocated for each block-scope object as we enter the block and released as we leave it, but in practice that's (usually) not the case - space for all block-scope objects within a function is allocated on function entry, and released on function exit1.
Different objects in different scopes may map to the same memory behind the scenes. Consider something like
void bletch( void )
{
if ( some_condition )
{
int foo = some_function();
printf( "%d\n", foo );
}
else
{
int bar = some_other_function();
printf( "%d\n", bar );
}
It's impossible for both foo and bar to exist at the same time, so there's no reason to allocate separate space for both - the compiler will (usually) allocate space for one int object at function entry, and that space gets used for either foo or bar depending on which branch is taken.
So, what happens with responseData is that space for one 8000-character array is allocated on function entry, and that same space gets used for each iteration of the loop. That's why you need to clear it out on each iteration, either with a memset call or with an initializer like
char responseData[8000] = {0};
As M.M points out in a comment, this isn't true for variable-length arrays (and potentially other variably modified types) - space for those is set aside as needed, although where that space is taken from isn't specified by the language definition. For all other types, though, the usual practice is to allocate all necessary space on function entry.

Memory allocation of struct member variables

I am new to C. I have these two files set up in this way.
I do not fully understand how I am able to assign values in the Item array without dynamically allocating memory.
The line Collection c; places all fields on the stack, so is that why I can directly set array members?
//collection.c
typedef struct {
uint32 price;
uint32 itemId;
} Item;
typedef struct {
Item item[MAX_SIZE];
uint32 name;
} Collection;
void function(Collection * ptr)
{
int i;
uint32 id = 0;
for(i = 0; i < MAX_SIZE; i++)
{
ptr->item[i].price = 10;
ptr->item[i].itemId = id;
id++;
}
}
//collection_main.c
Collection c; //global struct variable
//calls function in collection.c
function(&c);
I do not fully understand how I am able to assign values in the Item array without dynamically allocating memory.
First, as you are new to C, be aware of a potential issue with passing C functions pointers (which is quite reasonable, BTW). Unless you can guarantee that your calling code will always pass a valid pointer you need to check that pointer value in the function as best you can. That will typically amount to checking for a non-null pointer like this :
if ( ptr == NULL )
return <whatever to signal an error> ;
In this case you did allocate memory, because you created a Collection variable and that contains allocated space for the required fields.
The line Collection c; places all fields on the stack,
If it's in a function it will (typically) allocate space on that function's stack frame, which you should logically view as a separate area that the calling code cannot access. Make no assumptions about the layout of the stack. A very typical bug is to try and return a pointer to an item declared inside a function, and even supposedly experienced programmers have been known to do it.
Another potential bug in passing a pointer to a function is trying to access beyond the limits of the space allocated and pointed to. This can do things like corrupt other variables or even crash code. Your own code is correctly using the declared constant size of the array, so no problem.
If you do this outside of a function (which is possible), you would be using space reserved by the OS for these type of variables. That may not be on the stack but elsewhere. The OS gets that information from the compiled code file.
so is that why I can directly set array members ?
C code (and the executable binary that's produced by the compiler) does not care or check whether the pointers you pass are valid or not. So it's possible to pass a bad pointer to a C function and cause chaos.
In this case you did allocate all the required valid memory when you declared the variable and you passed a pointer to that variable. So no problem.
Dynamic memory allocation
It is more usual to consider explicit allocation using the malloc() family of functions as dynamic allocation. Allocations for local and global variables may be dynamic in the sense that they can happen at runtime but the allocation and deallocation are not the responsibility of the programmer to explicitly control so you do not generally need to think about these as part of dynamic memory allocation.
A minor point to close :
uint32 name ;
I'd consider this a bad choice of field name. Using "name" implies a string, whereas you probably mean a string id from e.g. an array. So try something like :
uint32 nameid ;
instead.
You'd be surprised how many coding problems crop up in a production environment simply because of a poor choice of variable name. Make them informative if possible and practical.
This is just a good coding habit to get into, IMO.

Does C allocate memory automatically for me?

I have been writing C for only a scant few weeks and have not taken the time to worry myself too much about malloc(). Recently, though, a program of mine returned a string of happy faces instead of the true/false values I had expected to it.
If I create a struct like this:
typedef struct Cell {
struct Cell* subcells;
}
and then later initialize it like this
Cell makeCell(int dim) {
Cell newCell;
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim -1);
}
return newCell; //ha ha ha, this is here in my program don't worry!
}
Am I going to end up accessing happy faces stored in memory somewhere, or perhaps writing over previously existing cells, or what? My question is, how does C allocate memory when I haven't actually malloc()ed the appropriate amount of memory? What's the default?
Short answer: It isn't allocated for you.
Slightly longer answer: The subcells pointer is uninitialized and may point anywhere. This is a bug, and you should never allow it to happen.
Longer answer still: Automatic variables are allocated on the stack, global variables are allocated by the compiler and often occupy a special segment or may be in the heap. Global variables are initialized to zero by default. Automatic variables do not have a default value (they simply get the value found in memory) and the programmer is responsible for making sure they have good starting values (though many compilers will try to clue you in when you forget).
The newCell variable in you function is automatic, and is not initialized. You should fix that pronto. Either give newCell.subcells a meaningful value promptly, or point it at NULL until you allocate some space for it. That way you'll throw a segmentation violation if you try to dereference it before allocating some memory for it.
Worse still, you are returning a Cell by value, but assigning it to a Cell * when you try to fill the subcells array. Either return a pointer to a heap allocated object, or assign the value to a locally allocated object.
A usual idiom for this would have the form something like
Cell* makeCell(dim){
Cell *newCell = malloc(sizeof(Cell));
// error checking here
newCell->subcells = malloc(sizeof(Cell*)*dim); // what if dim=0?
// more error checking
for (int i=0; i<dim; ++i){
newCell->subCells[i] = makeCell(dim-1);
// what error checking do you need here?
// depends on your other error checking...
}
return newCell;
}
though I've left you a few problems to hammer out..
And note that you have to keep track of all the bits of memory that will eventually need to be deallocated...
There is no default value for your pointer. Your pointer will point to whatever it stores currently. As you haven't initialized it, the line
newCell.subcells[i] = ...
Effectively accesses some uncertain part of memory. Remember that subcells[i] is equivalent to
*(newCell.subcells + i)
If the left side contains some garbage, you will end up adding i to a garbage value and access the memory at that uncertain location. As you correctly said, you will have to initialize the pointer to point to some valid memory area:
newCell.subcells = malloc(bytecount)
After which line you can access that many bytes. With regards to other sources of memory, there are different kind of storage that all have their uses. What kind you get depends on what kind of object you have and which storage class you tell the compiler to use.
malloc returns a pointer to an object with no type. You can make a pointer point to that region of memory, and the type of the object will effectively become the type of the pointed to object type. The memory is not initialized to any value and access usually is slower. Objects so obtained are called allocated objects.
You can place objects globally. Their memory will be initialized to zero. For points, you will get NULL pointers, for floats you will get a proper zero too. You can rely on a proper initial value.
If you have local variables but use the static storage class specifier, then you will have the same initial value rule as for global objects. The memory usually is allocated the same way like global objects, but that's in no way a necessity.
If you have local variables without any storage class specifier or with auto, then your variable will be allocated on the stack (even though not defined so by C, this is what compilers do practically of course). You can take its address in which case the compiler will have to omit optimizations like putting it into registers of course.
Local variables used with the storage class specifier register, are marked as having a special storage. As a result, you cannot take its address anymore. In recent compilers, there is normally no need to use register anymore, because of their sophisticated optimizers. If you are really expert, then you may get some performance out of it if using it, though.
Objects have associated storage durations that can be used to show the different initialization rules (formally, they only define how long at least the objects live). Objects declared with auto and register have automatic storage duration and are not initialized. You have to explicitly initialize them if you want them to contain some value. If you do not, they will contain whatever the compiler left on the stack before they began lifetime. Objects that are allocated by malloc (or another function of that family, like calloc) have allocated storage duration. Their storage is not initialized either. An exception is when using calloc, in which case the memory is initialized to zero ("real" zero. i.e all bytes 0x00, without regard to any NULL pointer representation). Objects that are declared with static and global variables have static storage duration. Their storage is initialized to zero appropriate for their respective type. Note that an object must not have a type, but the only way to get a type-less object is using allocated storage. (An object in C is a "region of storage").
So what is what? Here is the fixed code. Because once you allocated a block of memory you can't get back anymore how many items you allocated, best is to always store that count somewhere. I've introduced a variale dim to the struct that gets the count stored.
Cell makeCell(int dim) {
/* automatic storage duration => need to init manually */
Cell newCell;
/* note that in case dim is zero, we can either get NULL or a
* unique non-null value back from malloc. This depends on the
* implementation. */
newCell.subcells = malloc(dim * sizeof(*newCell.subcells));
newCell.dim = dim;
/* the following can be used as a check for an out-of-memory
* situation:
* if(newCell.subcells == NULL && dim > 0) ... */
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim - 1);
}
return newCell;
}
Now, things look like this for dim=2:
Cell {
subcells => {
Cell {
subcells => {
Cell { subcells => {}, dim = 0 }
},
dim = 1
},
Cell {
subcells => {
Cell { subcells => {}, dim = 0 }
},
dim = 1
}
},
dim = 2
}
Note that in C, the return value of a function is not needed to be an object. No storage at all is required to exist. Consequently, you are not allowed to change it. For example, the following is not possible:
makeCells(0).dim++
You will need a "free function" that free's the allocated memory again. Because storage for allocated objects is not freed automatically. You have to call free to free that memory for every subcells pointer in your tree. It's left as an exercise for you to write that up :)
Anything not allocated on the heap (via malloc and similar calls) is allocated on the stack, instead. Because of that, anything created in a particular function without being malloc'd will be destroyed when the function ends. That includes objects returned; when the stack is unwound after a function call the returned object is copied to space set aside for it on the stack by the caller function.
Warning: If you want to return an object that has pointers to other objects in it, make sure that the objects pointed to are created on the heap, and better yet, create that object on the heap, too, unless it's not intended to survive the function in which it is created.
My question is, how does C allocate memory when I haven't actually malloc()ed the appropriate amount of memory? What's the default?
To not allocate memory. You have to explicity create it on the stack or dynamically.
In your example, subcells points to an undefined location, which is a bug. Your function should return a pointer to a Cell struct at some point.
Am I going to end up accessing happy faces stored in memory somewhere, or perhaps writing over previously existing cells, or what?
You are lucky that you got a happy face. On one of those unlucky days, it could've wiped your system clean ;)
My question is, how does C allocate memory when I haven't actually malloc()ed the appropriate amount of memory?
It doesn't. However, what happens is when you define you Cell newCell, the subCells pointer is initialized to garbage value. Which may be a 0 (in which case you'd get a crash) or some integer big enough to make it look like an actual memory address. The compiler, on such cases, would happily fetch whatever value is residing there and bring it back to you.
What's the default?
This is the behavior if you don't initialize your variables. And your makeCell function looks a little under-developed.
There are really three sections where things can be allocated - data, stack & heap.
In the case you mention, it would be allocated on the stack. The problem with allocating something on the stack is that it's only valid for the duration of the function. Once your function returns, that memory is reclaimed. So, if you return a pointer to something allocated on the stack, that pointer will be invalid. If you return the actual object though (not a pointer), a copy of the object will automatically be made for the calling function to use.
If you had declared it as a global variable (e.g. in a header file or outside of a function) it would be allocated in the data section of memory. The memory in this section is allocated automatically when your program starts and deallocated automatically when it finishes.
If you allocate something on the heap using malloc(), that memory is good for as long as you want to use it - until you call free() at which point it is released. This gives you the flexibility to allocate and deallocate memory as you need it (as opposed to using globals where everything is allocated up front and only released when your program terminates).
Local variables are "allocated" on the stack. The stack is a preallocated amount of memory to hold those local variables. The variables cease to be valid when the function exits and will be overwritten by whatever comes next.
In your case, the code is doing nothing since it doesn't return your result. Also, a pointer to an object on the stack will also cease to be valid when the scope exits, so I guess in your precise case (you seems to be doing a linked list), you will need to use malloc().
I'm going to pretend I'm the computer here, reading this code...
typedef struct Cell {
struct Cell* subcells;
}
This tells me:
We have a struct type called Cell
It contains a pointer called subcells
The pointer should be to something of type struct Cell
It doesn't tell me whether the pointer goes to one Cell or an array of Cell. When a new Cell is made, the value of that pointer is undefined until a value is assigned to it. It's Bad News to use pointers before defining them.
Cell makeCell(int dim) {
Cell newCell;
New Cell struct, with an undefined subcells pointer. All this does is reserve a little chunk of memory to be called newCell that is the size of a Cell struct. It doesn't change the values that were in that memory - they could be anything.
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim -1);
In order to get newCell.subcells[i], a calculation is made to offset from subcells by i, then that is dereferenced. Specifically, this means the value is pulled from that memory address. Take, for instance, i==0... Then we would be dereferencing the subcells pointer itself (no offset). Since subcells is undefined, it could be anything. Literally anything! So, this would ask for a value from somewhere completely random in memory. There's no guarantee of anything with the result. It may print something, it may crash. It definitely should not be done.
}
return newCell;
}
Any time you work with a pointer, it's important to make sure it's set to a value before you dereference it. Encourage your compiler to give you any warnings it can, many modern compilers can catch this sort of thing. You can also give pointers cutesy default values like 0xdeadbeef (yup! that's a number in hexadecimal, it's just also a word, so it looks funny) so that they stand out. (The %p option for printf is helpful for displaying pointers, as a crude form of debugging. Debugger programs also can show them quite well.)

Resources