【问题标题】:memcpy fails but assignment doesn't on character pointersmemcpy 失败,但分配不在字符指针上
【发布时间】:2010-11-01 20:32:05
【问题描述】:

实际上,当我使用指向字符的指针时,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;
}

【问题讨论】:

  • C 还是 C++?我认为有太多的 C 运行时库 (CRT) 调用和原始指针,无法成为 C++ 代码。
  • 我看不到任何 C++,所以我删除了 C++ 标签。如果我错了,请纠正我。
  • @sbi:你错了。该代码使用 new[] 来分配内存,并且仅限于 C++。虽然新的很容易被malloc取代。

标签: c pointers char memcpy


【解决方案1】:

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

【讨论】:

  • 是的,在这种特殊情况下很难看到这个问题的影响,因为charIn == ppc == &pccharIn2 == ppc2 = &pc。所以它们都指向同一个地方;两个分配都被取消引用,*charIn=stat*charIn2=dyn。所以最终结果是pc == *charIn == *charIn2 == dyn。我认为这不是 OP 的意图,但它最终掩盖了静态与堆栈分配的问题。
【解决方案2】:

char stat[5];

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

【讨论】:

    【解决方案3】:

    就像 Preet 所说的,我认为问题不在于 memcpy。在您的函数“setStaticAndDynamicPointers”中,您正在设置一个指向在该函数调用的堆栈上创建的自动变量的指针。到函数退出时,“stat”变量指向的内存将不复存在。结果,第一个参数 **charIn 将指向不存在的东西。也许您可以在此处阅读有关堆栈帧(或激活记录)的更详细信息:link text

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        请注意,赋值语句中表达式中的括号与 memcpy 表达式中的括号位于不同的位置。所以他们做不同的事情也就不足为奇了。

        【讨论】:

          【解决方案6】:

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

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

          #2 每个指针变量都有一个类型,它指示指向的数据类型。一个char * 指向一个charint * 指向 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
          

          这是可行的,因为数组只是一个相同元素的列表(在本例中为 ints),按顺序排列在单个连续的内存块中。指针开始指向数组中的第一个元素。然后,指针算法允许我们将指针逐个元素地推进数组。这适用于任何类型的指针(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)
          

          我希望解释是有帮助的。祝你好运!

          【讨论】:

            【解决方案7】:

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

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

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

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

            然后,访问冲突消失了,我得到了正确的结果。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-08-15
              • 1970-01-01
              • 2022-06-15
              • 2015-02-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多