tags:

views:

1338

answers:

6

In c (or maybe c++) , what's the difference between

char myarr[16]={0x00};

and

char myarr[16];
memset(myarr, '\0', sizeof(myarr));

??

edit: I ask this because in vc++ 2005 the result is the same..
edit more : and

char myarr[16]={0x00,}; 
?
maybe can get more comprehensive answer and not ambigous as some answers below refer to this kind of code,ie. put comma just before closing curly braces. Also the result is the same in vc++ 2005.

+14  A: 
Johannes Schaub - litb
Please don't use code blocks for quotations (because its uses a monospaced font and it may result in an unnecessary horizontal scrollbar; annoying :/).
cic
cic, i used <pre> because otherwise it removes the newlines and puts all in one line. i wasn't sure how to fix that.
Johannes Schaub - litb
ok, fixed it. going to community mode automatically tho. but i think that was worth it, since it looks way nicer now, without ugly horizontal scrollbar :)
Johannes Schaub - litb
+17  A: 

ISO/IEC 9899:TC3 6.7.8, paragraph 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.

Arrays with static storage duration are initialized to 0, so the C99 spec guarantees the not explicitly initialized array elements to be set to 0 as well.


In my first edit to this post, I spouted some nonsense about using compound literals to assign to an array after initialization. That does not work. If you really want to use compound literals to set an array's values, you have to do something like this:

#define count(ARRAY) (sizeof(ARRAY)/sizeof(*ARRAY))

int foo[16];
memcpy(foo, ((int [count(foo)]){ 1, 2, 3 }), sizeof(foo));

With some macro magic and the non-standard __typeof__ operator, this can be considerably shortened:

#define set_array(ARRAY, ...) \
    memcpy(ARRAY, ((__typeof__(ARRAY)){ __VA_ARGS__ }), sizeof(ARRAY))

int foo[16];
set_array(foo, 1, 2, 3);
Christoph
@litb: removed the stuff about compound literals for now - I'll repost something when I'm sure what's going on; I got confused because array arguments are passed as pointers, so my test code compiled fine...
Christoph
I'm a little confused because I don't see litb's comment or where SoapBox talked about compound literals but what you had in your answer was definitely wrong, you can't assign directly to raw arrays.
Robert Gamble
ah, ok. well i already removed my comments because they had no contexts anymore :) well good luck, i would enjoy having a nicer syntax for setting the array :)
Johannes Schaub - litb
for the records - i recommended char *c = (char[16]){0}; and char (*c)[16]; c = as alternatives. the first one would fail completely, because the size is lost. the second one would require *c to access the array - also ugly.
Johannes Schaub - litb
+2  A: 

Perhaps char myarr[16]={0x00}; isn't a good example to begin with, since both the explicit and implicit member initializations use zeros, making it harder to explain what's happening in that situation. I thought that a real-life example, with non-zero values could be more illustrative:

/**
 * Map of characters allowed in a URL
 *
 * !, \, (, ), *, -, ., 0-9, A-Z, _, a-z, ~
 *
 * Allowed characters are set to non-zero (themselves, for easier tracking)
 */
static const char ALLOWED_IN_URL[256] = {
/*          0      1      2      3      4      5      6      7      8      9*/
/*   0 */   0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
/*  10 */   0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
/*  20 */   0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
/*  30 */   0,     0,     0,    '!',    0,     0,     0,     0,     0,   '\'',
/*  40 */  '(',   ')',   '*',    0,     0,    '-',   '.',    0,    '0',   '1',
/*  50 */  '2',   '3',   '4',   '5',   '6',   '7',   '8',   '9',    0,     0,
/*  60 */   0,     0,     0,     0,     0,    'A',   'B',   'C',   'D',   'E',
/*  70 */  'F',   'G',   'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
/*  80 */  'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',   'X',   'Y',
/*  90 */  'Z',    0,     0,     0,     0,    '_',    0,    'a',   'b',   'c',
/* 100 */  'd',   'e',   'f',   'g' ,  'h',   'i',   'j',   'k',   'l',   'm',
/* 110 */  'n',   'o',   'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
/* 120 */  'x',   'y',   'z',    0,     0,     0,    '~',
};

This is a lookup table that can be used when URL-encoding a string. Only the characters that are allowed in a URL are set to a non-zero value. A zero means that the character is not allowed and needs to be URL-encoded (%xx). Notice that the table abruptly ends with a comma after the tilde character. None of the characters following the tilde are allowed and so should be set to zero. But instead of writing many more zeros to fill the table up to 256 entries, we let the compiler implicitly initialize the rest of the entries to zero.

Ates Goral
That seems like a very brittle way to code the ALLOWED_IN_URL array. Why not use C99 designated initializers? const char ALLOWED_IN_URL[256] = { ['A'] = 1, ['B'] = 1, /* ... */ }, or if you are using gcc, { ['A'...'Z'] = 1, ['a'...'z'] = 1, /* ... */}
Hudson
@Hudson: Oh my! I absolutely didn't know about that syntax. It's incredibly convenient for a look-up table like this. Thanks for the tip.
Ates Goral
A: 

Practically they're the same. The first form is guaranteed to init the whole type to 0x00 (even padding space between structure elements for example), and this is defined since C90. Unfortunately gcc gives a warning for the first form with the -Wmissing-field-initializers option. More details here:

http://www.pixelbeat.org/programming/gcc/auto_init.html

pixelbeat
+1  A: 

Given the hard-to-dispute fact that = { 0 } is infinitely more readable than memset(..., ..., ... sizeof ...), then the following would discourage explicitly using memset:

In Visual Studio 2005, compiling for Windows Mobile, full optimized release build:

; DWORD a[10] = { 0 };

mov         r3, #0
mov         r2, #0x24
mov         r1, #0
add         r0, sp, #4
str         r3, [sp]
bl          memset
add         r4, sp, #0
mov         r5, #0xA

; DWORD b[10];
; memset(b, 0, sizeof(b));

mov         r2, #0x28
mov         r1, #0
add         r0, sp, #0x28
bl          memset
add         r4, sp, #0x28
mov         r5, #0xA

Pretty much the same.

Johann Gerell
-1 you do understand that in ANSI C (atleast) the array[] = {0}; construct only works when you define an array and when you try to do it later on you get a compile error!
hhafez
... so you still need to use a memset when trying to zero out an array other than at time of decleration. Not much you can do here to save readibility
hhafez
You do understand that this SO question deals with initialization, right? So my claim that "[] = { 0 }" is more readable than memset is valid, or do you have a problem with that?
Johann Gerell
A: 

Defining initial values in the variable declaration happens at a different place than using memset.

For the former case the zeros are defined in some form in the binary as zero init memory (or non-zero depending on what you initialize to), and you hope that the loader honors that, has ABSOLUTELY nothing to do with C language standards. The latter, using memset depends on the C library which you would also works. I have more faith in the library.

I do a lot of embedded code where you learn to avoid the bad habit of initializing variables as part of the variable declaration and instead do it within the code.

For standard operating systems, Linux, Windows, etc the init during variable declaration is fine, you will get an imperceptible performance increase, but if you are running an operating system you are on a platform that is fast enough to not see that difference.

Depending on the binary type the former case of the init during declaration can make the binary larger. This is extremely easy to test for. Compile your binary as above, then change the array size from [16] to [16000] then compile again. Then compile without the = {0x00} and compare the three binary sizes.

For most systems that most programmers will ever see, there is no functional difference. I recommend the memset as a habit. Despite what standards say many if not most C compilers (of which most programmers will never see in their careers) wont like that init because the number of elements doesnt match the size. Most compilers do not conform to the standards even if they claim to. Instead develop good habits that avoid shortcuts or pretty much anything that should work for standard X but is different from the prior standard M. (Avoid any gee whiz compiler or standards based tricks).

dwelch