0

I want to initialize the last 4 bytes of a char array with 0 (set all 32 bits to zero). But the assignment is changing only one byte in the array. How can I change this byte and the next three in a single command, instead of looping through all 4 bytes? Is this possible?

#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    char buf[8 + 4]; // 8 bytes of garbage + 4 = 32 safety bits
    buf[8] = (uint32_t)0; // turns all safety bits into zero???
    cout << hex << setfill(' ');
    for (int i=0; i<8 + 4; i++) {
        cout << setw(3) << (int)buf[i];
    }
    cout << dec << endl;
    return 0;
}

That's displaying:

  0  9 40  0  0  0  0  0  0  8 40  0
     ^  ^                    ^  ^
   ok garbage              undesired
Rodrigo
  • 4,706
  • 6
  • 51
  • 94
  • 4
    Why not initialize everything to 0 ? i.e. `char buf[8 + 4] = {0};` – Pavan Yalamanchili Dec 05 '18 at 03:36
  • @PavanYalamanchili, yes it would work. But if I need to change four bytes somewhere after it's been initialized, do I need a loop? – Rodrigo Dec 05 '18 at 03:40
  • 2
    If you are feeling adventurous `*((int *)(buf + 8)) = 0;` – Pavan Yalamanchili Dec 05 '18 at 03:51
  • 1
    `buf[8] = buf[9] = buf[10] = buf[11] = 0;` – Galik Dec 05 '18 at 03:57
  • @PavanYalamanchili Yes, that's the kind of pointer operation I was after... I should have programmed more in C++ and less in Delphi... :( Thank you! – Rodrigo Dec 05 '18 at 03:58
  • @Rodrigo Just be aware that pointer aliasing in that way is technically undefined behavior, and therefore you do not have a guarantee that it'll work correctly. It **MAY** work as expected today, with your current compiler, but port to some other platform, or get a compiler upgrade two years from now and it could start failing. The compiler is not constrained to align your character array any way in particular, and if it happens to be odd byte aligned on a 68000 and you try this, it **will** crash. – dgnuff Dec 05 '18 at 04:07
  • @dgnuff So what's your suggestion, considering the 3 answers already shown? – Rodrigo Dec 05 '18 at 04:23
  • I'd use the idea presented by Serge in his answer. It's safe, well defined behavior and therefore guaranteed to work in all cases. While it is often attractive, because it can provide a "quick and easy" solution, it's best to get into the habit of avoiding undefined behavior. Your programs will be more robust if you do so, and when you are programming professionally and getting paid for it, this sort of thing matters. – dgnuff Dec 05 '18 at 04:27
  • @dgnuff I see. But isn't Galik's comment above safe as well? – Rodrigo Dec 05 '18 at 04:31
  • 1
    @Rodrigo Galik's suggestion is safe. The danger is when you take an array of characters, aim a pointer to int at the array, and then access it through that pointer. As noted in https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule this Stackoverflow question, the rules regarding pointer aliasing are quite strict, both in C and in C++. – dgnuff Dec 05 '18 at 04:40

2 Answers2

5

if you do not want to initialize the whole array, you can use memset or a similar function.

#include <string.h>

...
memset(&buf[8], 0, 4);

based on the comments, i added the a more c++like way to do the same:

#include <algorithm>
...
 std::fill(&a[8],&a[8+4],0);
Serge
  • 11,616
  • 3
  • 18
  • 28
  • I always feel that direct commands, like `*((int *)(buf + 8)) = 0;` and `buf[8] = buf[9] = buf[10] = buf[11] = 0;`, given in the comments, are faster. Though I'm not sure. – Rodrigo Dec 05 '18 at 04:02
  • 1
    Chiming back in here, let the compiler worry about optimizing this. Given the level to which optimization has improved over the years, you'd be surprised at how good a job the compiler can do. Take a look at this example: https://godbolt.org/z/hM9QxA Note how the compiler is smart enough to completely avoid the use of `modify1` and `modify2` when evaluating the parameter to `printf`, and instead it figures the answer ahead of time and pushes it directly on the stack. – dgnuff Dec 05 '18 at 04:37
  • 2
    Since this question is marked C++, a more appropriate solution would use `std::fill`. – paddy Dec 05 '18 at 04:39
  • @paddy which internally may involve a call to `memset` or equivalent when filling a range of POD values. – Remy Lebeau Dec 05 '18 at 04:58
  • Only modern compiler(say: gcc>=7) generates the same output. So it is better to use `uint32_t *` or `memset` instead of multi assignments. And for MSVC, the outputs differ much more. Do not rely on compilers. @JerryJeremiah – shawn Dec 05 '18 at 11:31
3

There is also another option:

*(uint32_t*)(&buf[8]) = 0;

Or, more c++ way:

#include <algorithm>

std::fill(buf + 8, buf + 12, 0);
snake_style
  • 1,139
  • 7
  • 16