I am trying to understand a line of C-code which includes using a pointer to struct value (which is a pointer to something as well).
Example C-code:
// Given
typedef struct {
uint8 *output
uint32 bottom
} myType;
myType *e;
// Then at some point:
*e->output++ = (uint8) (e->bottom >> 24);
Source: https://www.rfc-editor.org/rfc/rfc6386#page-22
My question is:
What exactly does that line of C-code do?
"What exactly does that line of C-code do?"
Waste a lot of time having to carefully read it instead just knowing at a glance. If I was doing code review of that, I'd throw it back to the author and say break it up into two lines.
The two things it does is save something at e->output, then advance e->output to the next byte. I think if you need to describe code with two pieces though, it should be on two lines with two separate statements.
As pointed out by Deduplicator in the comments above, looking at an operator precedence table might help.
*e->output++ = ... means "assign value ... to the location e->output is pointing to, and let e->output point to a new location 8 bits further afterwards (because output is of type uint8).
(uint8) (e->bottom >> 24) is then evaluated to get a value for ...
The line
*e->output++ = (uint8) (e->bottom >> 24);
does the following:
Find the field bottom of the structure pointed to by the pointer e.
Fetch the 32-bit value from that field.
Shift that value right 24 bits.
Re-interpret that value as a uint8_t, which now contains the high order byte.
Find the field output of the structure. It's a pointer to uint8_t.
Store the uint8_t we computed earlier into the address pointed to by output.
And finally, add 1 to output, causing it to point to the next uint8_t.
The order of some of those things might be rearranged a bit as long as the result
behaves as if they had been done in that order. Operator precedence is a completely
separate question from order in which operations are performed, and not really
relevant here.
Related
I was reading this article and thought that that everything was perfectly clear until I stumble upon this:
Again, most real Scheme systems use a slightly different implementation; for example, if GET_PAIR subtracts off the low bits of x, instead of masking them off, the optimizer will often be able to combine that subtraction with the addition of the offset of the structure member we are referencing, making a modified pointer as fast to use as an unmodified pointer.
How exactly one can achieve this subtraction and how the optimizer will do its magic to modify the pointer as fast as unmodified pointer?
The trick presented in the article is to encode type information into the unused three lowest bits of an 8 byte aligned pointer. After using this information to find out the type,
#define PAIR_P(x) (((int) (x) & 7) == 2)
one has to clear those additional bits before using the pointer as an address again.
#define GET_PAIR(x) ((struct pair *) ((int) (x) & ~7))
Note that at this point, we already know the type, so we know the value of the three least significant bits. They will always be 0b010 (decimal 2). So, instead of writing ((int) (x) & ~7), the author suggests to rather write ((int) (x) - 2). The idea is that if you write code like this,
if (PAIR_P(x))
{
SCM * thing = GET_PAIR(x)->cdr;
/* Use the thing… */
}
because we are accessing the cdr member inside the struct pair pointed to by the x (after clearing out the lower bits), the compiler will generate code to adjust the pointer appropriately. Something like this.
SCM * thing = (SCM *) ((char *)((int) (x) - 2)) + offsetof(struct pair, cdr));
Thanks to the associativity of integer addition and subtraction, we can omit one level of parenthesis and get (not showing the outer pointer casts that produce no machine code anyway)
(int) (x) - 2 + offsetof(struct pair, cdr)
where both, the 2 and the offsetof(struct pair, cdr) are compile-time constants and can be folded into a single constant. If we had asked for the car member (which has offset 0), this trick wouldn't help, but helping every other time is not too bad.
A modern optimizer might be able to figure out by itself that after we've just tested that (x & 7) == 2, x & ~7 is equivalent to x - 2 so the trick might not be required any more these days. You would like to measure this before you rely on it, though.
I'm trying to translate this snippet :
ntohs(*(UInt16*)VALUE) / 4.0
and some other ones, looking alike, from C to Swift.
Problem is, I have very few knowledge of Swift and I just can't understand what this snippet does... Here's all I know :
ntohs swap endianness to host endianness
VALUE is a char[32]
I just discovered that Swift : (UInt(data.0) << 6) + (UInt(data.1) >> 2) does the same thing. Could one please explain ?
I'm willing to return a Swift Uint (UInt64)
Thanks !
VALUE is a pointer to 32 bytes (char[32]).
The pointer is cast to UInt16 pointer. That means the first two bytes of VALUE are being interpreted as UInt16 (2 bytes).
* will dereference the pointer. We get the two bytes of VALUE as a 16-bit number. However it has net endianness (net byte order), so we cannot make integer operations on it.
We now swap the endianness to host, we get a normal integer.
We divide the integer by 4.0.
To do the same in Swift, let's just compose the byte values to an integer.
let host = (UInt(data.0) << 8) | UInt(data.1)
Note that to divide by 4.0 you will have to convert the integer to Float.
The C you quote is technically incorrect, although it will be compiled as intended by most production C compilers.¹ A better way to achieve the same effect, which should also be easier to translate to Swift, is
unsigned int val = ((((unsigned int)VALUE[0]) << 8) | // ² ³
(((unsigned int)VALUE[1]) << 0)); // ⁴
double scaledval = ((double)val) / 4.0; // ⁵
The first statement reads the first two bytes of VALUE, interprets them as a 16-bit unsigned number in network byte order, and converts them to host byte order (whether or not those byte orders are different). The second statement converts the number to double and scales it.
¹ Specifically, *(UInt16*)VALUE provokes undefined behavior because it violates the type-based aliasing rules, which are asymmetric: a pointer with character type may be used to access an object with any type, but a pointer with any other type may not be used to access an object with (array-of-)character type.
² In C, a cast to unsigned int here is necessary in order to make the subsequent shifting and or-ing happen in an unsigned type. If you cast to uint16_t, which might seem more appropriate, the "usual arithmetic conversions" would then convert it to int, which is signed, before doing the left shift. This would provoke undefined behavior on a system where int was only 16 bits wide (you're not allowed to shift into the sign bit). Swift almost certainly has completely different rules for arithmetic on types with small ranges; you'll probably need to cast to something before the shift, but I cannot tell you what.
³ I have over-parenthesized this expression so that the order of operations will be clear even if you aren't terribly familiar with C.
⁴ Left shifting by zero bits has no effect; it is only included for parallel structure.
⁵ An explicit conversion to double before the division operation is not necessary in C, but it is in Swift, so I have written it that way here.
It looks like the code is taking the single byte value[0]. This is then dereferenced, this should retrieve a number from a low memory address, 1 to 127 (possibly 255).
What ever number is there is then divided by 4.
I genuinely can't believe my interpretation is correct and can't check that cos I have no laptop. I really think there maybe a typo in your code as it is not a good thing to do. Portable, reusable
I must stress that the string is not converted to a number. Which is then used
I was able to use bit operations to "turn off" binary digits of a number.
Ex:
x = x & ~(1<<0)
x = x & ~(1<<1)
(and repeat until desired number of digits starting from the right are changed to 0)
I would like to apply this technique to a pointer's address.
Unfortunately, the & operator cannot be used with pointers. Using the same lines of code as above, where x is a pointer, the compiler says "invalid operands to binary & (have int and int)."
I tried to typecast the pointers as ints, but that doesn't work as I assume the ints are too small (and I just realized I'm not allowed to cast).
(note: though this is part of a homework problem, I've already reasoned out why I need to turn off some digits after a good couple hours, so I'm fine in that regard. I'm simply trying to see if I can get a clever technique to do what I want to do here).
Restrictions: I cannot use loops, conditionals, any special functions, constants greater than 255, division, mod.
(edit: added restrictions to the bottom)
Use uintptr_t from <stdint.h>. You should always use unsigned types for bit twiddling, and (u)intptr_t is specifically chosen to be able to hold a pointer's value.
Note however that adjusting a pointer manually and dereferencing it is undefined behaviour, so watch your step. You shall be able to recover the exact original value of the pointer (or another valid pointer) before doing so.
Edit : from your comment I understand that you don't plan on dereferencing the twiddled pointer at all, so no undefined behaviour for you. Here is how you can check if your pointers share the same 64-byte block :
uintptr_t p1 = (uintptr_t)yourPointer1;
uintptr_t p2 = (uintptr_t)yourPointer2;
uintptr_t mask = ~(uintptr_t)63u; // Shave off 5 low-order bits
return (p1 & mask) == (p2 & mask);
C language standard library includes the (optional though) type intptr_t, for which there is guarantee that "any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer".
Of course if you perform bitwise operation on the integer than the result is undefined behaviour.
Edit:
How unfortunate haha. I need a function to show two pointers are in
the same 64-byte block of memory. This holds true so long as every
digit but the least significant 6 digits of their binary
representations are equal. By making sure the last 6 digits are all
the same (ex: 0), I can return true if both pointers are equal. Well,
at least I hope so.
You should be able to check if they're in the same 64 block of memory by something like this:
if ((char *)high_pointer - (char *)low_pointer < 64) {
// do stuff
}
Edit2: This is likely to be undefined behaviour as pointed out by chris.
Original post:
You're probably looking for intptr_t or uintptr_t. The standard says you can cast to and from these types to pointers and have the value equal to the original.
However, despite it being a standard type, it is optional so some library implementations may choose not to implement it. Some architectures might not even represent pointers as integers so such a type wouldn't make sense.
It is still better than casting to and from an int or a long since it is guaranteed to work on implementations that supply it. Otherwise, at least you'll know at compile time that your program will break on a certain implementation/architecture.
(Oh, and as other answers have stated, manually changing the pointer when casted to an integer type and dereferencing it is undefined behaviour)
char errorString[20];
*(UInt32*)(errorString + 1) = CFSwapInt32HostToBig(statusCode);
I found this in a book about audio programming and, considering CFSwapInt32HostToBig returns an Int32, I can't understand why does it need to make that strange cast and why it assigns starting with the address of the second element (+1) in the char buffer.
What will errorString contain after this assignment?
errorString+1 (which is of type char*) is casted to pointer to UInt32 and then dereferenced. Hence, the four consequent bytes of errorString, from the second to the fifth (errorString[1] ... errorString[4]), will contain a binary representation of an integer that is result of CFSwapInt32HostToBig(statusCode).
I can't understand why does it need to make that strange cast
The cast is necessary to avoid truncating the data to a single char: if you drop the cast, like this
*(errorString + 1) = CFSwapInt32HostToBig(statusCode);
the assignment will modify a single char. Effectively, it's this:
*(errorString + 1) = (char)CFSwapInt32HostToBig(statusCode);
which is not what the author of the code wanted.
As far as adding a byte goes, the answer depends on the use of errorString: most likely, some other piece of data is supposed to go there.
CFSwapInt32HostToBig returns a value of a 32-bit type but errorString is an array of char.
The programmer wants to store the 4 bytes into the array of char starting from position &errorString[1].
Note that is not safe and should be avoided as it breaks aliasing rules and may break alignment.
I'm using an AVR microcontroller to write to a programmable frequency divider chip via the I2C bus. At certain intervals I'm trying to have the following function is called to update the frequency output of the chip:
void 1077WriteDiv(int16_t data)
{
uint8_t upperByte = (uint8_t)((uint16_t)data>>2);
i2c_start(DS1077_BASE_ADDRESS);
i2c_write(DIVIDE_REGISTER);
i2c_write(upperByte);
i2c_write(0x0);
i2c_stop();
}
I'm trying to get the top 8 bits of a ten bit value in the "data" variable and write it out. The second "write" command writes the lower 8 bits of the "divide" register on the chip, 0 in this case.
As a test case I'm incrementing the "data" variable (which has to be signed for certain reasons) from zero, shifting it left 2 bits and calling this function each time. I get garbage out. However, when I do this:
void 1077WriteDiv(int16_t data)
{
//uint8_t upperByte = (uint8_t)((uint16_t)data>>2);
static uint8_t thing = 0;
i2c_start(DS1077_BASE_ADDRESS);
i2c_write(DIVIDE_REGISTER);
i2c_write(thing++);
i2c_write(0x0);
i2c_stop();
}
Everything works as expected. There's obviously some kind of problem in how I'm shifting and typecasting the original "data" variable, but I've tried all kinds of permutations with the same results. It would be much appreciated if anyone could point out where I might be going wrong.
Try
uint8_t upperByte = (uint8_t) ((data & 0x3FC) >> 2);
You cannot rely on the cast to a smaller int to delete the high-order bits that you are trying to get rid of.
i2c_write(thing++);
Would mean your divider increments every call. If you increment "data" and shift it right by two then your divider increments every four calls. Your two code sections are not equivalent.
What interval are you calling this function at? What is "garbage out"? How do you know the value passed into the function is correct? How do you know the value sent out to the DS1077 is wrong?
Check all your assumptions.
The cast and shift look fine to me. At least I think they'd work in any C compiler I've ever used. From a C standard perspective you can refer to this draft (ISO/IEC 9899:TC2 6.3 Conversions):
Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type
Which is the only one I have access to right now. Perhaps someone else can chime in on the standard question. The compiler may not be standard compliant...