tags:

views:

81

answers:

7

What's the best/recommended way to copy data between a byte array and an integer in C? Currently I'm using memcpy, which doesn't feel right to me. A sample of the sort of thing I'm doing is below.

struct alpha {
        unsigned char byte_array[20];
}

void function(struct alpha *st) {
        unsigned long num;

        /* Do some stuff */

        memcpy(st->byte_array, &num, sizeof(unsigned long));

        /* Do more stuff */

        memcpy(&num, st->byte_array, sizeof(unsigned long));
}

I assume I want to use casts somehow, but I'm not confident of how casting and pointer (de)referencing interacts, particularly when arrays get involved.

+1  A: 

It looks exactly the right way to me. Which is to say, when I had to do this, it was the way I did it.

Brian Hooper
+1 for your definition of the "right way".
JeremyP
A: 
Michael Dorgan
That's something like what I expected, but isn't `st->byte_array[0]` the same as `*(st->byte_array)`, which is a byte, rather than a pointer? The `(unsigned long *)` cast appears to be casting a byte to (a pointer to an unsigned long), whereas I'd thought I'd want to cast (a pointer to a byte) to (a pointer to an unsigned long)…
me_and
Dang it - forgot the ampersand. Wish this place had a quick syntax compiler :)
Michael Dorgan
The [0] subscripts are incorrect. The proper cast would be to convert the byte array into an unsigned-long pointer; if you need to index k bytes into it, *((unsigned long *)(st->byte_array+k)) = num; or if you need to index k longwords into it, ((unsigned long *)st->byte_array)[0] = num;
supercat
A: 

It is not wrong, unless you know that "who" wrote the data into the array wrote them in an endianness different from the one used on your system. Say, e.g., if those data come from a "stream" sent by "someone" over the net. Then, if the protocol uses the so called "network byte order" (namely big endian), and your machine is not big endian, then you obtain wrong values.

ShinTakezou
A: 

Is there any particular reason you need to copy instead of just aliasing the two? e.g.:

union XXX { char byte_array[20]; unsigned long num; };

In theory, you don't get defined results when you write to one member of the union then read from the other member. In reality, there's essentially no possibility of getting anything different from what you're getting now -- except (of course) that you don't have to copy data to get from one view to the other -- you just use x.num to look at it as an unsigned long, and x.byte_array to look at it as an array of bytes.

Jerry Coffin
A: 

I prefer some default functions for this requirement,

for string to ineger

int atoi ( const char * str );

and for string to integer

char *  itoa ( int value, char * str, int base );


#include <stdio.h>
#include <stdlib.h>

int main()
{
 unsigned long int number;
 unsigned char string[256];

 printf ("Enter a number: ");
 fgets ( string, 256, stdin );
 number = atoi (string);
 printf("number = %d\n",number);

 //converting int to string
 itoa (number,string,10);    //10 is base here for decimal, 16 is used for Hex and 2 for Binary
 printf ("string = %s\n",string);

 return 0;
}

as per me atoi() function is fine. But in case you don't want to use itoa() or it is not available to you then you can just use sprintf()

sprintf (string,"%ld",number);

I hope it helps

Thanks Alok.kr.

Kumar Alok
atoi and itoa are for converting numbers to their ASCII representation and vice versa. This has nothing to do with the question which is about copying bytes without conversion.
JeremyP
+2  A: 

memcpy is the standard and portable tool for that effect. Modern optimized compilers will inline this call to something well adapted to your situation, e.g data types, allignement, size (if known at compile time), processor... So I think you should definitively stick to that and not mess around with some handmade optimizations.

Jens Gustedt
This is 100% correct.
caf
+1  A: 

Here's how to do it with casts:

    /* Do some stuff */

    *(unsigned long *)st = num;

    /* Do more stuff */

    num = *(unsigned long *)st;

You're casting your struct pointer to a pointer to an unsigned long, then dereferencing the pointer in the assignment statements.

tomlogic
That seems to assume that the array is always the first element in the structure. It is here, but that's really just a simplified example…
me_and
@me_and: true. Replace `st` with `st->byte_array` to reference the byte array.
tomlogic
Much worse though, it assumes that `st->byte_array` is correctly aligned for access as an `unsigned long`.
caf
@caf: Byte order is thankfully not an issue for my application.
me_and
@me_and: This isn't about byte order - it's the fact that on many processors, a `long` can't be directly read from just any arbitrary memory location, but only one that is a multiple of the required *alignment* (which is typically the same as the size of the `long`). An `unsigned char` array does not necessarily have the correct alignment. This is why your original `memcpy()` solution is correct, and this one is not.
caf
@caf: good point on alignment -- I spend most of my time doing embedded development on platforms without alignment requirements.
tomlogic