实际上,当我使用指向字符的指针时,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;
}
答案 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表达式中的括号位于不同的位置。所以他们做不同的事情并不令人惊讶。