Converting uint16_t to decimal and storing it in char array, bitwise. Error in retrieving the data in uint16_t variable

1.5k views Asked by At
int main()  
{  
    unsigned char ptr[9];  
    uint16_t dat = 128, s;  
    int i, j = 1, k;  

    ptr[0] = ptr[0] & 0;  
    j = -1;  

    for(i = 0;i <= 15;i++)  
    {  
        if(i % 8 == 0)
            j++;

        s = dat << i;  

        if (s & (1 << 15))  
            ptr[j] |= 1 << (7 - i%8);  
        else  
            ptr[j] |= 0 << (7 - i%8);  
    }  

    j = -1;  
    s &= 0;  

    for(i = 0;i <= 15;i++)  
    {         
        if(i % 8 == 0)  
            j++;  
        s |= (ptr[j] & (1 << (7 - i%8))) << (15 - i);  // <-----------  
        s |= (ptr[j] & (1 << i%8)) << (15 - i); // Correction
    }

    printf("%d", s);  

    return(0);
}

I am trying to pack a 16 byte integer into a character array by first converting into binary and then copying it into the array bit by bit. However, I am having trouble with the marked line. I am getting 16834 as the output. What am I doing wrong here?

1

There are 1 answers

1
Jonathan Leffler On BEST ANSWER

As written, the output is indeterminate because you never set ptr[1] to a known value. On my Mac, this minor variation of your code (primarily added some, headers, spaces and printf() statements):

#include <stdint.h>
#include <stdio.h>

int main(void)
{
    unsigned char ptr[9];
    uint16_t dat = 128, s;
    int i, j = 1;

    ptr[0] = ptr[0] & 0;
    j = -1;
    printf("0x%.2X 0x%.2X\n", ptr[0], ptr[1]);

    for (i = 0; i <= 15; i++)
    {
        if (i % 8 == 0)
            j++;

        s = dat << i;

        if (s & (1 << 15))
            ptr[j] |= 1 << (7 - i % 8);
        else
            ptr[j] |= 0 << (7 - i % 8);
    }
    printf("0x%.2X 0x%.2X\n", ptr[0], ptr[1]);

    j = -1;
    s &= 0;

    for (i = 0; i <= 15; i++)
    {
        if (i % 8 == 0)
            j++;
        s |= (ptr[j] & (1 << (7 - i % 8))) << (15 - i);  // <-----------
    }

    printf("0x%.2X\n", s);
    printf("%d\n", s);

    return(0);
}

shows the output:

0x00 0xF4
0x00 0xF4
0x5510
21776

When modified to replace ptr[0] = ptr[0] & 0; with ptr[0] = ptr[1] = 0;, the output is:

0x00 0x00
0x00 0x80
0x4000
16384

You're unlucky that your machine zeroes the stack; it is going to much harder for you to spot mistakes like uninitialized variables.

Your choice of 128 as a test value makes life difficult; there is only one bit set. Were I testing, I'd use a distinctive pattern such as 0x3CA5 (0b0011'1100'1010'0101 in C++14 binary notation). When I change the code to use 0x3CA5, I get the output:

0x00 0x00
0x3C 0xA5
0x5411
21521

The good news: the first loop copies the bits so that the high-order byte of the number is in ptr[0] and the low-order byte is in ptr[1]. The bad news: the second loop seems to mangle things horribly.

Here's an instrumented version of the second loop:

    for (i = 0; i < 16; i++)
    {
        if (i % 8 == 0)
            j++;
        uint16_t s0 = s;
        s |= (ptr[j] & (1 << (7 - i % 8))) << (15 - i);  // <-----------
        printf("i: %2d, j: %d, 7-i%%8: %d, ptr[j]: 0x%.2X, &val: 0x%.2X, "
               "|val = 0x%.5X, s0: 0x%.4X, s: 0x%.4X\n",
               i, j, 7 - i % 8, ptr[j], (1 << (7 - i % 8)),
               (ptr[j] & (1 << (7 - i % 8))) << (15 - i),
               s0, s);
    }

The output is:

0x00 0x00
0x3C 0xA5
i:  0, j: 0, 7-i%8: 7, ptr[j]: 0x3C, &val: 0x80, |val = 0x00000, s0: 0x0000, s: 0x0000
i:  1, j: 0, 7-i%8: 6, ptr[j]: 0x3C, &val: 0x40, |val = 0x00000, s0: 0x0000, s: 0x0000
i:  2, j: 0, 7-i%8: 5, ptr[j]: 0x3C, &val: 0x20, |val = 0x40000, s0: 0x0000, s: 0x0000
i:  3, j: 0, 7-i%8: 4, ptr[j]: 0x3C, &val: 0x10, |val = 0x10000, s0: 0x0000, s: 0x0000
i:  4, j: 0, 7-i%8: 3, ptr[j]: 0x3C, &val: 0x08, |val = 0x04000, s0: 0x0000, s: 0x4000
i:  5, j: 0, 7-i%8: 2, ptr[j]: 0x3C, &val: 0x04, |val = 0x01000, s0: 0x4000, s: 0x5000
i:  6, j: 0, 7-i%8: 1, ptr[j]: 0x3C, &val: 0x02, |val = 0x00000, s0: 0x5000, s: 0x5000
i:  7, j: 0, 7-i%8: 0, ptr[j]: 0x3C, &val: 0x01, |val = 0x00000, s0: 0x5000, s: 0x5000
i:  8, j: 1, 7-i%8: 7, ptr[j]: 0xA5, &val: 0x80, |val = 0x04000, s0: 0x5000, s: 0x5000
i:  9, j: 1, 7-i%8: 6, ptr[j]: 0xA5, &val: 0x40, |val = 0x00000, s0: 0x5000, s: 0x5000
i: 10, j: 1, 7-i%8: 5, ptr[j]: 0xA5, &val: 0x20, |val = 0x00400, s0: 0x5000, s: 0x5400
i: 11, j: 1, 7-i%8: 4, ptr[j]: 0xA5, &val: 0x10, |val = 0x00000, s0: 0x5400, s: 0x5400
i: 12, j: 1, 7-i%8: 3, ptr[j]: 0xA5, &val: 0x08, |val = 0x00000, s0: 0x5400, s: 0x5400
i: 13, j: 1, 7-i%8: 2, ptr[j]: 0xA5, &val: 0x04, |val = 0x00010, s0: 0x5400, s: 0x5410
i: 14, j: 1, 7-i%8: 1, ptr[j]: 0xA5, &val: 0x02, |val = 0x00000, s0: 0x5410, s: 0x5410
i: 15, j: 1, 7-i%8: 0, ptr[j]: 0xA5, &val: 0x01, |val = 0x00001, s0: 0x5410, s: 0x5411
0x5411
21521

Except that the values in s0 and s are not what you want, and ignoring |val, the values are what we'd expect; the basic iteration structure is under control. However, the |val column shows that there is a problem — it was unexpected to need to use 5 hex digits in the output format! The i - 15 shift is not placing bits where you want and need them placed.

From here, you need to resolve the problem for yourself. What I hope this does is provide you with some insight in how to go about debugging such problems.

Incidentally, the question has been updated to offer:

s |= (ptr[j] & (1 << i%8)) << (15 - i); // Correction

In my test harness with 0x3CA5 as the initial value in dat, that produces the output:

0x00 0x00
0x3C 0xA5
i:  0, j: 0, 7-i%8: 7, ptr[j]: 0x3C, &val: 0x80, |val = 0x00000, s0: 0x0000, s: 0x0000
i:  1, j: 0, 7-i%8: 6, ptr[j]: 0x3C, &val: 0x40, |val = 0x00000, s0: 0x0000, s: 0x0000
i:  2, j: 0, 7-i%8: 5, ptr[j]: 0x3C, &val: 0x20, |val = 0x08000, s0: 0x0000, s: 0x8000
i:  3, j: 0, 7-i%8: 4, ptr[j]: 0x3C, &val: 0x10, |val = 0x08000, s0: 0x8000, s: 0x8000
i:  4, j: 0, 7-i%8: 3, ptr[j]: 0x3C, &val: 0x08, |val = 0x08000, s0: 0x8000, s: 0x8000
i:  5, j: 0, 7-i%8: 2, ptr[j]: 0x3C, &val: 0x04, |val = 0x08000, s0: 0x8000, s: 0x8000
i:  6, j: 0, 7-i%8: 1, ptr[j]: 0x3C, &val: 0x02, |val = 0x00000, s0: 0x8000, s: 0x8000
i:  7, j: 0, 7-i%8: 0, ptr[j]: 0x3C, &val: 0x01, |val = 0x00000, s0: 0x8000, s: 0x8000
i:  8, j: 1, 7-i%8: 7, ptr[j]: 0xA5, &val: 0x80, |val = 0x00080, s0: 0x8000, s: 0x8080
i:  9, j: 1, 7-i%8: 6, ptr[j]: 0xA5, &val: 0x40, |val = 0x00000, s0: 0x8080, s: 0x8080
i: 10, j: 1, 7-i%8: 5, ptr[j]: 0xA5, &val: 0x20, |val = 0x00080, s0: 0x8080, s: 0x8080
i: 11, j: 1, 7-i%8: 4, ptr[j]: 0xA5, &val: 0x10, |val = 0x00000, s0: 0x8080, s: 0x8080
i: 12, j: 1, 7-i%8: 3, ptr[j]: 0xA5, &val: 0x08, |val = 0x00000, s0: 0x8080, s: 0x8080
i: 13, j: 1, 7-i%8: 2, ptr[j]: 0xA5, &val: 0x04, |val = 0x00080, s0: 0x8080, s: 0x8080
i: 14, j: 1, 7-i%8: 1, ptr[j]: 0xA5, &val: 0x02, |val = 0x00000, s0: 0x8080, s: 0x8080
i: 15, j: 1, 7-i%8: 0, ptr[j]: 0xA5, &val: 0x01, |val = 0x00080, s0: 0x8080, s: 0x8080
0x8080
32896

This is not the complete answer.

I am trying to get the value of dat into s. I need it because I have to send the char array over a UDP socket. So at one end I will store integer as binary in a character array and then on the other end reassemble it as an integer.

You don't need to do bitwise operations to achieve the result you are after; bytewise operations work fine.

ptr[0] = dat >> 8;
ptr[1] = dat & 0xFF;  // You could get away without the '& 0xFF'

// Transmit over UDP

dat = (ptr[0] << 8) | ptr[1];

It is important that you use unsigned char (which you are doing); if you use plain char (or, worse, signed char), then you have to worry about sign bits and sign extension.

As an exercise for you in bit manipulation, this has some purpose; it is worth you spending some time to fix your code so it works. As a way of manipulating the data for transmission over the network, it is not a good way of working and should be jettisoned once you've learned what you can about bit manipulation.

One way to fix the problem — not necessarily the best, by any stretch of my imagination, but one which works — is to fix the the LHS of the lhs << (15 - i) shift so that it is either 0 or 1 that is shifted. For example:

((ptr[j] & (1 << (7 - i % 8))) ? 1 : 0) << (15 - i)