c - 试图理解 C 中的 extern

标签 c declaration definition extern

通过给出的答案here ,我正在尝试构建一个工作示例并尝试理解外部链接的行为。

这是我的例子:

extern.h

extern int global_counter;

int file_counter;

extern.c

#include "extern.h"

int global_counter = 5;

incrementor1.c

#include "extern.h"
#include <stdio.h>

void incrementor1_global()
{
    global_counter++;
    printf("From Incrementor1...\n");
    printf("Global counter is: %d\n", global_counter);
}

void incrementor1_local()
{
    file_counter++;
    printf("From Incrementor1...\n");
    printf("File counter is: %d\n", file_counter);
}

incrementor2.c

#include "extern.h"
#include <stdio.h>

void incrementor2_global()
{
    global_counter++;
    printf("From Incrementor2...\n");
    printf("Global counter is: %d\n", global_counter);
}

void incrementor2_local()
{
    file_counter++;
    printf("From Incrementor2...\n");
    printf("File counter is: %d\n", file_counter);
}

您可以自己将它们放入单独的文件中,如图所示。这是我的 main.c:

ma​​in.c

void incrementor1_global();
void incrementor1_local();

void incrementor2_global();
void incrementor2_local();

int main(char argc, char* argv[])
{
    incrementor1_global();
    incrementor2_global();

    incrementor1_local();
    incrementor2_local();
}

因为我将 .o 文件链接到我的 main,所以我只是声明函数,因为它们默认是外部的,所以据我所知,它们是由链接器链接的。最后,如果您想自己尝试,这里有一个 makefile。

CC=gcc

CFLAGS=-I.

DEPS = extern.h

OBJ = main.o extern.o incrementor1.o incrementor2.o  

%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

main: $(OBJ)
    gcc -o $@ $^ $(CFLAGS)


.PHONY: clean

clean:
    rm -f *.o  

因此根据上面链接的答案,我认为 global_counter 应该由所有翻译单位共享,而每个翻译单位都应该有自己的 file_counter 副本。但我得到的输出是:

From Incrementor1...
Global counter is: 6
From Incrementor2...
Global counter is: 7
From Incrementor1...
File counter is: 1
From Incrementor2...
File counter is: 2

所以,我认为两个 incrementor 都有一个 file_counter 副本,这是一个全局变量。但是现在我不知道如何复制给定的答案并让他们拥有自己的副本。有什么建议吗?

顺便说一下,如果我想在 extern.h 中给 file_counter 一个定义,因为它包含在两个增量器中,我会遇到多重定义错误。这让我更加困惑如何让他们拥有共享副本。

最佳答案

由于暂定定义,您遇到了问题

引用 C11,章节 §6.9.2

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.

尝试使用 -fno-common 标志进行编译,它应该会抛出与 file_counter 重新定义相关的错误。

引用online manual

-fno-common

In C code, this option controls the placement of global variables defined without an initializer, known as tentative definitions in the C standard. Tentative definitions are distinct from declarations of a variable with the extern keyword, which do not allocate storage.

Unix C compilers have traditionally allocated storage for uninitialized global variables in a common block. This allows the linker to resolve all tentative definitions of the same variable in different compilation units to the same object, or to a non-tentative definition. This is the behavior specified by -fcommon, and is the default for GCC on most targets. On the other hand, this behavior is not required by ISO C, and on some targets may carry a speed or code size penalty on variable references.

The -fno-common option specifies that the compiler should instead place uninitialized global variables in the data section of the object file. This inhibits the merging of tentative definitions by the linker so you get a multiple-definition error if the same variable is defined in more than one compilation unit. Compiling with -fno-common is useful on targets for which it provides better performance, or if you wish to verify that the program will work on other systems that always treat uninitialized variable definitions this way.

关于c - 试图理解 C 中的 extern,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43112580/

相关文章:

Swift View Controller 声明返回 nil

file - 在文件名中,是点。文件扩展名的一部分?

c - block 内变量的声明也是定义吗?

c - 使用 fgets() 对 C 程序进行利用

c# - vs2012 c# 与unix c server socket通信

java - 在同一个java文件中使用两个类

functional-programming - "launch the missiles"的起源是什么?

c - Qemu-Arm 文件访问?

c - 警告 : comparison with string literals results in unspecified behaviour

C++ - 函数范围内的函数声明?