1

I'm new to C and this is my first question: for the this structure:

typedef struct Branch
{
    Tree * thisTree;
    struct Branch  * nodes[];
} Branch;

it seems to work fine if I do the following:

Branch branch1;
branch1->nodes[0] = NULL;

even if I do not allocate memory for the pointer nr 0 in the array this way:

branch->node[0] = (Branch *) malloc(sizeof(Branch *));

if i check with this code:

if ( branch1->nodes[0] == NULL)
    printf("is NULL");

it prints to the output: is NULL

So my question is: has there been allocated memory for the pointer?

branch1->nodes[0]

because I have a lot of structures and if I initialise each branch with a fixed number of pointers I get a lot of allocated data (if I check with the sizeof function). Is this way: setting to NULL (above) a wrong way of thinking ?

My problem is that the allocation of memory for a pointer is 4 bytes. So not having a declared number of pointers in the array, when does it allocate memory for it ? Sorry I tried to keep the question simple but I need to reach a string through the structure pointer in the next branch

this means that the struct I use is

typedef struct Branch
{
    Tree * thisTree;
    char *string;
    struct Branch  * nodes[];
} Branch;

So if I do not

branch->node[0] = (Branch *) malloc(sizeof(Branch *));

and than

branch->node[0]->String = strdup("text");

I ge a compiler error.

  • Do not cast the result of `malloc` & friends in C. – too honest for this site Sep 04 '15 at 11:56
  • You did. C is not Java or Python, etc. A pointer is not a reference, but a first-class object. Use valgrind. A simple comparison might even work if you did not, you cannot rely on _undefined behaviour_ **not** to yield the correct result. – too honest for this site Sep 04 '15 at 11:59
  • @PeterSchneider: So you hate your compiler. – too honest for this site Sep 04 '15 at 12:00
  • Doesn't he need casting to ensure that array notation works properly should he use it? – user3079666 Sep 04 '15 at 12:01
  • So a reference in Java would not "allocate" (i.e. take up) memory? – Peter - Reinstate Monica Sep 04 '15 at 12:01
  • @PeterSchneider, a reference would only allocate memory if you used the `new` keyword, otherwise it would just point to what you assign to it - and thus also modify it etc, which is cause for some attention – user3079666 Sep 04 '15 at 12:03
  • 1
    @user3079666: Read the [standard](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p1). Casting inhibits type checking by the compiler. If you have a different return type or argument type than expected, the compiler cannot warn you. So use casts **only** if required and you **really** know what you are doing. – too honest for this site Sep 04 '15 at 12:06
  • @PeterSchneider: The space for the reference is allocated, but is you set this to an object like `None` (Python, sorry, I do not know the Java equivalent), the space for the object has to be allocated (`None` as a unique object has been allocated before already, but the idea is the same). A pointer is an object by itself, with the address being its value. A reference has no value of its own. – too honest for this site Sep 04 '15 at 12:08
  • 3
    @BlindWhiteLabMouse You need to be very careful about what you're doing here. Your `struct Branch * nodes[];` is very special, and is called a [flexible array member](https://en.wikipedia.org/wiki/Flexible_array_member). You have not allocated any memory for this, so doing `branch1->nodes[0]` is undefined behavior. – nos Sep 04 '15 at 12:10
  • @Olaf, I know and love how in C you can pretty much do what you want, but from my own university to the open courseware of the greatest institutions professors suggest that we cast, *unless* we really know what we are doing and are going to intentionally handle it as something else.. But I do accept the view that it is perfectly unnecessary as stated here, regardless of what's safer for novices: http://stackoverflow.com/q/605845/3079666 – user3079666 Sep 04 '15 at 12:11
  • 1
    @Olaf C++ addressed some deficiencies in C, among them the all-too-permissive type system. Certain implicit conversions deemed dangerous were outlawed, among them the one from void* to other pointers. I find the reasoning valid. I generally try to make my C programs C++ programs as well. I am aware of the counter arguments to that particular cast but find them either invalid or think they don't outweigh the benefits. I love my compiler. It warns me if I use a function (like malloc) without declaring it first (any sane language would not allow such danger in the first place, another lesson). – Peter - Reinstate Monica Sep 04 '15 at 12:11
  • @user3079666: That is absolute nonsense. A cast inhibits type-checking. So you are telling me they told you to hinder the compiler to help detect type errors? If that is true, I instantly would change the university. That way you are effectively using C as a more powerful Assembler. – too honest for this site Sep 04 '15 at 12:15
  • @Olaf But C *is* a more powerful assembler! (not more, and not less). Note that's not a verdict about quality or suitability; it's just a factual statement. – Peter - Reinstate Monica Sep 04 '15 at 12:15
  • @PeterSchneider: C is not C++! Identical syntax does not imply identical semantics. If you still use `malloc` in C++, you are doing something wrong and could well stick with C. C++ is OOP, so you should use it accordingly. But I well know people trying to program in Python as they would in C, too. – too honest for this site Sep 04 '15 at 12:17
  • @Olaf The cast does not inhibt type checking any more than the permissive language does. But it *adds* explicit user intent. – Peter - Reinstate Monica Sep 04 '15 at 12:17
  • @Olaf Can you give an example where the semantics of a C program are significantly different when compiled as C++? C++ was intentionally designed to translate C programs with minimal syntactical problems and semantical surprises. – Peter - Reinstate Monica Sep 04 '15 at 12:20
  • @PeterSchneider: A cast tells the compiler to effectively "shut up" and accept whatever you tell it. That is exactly what the cast does. – too honest for this site Sep 04 '15 at 12:21
  • @Olaf You can't effectively shut up a mute compiler, or rather it's a nop. – Peter - Reinstate Monica Sep 04 '15 at 12:22
  • @PeterSchneider: This is not the place to give a tutorial about C. But: type of logical operators, semantics of `const` keyword, `enum`s for a start. You actually should know this. What was the last C standard you read? – too honest for this site Sep 04 '15 at 12:22
  • @Olaf, a cast does enforce what you want, but void is just a heap of bits to the compiler and can be interpreted as thing it shouldn't, that's why they suggest casting. Also I linked to the appropriate thread for all this, and we are getting way off topic (http://stackoverflow.com/q/605845/3079666) – user3079666 Sep 04 '15 at 12:23
  • @user3079666: A cast tells the compiler you are aware what you are doing and to accept it. C does not have different casts like `reinterpret`, `static`, etc. Guess why these were added to C++ (not just because of OOP-issues). – too honest for this site Sep 04 '15 at 12:25
  • @PeterSchneider casting the return value of malloc() can have very surprising results if you forgot to include stdlib.h in C. Without the cast you're warned about the surprises to come. – nos Sep 04 '15 at 12:27
  • @Olaf, they were added partly because classes in C++ have virtual function pointers and other stuff that are placed differently and should be properly interpreted, but when you allocate memory, you are supposed to know what you're using it for. – user3079666 Sep 04 '15 at 12:27
  • @user3079666: This is C-only. Using `malloc` in C++ is s good indicator of poor design. As much as `s = "Hello"; for i in range(len(s)): print(s[i])` in Python. – too honest for this site Sep 04 '15 at 12:28
  • @Olaf, I disagree, it takes more reading to use `malloc` in C++ but it is just a low level approach to the same problem, after all I can assure you that not too deep behind `new` you will find `malloc` in the source code. C++ was built on C, you can't avoid memory allocation, you just choose whether you can handle it on your own or you need something else for safety. Another place where it is needed is graphics programming where you even use `memcpy` and other such functions, done that, loved it! – user3079666 Sep 04 '15 at 12:33
  • @Olaf, we're off topic, I already cited a long, protected thread where this is debated extensively so I suggest we stop before we get flagged. – user3079666 Sep 04 '15 at 12:37
  • "C++ was build ..." The first C++ implementations used a preprocessor generating C code. But with the first standard, C++ diverged from C. C99 was a try to become closer to C++ (`_Bool`, ...), but they did/could not cut old connections that consequent. Anyway, You both should read the history (if you do not remember yourself at that time). I'm out here. – too honest for this site Sep 04 '15 at 12:38
  • @nos As I said, I'm also warned if I forget to include stdlib.h or provide a declaration some other way, so that is not an argument any more. – Peter - Reinstate Monica Sep 04 '15 at 12:42

2 Answers2

0

No. The null pointer does not allocate any memory to store it on the heap because there is nothing to allocate (hence the "null").

Your nodes array does allocate memory to store null pointers itself, but just as much memory it would take to store null pointers array of integers, floats, structs, you name it.

Mirza
  • 213
  • 2
  • 8
  • My problem is that the allocation of memory for a pointer is 4 bytes. So not having a declared number of pointers in the array, when does it allocate memory for it ? – BlindWhiteLabMouse Sep 04 '15 at 11:58
  • Yes, it does have to allocate memory for _pointer itself_. Meaning an array of 10 integers, 10 structs or 10 doubles would allocate 40 bytes (4bytes * 10) or 80 bytes (8bytes * 10), running on 32-bit or 64-bit operating system respectively. Setting those pointers to null is **not** going to allocate neither more nor less bytes. – Mirza Sep 04 '15 at 12:02
  • That is the question, How many ? because in the struct I did not specify the number of pointers in the array – BlindWhiteLabMouse Sep 04 '15 at 12:07
  • 2
    @BlindWhiteLabMouse You have (unknowingly) used a feature of C called "flexible array member" when you didn't specify the number of pointers. You need to allocate memory for your `branch->nodes` elements, and you cannot do that on the stack. If you want to allocate e.g. room for 10 node pointers, you need to do `Branch *branch1 = malloc(sizeof(Branch) + 10 * sizeof(Branch*))`. Only then can you access branch1->nodes[0]. And after that, you can allocate memory for `branch1->nodes[0]`, when allocating that memory, you need to account for how many nodes you want in the `branch1->nodes[0]`. – nos Sep 04 '15 at 12:14
  • @nos ok, thank you. My problem is that at the moment I create the structure I do not know how many pointers I will have. So I understand that every time I add something I need to recreate the struct – BlindWhiteLabMouse Sep 04 '15 at 12:19
  • 1
    @BlindWhiteLabMouse Yes. You might be better off using just a normal pointer to your array of pointers. i.e. `struct Branch **nodes;` instead of `struct Branch * nodes[];` , that makes it easier to just reallocate `branch->nodes` and not `branch` itself as well. Or if you don't need direct indexing, make `branch->nodes` a linked list. – nos Sep 04 '15 at 12:23
  • @BlindWhiteLabMouse, alternatively, use a linked list instead of an array. – John Bollinger Sep 04 '15 at 18:00
0

Essentially you are allocating memory to the pointer, and placing the value NULL in there, which in C is normally 0. But the value has to be placed in allocated memory, in this case allocated to the pointer, so yes, there is still allocated memory to the pointer, you just set it's value to zero.

You would however have nullified the pointer, losing access to that memory, if you had written branch->node = NULL;, but that is not the right way to remove it, as the memory is still allocated but just unreachable, instead you should use free(branch->node); to un-allocate the memory, in case you want to do that.

Concerning the number of allocated positions in memory, that would be defined by the times you multiply the sizeof() function:

int number = 2; //assume you want two elements
branch->node[0] = (Branch *) malloc(number*sizeof(Branch *));
user3079666
  • 1,159
  • 10
  • 23