gpt4 book ai didi

C 在不同文件中定义的相同全局变量

转载 作者:太空狗 更新时间:2023-10-29 16:33:46 27 4
gpt4 key购买 nike

我正在阅读 this code from here (用中文(表达)。 C中有一段代码是关于测试全局变量的。变量a已在文件 t.h 中定义已包含两次。在文件中 foo.c定义了一个 struct b有一些值(value)和 main功能。在 main.c文件,定义了两个未初始化的变量。

/* t.h */
#ifndef _H_
#define _H_
int a;
#endif

/* foo.c */
#include <stdio.h>
#include "t.h"

struct {
char a;
int b;
} b = { 2, 4 };

int main();

void foo()
{
printf("foo:\t(&a)=0x%08x\n\t(&b)=0x%08x\n
\tsizeof(b)=%d\n\tb.a=%d\n\tb.b=%d\n\tmain:0x%08x\n",
&a, &b, sizeof b, b.a, b.b, main);
}

/* main.c */
#include <stdio.h>
#include "t.h"

int b;
int c;

int main()
{
foo();
printf("main:\t(&a)=0x%08x\n\t(&b)=0x%08x\n
\t(&c)=0x%08x\n\tsize(b)=%d\n\tb=%d\n\tc=%d\n",
&a, &b, &c, sizeof b, b, c);
return 0;
}

使用Ubuntu GCC 4.4.3编译后,结果如下:
foo:    (&a)=0x0804a024
(&b)=0x0804a014
sizeof(b)=8
b.a=2
b.b=4
main:0x080483e4
main: (&a)=0x0804a024
(&b)=0x0804a014
(&c)=0x0804a028
size(b)=4
b=2
c=0

变量 ab两个函数的地址相同,但大小为 b已经改变。我无法理解它是如何工作的!

最佳答案

您违反了 C 的“一个定义规则”,结果是未定义的行为。 “一个定义规则”并没有在标准中正式说明。我们正在查看不同源文件(又名,翻译单元)中的对象,因此我们关注“外部定义”。阐明了“一个外部定义”语义(C11 6.9 p5):

An external definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.



这基本上意味着您最多只能定义一个对象 一次 . (如果它从未在程序中的任何地方使用过,则 else 子句允许您根本不定义外部对象。)

请注意,您对 b 有两个外部定义。 .一种是您在 foo.c 中初始化的结构,另一个是 main.c中的暂定定义,(C11 6.9.2 p1-2):

If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.



所以你有多个 b 的定义.但是,还有另一个错误,因为您定义了 b与不同的类型。首先请注意,允许对具有外部链接的同一对象进行多个声明。但是,当在两个不同的源文件中使用相同的名称时,该名称指的是同一个对象(C11 6.2.2 p2):

In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function.



C 对同一对象的声明进行了严格限制(C11 6.2.7 p2):

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.



由于 b 的类型在您的每个源文件中实际上并不匹配,行为是未定义的。 (什么构成兼容类型在C11 6.2.7中都有详细描述,但基本上归结为类型必须匹配。)

所以你有两个错误 b :
  • 多重定义。
  • 具有不兼容类型的多个声明。

  • 从技术上讲,您对 int a 的声明在您的两个源文件中也违反了“一个定义规则”。请注意 a有外部链接(C11 6.2.2 p5):

    If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.



    但是,根据之前 C11 6.9.2 的引用,那些 int a暂定定义是外部定义,您只能从顶部的 C11 6.9 引用中选择其中一个。

    通常的免责声明适用于未定义的行为。任何事情都可能发生,包括您观察到的行为。

    C 的一个常见扩展是允许多个外部定义,并在信息性附录 J.5 (C11 J.5.11) 中的 C 标准中进行了描述:

    There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).



    (重点是我的。)因为 a 的定义同意,那里没有害处,但 b 的定义不同意。此扩展解释了为什么您的编译器不会提示存在多个定义。根据 C11 6.2.2 的引用,链接器将尝试协调对同一对象的多个引用。

    链接器通常使用两种模型之一来协调多个翻译单元中同一符号的多个定义。它们是“通用模型”和“引用/定义模型”。在“通用模型”中,多个具有相同名称的对象在 union 中被折叠成单个对象。样式方式使对象具有最大定义的大小。在“Ref/Def Model”中,每个外部名称必须只有一个定义。

    GNU 工具链默认使用“通用模型”和“宽松的引用/定义模型”,它对单个翻译单元强制执行严格的一个定义规则,但不会提示多个翻译单元的违规。

    可以使用 -fno-common 在 GNU 编译器中抑制“通用模型”。选项。当我在我的系统上对此进行测试时,它导致类似于您的代码的“严格引用/定义模型”行为:
    $ cat a.c
    #include <stdio.h>
    int a;
    struct { char a; int b; } b = { 2, 4 };
    void foo () { printf("%zu\n", sizeof(b)); }
    $ cat b.c
    #include <stdio.h>
    extern void foo();
    int a, b;
    int main () { printf("%zu\n", sizeof(b)); foo(); }
    $ gcc -fno-common a.c b.c
    /tmp/ccd4fSOL.o:(.bss+0x0): multiple definition of `a'
    /tmp/ccMoQ72v.o:(.bss+0x0): first defined here
    /tmp/ccd4fSOL.o:(.bss+0x4): multiple definition of `b'
    /tmp/ccMoQ72v.o:(.data+0x0): first defined here
    /usr/bin/ld: Warning: size of symbol `b' changed from 8 in /tmp/ccMoQ72v.o to 4 in /tmp/ccd4fSOL.o
    collect2: ld returned 1 exit status
    $

    我个人认为无论多个对象定义的解析模型如何,都应该始终提供链接器发出的最后一个警告,但这既不存在也不存在。

    引用文献:
    Unfortunately, I can't give you the link to my copy of the C11 Standard
    What are extern variables in C?
    The "Beginner's Guide to Linkers"
    SAS Documentation on External Variable Models

    关于C 在不同文件中定义的相同全局变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17800187/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com