0

I am trying to read two integers, stored consecutively, from a memory block (i have a pointer void *block pointing to the contents of the block) using memcpy. The first one is read just fine using:

memcpy(&test, block, sizeof(int));

I try to read the second using:

memcpy(&test, block + sizeof(int), sizeof(int));

(Of course i am having those stataments in different execution instances of the program, so the problem is not that test is being overriden)

but i fail to get the correct result! What am i doing wrong here?

nikos
  • 2,893
  • 11
  • 30
  • 39
  • Are those two snippets meant to be the same? – jimw Apr 16 '12 at 22:26
  • 1
    Please don't tell us what _doesn't_ happen -- tell us what _does_ happen. What result do you expect, and what are you seeing? How do you know the result is incorrect? Do you print it? If so, please show that code as well. – Adam Liss Apr 16 '12 at 22:30
  • 4
    You say that `block` is of the type `void*`... yet you are performing arithmetic on it. This wouldn't even compile. – Ed S. Apr 16 '12 at 22:38
  • @EdS.: It will compile on GCC without warnings, even when using `-Wall -Wextra`, which is why I always use `-Wpointer-arith`. – Dietrich Epp Apr 16 '12 at 23:14
  • @DietrichEpp: Wow.... what does it actually do to the pointer? How could a non-standards compliant piece of code like this compile on GCC without warning? – Ed S. Apr 16 '12 at 23:16
  • @EdS: GCC compiles an enormous amount of non-standards-compliant code, that's what language extensions are. For example: `__attribute__` syntax, inline assembler, labels as values, statement expressions, `typeof` syntax. For arithmetic on `void *`, according to the GCC manual GCC treats it as if `sizeof(void) == 1`. It's very convenient if you're not targeting other compilers. http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Pointer-Arith.html – Dietrich Epp Apr 16 '12 at 23:46

4 Answers4

7

This is nonstandard:

void *block = ...; 
block + sizeof(int); // where does this point to?

If you want to do pointer arithmetic, cast to a type of known size first (the pointer addition is implicitly multiplied by the size of the underlying type, and sizeof(unsigned char) is always 1):

void *block = ...;
(unsigned char *) block + sizeof(int);

Or use the even easier version,

void *block = ...;
(int *) block + 1;

So the final code is:

int test;
void *block = ...;
memcpy(&test, block, sizeof(test));
// do something...?
memcpy(&test, (int *) block + 1, sizeof(test));

Or a simpler version,

int test[2];
void *block = ...;
memcpy(&test, block, sizeof(test));

Don't do the following:

test = *(int *) block;

It will crash on some systems (typically SIGBUS) if block is unaligned.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 2
    Should be `&test` in the call(s) to `memcpy`. I am honestly curious as to which part actually solved the OP's problem as I am thoroughly confused by the question.... – Ed S. Apr 16 '12 at 22:47
  • @EdS. Fixed, thanks. It could have been arithmetic on `void *` which caused the problem, GCC handles it nicely but other compilers don't. – Dietrich Epp Apr 16 '12 at 22:56
  • I think `(int *) block + 1` may also fail if the data isn't aligned. It would be safest to use `sizeof (int) + (char*)block`. – Ben Voigt Apr 16 '12 at 23:04
  • @BenVoigt: You're right, but it will only fail on very obscure systems (ones that don't use byte addressing). I'm not sure it's worth being *that* portable, and those who work on such systems know who they are. – Dietrich Epp Apr 16 '12 at 23:12
  • @DietrichEpp: My understanding of the C++ standard is that a compiler (where the target system has a strong alignment requirement) would be completely justified in masking off the low bits of the address. "Converting a prvalue of type "pointer to `T1`" to the type "pointer to `T2`" (where `T1` and `T2` are object types **and where the alignment requirements of `T2` are no stricter than those of `T1`**) and back to its original type yields the original pointer value." from section 5.2.10 – Ben Voigt Apr 17 '12 at 02:25
  • @BenVoigt: This isn't about C++, and yes, I said that I'm aware that it's strictly speaking undefined behavior (6.3.2.3 paragraph 7 in n1256). I consider it to be practically portable, in the same way that I don't care about systems where `CHAR_BIT != 8`. – Dietrich Epp Apr 17 '12 at 03:55
1

You're reading the same memory location twice! You probably want something like the following:

int test;
memcpy(&test, (char *)block + 0*sizeof(int), sizeof(int));
printf("%d\n",test);
memcpy(&test, (char *)block + 1*sizeof(int), sizeof(int));
printf("%d\n",test);

Note that this is (intentionally) extremely verbose; a more succinct correction would be:

int test[2];
memcpy(test, block, 2*sizeof(int));
printf("%d\n%d\n",test[0],test[1]);
mbauman
  • 30,958
  • 4
  • 88
  • 123
1

The first one is read just fine using:

memcpy(&test, block + sizeof(int), sizeof(int));

I try to read the second using:

memcpy(&test, block + sizeof(int), sizeof(int));

That is the same piece of code... so it will do the same thing (assuming you have not modified test or block in between the calls, which you have not shown us).

You are overwriting the first value at &test. Assuming test is a pointer to int with enough valid memory for holding two of them...

int test[2];
memcpy(test, block, sizeof(test));

If test is just an int then...

int a, b;
memcpy(&a, block, sizeof(a));
memcpy(&b, (int*)block + 1, sizeof(b));
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • 1
    I agree here, if you need to read two ints, why not just get them over with in one memcpy call. +1 – Youssef G. Apr 16 '12 at 22:30
  • @nikos: You are copying to the location at `&test` twice in a row. `test` will hold the value of the last call, the copy is correct (though there is no reason to do it twice when you could just read the whole block in...) – Ed S. Apr 16 '12 at 22:30
  • @EdS: of course i am not doing it to the same execution instance of the program – nikos Apr 16 '12 at 22:35
  • @nikos: What do you mean "of course"? I can only see what you present to us, what else should I assume about your code? Obviously you don't understand what is going on, how can I possibly know your level of proficiency? – Ed S. Apr 16 '12 at 22:36
  • @EdS: I didn't mean to be offensive!I just said that because it was said in the question that the first line is being executed fine, so the problem could not be the override of `test` :) – nikos Apr 16 '12 at 22:39
  • @nikos: Neither did I... just saying that your question is overly vague, and making assumptions about what you are *actually* doing in code you have not shown us is a waste of time. What type is `test`? Your code as it stands would not even compile as you are performing arithmetic on a `void*` which is illegal. – Ed S. Apr 16 '12 at 22:40
  • @nikos: I gave you two code samples that should work given your situation. Did you try them? – Ed S. Apr 16 '12 at 22:43
  • @EdS: It compiles just fine using a gcc compiler! It just gives `0` as a result (while another integer is the expected answer). – nikos Apr 16 '12 at 22:43
  • @nikos: Then `block` is not a `void*`. This is part of the spec, that would be a pretty big bug (what behavior would you expect exactly when incrementing a `void*`? The size of the actual type is unknown, so arithmetic on the pointer makes no sense). – Ed S. Apr 16 '12 at 22:44
  • @EdS.: Because the standard doesn't specify what the behavior would be, it leaves the door open for individual compiler vendors to implement any behavior they choose as an extension. – Ben Voigt Apr 17 '12 at 02:27
  • @BenVoigt: Sure, I guess I was just very surprised that gcc wouldn't even utter a warning. – Ed S. Apr 17 '12 at 15:44
  • @EdS.: Did you try compiling with `-pedantic`? – Ben Voigt Apr 17 '12 at 17:28
-1

You read both ints from the same address. Also this way is too verbose. Try:

int *a = (int *)block;

Then you can get them simply by a[0] and a[1]

Israel Unterman
  • 13,158
  • 4
  • 28
  • 35
  • 7
    If `block` doesn't have the alignment required for an `int`, this is undefined behavior; `memcpy` is not. – Stephen Canon Apr 16 '12 at 22:31
  • But the question asked talked about two integer already stored within `block`... I believe they got there via assigmnent. Furthermore, memcpy copies bytes sequentially. If the numbers stored in `block` were stored via assignment, it's not guaranteed that the the copied bytes will match the integer ordering. `memcpy` is not the way to do it. – Israel Unterman Apr 17 '12 at 09:29
  • You are mistaken. memcpy ... copies memory. After copying, the byte ordering will be identical in the source and destination, which is precisely what is required. – Stephen Canon Apr 17 '12 at 12:03