tags:

views:

120

answers:

6

If I allocate a C array like this:

int array[ 5 ];

Then, set only one object:

array[ 0 ] = 7;

How can I check whether all the other keys ( array[1], array[2], …) are storing a value? (In this case, of course, they aren't.)

Is there a function like PHP's isset()?

if ( isset(array[ 1 ]) ) ...
+5  A: 

You can't

There values are all undefined (thus random).

You could explicitly zero out all values to start with so you at least have a good starting point. But using magic numbers to detect if an object has been initialized is considered bad practice (but initializing variables is considered good practice).

int array[ 5 ] = {};

But if you want to explicitly check if they have been explicitly set (without using magic numbers) since creation you need to store that information in another structure.

int array[ 5 ]   = {};  // Init all to 0
int isSet[ 5 ]   = {};  // Init all to 0 (false)

int getVal(int index)          {return array[index];}
int isSet(int index)           {return isSet[index];}
void setVal(int index,int val) {array[index] = val; isSet[index] = 1; }
Martin York
that depends. c might initialize values (i think it does it for static and global vars)
knittl
@knittl: From the context of the question it is imposable to tell, so I erred on the side of caution. And it does no harm to explicitly initialize using the initializer list.
Martin York
There's no `std::map` in C.
KennyTM
@Martin: It **does** do harm. In virtually all real implementations, any initialization (even all zero) of statics/globals moves the object from the bss section to the data section, increasing the size of your program. And for automatic variables, unnecessary initialization wastes time every time the function is called.
R..
@R..: I will give you that: **"In performance critical systems it will do harm"**.
Martin York
is {} standard? I thought it should be {0}?
Nyan
@Nyan: If the no of elements within {} does not equal the size of the object it is initializing it is 0 filled.
Martin York
But i compile with gcc -std=c99 -pedantic-errors and gcc reports it's non-standard.
Nyan
Opps: I stand corrected. What about c89 and C++?
Martin York
`{}` is valid in C++ but not C; many C compilers accept it as an extension. `{0}` is the universal zero-initializer for C and works for variables of any type.
R..
+5  A: 

There isn't things like this in C. A static array's content is always "set". You could, however, fill in some special value to pretend it is uninitialized, e.g.

// make sure this value isn't really used.
#define UNINITIALIZED 0xcdcdcdcd

int array[5] = {UNINITIALIZED, UNINITIALIZED, UNINITIALIZED, UNINITIALIZED, UNINITIALIZED};

array[0] = 7;

if (array[1] != UNINITIALIZED) {
   ...
KennyTM
I guess this is the only option possible in C
rano
Thanks, don't know why this didn't occur to me before!
pop850
+2  A: 

In C, all the elements will have values (garbage) at the time of allocation. So you cannot really have a function like what you are asking for.

However, you can by default fill it up with some standard values like 0 or INT_MIN using memset() and then write an isset() code.

BiGYaN
`memset` cannot fill with `INT_MIN`. It only fills bytes.
R..
The garbage is only true of automatic variable and heap allocation. global and static arrays will be initialized to 0 if not otherwise specified.
dmckee
@R.., While that is true, on most modern platforms `INT_MIN` is `~0`, and would fill all the elements of an `int[]` with `-1`.
strager
@R I meant INT_MIN as ~0 as @stager has pointed out.
BiGYaN
`INT_MIN` is only `~0` on a sign/magnitude representation of signed integers, which is the rarest type and might not exist at all in practice. On most systems (twos complement), `~0==-1`.
R..
+1  A: 

One solution perhaps is to use a separate array of flags. When you assign one of the elements, set the flag in the boolean array.

You can also use pointers. You can use null pointers to represent data which has not been assigned yet. I made an example below:

int * p_array[3] = {NULL,NULL,NULL};
        p_array[0] = malloc(sizeof(int));
        *p_array[0] = (int)0;
        p_array[2] = malloc(sizeof(int));
        *p_array[2] = (int)4;
        for (int x = 0; x < 3; x++) {
            if (p_array[x] != NULL) {
                printf("Element at %i is assigned and the value is %i\n",x,*p_array[x]);
            }else{
                printf("Element at %i is not assigned.\n",x);
            }
        }

You could make a function which allocates the memory and sets the data and another function which works like the isset function in PHP by testing for NULL for you.

I hope that helps you.

Edit: Make sure the memory is deallocated once you have finished. Another function could be used to deallocate certain elements or the entire array.

I've used NULL pointers before to signify data has not been created yet or needs to be recreated.

Matthew Mitchell
+2  A: 

I don't know php, but one of two things is going on here

  • the php array is actually a hash-map (awk does that)
  • the php array is being filled with nullable types

in either case there is a meaningful concept of "not set" for the values of the array. On the other hand a c array of built in type has some value in every cell at all times. If the array is uninitialized and is automatic or was allocated on the heap those values may be random, but they exist.

To get the php behavior:

  • Implement (or find a library wit) and use a hashmap instead on an array.
  • Make it an array of structures which include an isNull field.
  • Initialize the array to some sentinal value in all cells.
dmckee
+1 for showing difference between PHP and C
alex
+1  A: 

An approach I like is to make 2 arrays, one a bit-array flagging which indices of the array are set, and the other containing the actual values. Even in cases where you don't need to know whether an item in the array is "set" or not, it can be a useful optimization. Zeroing a 1-bit-per-element bit array is a lot faster than initializing an 8-byte-per-element array of size_t, especially if the array will remain sparse (mostly unfilled) for its entire lifetime.

One practical example where I used this trick is in a substring search function, using a Boyer-Moore-style bad-character skip table. The table requires 256 entries of type size_t, but only the ones corresponding to characters which actually appear in the needle string need to be filled. A 1kb (or 2kb on 64-bit) memset would dominate cpu usage in the case of very short searches, leading other implementations to throw around heuristics for whether or not to use the table. But instead, I let the skip table go uninitialized, and used a 256-bit bit array (only 32 bytes to feed to memset) to flag which entries are in use.

R..