can you address single bits of an int?

Viewed 170

As I understand it, addressing a single bit in an int variable seems possible, if it is passed as a pointer. Am I correct?

uint8_t adcState[8];
uint16_t adcMessageId[8];

void adcEnable(uint8_t* data) {
    // Set ADC Input as enabled
    adcState[(uint8_t) data[0]] = 1;

    // Get ADC Message ID
    adcMessageId[(uint8_t) data[0]] = data[2] << 8 | data[1];
}

So far this is what I figured out that:

  1. The function receives a pointer to 8bit int as an argument

  2. It takes the least significant digit of that int (the pointer is treated as an array, and its first field is being read), and uses it as a field number for adcState array, which then is set to 1. For example this would mean if data was 729, the data[0] would be '9' and therefore the adcsState[9] becomes 1. Is it possible? Can you use the pointers like this?

  3. For the adcMessageId array a similar approach is taken. However here the value it is assigned depends on the third and second digit of the data int.

    I don't understand the shift over here. Being a uint8_t value it has only 8 bits, so shifting with 8 bits always gives 0000 0000. Therefore an OR with data[1] would be just data[1] itself... In our example, the adcMessageId[9] would become ('7' << 8) bitwise OR with '2', so just '2'.

Something in my logic seems wrong.

3 Answers

It would seem data is pointing to an array, not a single 8 bit int, and that:

  1. The first element of the array is a pointer into the arrays adcState and adcMessageId

  2. The second and third elements of the array comprise a data value for the array adcMessageId

  3. As commenter @Eugene Sh. pointed out, data[2] is promoted to an int before shifting, so no bits are lost.

The pointer notation uint8_t * is as valid as array notation uint8_t [] in a function signature for passing an array; it's often how char * strings are passed, and arrays decay to a pointer to their first element when passed to functions anyway.

You have a few misconceptions. Or maybe just wrong wording.

The function receives a pointer to 8bit int as an argument

More precisely it gets a pointer to an array of 8bit integers. Otherwise your usage would be invalid. Probably it gets a pointer to a string.

It takes the least significant digit of that int (the pointer is treated as an array, and its first field is being read),

That is wrong. You seem to use it as a pointer to a string holding a number. In that case you access the first character, which is the MOST significant decimal digit.

and uses it as a field number for adcState array, which then is set to 1. For example this would mean if data was 729, the data[0] would be '9' and therefore the adcsState[9] becomes '1'. Is it possible? Can you use the pointers like this?

You are messing up things a bit. If you want to access decimal digits, we are talking about strings and there the first element is '7' which is not to be confused with 7.

For the adcMessageId array a similar approach is taken. However here the value it is assigned depends on the third and second digit of the data int.

Maybe you should not talk about int if you are using strings.

I don't understand the shift over here. Being a uint8_t value it has only 8 bits, so shifting with 8 bits always gives 0000 0000. Therefore an OR with data[1] would be just data[1] itself... In our example, the adcMessageId[9] would become ('7' << 8) bitwise OR with '2', so just '2'.

That was already addressed in comments and Govind Parmar's answer: Integer promotion takes place before shifting.

  1. The function receives a pointer to 8bit int as an argument

Yes, roughly. And the function implementation assumes that the pointed-to uint8_t can be treated as the first element of an array of at least three uint8_t.

  1. It takes the least significant digit of that int (the pointer is treated as an array, and its first field is being read), and uses it as a field number for adcState array, which then is set to 1. For example this would mean if data was 729, the data[0] would be '9' and therefore the adcsState[9] becomes '1'. Is it possible? Can you use the pointers like this?

No, you have completely misunderstood. data[0] means exactly the same thing as *data. It refers to the 8-bit unsigned integer to which data points. The number 729 is too large to be represented as a uint8_t, but if the object to which data pointed had the value 129 then data[0] would evaluate to 129.

You are perhaps confused by the appearance later in the function of data[1] and data[2]. These refer to two uint8_t objects following *data in memory, such as will reliably be present if data points to the first element of an array, as I alluded to above. Indexing with [] does not have the effect of slicing the uint8_t to which data points.

Pay attention also that I am saying "the object to which data points". One does not pass a uint8_t value directly as this function's parameter. It is not anticipating that an integer value would be reinterpreted as a pointer. You pass a pointer to the data you want the function to work with.

  1. For the adcMessageId array a similar approach is taken. However here the value it is assigned depends on the third and second digit of the data int.

In the adcMessageId case, again data[0] refers to the uint8_t to which data points. data[1] refers to another whole uint8_t following that in memory, and data[2] to the next after that.

I don't understand the shift over here. Being a uint8_t value it has only 8 bits, so shifting with 8 bits always gives 0000 0000.

uint8_t has only 8 bits, but all integer values narrower than int are converted to an integer type at least as wide as int, or perhaps to a floating-point type, for arithmetic operations. The specific promoted type depends in part on what the other operand is, and the result has the same, promoted type. Type int is at least 16 bits wide on all conforming C implementations. Thus this ...

data[2] << 8 | data[1]

... intends to pack the two uint8_t values data[2] and data[1] into one 16-bit integer, data[2] in the most-significant position. It's not entirely safe because the elements of data will be promoted to (signed) int instead of unsigned int, but that will present an issue only on implementations where int is 16 bits wide (which are uncommon these days), and even then, only if the value of data[2] is larger than 127. A safer way to express it would involve explicit casts:

(unsigned int) data[2] << 8 | (unsigned int) data[1]
Related