C - 外部、静态、包含

C - extern, static, include

C 中的作用域非常混乱。我有一个变量:"int qwe"。此 var 应该在一个或多个文件中可见 - f1.c 在这种情况下,但在另一个 f2.c .

中不可见

假设我有:main.c、f1.c、f2.c、header.h

主要: 呼叫 f1(); 呼叫 f2();

header:

#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED

int qwe = 1;
void f1();
void f2();
#endif // HEADER_INCLUDED

f1.c:

#include <stdio.h>

extern int qwe;

void f1(){
    printf("In f1: %d\n", qwe);
}

f2.c:

#include <stdio.h>

static int qwe = 2;

void f2(){
    printf("In f2: %d\n", qwe);
}

现在这变得令人困惑。有定义和声明。我在 header 中定义了 qwe,在 f1.c 中声明了它。那是对的吗?是否应该在 header 中定义并在 f1.c 中声明?我尝试了后一种情况,但出现错误 - "multiple definition of qwe"。当我从 f1.c 中删除 #include 指令时,它起作用了……当我删除 extern 关键字时它也起作用。 extern 是多余的吗?

f2.c 我想它没问题并且表现符合预期,是吗?但是,如果我将#include 与header 放在一起,它就会中断。这是为什么?

什么时候应该在源文件中使用#include?如果我不包含它 f1.c 或 f2.c 它会起作用...

此外,如果我在函数中将变量定义为静态变量,例如 static int i = 0;当函数存在时,这个变量不会被销毁,它会保留它的内存和数据。下次调用相同的函数时,将可以访问它,对吗?但是 var 不会重新初始化为 0,即定义的行不会执行。正确吗?

生活折磨着我:(

在header中声明变量。在例如 f1.c 中,定义变量,例如int qwe = 1; // 在全局范围内。

在所有想要 see/change qwe 的文件中,包含声明变量的 header。 Easy-peasy.

I have defined qwe in the header, declared it in f1.c. Is that correct?

不对,应该是反过来的。您应该在单个翻译单元中定义一个全局变量(这是极客对 .C 文件的说法),但您可以根据需要在任意多个翻译单元中声明它。由于 headers 可能包含在许多翻译单元中,因此声明进入 headers。

When should i use #include in source files?

当 header 有您的翻译单元编译所需的任何内容时,您就可以这样做,只有极少数例外。请注意,在某些情况下,可能需要或希望手动进行前向声明而不包括 header.

Also, if I define a variable as static inside a function, like static int i = 0; This variable will not be destroyed when function exist, it will keep it's memory and data.

没错,函数内部的静态变量只会被赋初始值一次,并且只要你的程序继续运行.[=16就会一直保留你赋给它的值=]

在 C 中,您通常应该使用 header 文件来 声明 数据而不是 定义 数据。您不想在 header 中定义全局数据,因为它会在多个模块中被冗余定义。 header 向多个模块指示某处存在某些数据或函数及其类型,以及通用常量和宏 (#defines)。除此之外,C 中的事情非常简单。从技术上讲,几乎任何东西都是全局的,除非你声明它 static,将它的范围限制在它在 定义的 中的模块中。 extern 中数据的声明 headers 和函数原型帮助编译器知道这些项目正在被特定模块访问以及访问的数据类型是什么,以便可以生成正确的代码。

你有:

  • 两个函数 f1f2 在不同的模块中定义 使用 main。所以这些需要在header文件中声明

  • 一个全局数据项qwe被多个模块使用。

  • A static qwe 在一个模块中使用。

假设您希望使用一个 header 文件完成此操作(您可能需要单独的文件用于 f1f2 和全局数据 - 见下文),您可以设置 你的 header 为:

#ifndef MY_HEADER_H
#define MY_HEADER_H

extern int qwe;

void f1(void);
void f2(void);
#endif // MY_HEADER_H

然后在你的 main.c:

...
#include "my_header.h"

int qwe;    // This is global and can be accessed from other modules

void main(...)
{
    // call f1
    // call f2
   ...

我只是在main.c中任意定义了全局变量qwe。如果你有几个全局变量,你可以在它们自己的 glob_data.c 模块中 定义 它们,例如,并拥有它自己的 header, glob_data.h, 声明它们。任何其他需要访问它们的模块都将包含 glob_data.h header 以便可以在该模块上正确地进行编译以访问该数据。将全局数据保存在单独的 headers 中有助于处理像你有一个 static 数据实例与全局数据实例存在冲突的情况。当你想用 static 项目编译时,你可以避免包含该全局项目的数据 header 文件。

然后在你的C文件中,f1.c:

...system headers included...
#include "my_header.h"

void f1() {
    printf("In f1: %d\n", qwe);
}

并且在 f2.c 中:

...system headers included...
#include "my_header.h"  // Only if it doesn't contain `extern int qwe;`

static int qwe = 2;   // This hides the global qwe and is known only
                      // to f2.c

void f2(){
    printf("In f2: %d\n", qwe);
}

正如我上面提到的,您可能希望将函数原型和全局数据声明分开在单独的 header 中。这样,您可以只包含每个模块中需要的内容并避免冲突,例如当您有 static int qwe; 与全局 int qwe;.

您需要声明 header 中的变量,并且定义 一个并且只有一个 [=15] =] 文件.

在 C 语言中,您不能使用不 "belong" 给定翻译单元(源文件)的变量。所以它必须在整个程序中恰好由一个翻译单元定义。

当您将变量声明为 extern 时,您是在告诉编译器该符号(可能)在您的翻译单元(c 文件)外部。

可能还值得注意的是,当您尝试声明一个变量 而没有 extern 时,该变量 也被定义 , 例如:

 /* Declares, but does not defines external symbol 'foo' */
 extern int foo;

 /* Both declares AND defines bar */
 int bar;

这也不同于函数在 "default" 声明语法未定义函数的情况下的工作方式:

 /* Declare, but don't define spam */
 void spam(void);

 /* Declare, but don't define eggs */
 extern void eggs(void);

 /* Declare & define 'cheese' */
 void cheese(void){ return; }

因此您的示例应该更像这样:

qwe.h:

#ifndef QWE_H
#define QWE_H
/* Declare qwe here */
extern int qwe;
#endif

f1.c:

/* DEFINE qwe here */
int qwe = 1;

f2.c:

#include "qwe.h"  /*header includes the `extern int qwe` declaration */
void my_function(void)
{
   /* use external symbol here! */
   qwe = 10;
}

范围界定系统并不那么混乱。规则是这样的:

  • 如果您在 .c 文件中定义某些内容,您程序中的每个其他 .c 文件都可以访问它(它被放在全局命名空间中)。

  • 如果在其定义前指定static,则只有SAME文件中的内容才能看到它。这应该是您不希望其他 .c 文件能够访问的所有函数和变量的默认位置。

记住 extern 只是告诉编译器在当前文件中没有定义有问题的 variable/function 是非常重要的。这可以防止编译器因为找不到符号而发出错误,但这与作用域无关——你的 .c 文件会看到全局命名空间中的所有内容,如果你没有遵循规则的第二部分,你很快就会在 link 时间发现这件事。

头文件同样与作用域无关。它们只是放置一堆 extern 语句和宏的方便地方。