4
typedef struct dict_pair { void *key; void *value; struct dict_pair *head; struct dict_pair *tail; } dict; dict* NewDictionary(void) { dict *dictionary = malloc(sizeof(dict_pair)); dictionary->head = null; dictionary->tail = null; } int main(void) { dict *dictionary = NewDictionary(); } 

I had initially planned to set the structs as null but the compiler does not allow it. How can I check if a struct is assigned or not?

Also, can I refer recursively declare the same struct inside the struct?

8
  • 8
    Use NULL instead of null. null isn't part of C. Commented Nov 18, 2009 at 21:05
  • write it as an answer, I wanna give you props for it. Commented Nov 18, 2009 at 21:06
  • Wait... so the problem is that "null" was not recognized? then yes, the answer is use "NULL" instead. Or 0, if NULL happens to not be defined in your dev environment, which is rare these days but still possible. Commented Nov 18, 2009 at 21:07
  • What problem are you trying to solve? It seems like your NewDictionary() function already NULLs out your head and tail pointers. Isn't checking those enough? Commented Nov 18, 2009 at 21:09
  • 1
    @nubela . just a suggestion. Do not accept the first answer you look at, give people the time to write a more coomprehensive answer. Just being rewarded for having been the fastest to answer is not going to improve the overall quality of SO answers. Commented Nov 18, 2009 at 21:33

5 Answers 5

8

C doesn't have null, it has NULL. So try this:

dict* NewDictionary(void) { return calloc(sizeof(dict)); } 

This fixes a few problems:

  1. You were leaving value and key uninitialized, so they could hold random garbage. Using calloc() will initialize everything to 0, which in pointer context is NULL. It won't even take that much more processing time.
  2. You weren't returning anything. This is undefined behavior. If you function ends without a return statement, it's only by sheer luck that anything will be returned.
  3. You were using dict_pair instead of struct dict_pair. In C++, struct names are in the regular type namespace, i.e. t x = { 0 }; is valid C++, but in C you'd need to say struct t x = { 0 };.
  4. You weren't checking the return value of malloc() (now calloc() but same rules apply). If there isn't enough memory, calloc() returns NULL. I'd hate to dereference a NULL pointer on accident. We don't have to check the return value here because I've done away with all the intermediate steps - calloc() is enough for us.

Note that calloc() is slightly less portable. Even though the standard does require that void *p = 0 sets the pointer to a null pointer, it doesn't require that the null pointer be "all bits set to zero", which is what calloc() technically does. If you don't want to use calloc() for this reason, here's a version that does the same thing with malloc():

dict* NewDictionary(void) { dict *dictionary = malloc(sizeof(dict)); if(dictionary) { dictionary->head = NULL; dictionary->tail = NULL; dictionary->value = NULL; dictionary->key = NULL; } return dictionary; } 

Or:

dict* NewDictionary(void) { dict *dictionary = malloc(sizeof(dict)); if(dictionary == NULL) return NULL; dictionary->head = NULL; dictionary->tail = NULL; dictionary->value = NULL; dictionary->key = NULL; return dictionary; } 

See how much nicer the calloc() version is?

As to your second question:

Also, can I refer recursively declare the same struct inside the struct?

No, you can't do this:

struct t { struct t x; } 

But you can do this (which is what you're doing, and what you want):

struct t { struct t *x; } 

You can have a pointer to a struct inside the struct itself, but you can't have the actual struct inside the struct itself. What you're doing is perfectly legal, because you're using pointers.

Sign up to request clarification or add additional context in comments.

3 Comments

The calloc version might be nicer, but it's technically not portable. It sets the underlying representation of the pointers to all-bits-zero, but that's not necessarily the same thing as NULL (even though the value 0 when converted to a pointer compares equal to the null pointer). (Not that I'm aware of any place where all-bits-zero pointers aren't equal to NULL, but something to bear in mind).
@caf - I was just made aware of that point. I've checked and, though I thought the wording was just "calloc sets stuff to zero" it does specify "all bits zero" which means that, yes, I do believe this super-edge-case is valid.
Yes, it's worded that way because it's not reasonable for implementation to know how you're going to treat the returned memory. The same applies to using calloc to allocate floating point values - all-bits-zero is not necessarily 0.0f
1

You may want to consider calloc rather than malloc.

calloc fills the memory it allocates with 0s, so you'll have your head and tail as NULL w/o explicit assignment.

4 Comments

calloc() set memory to all-bits-zero, such that if you looked at the RAM with a magnifying glass you would see "00000000…". This is fine for integers, but does not at all mean you'll get floating-point zeros or null pointers.
@sgm - That's tricky. I know void *p = 0; is identical to a null pointer by the standard, but I don't know about calloc() because it says "all-bits zero" rather than just "assigned to zero."
@Chris: casting 0 to void* might involve conversions, so it's indeed true that a pointer with bit representation 0 (as returned by calloc()) isn't guaranteed to be a null pointer
If you dig through some museums, you may find a machine where 0 address is not same as NULL, but I doubt even that. Same applies to floating point numbers, even more so, as there is a standard for those. For all practical purposes, calloc does what it's expected to do.
1

I'd use a statically allocated variable for initialization:

dict* NewDictionary(void) { static dict null_dict; // no initializer, so zero by default dict *dictionary = malloc(sizeof *dictionary); *dictionary = null_dict; return dictionary; } 

This guarantees that member are correctly zeroed, regardless whether they're pointers, floating point or integer types.

Comments

0

You can set them as NULL, but not as null. C is case-sensitive, and the NULL constant is all caps.

And to answer your second question, yes, struct definitions can be recursive in a sense. The internal reference has to be a pointer to the struct, instead of a straight definition of the struct. If the latter was allowed, you would end up with an infinitely recursing struct definition, which would be a bad thing. See Chris Lutz's answer for more details.

Comments

0

I'm using a trick which is working for me.

struct hello{ ....; ....; ....; }; struct hello *t; t=malloc(sizeof(struct hello)); free(t); t=NULL; 

Now u can easily check if t is initialized or not. And there is no memory leak at all.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.