0

Actually, memcpy works just fine when I use pointers to characters, but stops working when I use pointers to pointers to characters.

Can somebody please help me understand why memcpy fails here, or better yet, how I could have figured it out myself. I am finding it very difficult to understand the problems arising in my c/c++ code.

char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;
setStaticAndDynamicPointers(ppc, ppc2);

char c;
c = (*ppc)[1];  
assert(c == 'b');                     // assertion doesn't fail.

memcpy(&c,&(*ppc[1]),1);

if(c!='b')
  puts("memcpy didn't work.");  // this gets printed out.

c = (*ppc2)[3];
assert(c=='d');                      // assertion doesn't fail.
memcpy(&c, &(*ppc2[3]), 1);

if(c != 'd')
  puts("memcpy didn't work again.");

memcpy(&c, pc, 1);
assert(c == 'a');   // assertion doesn't fail, even though used memcpy

void setStaticAndDynamicPointers(char **charIn, char **charIn2)
{
  // sets the first arg to a pointer to static memory.
  // sets the second arg to a pointer to dynamic memory.
  char stat[5];
  memcpy(stat, "abcd", 5);
  *charIn = stat;

  char *dyn = new char[5];
  memcpy(dyn, "abcd", 5);
  *charIn2 = dyn;
}
3
  • 3
    C or C++? Way too many C runtime library (CRT) calls and raw pointers for this to be C++ code, I would think. Commented Nov 1, 2010 at 20:38
  • I can't see any C++ in that, so I removed the C++ tag. Correct me if I'm wrong. Commented Nov 1, 2010 at 20:42
  • 1
    @sbi: You are wrong. The code is using new[] to allocate memory and that is C++ only. Although the new could easily be replaced by malloc. Commented Nov 1, 2010 at 21:00

7 Answers 7

4

your comment implies that char stat[5] should be static, but it isn't. As a result charIn points to a block that is allocated on the stack, and when you return from the function, it is out of scope. Did you mean static char stat[5]?

Sign up to request clarification or add additional context in comments.

1 Comment

yes, and in this particular case it's difficult to see the effect of this problem, because charIn == ppc == &pc and charIn2 == ppc2 = &pc. So they both point to the same place; both assignments are dereferenced, *charIn=stat and *charIn2=dyn. And so the end result is that pc == *charIn == *charIn2 == dyn. I don't think that's what the OP intended, but it does end up obscuring the issue of static vs. stack allocation.
0

char stat[5];

is a stack variable which goes out of scope, it's not // sets the first arg to a pointer to static memory.. You need to malloc/new some memory that gets the abcd put into it. Like you do for charIn2

Comments

0

Just like what Preet said, I don't think the problem is with memcpy. In your function "setStaticAndDynamicPointers", you are setting a pointer to an automatic variable created on the stack of that function call. By the time the function exits, the memory pointed to by "stat" variable will no longer exist. As a result, the first argument **charIn will point to something that's non-existent. Perhaps you can read in greater detail about stack frame (or activation record) here: link text

You have effectively created a dangling pointer to a stack variable in that code. If you want to test copying values into a stack var, make sure it's created in the caller function, not within the called function.

Comments

0

In addition to the definition of 'stat', the main problem in my eyes is that *ppc[3] is not the same as (*ppc)[3]. What you want is the latter (the fourth character from the string pointed to by ppc), but in your memcpy()s you use the former, the first character of the fourth string in the "string array" ppc (obviously ppc is not an array of char*, but you force the compiler to treat it as such).

When debugging such problems, I usually find it helpful to print the memory addresses and contents involved.

Comments

0

Note that the parenthesis in the expressions in your assignment statements are in different locations from the parenthesis in the memcpy expressions. So its not too suprising that they do different things.

Comments

0

When dealing with pointers, you have to keep the following two points firmly in the front of your mind:

#1 The pointer itself is separate from the data it points to. The pointer is just a number. The number tells us where, in memory, we can find the beginning of some other chunk of data. A pointer can be used to access the data it points to, but we can also manipulate the value of the pointer itself. When we increase (or decrease) the value of the pointer itself, we are moving the "destination" of the pointer forward (or backward) from the spot it originally pointed to. This brings us to the second point...

#2 Every pointer variable has a type that indicates what kind of data is being pointed to. A char * points to a char; a int * points to an int; and so on. A pointer can even point to another pointer (char **). The type is important, because when the compiler applies arithmetic operations to a pointer value, it automatically accounts for the size of the data type being pointed to. This allows us to deal with arrays using simple pointer arithmetic:

int *ip = {1,2,3,4};
assert( *ip == 1 );    // true

ip += 2;   // adds 4-bytes to the original value of ip
           // (2*sizeof(int)) => (2*2) => (4 bytes)

assert(*ip == 3);      // true

This works because the array is just a list of identical elements (in this case ints), laid out sequentially in a single contiguous block of memory. The pointer starts out pointing to the first element in the array. Pointer arithmetic then allows us to advance the pointer through the array, element-by-element. This works for pointers of any type (except arithmetic is not allowed on void *).

In fact, this is exactly how the compiler translates the use of the array indexer operator []. It is literally shorthand for a pointer addition with a dereference operator.

assert( ip[2] == *(ip+2) );  // true

So, How is all this related to your question?

Here's your setup...

char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;

for now, I've simplified by removing the call to setStaticAndDynamicPointers. (There's a problem in that function too—so please see @Nim's answer, and my comment there, for additional details about the function).

char c;
c = (*ppc)[1];  
assert(c == 'b');     // assertion doesn't fail.

This works, because (*ppc) says "give me whatever ppc points to". That's the equivalent of, ppc[0]. It's all perfectly valid.

memcpy(&c,&(*ppc[1]),1);

if(c!='b')
    puts("memcpy didn't work.");  // this gets printed out.

The problematic part —as others have pointed out— is &(*ppc[1]), which taken literally means "give me a pointer to whatever ppc[1] points to."

First of all, let's simplify... operator precedence says that: &(*ppc[1]) is the same as &*ppc[1]. Then & and * are inverses and cancel each other out. So &(*ppc[1]) simplifies to ppc[1].

Now, given the above discussion, we're now equipped to understand why this doesn't work: In short, we're treating ppc as though it points to an array of pointers, when in fact it only points to a single pointer.

When the compiler encounters ppc[1], it applies the pointer arithmetic described above, and comes up with a pointer to the memory that immediately follows the variable pc -- whatever that memory may contain. (The behavior here is always undefined).

So the problem isn't with memcopy() at all. Your call to memcpy(&c,&(*ppc[1]),1) is dutifully copying 1-byte (as requested) from the memory that's pointed to by the bogus pointer ppc[1], and writing it into the character variable c.

As others have pointed out, you can fix this by moving your parenthesis around:

memcpy(&c,&((*ppc)[1]),1)

I hope the explanation was helpful. Good luck!

Comments

0

Although the previous answers raise valid points, I think the other thing you need to look at is your operator precedence rules when you memcpy:

memcpy(&c, &(*ppc2[3]), 1);

What happens here? It might not be what you're intending. The array notation takes higher precedence than the dereference operator, so you first attempt perform pointer arithmetic equivalent to ppc2++. You then dereference that value and pass the address into memcpy. This is not the same as (*ppc2)[1]. The result on my machine is an access violation error (XP/VS2005), but in general this is undefined behaviour. However, if you dereference the same way you did previously:

memcpy(&c, &((*ppc2)[3]), 1);

Then that access violation goes away and I get proper results.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.