C 定义和声明应该匹配吗?

C 定义和声明应该匹配吗?,c,C,在我的.h文件中,我有 extern int a[4]; 在我的.c文件中 int a[10]; 这有什么问题吗 声明和定义大小问题?不对 如果我在其中一个文件中写入sizeof(a),输出是什么? 这是未定义的行为吗?如果在源文件中包含头文件,a的两个声明必须与C声明的类型相同: (C11,6.7p4)“同一范围内引用同一对象或函数的所有声明应指定兼容类型。” 即使两个声明位于两个翻译单元中,它们也需要具有相同的类型: (C11,6.2.7p2)“引用同一对象或函数的所有声明应具有兼容类型

在我的.h文件中,我有

extern int a[4];
在我的.c文件中

int a[10];
这有什么问题吗

声明和定义大小问题?不对

如果我在其中一个文件中写入
sizeof(a)
,输出是什么?
这是未定义的行为吗?

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

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

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

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

看起来是这样的:

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

int main()
{
    return 0;
}
gcc报告的冲突类型为:

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];
            ^

它是@ouah所说的形式上未定义的行为,并且容易出错,因此它不应该存在于生产代码中

但它将被接受,结果正确,但大多数(如果不是全部的话)通用编译器(gcc、clang、msvc-do)

如果包含包含
extern int a[4]的.h文件inta[10]的.c中的code>您将得到一个错误,因为您正在将重新定义为不同的类型(正如其他人已经说过的)

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

您只需在定义它的.c中获得
sizeof(a)==10*sizeof(int)
,并在包含.declaration的其他编译单元中获得
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;
}
这在fortran的commons中通常使用,在这里编译单元只能声明公共的开始,但我无法想象C中出现这样一种恐怖的真实用例


它起作用的原因是什么 编译器无法在编译时检测到同一程序中存在不兼容类型的声明,因为它们位于不同的翻译单元中,因此会被处理,但编译阶段不同,可能在不同的时间

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

为什么你不应该使用它 您可以说,当标准定义为未定义的行为时,没有什么可以阻止编译器执行写操作。但是汉斯·帕桑曾经给我一个链接到一个。以下是一些摘录:

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

[实现]内存功能表示为三元组(基本、绑定、权限),松散地打包为256位值。在这里,base提供了到虚拟地址区域的偏移量,而bound限制了所访问区域的大小。。。特殊的能力加载和存储指令允许能力溢出到堆栈或存储在数据结构中,就像指针一样。。。需要注意的是,指针减法是不允许的

权限的添加允许功能成为向引用内存授予某些权限的令牌。例如,内存功能可能具有读取数据和功能的权限,但不具有写入权限(或仅写入数据而不具有写入功能)。尝试任何不允许的操作将导致陷阱

[结果]证实,在不牺牲低级语言优势的情况下,有可能保留能力系统内存模型(提供不可绕过的内存保护)的强大语义

(强调我的)


TL/DR:没有什么可以阻止未来的编译器在对象(已编译)模块内为数组添加大小信息,如果它们不兼容,则会引发错误。目前已有研究表明这些特征必须匹配。为什么要找麻烦?@n.m.问题是如果他们不匹配怎么办?编译器对此很满意。。但是分配的内存是10*sizeof(int),所以sizeof(a)应该是10*sizeof(int)或者sizeof(a),为什么不试试呢?似乎它甚至没有编译:
冲突的类型为'a'
,您将由谁来为您投票?这个问题没有研究effort@Gopi:测试比等待答案要快。如果你手头没有IDE,那么像我一样使用在线IDE。。这样说吧:我能够比任何人都更快地在线测试,而你在开始写问题之前就已经领先了。你怎么可能这么快就需要答案,而你却没有任何能力去编码呢?加上1,做OP在发布之前应该自己做的事情question@musefan如果.h不包含在.c中,而是包含在另一个.c中,则即使是未定义的行为,也不会出现错误。@ouah您完全正确(参见我的答案中的一个工作示例),但看起来C程序员不喜欢这样的笑话:-(我对这里“兼容”的用法很感兴趣。有没有不一样的类型,但仍然符合这个要求?也许不同的简历资格?@Quentin type compatibility本质上是“类型相同”+一些其他情况。具有不同限定符的类型不兼容:例如,
extern const int x;
int x=42;
不兼容
#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);
}
sizeof(a)=40
sizeof(a)=16
  0  1  2  3