bit programing in C [duplicate] - c

This question already has answers here:
How do I split up a long value (32 bits) into four char variables (8bits) using C?
(6 answers)
Closed 8 months ago.
I am new to bits programming in C and finding it difficult to understand how ipv4_to_bit_string() in below code works.
Can anyone explain that, what is happening when I pass integer 1234 to this function. Why integer is right shifted at 24,16,8 and 4 places?
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct BIT_STRING_s {
uint8_t *buf; /* BIT STRING body */
size_t size; /* Size of the above buffer */
int bits_unused; /* Unused trailing bits in the last octet (0..7) */
} BIT_STRING_t;
BIT_STRING_t tnlAddress;
void ipv4_to_bit_string(int i, BIT_STRING_t *p)
{
do {
(p)->buf = calloc(4, sizeof(uint8_t));
(p)->buf[0] = (i) >> 24 & 0xFF;
(p)->buf[1] = (i) >> 16 & 0xFF;
(p)->buf[2] = (i) >> 8 & 0xFF;
(p)->buf[3] = (i) >> 4 & 0xFF;
(p)->size = 4;
(p)->bits_unused = 0;
} while(0);
}
int main()
{
BIT_STRING_t *p = (BIT_STRING_t*)calloc(1, sizeof(BIT_STRING_t));
ipv4_to_bit_string(1234, p);
}

An IPv4 address is four eight-bit pieces that have been put together into one 32-bit piece. To take the 32-bit piece apart into the four eight-bit pieces, you extract each eight bits separately. To extract one eight-bit piece, you shift right by 0, 8, 16, or 24 bits, according to which piece you want at the moment, and then mask with 0xFF to take only the low eight bits after the shift.
The shift by 4 instead of 0 appears to be an error.
The use of an int for the 32-bit piece appears to be an error, primarily because the high bit may be set, which indicates the int value is negative, and then the right-shift is not fully defined by the C standard; it is implementation-defined. An unsigned type should be used. Additionally, int is not necessarily 32 bits; it is preferable to use uint32_t, which is defined in the <stdint.h> header.

Related

Am I using memcpy wrong?

I'm trying to use memcpy to copy part of an unsigned int to another unsigned int within the same struct I made. But my program only prints the first printf statement and then says: Process returned -1073741819 (0xC0000005)
Am I using memcpy wrong?
#include <stdio.h>
#include <string.h>
int main()
{
struct time
{
unsigned int hours:5;
unsigned int minutes:6;
unsigned int seconds:6;
};
struct time t = {0x10,0b101011,45};
printf("The time is : %d:%d:%d\n", t.hours, t.minutes, t.seconds);
memcpy(t.minutes, t.seconds, 2);
printf("The time is : %d:%d:%d\n", t.hours, t.minutes, t.seconds);
return 0;
}
I've already done t.minutes = t.seconds and that copies the whole number, but I only want a portion of it.
In response to your clarification in the comments:
When I say part of number I mean I'm trying to copy the most significant 2 bits of the unsigned int.
The way to copy individual bits is by doing bit manipulation with bitwise operators.
The two most significant bits in your 6-bit fields are therefore represented by the value 0x30 (110000 in binary). To copy these from one to another, simply clear out those bits in the destination, then mask the source and combine with bitwise-OR:
unsigned int mask = 0x30;
t.minutes = (t.minutes & ~mask) | (t.seconds & mask);
Breakdown of the above:
~mask inverts the mask, meaning that bits 4 and 5 will be 0 and all other bits will be 1
this value is then ANDed with minutes, resulting in clearing bits 4 and 5
the opposite occurs when ANDing the mask with seconds, resulting in only bits 4 and 5 being preseved, and all other bits cleared
the two values are then combined with OR and assigned to minutes

value in array will be printed as 0 even after changing it in c

So I made a custom type by using typedef unsigned char byte;, and then declared an array of it, like using byte mem[255];. I used mem[0] = 0x10100000; to init the first value, but when I print it using printf("%d", mem[0]); I get 0. Why?
An unsigned char can typically only hold values between 0 and 255. The hex value 0x10100000 is well out of range for that type, so (essentially) only the low-order byte of that value is used, which is 0.
Presumably you wanted to use a binary constant. Not all compilers support that, but those that do would specify it as 0b10100000. For those than don't you can use the hex value 0xA0.
You're assigning it the hexidecimal number 0x10100000 which is far larger than a single character, and thus can't be stored in a byte. If you want to use a binary number, and your compiler supports this, you might try using 0b10100000 instead.
unsigned char can only hold the value of ((1 << CHAR_BIT) - 1)
You can check what is the maximum value yourself
#include <stdio.h>
#include <limits.h>
int main(void)
{
printf("%u\n", (1 << CHAR_BIT) - 1);
}
On most systems it is 255 or 0xff.
When you assign the unsigned char with 0x10100000 only the lowest two hex digits will be assigned (in your case 0x00).
If you wanted to copy all the bytes from the 0x10100000 to the byte array mem you defined, the assignment will not work. You need to copy then instead:
#include <stdio.h>
#include <limits.h>
#include <string.h>
typedef unsigned char byte;
int main(void)
{
byte mem[100];
memcpy(mem, &(unsigned){0x10100000}, sizeof(0x10100000));
for(size_t index = 0; index < sizeof(0x10100000); index++)
{
printf("mem[%zu] = 0x%hhx\n", index, mem[index]);
}
}
Output:
mem[0] = 0x0
mem[1] = 0x0
mem[2] = 0x10
mem[3] = 0x10
https://godbolt.org/z/cGYa8MTef
Why in this order? Because the machine, where godbolt is run, uses little endioan. https://en.wikipedia.org/wiki/Endianness
0x prefix means that number hexadecimal. If you wanted to use binary number then gcc supports 0b prefix which is not standard.
mem[0] = 0b10100000
You can also create .h file
#define b00000000 0
#define b00000001 1
#define b00000010 2
#define b00000011 3
/* .... */
#define b11111110 254
#define b11111110 255
and use those definitions portable way
mem[0] = b10100000;
You can't fit a 32 bit value inside an 8 bit variable (mem[0]). Do you perhaps mean to do this?
*(int *)mem = 0x10100000;

Why does compound bit shifting not set leading nibble to 0? [duplicate]

This question already has answers here:
Bit shifting a byte by more than 8 bit
(2 answers)
Closed 3 years ago.
I am trying to clear the first nibble in a 16-bit unsigned integer (set it to 0).
One of the approaches I attempted was to use left bit shifting and then right bit shifting to clear the first four bits.
Here is my program.
#include <stdio.h>
#include <stdint.h>
int main() {
uint16_t x = 0xabcd; // I want to print out "bcd"
uint16_t a = (x << 4) >> 4;
printf("After clearing first four bits: %x\n", a);
return 0;
}
I am expecting the first bit shift (x << 4) to evaluate to 0xbcd0 and then the right bit shift to push a leading 0 in front - 0x0bcd. However, the actual output is 0xabcd.
What confuses me more is if I try to use the same approach to clear the last four bits,
uint16_t a = (x >> 4) << 4;
it works fine and I get expected output: abc0.
Why, in the program above, does the right bit shifting not push a leading 0?
Happens due to integer promotion. On systems where int is larger than 16 bits, your expression is converted to
uint16_t a = (int)((int)x << 4) >> 4;
and the upper bits are not stripped therefore.
if you want to print out "bcd", maybe you can do like this:
#include <stdio.h>
#include <stdint.h>
int main() {
uint16_t x = 0xabcd; // I want to print out "bcd" // step 1
uint16_t c = x << 4 ; // step 2
uint16_t a = c >> 4; // step 3
printf("After clearing first four bits: %x\n", a);
return 0;
}
above output is :
After clearing first four bits: bcd
explanation:
when run step 1:
x is 1010101111001101 (binary) // 1010(a) 1011(b) 1100(c) 1101(d)
go to step 2:
c is 1011110011010000 (binary) // 1011(b) 1100(c) 1101(d) 0000(0)
finally go to step 3:
a is 101111001101 (binary) // 1011(b) 1100(c) 1101(d)

Casting uint8_t array into uint16_t value in C

I'm trying to convert a 2-byte array into a single 16-bit value. For some reason, when I cast the array as a 16-bit pointer and then dereference it, the byte ordering of the value gets swapped.
For example,
#include <stdint.h>
#include <stdio.h>
main()
{
uint8_t a[2] = {0x15, 0xaa};
uint16_t b = *(uint16_t*)a;
printf("%x\n", (unsigned int)b);
return 0;
}
prints aa15 instead of 15aa (which is what I would expect).
What's the reason behind this, and is there an easy fix?
I'm aware that I can do something like uint16_t b = a[0] << 8 | a[1]; (which does work just fine), but I feel like this problem should be easily solvable with casting and I'm not sure what's causing the issue here.
As mentioned in the comments, this is due to endianness.
Your machine is little-endian, which (among other things) means that multi-byte integer values have the least significant byte first.
If you compiled and ran this code on a big-endian machine (ex. a Sun), you would get the result you expect.
Since your array is set up as big-endian, which also happens to be network byte order, you could get around this by using ntohs and htons. These functions convert a 16-bit value from network byte order (big endian) to the host's byte order and vice versa:
uint16_t b = ntohs(*(uint16_t*)a);
There are similar functions called ntohl and htonl that work on 32-bit values.
This is because of the endianess of your machine.
In order to make your code independent of the machine consider the following function:
#define LITTLE_ENDIAN 0
#define BIG_ENDIAN 1
int endian() {
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
}
So for each case you can choose which operation to apply.
You cannot do anything like *(uint16_t*)a because of the strict aliasing rule. Even if code appears to work for now, it may break later in a different compiler version.
A correct version of the code could be:
b = ((uint16_t)a[0] << CHAR_BIT) + a[1];
The version suggested in your question involving a[0] << 8 is incorrect because on a system with 16-bit int, this may cause signed integer overflow: a[0] promotes to int, and << 8 means * 256.
This might help to visualize things. When you create the array you have two bytes in order. When you print it you get the human readable hex value which is the opposite of the little endian way it was stored. The value 1 in little endian as a uint16_t type is stored as follows where a0 is a lower address than a1...
a0 a1
|10000000|00000000
Note, the least significant byte is first, but when we print the value in hex it the least significant byte appears on the right which is what we normally expect on any machine.
This program prints a little endian and big endian 1 in binary starting from least significant byte...
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void print_bin(uint64_t num, size_t bytes) {
int i = 0;
for(i = bytes * 8; i > 0; i--) {
(i % 8 == 0) ? printf("|") : 1;
(num & 1) ? printf("1") : printf("0");
num >>= 1;
}
printf("\n");
}
int main(void) {
uint8_t a[2] = {0x15, 0xaa};
uint16_t b = *(uint16_t*)a;
uint16_t le = 1;
uint16_t be = htons(le);
printf("Little Endian 1\n");
print_bin(le, 2);
printf("Big Endian 1 on little endian machine\n");
print_bin(be, 2);
printf("0xaa15 as little endian\n");
print_bin(b, 2);
return 0;
}
This is the output (this is Least significant byte first)
Little Endian 1
|10000000|00000000
Big Endian 1 on little endian machine
|00000000|10000000
0xaa15 as little endian
|10101000|01010101

Setting bits in a bit stream

I have encountered the following C function while working on a legacy code and I am compeletely baffled, the way the code is organized. I can see that the function is trying to set bits at given position in bit stream but I can't get my head around with individual statements and expressions. Can somebody please explain why the developer used divison by 8 (/8) and modulus 8 (%8) expressions here and there. Is there an easy way to read these kinds of bit manipulation functions in c?
static void setBits(U8 *input, U16 *bPos, U8 len, U8 val)
{
U16 pos;
if (bPos==0)
{
pos=0;
}
else
{
pos = *bPos;
*bPos += len;
}
input[pos/8] = (input[pos/8]&(0xFF-((0xFF>>(pos%8))&(0xFF<<(pos%8+len>=8?0:8-(pos+len)%8)))))
|((((0xFF>>(8-len)) & val)<<(8-len))>>(pos%8));
if ((pos/8 == (pos+len)/8)|(!((pos+len)%8)))
return;
input[(pos+len)/8] = (input[(pos+len)/8]
&(0xFF-(0xFF<<(8-(pos+len)%8))))
|((0xFF>>(8-len)) & val)<<(8-(pos+len)%8);
}
please explain why the developer used divison by 8 (/8) and modulus 8 (%8) expressions here and there
First of all, note that the individual bits of a byte are numbered 0 to 7, where bit 0 is the least significant one. There are 8 bits in a byte, hence the "magic number" 8.
Generally speaking: if you have any raw data, it consists of n bytes and can therefore always be treated as an array of bytes uint8_t data[n]. To access bit x in that byte array, you can for example do like this:
Given x = 17, bit x is then found in byte number 17/8 = 2. Note that integer division "floors" the value, instead of 2.125 you get 2.
The remainder of the integer division gives you the bit position in that byte, 17%8 = 1.
So bit number 17 is located in byte 2, bit 1. data[2] gives the byte.
To mask out a bit from a byte in C, the bitwise AND operator & is used. And in order to use that, a bit mask is needed. Such bit masks are best obtained by shifting the value 1 by the desired amount of bits. Bit masks are perhaps most clearly expressed in hex and the possible bit masks for a byte will be (1<<0) == 0x01 , (1<<1) == 0x02, (1<<3) == 0x04, (1<<4) == 0x08 and so on.
In this case (1<<1) == 0x02.
C code:
uint8_t data[n];
...
size_t byte_index = x / 8;
size_t bit_index = x % 8;
bool is_bit_set;
is_bit_set = ( data[byte_index] & (1<<bit_index) ) != 0;

Categories

Resources