Understanding memcpy

12,648

Solution 1

A few problems with your code as it stands:

  • You copy 4 bytes, but the destination is type int. Since int is not guaranteed to be any particular size, you need to make sure that it is at least 4 bytes long before you do that sort of memcpy.
  • memcpy works on the byte level, but integers are a series of bytes. Depending on your target architecture, the bytes within an integer may be arranged differently (big-endian, little-endian, etc). Using memcpy on integers may or may not do what you expect. It's best to use byte arrays when learning how memcpy and friends work.
  • Your second memcpy uses pB+1 as the target. This doesn't advance the pointer one byte, it advances it by sizeof(*pB) bytes. In this case, that leaves it pointing to an invalid address (past the end of the variable). This call to memcpy will corrupt random memory, which can crash your program or cause unpredictable results.

Solution 2

I don't think memcpy() is designed for what you want. In general, you would use memcpy() to copy one or more whole objects (where an object might be an int, a char, a long long, etc.)

    int a[4] = { 1, 2, 3, 4 };
    int b[3];
    int c[5] = { 0 };

    ::memcpy(b, a, 3 * sizeof(int));   // b is { 1, 2, 3 }
    ::memcpy(c+2, b, 3 * sizeof(int)); // c is { 0, 0, 1, 2, 3 }

c+2 is not "c + 2 bytes". It is "c + 2 ints" (8 bytes on a Win32/x86 system).

You can access the individual bytes by casting to a char or unsigned char pointer, but I don't recommend that unless you really understand what you're doing as there are many pitfalls.

    unsigned x = 0;
    unsigned char *px = reinterpret_cast<unsigned char *>(&x);

    px[0] = 0xFF;
    px[2] = 0xAA;

One of the dangers here is that you are assuming knowledge about how the computer stores an integer. On an x86 system, x will be 0x00AA00FF but on a Sun Sparc system it will be 0xFF00AA00.

if you need to set parts of an integer it is often better to use "or" and "shift".

    x = (0xFF<<24) | (0xAA<<8);

will give you 0xFF00AA00 on any architecture. 0xFF<<24 shifts the value 0xFF by 24 bits to the left, making 0xFF000000. 0xAA<<8 shifts the value 0xAA by 8 bits to the left, making 0x0000AA00.

We "or" them together, giving 0xFF00AA00.

Solution 3

Look into pointer arithmetic: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html . Adding a value to a pointer actually incrememnts by that many units of whatever size you're dealing with.

Share:
12,648
Greg G
Author by

Greg G

Updated on August 03, 2022

Comments

  • Greg G
    Greg G almost 2 years
    int a = 10;
    int* pA = &a;
    long long b = 200;
    long long* pB = &b;
    
    memcpy (pB,pA,4);
    memcpy (pB+1,pA,4);
    
    cout<<"I'm a memcpy!: "<<*(pB)<<endl;
    

    I'm doing some tests with memcpy to teach myself how memory works. What I am trying to do is make b = to "1010". I can copy the value from a to b, but then I try to offset the memory by 1 byte and write another 10 but it doesn't work it only outputs "10".

    What would I need to do to get a value of 1010?

  • Greg G
    Greg G about 12 years
    Cool I learned a lot. I do have one question though, you say that an int isnt' garanteed to take up 4bytes...I thought it was? Is 4bytes just the limit that it can take up? When I create an int and intilize it to a value does it only use the number of bits, or bytes nesccary?
  • Michael J
    Michael J about 12 years
    Int size is machine and compiler dependent. I can't think of a modern compiler/CPU that don't have 4-byte ints, but I can think of lots of old ones. It is likely that future machines will have bigger ints. When we get 128-bit machines, ints will probably be 64 bits (8-bytes).
  • bta
    bta about 12 years
    @user1372122- The basic types short, int, long, and long long don't have standardized sizes. int has a fixed size, but that fixed size varies from platform to platform (for example between x86 and MIPS CPUs, or between 32-bit and 64-bit OSes). There are datatypes, however, that are guaranteed to have specific sizes. They have names like uint32_t and int16_t (for a 32-bit unsigned integer and 16-bit signed integer, respectively) and they come from stdint.h.
  • user207421
    user207421 over 6 years
    You appear to be commenting on this answer. You should make that clear. Your own answer assumes an x86 recent enough to have SIMD instructions.