Explanation of *-- bitshifting example

A

Angus

Here is the code:

void FillHeader(char* buf, size_t len)
{
*--buf = len;
*--buf = len>>8;
}

int main(){

//this buffer is of form where first two bytes denote data size,
data follows
char buf[8]; //buffer to use
buf[0] = 0; //size fields
buf[1] = 0;
buf[2] = '1'; //start of data
buf[3] = '2';
buf[4] = '3';
buf[5] = '4';
buf[6] = '5';
buf[7] = '6';

//we specify start of data and size of data
FillHeader(&buf[2], 6);


//Next test will be where buffer has first 2 bytes with header
value. Size of data goes in bytes 2 and 3
char buf2[10]; //buffer to use
buf2[0] = 127; //header bytes
buf2[1] = 127;
buf2[2] = 0; //size fields
buf2[3] = 0;
buf2[4] = '1'; //give some data
buf2[5] = '2';
buf2[6] = '3';
buf2[7] = '4';
buf2[8] = '5';
buf2[9] = '6';
FillHeader(&buf2[4], 6);

return 0;
}

It works fine but I don't really understand the FillHeader function.
The *--buf specifically. How does this work?
 
A

Alf P. Steinbach

* Angus:
Here is the code:

void FillHeader(char* buf, size_t len)
{
*--buf = len;

Here you have formal UB for any value outside the range of 'char'.

*--buf = len>>8;
}

int main(){

//this buffer is of form where first two bytes denote data size,
data follows
char buf[8]; //buffer to use
buf[0] = 0; //size fields
buf[1] = 0;
buf[2] = '1'; //start of data
buf[3] = '2';
buf[4] = '3';
buf[5] = '4';
buf[6] = '5';
buf[7] = '6';

//we specify start of data and size of data
FillHeader(&buf[2], 6);


//Next test will be where buffer has first 2 bytes with header
value. Size of data goes in bytes 2 and 3
char buf2[10]; //buffer to use
buf2[0] = 127; //header bytes
buf2[1] = 127;
buf2[2] = 0; //size fields
buf2[3] = 0;
buf2[4] = '1'; //give some data
buf2[5] = '2';
buf2[6] = '3';
buf2[7] = '4';
buf2[8] = '5';
buf2[9] = '6';
FillHeader(&buf2[4], 6);

return 0;
}

It works fine but I don't really understand the FillHeader function.

If you don't understand the FillHeader routine then you're do not understand
enough to say whether the code works fine or not.

The *--buf specifically. How does this work?

'--' is the decrement operator.

As prefix operator, placed in front of the argument, it decrements the value of
the argument and the expression result is the new value.

'*' is the pointer dereferencing operator.

'>>' is, in this context, the bitwise shift right operator.

'FillHeader' is a C-oriented low level and almost obfuscated way to fill in a 16
bit big-endian size field.

In C++ you might do the last buffer like this:

std::string buf;

buf += char(127);
buf += char(128);
buf += (6 >> 8);
buf += (6 & 0xFF);
buf += "123456";
some_c_function( buf.data() );

Cheers & hth.,

- Alf
 
F

Francesco S. Carta

Here is the code:

void FillHeader(char* buf, size_t len)
{
*--buf = len;
*--buf = len>>8;

}
[snip]

It works fine but I don't really understand the FillHeader function.
The *--buf specifically.  How does this work?

This one:

*--buf = len;

Means:

1 - decrement pointer "buf" by one pointer-unit
2 - assign "len" to the object pointed to by "buf"

If it were using the post-decrement:

*buf-- = len;

That would "translate" to:

1 - assign "len" to the object pointed to by "buf"
2 - decrement pointer "buf" by one pointer-unit

Hope that's clear - as a language explanation - I didn't examine your
snippet.
 
A

Andrey Tarasevich

Alf said:
* Angus:

Here you have formal UB for any value outside the range of 'char'.

Not exactly.

Firstly, "integer assignment overflow" (or more precisely, an integral
conversion applied to an integral value that doesn't fit into the
destination type's range) doesn't cause UB in C++. The result is
implementation-defined, but there's no UB here (see 4.7).

Secondly, in the particular implementation when 'char' is unsigned, the
result is actually defined.
 
J

Joe Smith

void FillHeader(char* buf, size_t len)
{
*--buf = len;
*--buf = len>>8;
}

Assuming an unsigned character type, so the behavior is specificed.

Why would the following not be used?

void FillHeader(char* buf, uint16_t len)
{

buf[-2]=len>>8;
buf[-1]=len;
}

Does indexing not work with negative integers? I'm not sure I ever knew if
they did, but it seems logical enough. I've never had any reason to use
negative indices before.

Also note the use of uint16_t. This would allow the compiler to warn about
the fact that a 32 or 64 bit size_t, is being used for only 16 bits, if the
warning level is set high enough. That might help to to remind the
developers that the code will break if there is ever some data block larger
than 2^16-1 bytes.
 
J

James Kanze

Here you have formal UB for any value outside the range of 'char'.

Implementation defined. It's undefined behavior if an
arithmetic operations results in a value not representable in
the type being used, but it's implementation defined (including
the possibility of an implementation defined signal) when
converting to a signed integral type, and fully defined if char
is unsigned.

Having said that, when doing such operations, 1) I try to target
unsigned types, even if it requires a cast, and 2) I'll mask
anyway (e.g. assign len & 0xFF), since I think it makes the code
clearer.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,838
Latest member
KandiceChi

Latest Threads

Top