Hello there,
I was recently maintaining some old C code for the Windows platform and it always amazes me how things
turn up strange sometimes either with C itself or with Windows. Windows coding is so strange sometimes
that i could probably write forever on that, but here i limit it to a C conundrum.
This involves the C function "memset" which has prototype:
memset(void*, int c, size_t n);
The name itself, "memset", implies that you can set memory with this function, but guess what,
you cant, unless of course it is zero or some other value that fits a narrow range of applications
not really worth mentioning here. In fact, the only thing this is really good for is if you
want to zero your memory out, and then you have to be aware of the fictitious prototype definition
because two out of the three arguments are not represented correctly.
First we have the void *, which normally would mean you could pass any type of pointer you wanted
to pass such as an int* and then the function would know that you are using a pointer to type int.
But no way. That void* means, at least here, that everything gets automatically cast down to type char
or perhaps unsigned char. This of course means it only operates on bytes (ANSI version).
Next we have "int c", which is also NOT true. It's not an integer, it's a char or unsigned char.
In any case, it's also cast down to type char or unsigned char, so you can only "set" chars (bytes)
not integers or anything else.
Now we come to "size_t n", which is somewhat correct, if you know about the limit to type char above.
If you dont, then this is not true either. The reason you might not know is because of the first
two arguments, which are so misleading that you would think you can set 'n' equal to the number of
elements you want to set to that so called integer, 'c'.
But yeah, assuming you know the pitfalls already then i guess we can say that the third arg is ok.
What is problematic about this function is that so much of it is misleading i cant help but think
that somebody, somewhere, screwed up when the language was first being invented. I dont think this
happens too much in languages, but it did this time.
Example that wont work:
int a[20];
memset((int*)a,0,20);
Looks like it should work because we cast 'a' into an int pointer, and there are 20 elements. However,
again since that last count must be the count of bytes, we should do something like this:
int a[20]
memset((int*)a,0,20*4);
However that (int*)a part is fictitious because 'a' is automatically cast to type char (or uchar).
So the final version ends up being:
memset(a,0,20*4);
although more appropriate is:
memset(a,0,sizeof(a));
and that works only because sizeof returns the entire number of bytes that 'a' takes up for storage
not the number of elements.
So one question that comes up is just what good is that "int c" then? It is only good if c=0 or if
you have a very special case where you fill every byte with the (char)((int)c) value and it happens
to work out to the right four bytes for every integer in the array. You still have to remember though
to use the right value for 'n' also or all the ints would get set right, and it is sort of a hack
anyway.
Very strange, huh? What do you think?
I was recently maintaining some old C code for the Windows platform and it always amazes me how things
turn up strange sometimes either with C itself or with Windows. Windows coding is so strange sometimes
that i could probably write forever on that, but here i limit it to a C conundrum.
This involves the C function "memset" which has prototype:
memset(void*, int c, size_t n);
The name itself, "memset", implies that you can set memory with this function, but guess what,
you cant, unless of course it is zero or some other value that fits a narrow range of applications
not really worth mentioning here. In fact, the only thing this is really good for is if you
want to zero your memory out, and then you have to be aware of the fictitious prototype definition
because two out of the three arguments are not represented correctly.
First we have the void *, which normally would mean you could pass any type of pointer you wanted
to pass such as an int* and then the function would know that you are using a pointer to type int.
But no way. That void* means, at least here, that everything gets automatically cast down to type char
or perhaps unsigned char. This of course means it only operates on bytes (ANSI version).
Next we have "int c", which is also NOT true. It's not an integer, it's a char or unsigned char.
In any case, it's also cast down to type char or unsigned char, so you can only "set" chars (bytes)
not integers or anything else.
Now we come to "size_t n", which is somewhat correct, if you know about the limit to type char above.
If you dont, then this is not true either. The reason you might not know is because of the first
two arguments, which are so misleading that you would think you can set 'n' equal to the number of
elements you want to set to that so called integer, 'c'.
But yeah, assuming you know the pitfalls already then i guess we can say that the third arg is ok.
What is problematic about this function is that so much of it is misleading i cant help but think
that somebody, somewhere, screwed up when the language was first being invented. I dont think this
happens too much in languages, but it did this time.
Example that wont work:
int a[20];
memset((int*)a,0,20);
Looks like it should work because we cast 'a' into an int pointer, and there are 20 elements. However,
again since that last count must be the count of bytes, we should do something like this:
int a[20]
memset((int*)a,0,20*4);
However that (int*)a part is fictitious because 'a' is automatically cast to type char (or uchar).
So the final version ends up being:
memset(a,0,20*4);
although more appropriate is:
memset(a,0,sizeof(a));
and that works only because sizeof returns the entire number of bytes that 'a' takes up for storage
not the number of elements.
So one question that comes up is just what good is that "int c" then? It is only good if c=0 or if
you have a very special case where you fill every byte with the (char)((int)c) value and it happens
to work out to the right four bytes for every integer in the array. You still have to remember though
to use the right value for 'n' also or all the ints would get set right, and it is sort of a hack
anyway.
Very strange, huh? What do you think?