【问题标题】:Different pointer value before and after return返回前后指针值不同
【发布时间】:2021-03-26 22:55:00
【问题描述】:

我有一个奇怪的问题,我使用 malloc 分配了内存并返回了这个新分配的内存的地址。但是这个地址在return前后不一样(函数内外)。

这里是代码(只关心最后 3 行):

char* InfoFile_getValue(char* projectName, char* key)
{
    char* returnValue = NULL;

    char projectInfoPath[200];
    sprintf(projectInfoPath,"projects/%s/info.txt",projectName);

    FILE* fp = fopen(projectInfoPath,"r");

    if (fp != NULL) {
        char* ptr;
        size_t len = 0;
        char* lineFromFile = NULL;
        while(getline(&lineFromFile, &len, fp)!=-1)
        {
            if (strstr(lineFromFile,key))
            {
                ptr = lineFromFile + strlen(key);
                ptr = ptr + strspn(ptr, ": ");
                returnValue = malloc(strlen(ptr)+1);
                strcpy(returnValue,ptr);
                break;
            } 
        }
        free(lineFromFile);
        fclose(fp);
    }
    
    printf("Inside size: %d\n",sizeof(returnValue));
    printf("String address inside function %p\n", returnValue);
    return returnValue;
}

然后我调用这个函数:

char* baseString = InfoFile_getValue(projectName, "base");
printf("Outside size: %d\n",sizeof(baseString));
printf("String address outside function: %p\n",baseString);
printf("%s\n", baseString);

我使用 CC 和以下标志编译了这个:

pkg-config --cflags gtk+-3.0 -Wno-incompatible-pointer-types -Wno-int-conversion -Wno-discarded-qualifiers pkg-config --libs gtk+-3.0 -lpthread -lm

它给出了以下结果:

Inside size: 8
String address inside function 0x5567d9ff4540
Outside size: 8
String address outside function: 0xffffffffd9ff4540
Naruszenie ochrony pamięci (zrzut pamięci)

看起来它将此地址削减为 4 个字节并在其前面加上 0xff,但我不知道为什么,我从未遇到过这样的问题。任何建议都会有所帮助。

【问题讨论】:

  • sizeof(returnValue) = pointer 的 sizeof,而不是它指向的内容。只是说。不知道这是否是意图,但无论如何,这就是你得到的。
  • 是的,我想检查指针的大小以确保它到处都是 8 个字节。只是为了确保。
  • 通常你只会在调用者没有为调用做适当的准备时看到这样的东西。即,它没有合适的原型,并假设 int 返回,热情的警告-sqelchers 通过硬铸造它来“修复”它。在这种情况下,如果intchar* 的大小不同(x64 平台的典型情况),则实际上只获得了 64 位结果中的 32 位;余数来自幻象int 的msb。但我在这里看不到这样的恶作剧。使用原型,这段代码看起来很合适。如果您声称的内容属实,则应该使用InfoFile_getValue 进行复制,大部分内容都会被毁掉。这是真的吗?
  • 谢谢大家,你们太快了!是的,我错过了正确的功能原型。 InfoFile_getValue 在单独的文件中,我忘记包含“infoFile.h”。我最终需要学会阅读警告或以某种方式将隐式声明警告视为错误。再次感谢,你太棒了!
  • 嗯。你的编译器应该告诉你这是缺失的。如果没有,您需要调高警告级别:-Wall -Wextra。当然,您需要注意警告。

标签: c pointers


【解决方案1】:

您似乎得到了地址低 32 位的符号扩展。只是一个疯狂的猜测,但我认为编译器将 InfoFile_getValue 视为返回 int 的函数,而指针长度为 64 位。

规则是函数必须在使用前声明。如果不是,C 假定它被声明为int func(),这是一个接受任何参数并返回一个 int 的函数。你应该确保你有:

char* InfoFile_getValue(char* projectName, char* key);

之前包含char* baseString = InfoFile_getValue(projectName, "base");的函数(可能是main)

【讨论】:

    【解决方案2】:

    这看起来像符号扩展,所以这很像 C90 隐式 int 错误。也就是说,编译器认为该函数返回 int 的值为 0xd9ff4540,在 32 位系统上很可能是负 2 的补码。然后不知何故,由于%p,它被转换为64位,你得到了符号扩展。

    解决这个错误的最简单方法已经停止使用 C90,它已经有 30 年的历史了,坏掉了而且很危险。仅仅摆脱隐式 int 就足以将您的代码移植到标准 C 中。

    如果您使用它,请确保函数声明和定义相同,并且调用者可以看到函数声明。然后最大化编译器警告并注意它们。

    【讨论】:

      【解决方案3】:

      由于调用者准备不足,您遇到了值截断和符号扩展。 IE。由于缺乏适当的原型,调用者不知道实际的函数返回类型。除非您密切关注 C 语言,否则这可能会变得更糟,尤其是在处理在 intvoid* 大小相同的平台上似乎可以正常工作的代码库时。

      最简单的重现此情况的案例如下,并记录了必须发生的事情。

      ma​​in.c

      #include <stdio.h>
      
      int main()
      {
          void *p = foo();
          printf("main: p = %p\n", p);
      }
      

      foo.c

      #include <stdio.h>
      
      void *foo()
      {
          static int x;
          void *p = &x;
          printf("foo: p = %p\n", p);
          return p;
      }
      

      在为 x86(intvoid* 通常大小相同)和 x64(int 通常为 32 位,void 为 64 位)构建后执行上述代码将暴露这两个问题以及在前一种情况下如何错过这一点的微妙之处。两者都应该显示与此类似的警告(无论如何您都应该将其视为错误)

      1>main.c(13): warning C4013: 'foo' undefined; assuming extern returning int
      1>main.c(13): warning C4047: 'initializing': 'void *' differs in levels of indirection from 'int'    
      

      在两个平台上运行的结果(显然这里的值可能因您的系统而异)

      x86

      foo: p = 00A38138
      main: p = 00A38138
      

      x64

      foo: p = 00007FF7E7B0C160
      main: p = FFFFFFFFE7B0C160
      

      有了它,您有望看到两件事情的重要性:

      1. 始终确保您在源代码中调用的函数在使用前已正确原型化。
      2. 始终将警告作为错误进行编译以捕获此类问题。

      可能最重要的事情是,如果不执行上述两项操作,代码 still 似乎在 x86 上运行良好,并且 still在 x64 上编译。前者会让你产生一种虚假的成就感,而后者只是进一步证实了这一点,让追查这样的问题特别烦人。让编译器帮助你。确保 (1) 和 (2) 在您的日常生活中。

      【讨论】:

      • 谢谢,这正是发生的事情。我从 x86 迁移到 x64,之后我遇到了这个问题。现在我学到了一些新东西并添加了 -Werror-implicit-function-declaration 以防止将来出现类似的错误。
      猜你喜欢
      • 2021-08-22
      • 2018-05-30
      • 1970-01-01
      • 2017-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多