Search code examples
cbufferasciimemcpy

How can I copy 4 letter ascii word to buffer in C?


I am trying to copy the word: 0x0FF0 to a buffer but unable to do so.
Here is my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <time.h>
#include <linux/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>


void print_bits(unsigned int x);

int main(int argc, char *argv[])
{


    char buffer[512];

    unsigned int init = 0x0FF0;
    unsigned int * som = &init;

    printf("print bits of som now: \n");
    print_bits(init);
    printf("\n");

    memset(&buffer[0], 0, sizeof(buffer)); // reinitialize the buffer
    memcpy(buffer, som, 4); // copy word to the buffer

    printf("print bits of buffer[0] now: \n");
    print_bits(buffer[0]);
    printf("\n");


    return 0;
}

void print_bits(unsigned int x)
{
    int i;
    for (i = 8 * sizeof(x)-17; i >= 0; i--) {
        (x & (1 << i)) ? putchar('1') : putchar('0');
    }
    printf("\n");
}  

this is the result I get in the console:
enter image description here

Why am I getting different values from the bit printing if I am using memcpy?

Don't know if it has something to do with big-little-endian but I am losing 4 bits of 1's here so in both of the methods it shouldn't happen.


Solution

  • You are mixing up types and relying on specific settings of your architecture/platform; This already breaks your existing code, and it may get even more harmful once you compile with different settings.

    Your buffer is of type char[512], while your init is of type unsigned int. First, it depends on the settings whether char is signed or unsigned char. This is actually relevant, since it influences how a char-value is promoted to an unsigned int-value. See the following code that demonstrated the difference using explicitly signed and unsigned chars:

    signed char c = 0xF0;
    unsigned char uc = c;
    unsigned int ui_from_c = c;
    unsigned int ui_from_uc = uc;
    
    printf("Singned char c:%hhd; Unsigned char uc:%hhu; ui_from_c:%u ui_from_uc:%u\n", c, uc, ui_from_c,ui_from_uc);
    // output: Singned char c:-16; Unsigned char uc:240; ui_from_c:4294967280 ui_from_uc:240
    

    Second, int may be represented by 4 or by 8 bytes (which can hold a "word"), yet char will typically be 1 byte and can therefore not hold a "word" of 16 bit. Third, architectures can be big endian or little endian, and this influences where a constant like 0x0FF0, which requires 2 bytes, would actually be located in a 4 or 8 byte integral representation.

    So it is for sure that buffer[0] selects just a portion of that what you think it does, the portion might get promoted in the wrong way to an unsigned int, and it might even be a portion completely out of the 0x0FF0-literal.

    I'd suggest to use fixed-width integral values representing exactly a word throughout:

    #include <stdio.h>
    #include <stdint.h>
    
    void print_bits(uint16_t x);
    
    int main(int argc, char *argv[])
    {
    
    
        uint16_t buffer[512];
    
        uint16_t init = 0x0FF0;
        uint16_t * som = &init;
    
        printf("print bits of som now: \n");
        print_bits(init);
        printf("\n");
    
        memset(buffer, 0, sizeof(buffer)); // reinitialize the buffer
        memcpy(buffer, som, sizeof(*som)); // copy word to the buffer
    
        printf("print bits of buffer[0] now: \n");
        print_bits(buffer[0]);
        printf("\n");
    
    
        return 0;
    }
    
    void print_bits(uint16_t x)
    {
        int i;
        for (i = 8 * sizeof(x); i >= 0; i--) {
            (x & (1 << i)) ? putchar('1') : putchar('0');
        }
        printf("\n");
    }