定义和声明应该匹配吗?

时间:2015-07-17 08:22:14

标签: c

在我的.h文件中我有

2.times do
  owner = Owner.create(name: Faker::Name.name)
  owner.save

   3.times do
    shop = Shop.create
    shop.name = Faker::Company.name
    shop.owner = owner
    shop.address = "#{Faker::Address.street_address} #{Faker::Address.building_number}"
    shop.save
   end
   owner.save
end

在我的.c文件中

extern int a[4];

这有什么问题吗?

声明和定义大小很重要?不对吗?

如果我在其中一个文件中写int a[10]; ,那么输出是什么? 这是未定义的行为吗?

3 个答案:

答案 0 :(得分:7)

如果在源文件中包含头文件,则a的两个声明必须与C表示的相同类型:

  

(C11,6.7p4)“在相同范围内引用同一对象或函数的所有声明都应指定兼容类型。”

即使两个声明都在两个翻译单元中,它们也需要具有相同的类型:

  

(C11,6.2.7p2)“引用同一对象或函数的所有声明都应具有兼容类型;否则,行为未定义。”

答案 1 :(得分:6)

看起来像这样:

extern int a[4];
int a[10];

int main()
{
    return 0;
}

gcc报告a:

的冲突类型
cc -Wall -g -ggdb -pipe -pedantic -std=gnu99    test.c   -o test
test.c:2:5: error: conflicting types for ‘a’
int a[10];
     ^
test.c:1:12: note: previous declaration of ‘a’ was here
extern int a[4];
            ^

答案 2 :(得分:0)

正如@ouah所说,它是正式的未定义行为,并且容易出错所以它永远不会存在于生产代码中

但它会被正确的结果所接受,但大多数(如果不是全部)常见的编译器(gcc,clang,msvc)

如果在包含extern int a[4];的.c中包含包含int a[10];的.h文件,则会收到错误,因为您正在将其重新定义为其他类型(正如其他人已经说过的那样)。

如果只在其他编译单元中包含.h,链接器应忽略大小并正确链接。

只需在定义它的.c中获得sizeof(a) == 10 * sizeof(int),在包含.declaring的其他编译单元中获得sizeof(a) == 4 * sizeof(int)

工作示例:

foo.c:

#include <stdio.h>

int a[10];

void display();

int main() {
    for(int i=0; i<sizeof(a)/sizeof(a[0]); i++) {
        a[i] = i;
    }
    printf("sizeof(a)=%d\n", sizeof(a));
    display();
    return 0;
}

foo2.c:

#include <stdio.h>

extern int a[4];

void display() {
    printf("sizeof(a)=%d\n", sizeof(a));
    for(int i=0; i<sizeof(a)/sizeof(a[0]); i++) {
        printf(" %2d", a[i]);
    }
    fputs("\n", stdout);
}

编译+链接:cc foo.c foo2.c -o foo:甚至没有警告

执行:

sizeof(a)=40
sizeof(a)=16
  0  1  2  3

这通常用于fortran的公共区域,其中编译单元只能声明一个公共的开头,但我无法想象C中这样的恐怖的真实用例。

它起作用的原因

编译器无法在编译时检测到同一程序中存在不兼容类型的声明,因为它们位于不同的转换单元,因此处理但编译阶段不同 - 可能在不同的时间。

在链接时,链接器只能看到a的不同声明的地址,并确保所有.o(或.obj)都获得相同的地址。在不破坏多语言兼容性的情况下,很难做到不同:它是在C模块和汇编语言之间共享数组的方式。

为什么你不应该使用它

你可以说,当面对标准定义为未定义的行为时,没有什么能阻止编译器执行写操作。但汉斯帕斯特曾经给我一个article on research for future compilers的链接。以下是一些摘录:

本文是关于C抽象机器的一种新的内存安全解释,它提供了更强大的保护,有利于安全性和调试 ... [作者]证明C的内存安全实现不仅可以支持指定的C抽象机,还可以支持与现有代码兼容的更广泛的解释。通过在硬件中强制执行模型,我们的实现提供了可用于为C ...提供高级安全属性的内存安全性。

[实现]内存功能表示为三元组(基本,绑定,权限),它松散地打包成256位值。这里base为虚拟地址区域提供了一个偏移量,并且限制了访问区域的大小...特殊功能加载和存储指令允许将功能溢出到堆栈或存储在数据结构中,就像指针一样...不允许指针扣除的警告。

添加权限允许标记授予引用内存的某些权限。例如,内存功能可能具有读取数据和功能的权限,但不能写入它们(或者只是写入数据而不是写入数据)。尝试任何不允许的操作都会导致陷阱。

[]结果证实,可以保留功能系统内存模型的强语义(提供不可绕过的内存保护),而不会牺牲低级语言的优势。

(强调我的)

TL / DR:没有什么可以阻止未来的编译器为对象(已编译)模块内的数组添加大小信息,如果它们不兼容则会引发错误。目前存在对这些特征的研究