Reputation: 5243
I have such two struct
struct table_element
{
struct table_val * table_val_arr;
int count_arr;
};
struct hash_table
{
struct table_element table_element_arr[MAX_NUMBER];
};
and here my test method
void test(struct hash_table * table)
{
int count;
struct table_element * tab_element;
for(count = 0; count < MAX_NUMBER; count++)
{
tab_element = &table->table_element_arr[count];
if(tab_element->table_val_arr == NULL)
{
printf("\nNULLLL!!!!!\n");
}
else
{
printf("\nOK!!!!!\n");
}
}
}
and here how I use it
int main(int argc, char **argv)
{
struct hash_table m_hash_table;
test(&m_hash_table);
...
I expect that all value would be NULL
, but sometimes I get OK
sometimes NULL
...
What am I doing wrong?
How to init it with NULL?
Upvotes: 2
Views: 2324
Reputation: 1105
A colleague (not on SO) has suggested this answer: Partially initializing a C struct
Which says (in essence) if you initialise the first element of your structure, the compiler will automatically initialise everything else to zero or NULL (as appropriate) for you.
Copying from that...
10 If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static storage duration is not initialized explicitly, then:
—if it has pointer type, it is initialized to a null pointer;
—if it has arithmetic type, it is initialized to (positive or unsigned) zero;
—if it is an aggregate, every member is initialized (recursively) according to these rules;
—if it is a union, the first named member is initialized (recursively) according to these rules.
...
21 If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.
Upvotes: 1
Reputation: 1105
If you don't explicitly initialise a variable in C, it'll have an undefined value. eg.
int fish; // could be zero, -100, 3805, ...anything
int chips = 5; // will definitely be 5.
The same is true of pointers. They could point anywhere. And finally, the same is true of a structure's members.
There are two common approaches to this 'problem' depending on your needs.
memset
the whole thing to zero:struct hash_table m_hash_table;
memset( &m_hash_table, 0, sizeof(m_hash_table) );
Result: all the variables will be zero, all the pointers will be NULL1.
struct hash_table m_hash_table;
for (int i = 0; i < MAX_NUMBER; i++)
{
m_hash_table.table_element_arr[i].table_val_arr = NULL;
m_hash_table.table_element_arr[i].count_arr = 0;
}
A third option is to provide initialisation when you declare the struct, but it's logically equivalent to option 2.
struct hash_table m_hash_table = { { NULL, 0 }, { NULL, 0 }, ... /*etc*/ };
1 As per the comments, it is true that there exist some architectures where a bit pattern of all zeros is not equivalent to NULL, and hence the memset( ..., 0, ...)
approach is not strictly valid. However, for all practical purposes, on any modern platform, it's a perfectly valid, idiomatic solution.
(IMHO anyone using an architecture where this isn't true isn't going to be looking for advice on SO about how to initialise their structures!)
Upvotes: 3
Reputation: 1922
The struct hash_table m_hash_table;
is automatic storage, (vs say, static
, in which case it would be automatically initialised.) This means the contents of the variable are indeterminate. One could initialise it several ways, see initialisation, (or the other answers.) However, I think that this is important to know that memset
is not a proper way to initialise a null pointer, (the C FAQ has an entire section on null pointers.) Like Pascal's nil
or Java's null
, 0
in pointer context has a special meaning in C
, the null pointer. It commonly is all-bits-zero, leading to the mistaken impression that 0
is actually all-bits-zero, but this is not always the case. The general idiomatic way is to have a constructor in which you set any null pointers with explicit,
te->table_val_arr = 0; /* or NULL. */
te->count_arr = 0;
Edit: three initialisations are shown:
#include <stddef.h>
#include <assert.h>
/* `struct table_val` is undefined in this limited context. */
struct table_element {
int * table_val_arr;
int count_arr;
};
/** `te` is a value that gets initialised to be empty. */
static void table_element(struct table_element *const te) {
assert(te);
te->table_val_arr = 0; /* Or `NULL`, depending on your style. */
te->count_arr = 0;
}
struct hash_table {
struct table_element table_element_arr[100];
};
static size_t hash_table_size =
sizeof ((struct hash_table *)0)->table_element_arr
/ sizeof *((struct hash_table *)0)->table_element_arr;
/** `ht` is a value that gets initialised to be empty. */
static void hash_table(struct hash_table *const ht) {
size_t i;
assert(ht);
for(i = 0; i < hash_table_size; i++)
table_element(ht->table_element_arr + i);
}
/* This is automatically initialised to all-elements-zero, (which is not
necessary all-bits-zero.) */
static struct hash_table g_hash_table;
int main(void) {
struct hash_table m_hash_table = {{{0,0}}}; /* Initialiser. */
struct hash_table table; /* Garbage. */
hash_table(&table); /* Now fixed. */
return 0;
}
The dynamic way of using constructor functions is scalable to large objects and objects that one doesn't want to necessarily initialise with zero; C++
expands this greatly to RAII. The initialisation in the declaration is limited to constant expressions, and thus is probably the most efficient. The static
option changes the storage class of the object and is probably unsuitable except for objects that one wanted to declare static
anyway.
Upvotes: 3
Reputation: 532
You declared m_hash_table
as an automatic variable. Such variables are usually located on the stack. The stack space may be filled with random content.
You have three options.
static struct hash_table m_hash_table;
memset(&m_hash_table, 0, sizeof(m_hash_table));
struct hash_table m_hash_table = {};
UPDATE#1
According to this http://c-faq.com/null/machexamp.html information options #1 and #2 do not work correctly on some hardware. The option #3 gives the desired result.
UPDATE#2
The discussion below reveals a new truth. Option #1 is the best.
Upvotes: 3
Reputation: 223719
Non-static variables defined inside of a function have indeterminate values if not explicitly initialized, meaning you can't rely on anything they may contain.
You can fix this by giving an initializer for the variable:
struct hash_table m_hash_table = {{NULL, 0},{NULL, 0},/*repeat MAX_NUMBER times*/};
Or by using memset
:
memset(&m_hash_table, 0, sizeof(m_hash_table));
Upvotes: 4