memcpy失败但是赋值不在字符指针上

时间:2010-11-01 20:32:06

标签: c pointers char memcpy

实际上,当我使用指向字符的指针时,memcpy工作正常,但当我使用指向字符的指针时停止工作。

有人可以帮助我理解为什么memcpy在这里失败了,或者更好的是,我怎么能自己想出来的。我发现很难理解我的c / c ++代码中出现的问题。

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;
}

7 个答案:

答案 0 :(得分:4)

您的评论意味着char stat[5]应该是静态的,但事实并非如此。结果charIn指向在堆栈上分配的块,当您从函数返回时,它超出了范围。您的意思是static char stat[5]吗?

答案 1 :(得分:1)

在处理指针时,你必须牢记以下两点:

#1 指针本身指向的数据分开。指针只是一个数字。这个数字告诉我们,在内存中,我们可以找到一些其他数据块的开头。指针可用于访问指向的数据,但我们也可以操纵指针本身的值。当我们增加(或减少)指针本身的值时,我们将指针的“目标”从它最初指向的位置向前(或向后)移动。这将我们带到第二点......

#2 每个指针变量都有一个类型,表示指向的数据类型。 char *指向char; int *指向int;等等。指针甚至可以指向另一个指针(char **)。类型很重要,因为当编译器将算术运算应用于指针值时,它会自动考虑指向的数据类型的 size 。这允许我们使用简单的指针算法处理数组:

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

这是有效的,因为数组只是一个相同元素的列表(在本例中为int s),按顺序排列在一个连续的内存块中。指针开始指向数组中的第一个元素。指针算术然后允许我们逐个元素地推进指针通过数组。这适用于任何类型的指针(void *上不允许算术运算)。

实际上,这正是编译器如何转换数组索引器运算符[]的使用。对于带有解除引用运算符的指针添加,它实际上是简写。

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

那么,这一切与你的问题有什么关系呢?

这是你的设置......

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

目前,我已通过删除对setStaticAndDynamicPointers的调用进行了简化。 (这个功能也存在问题 - 所以请查看@Nim的答案,以及我在那里的评论,了解有关该功能的其他详细信息。)

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

这很有效,因为(*ppc)说“给我任何ppc点”。这相当于ppc[0]。这一切都完全有效。

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

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

有问题的部分 - 正如其他人指出的那样 - 是&(*ppc[1]),字面意思是“给我一个指向任何ppc [1]指向的指针”。

首先,让我们简化......运算符优先级表示:&(*ppc[1])&*ppc[1]相同。然后,&*会反转并相互抵消。因此&(*ppc[1])简化为ppc[1]

现在,鉴于上述讨论,我们现在已经准备好了解为什么这不起作用:简而言之,我们正在将ppc视为指向一个数组指针,实际上它只指向一个指针。

当编译器遇到ppc[1]时,它应用上面描述的指针算法,并提供一个指向紧跟在变量pc之后的内存的指针 - 无论内存可能包含什么。 (此处的行为始终未定义)。

所以问题不在于memcopy()。您对memcpy(&c,&(*ppc[1]),1)的调用是从虚假指针ppc[1]指向的内存中尽职地复制1字节(按要求),并将其写入字符变量c

正如其他人所指出的那样,你可以通过移动括号来解决这个问题:

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

我希望这个解释很有帮助。祝你好运!

答案 2 :(得分:0)

char stat[5];

是一个超出范围的堆栈变量,它是 // sets the first arg to a pointer to static memory.。你需要malloc / new一些内存,它将abcd放入其中。就像你对charIn2

所做的那样

答案 3 :(得分:0)

就像Preet所说的那样,我认为问题不在于memcpy。在函数“setStaticAndDynamicPointers”中,您将设置指向在该函数调用的堆栈上创建的自动变量的指针。当函数退出时,“stat”变量指向的内存将不再存在。结果,第一个参数** charIn将指向不存在的东西。也许你可以在这里详细阅读堆栈框架(或激活记录):link text

您已经有效地在该代码中创建了一个指向堆栈变量的悬空指针。如果要测试将值复制到堆栈var中,请确保它在调用函数中创建,而不是在被调用函数中创建。

答案 4 :(得分:0)

除了'stat'的定义外,我眼中的主要问题是*ppc[3](*ppc)[3]不同。你想要的是后者(ppc指向的字符串中的第四个字符),但是在你的memcpy()中你使用前者,“字符串数组”ppc中第四个字符串的第一个字符(显然ppc不是一个char*的数组,但你强制编译器将其视为这样)。

在调试此类问题时,我通常会发现打印所涉及的内存地址和内容很有帮助。

答案 5 :(得分:0)

虽然之前的答案提出了有效点,但我认为您需要注意的另一件事是memcpy时的运算符优先级规则:

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

这里发生了什么?它可能不是你想要的。数组符号takes higher precedence比解除引用运算符,因此您首先尝试执行等效于ppc2++的指针运算。然后,您取消引用值并将地址传递到memcpy这与 (*ppc2)[1]不同。我的机器上的结果是访问冲突错误(XP / VS2005),但通常这是未定义的行为。但是,如果您以与先前相同的方式取消引用:

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

然后访问违规就消失了,我得到了正确的结果。

答案 6 :(得分:0)

请注意,赋值语句中表达式中的括号与memcpy表达式中的括号位于不同的位置。所以他们做不同的事情并不令人惊讶。