-1

I would like to avoid using memset() on a structure like this:

typedef struct {
    int size;
    float param1;
} StructA;

typedef struct {
    StructA a;
    unsigned int state;
    float param2;
} Object;

Can I do something like this (pseudo code, I can't check right now)?

Object obj;
int *adr = (int*)&obj;
for (int i; i < sizeof(obj); i++) {
    *adr++ = 0;
}

Will it set every member of obj to zero?

EDIT: to answer questions on comments. I have been working on some cases (with uni-type structures), where memset is twice as slow than initializing by hand. So I will consider trying initializing multi-type structure as well.

Avoiding memcpy() would be nice too (avoiding the <string.h> lib).

chqrlie
  • 131,814
  • 10
  • 121
  • 189
alpereira7
  • 1,522
  • 3
  • 17
  • 30
  • 2
    `int* adr`-> `char* adr`. Besides this, the answer is yes. – Jabberwocky Nov 22 '16 at 10:30
  • 8
    Why would you like to avoid `memset()`? And if you really want to, don't use `int` use `unsigned char` instead. But you should use `memset()` instead, you really should. – Iharob Al Asimi Nov 22 '16 at 10:31
  • 1
    Actually what he does is the same as `memset`. – Jabberwocky Nov 22 '16 at 10:32
  • 2
    `I would like to avoid using memset` Why? If you're doing this for performance reasons, you're likely to be surprised. `memset` will likely be faster - without violating aliasing rules. – Andrew Henle Nov 22 '16 at 10:33
  • 2
    `memset` may be slower or not. The only way to find out is to profile it. – Jabberwocky Nov 22 '16 at 10:44
  • 4
    *memset is twice as slow than initializing "by hand"* You have either a broken standard library or a broken way to measure performance. – n. m. could be an AI Nov 22 '16 at 10:49
  • `memset` initializes the structure to all bits zero. This would initialize the integers and the floats to 0 on IEEE compliant architectures but the C Standard does not guarantee it. On an exotic system, this might not initialize `param1` or `param2` to `0.0`. If the hand coded initialization is faster, use that, or use a static initializer: `Object obj = { 0 };` – chqrlie Nov 28 '16 at 15:00

5 Answers5

5

The universal zero initializer initializes everything (recursively) to zero of the proper type.

Object object = {0};
Paul R
  • 208,748
  • 37
  • 389
  • 560
pmg
  • 106,608
  • 13
  • 126
  • 198
  • 1
    Yes, and there are multiple answers about that in SO: http://stackoverflow.com/questions/5434865/quickest-way-to-initialize-an-array-of-structures-to-all-0s, http://stackoverflow.com/questions/11152160/initializing-a-struct-to-0, http://stackoverflow.com/questions/7064314/what-is-0-in-c etc. But this solution allows *initializing* a structure variable during creation only, while `memset()` or a loop in the question allow *re-initializng* an already existing variable. – CiaPan Nov 22 '16 at 10:49
  • I tried it sometime back, it works. But I remember I was getting "missing initializer for member" warning. – MayurK Nov 22 '16 at 12:17
  • 1
    The text of the warning could be a little more helpful, maybe: "missing initializer for member; using 0 for unspecified members, recursively, as the Standard mandates." – pmg Nov 22 '16 at 12:36
  • Probably not relevant for this case but this method is dependent on *where* the variable is stored. – Toby Nov 22 '16 at 16:40
  • Nice answer, but like it was said, I do not want to set these structures at creation, but later. – alpereira7 Nov 25 '16 at 15:25
1

You can create a "zero" object, then copy it to other objects of the same type. Maybe it's faster than memset(), I haven't tested efficiency.

const Object zeroobject = {0};

/* ... */
Object obj;
memcpy(&obj, &zeroobject, sizeof obj);
/* assignment may work too */
obj = zeroobject;
pmg
  • 106,608
  • 13
  • 126
  • 198
  • 2
    `` is a header. The functions declared there are part of the C Standard Library. The best way to avoid the C Standard Library is to use a free-standing implementation. – pmg Nov 28 '16 at 12:49
  • 1
    @Bebs: as pmg points out, `memcpy` is not needed, `obj = zeroobject;` copies the default object onto `obj` as efficiently as possible, taking advantage of the intrinsic alignment of `Object` instances. `memcpy` and `memset` might need extra tests to determine how to perform the copy or the initialization and for small structures such as this, the overhead of a function call might be noticeable if `memset` or `memcpy` are not generated inline. Inspect the code generated by the compiler, run benchmarks, play with compiler options... but remember that micro-optimizing is short term only. – chqrlie Nov 29 '16 at 23:56
1

memset initializes the structure to all bits zero. This would initialize the integers and the floats to 0 on IEEE compliant architectures but the C Standard does not guarantee it. On an exotic system, this might not initialize param1 or param2 to 0.0.

If the hand coded initialization is faster, use that, or use a static initializer:

Object obj = { 0 };
chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

No.

I would prefer to use memset:

memset(&obj, 0, sizeof(obj));

Or if you want it to do your way:

char* adr = (char*)&obj;
for(int i = 0; i < sizeof(obj); i++)
{
    *adr++ = 0;
}
Paul R
  • 208,748
  • 37
  • 389
  • 560
alex
  • 124
  • 7
  • the second method uses an uninitialized `i` and breaks the strict aliasing rule, if you read the char `0`s from the struct via the members. – mch Nov 22 '16 at 10:35
  • 3
    @mch: I think `char *` is a special case where the strict aliasing rule does not apply. – Paul R Nov 22 '16 at 10:40
  • 1
    @PaulR if you write something via a `int *`, you are allowed to read it with a `char *`, but not the other way. http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule says "You can use `char*` for aliasing instead of your system's word. The rules allow an exception for `char*` (including `signed char` and `unsigned char`). It's always assumed that `char*` aliases other types. However this won't work the other way: there's no assumption that your struct aliases a buffer of chars." – mch Nov 22 '16 at 11:26
  • @mch: ah, OK - thanks for the clarification - so how is `memset` initialisation of a struct valid then, since a simple implementation presumably just writes zeroes via a `char *` pointer ? – Paul R Nov 22 '16 at 11:29
  • 1
    @PaulR this is a good question and I cannot find the link to the answer. If I remember correct `memset` is written in assembler to omit the strict aliasing rule. – mch Nov 22 '16 at 11:55
  • @mch: interesting - thanks - it certainly seems that you couldn't write a "correct" `memset` implementation in C without violating strict aliasing rules then. – Paul R Nov 22 '16 at 12:22
  • @mch That is not true. `memset` is specifically mentioned to work in terms of characters. Writing through `char *` *is* allowed, but it's programmers responsibility to make sure that there are no trap presentations and such. – user694733 Nov 22 '16 at 14:40
  • @user694733 You are allowed to use `memset` on an object or array, but if you are using a `char *` to set all elements of an object to zero, you break the strict aliasing rule if you read the values of the object members, so http://ideone.com/sAGR2J is undefined behaviour. – mch Nov 22 '16 at 15:04
  • @mch Code you linked is not UB. N1570 6.5p7 specifically mentions that character type is allowed to alias any object. Assignment through `char*` does not change the *effective type* of the original variable. As I said before, it may be possible that some types have trap presentations which might be set and then triggered when reading through original value, but whether that happens or not is *implementation defined* (explained in various paragraphs in 6.2.6.1). – user694733 Nov 22 '16 at 15:45
  • @user694733 a `char *` can alias any object, but in this case it is the opposite. An `int *` cannot alias a `char *`, so if you write with the `char *` you are not allowed to read the `int`: http://stackoverflow.com/a/99010/3684343 – mch Nov 22 '16 at 16:20
  • @mch *An `int *` cannot alias a `char *`* Agreed. But this answer doesn't do so, and neither does the ideone example either. I have read the answer you linked before, but it doesn't do very good job at explaining it, answers below it are better. If you want to contradict the chapters from the standard I gave, you should provide another from the standard that does so. – user694733 Nov 23 '16 at 09:46
  • @user694733 in my ideone link the line `printf("%d\n", a);` reads an `int` which is written via a `char *`. Is this example http://ideone.com/AlUDzO better? – mch Nov 23 '16 at 10:07
  • @mch Conversion to other pointer type and back is also legal (6.3.2.3p7) However, following code is UB since effective type is not `int`: http://ideone.com/UjOCo9 – user694733 Nov 23 '16 at 11:03
-3

You should be using memset, unless it's very hard to do. Like it doesn't exist in your environment and you can't add an equivalent. It's fast, checks all the right edge cases and is essentially bug-free.


That is close enough to working actually. But sizeof produces the size in bytes. You should switch the int to char.

Since all elements involved are of a size which is a multiple of four, you could do something like this, and still get the same result.

int* adr = (int*)&obj;
for (int i = 0; i < sizeof(obj); i+=4) {*adr = 0; adr += 4;}

Baring any padding the compiler might insert between the fields, for example. It might put each field on a 8 byte / 64 bits region, so access to it is faster.

Horia Coman
  • 8,681
  • 2
  • 23
  • 25
  • 1
    Are you *sure* `obj` can be aliased by an `int *`? – Andrew Henle Nov 22 '16 at 10:35
  • 1
    if `sizeof(obj)` is not a multiple of 4 this won't work at all. – Jabberwocky Nov 22 '16 at 10:36
  • @MichaelWalz yup, that's why I qualified the whole answer with "Since all elements involved are of a size which is a multiple of four". Whatever OP is trying to do is bad-practice anyway, but they should at least have more ways to shoot themselves in the foot. – Horia Coman Nov 22 '16 at 10:38
  • Apart from the strict aliasing problem, this also assumes that `sizeof(int) == 4`. – Paul R Nov 22 '16 at 10:38
  • @AndrewHenle what do you mean? – Horia Coman Nov 22 '16 at 10:38
  • @HoriaComan that may work today, but tomorrow someone adds a char field to the struct and breaks the program. And `sizeof (int)` may be different from 4. – Jabberwocky Nov 22 '16 at 10:39
  • @MichaelWalz but both today and tomorrow they should be using memset. – Horia Coman Nov 22 '16 at 10:39
  • @HoriaComan In general, you can't access a type via a pointer of a different type. That results in undefined behavior. See http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule – Andrew Henle Nov 22 '16 at 10:41
  • @HoriaComan of course `memset` is the way to go here, but you didn't mention this at all in your answer. – Jabberwocky Nov 22 '16 at 10:41
  • @MichaelWalz OP seems aware of memset. I imagine they're asking out of curiosity, or because they're in an environment where it's not available. I'll add an explicit warning to use memset. – Horia Coman Nov 22 '16 at 10:42
  • @AndrewHenle but `memset` actually does this. – Jabberwocky Nov 22 '16 at 10:43
  • 1
    @MichaelWalz - `memset` is a library function supplied by the system and is aware of what's possible on the platform. As part of the implementation, it's free to do things conforming C code can not. And a good implementation of `memset` will likely outperform code generated from C as `memset` can be extremely platform-specific, using instructions most compilers don't normally use because they are so platform-specific. – Andrew Henle Nov 22 '16 at 10:48
  • @AndrewHenle I agree with all that, but the struct aliasing rule does not apply to `char`, [read this](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule/99010#99010) near the end of the accepted answer. – Jabberwocky Nov 22 '16 at 10:58
  • @MichaelWalz Where does `char` come from? The answer above aliases the `struct` to an `int`. – Andrew Henle Nov 22 '16 at 11:01
  • @AndrewHenle OK, misunderstanding. For me the `int` solution was ruled out anyway because if won't work if the size of the struct is not a multiple of 4 (assumimg `sizeof int` is 4). – Jabberwocky Nov 22 '16 at 11:04