2

我在 C (linux(x86_64)) 中有这样的代码:

typedef struct 
{
   char k[32];
   int v;
}ABC;

ABC states[6] = {0};

ABC* get_abc()
{
   return &states[5];
}  

在 main() 中:

int main()
{
  ABC *p = get_abc();
     .
     .
     .
  printf("%d\n", p->v); 
} 

在访问 p->v 时,我在 printf 语句中遇到分段错误。我试图从 gdb 调试它,它说“无法访问内存”。这里有一件重要的事情是,当我编译这段代码时,gcc 会在ABC *p = get_abc();上向我发出警告。我正在尝试将指针从整数转换。我的问题是我从 get_abc() 返回结构的地址,那么为什么编译器会给我这样的警告?为什么编译器将其视为整数?我认为由于此警告,我遇到了分段错误,因为整数不能在 x86_64 中保存内存地址。

任何帮助,将不胜感激。

4

2 回答 2

5

在函数之前定义get_abc原型main。如果函数原型在该函数调用之前不可用,编译器将默认将该函数视为传递int参数并返回int。这里get_abc实际上返回 8 字节地址,但该值已被抑制为 4 字节,并存储在ABC *p导致崩溃的变量中。

ABC* get_abc(); 
int main()
{
    ABC *p = get_abc();
}

注意:在 int 大小和地址大小为 4 字节的 32 位机器上不会发生这种崩溃,因为不会发生抑制。但是那个警告会在那里。

于 2013-01-29T18:11:47.420 回答
2

您还没有向我们展示您的所有代码,但我可以肯定地猜测get_abc()and函数是在单独的源文件中定义的,并且在调用中main()没有可见的 visible 声明。get_abc()main()

您应该创建一个包含以下声明的头文件get_abc()

ABC *get_abc();

并且#include该标题在定义的文件get_abc()和定义的文件中都存在main()。(您还需要标头保护。)您需要将类型的定义移动ABC到该标头。

或者,作为一种快速而简单的解决方法,您可以在定义之前添加一个显式声明main()-- 但这是一个相当脆弱的解决方案,因为它取决于您是否完全正确地获得声明。

在没有可见声明的情况下,未声明的函数被假定为 return int。编译器看到您对 的调用get_abc(),生成代码来调用它,就好像它返回了一个int,然后将该值隐式转换int为指针。欢闹随之而来。

注意:实际上没有从int指针类型到指针类型的隐式转换,除了空指针常量的特殊情况,但许多编译器出于历史原因实现了这种隐式转换。此外,“隐式int”规则在 1999 版标准中被删除,但同样,由于历史原因,许多编译器仍然实施它。您的编译器应该具有启用更好警告的选项。如果您使用 gcc,请尝试gcc -pedantic -std=c99 -Wall -Wextra.

于 2013-01-29T18:17:18.913 回答