如何声明静态变量但不定义它

4
有时候我们需要预先声明一个静态变量并且后续使用它,但是这个声明的变量名可能是错误的,编译器无法检测到,出现问题了!
例如:
/* lots of codes */
static some_type some_name; /* pre-declaration */
                            /* but it may define "some_name" */
/* use some_name */

/* lots of codes */

static some_type someName = initialization; /* definition */
/* use someName */

/* lots of codes */

"some_name"和"someName"是不同的,我们在开始时使用了错误的变量。如果预声明语句未定义任何内容,编译器将检测到错误。

那么,如何声明静态变量但不定义它?我该如何将预声明更改为新的声明,以便编译器可以检测到错误的名称?


4
静态意味着它在编译时加载。为什么你不想初始化它? - Woot4Moo
2
Woot4Moo: 在C语言中将全局变量声明为'static'的作用是使其在当前的"编译单元"(通常是一个.c文件及其所有头文件)之外不可见。所有未被声明为'extern'的全局变量都会在编译时分配空间。 - Anton
7个回答

9

gcc在您所描述的情况下会发出警告:

./x.c:3010: warning: 'someName' defined but not used

解决方案:继续做你目前正在做的事情,但不要忽略编译器警告;)
编辑:根据您更新的问题:不,我不认为有一种简单地声明静态变量的方法(而不必定义它)。
常见的解决方案是确保所有全局范围变量仅声明一次,并在需要时使用初始化器。

"someName" 也被使用了,我稍微修改了我的帖子,谢谢。 - Lai Jiangshan

8
static some_type some_name; /*definition */

静态变量some_name已被初始化为0;这是定义,而不仅仅是声明。

在C语言中,我认为静态变量不能仅使用extern说明符进行声明,因为它的链接始终是内部的。


1
我认为您没有正确使用 "IMO"。那听起来不像是一个观点。 - Chris Lutz
1
这是Prasoon的观点;它也符合标准,因此他的观点是正确的(根据标准)。 - Jonathan Leffler

5

在C语言中,无法创建具有内部链接的对象的非定义声明(即您术语中的“预声明”)。

最接近此功能的是试探性定义,这就是您示例中所使用的方法。但如果出现拼写错误,则试探性定义会隐式产生独立定义,而不是链接器错误。


谢谢。这正是我所需要的。顺便提一下,我不得不查找“tentative definition”的定义。这篇帖子最终给了我所有我需要的信息:https://dev59.com/fHA75IYBdhLWcg3wy8fr#3095957 - mattgately

2

一些背景知识:

正如其他人所指出的那样,静态变量具有内部链接,这意味着它们只能在同一“编译单元”或源文件中使用。这意味着您不能在头文件中声明它,在一个编译单元中为其分配一个值,并期望该值出现在另一个编译单元中。

当您初始化全局变量(静态或非静态)时,编译器只是将初始值放入可执行文件中为变量分配的内存位置。换句话说,它总是有一个初始值。当然,您始终可以使用赋值语句覆盖该值。

建议:

如果您真的不知道变量的值在编译时是多少,那么您应该在初始化函数中动态分配它。

static some_type some_variable; /* = 0 by default */

/* some code */

void MyInitializations()
{
    some_variable = some_value;
}

如果您想在一个地方声明变量,比如头文件中,在源文件中定义它,则应使用'extern'声明。这告诉编译器不要担心变量的位置。链接器将找到变量的位置,就像在另一个文件中找到函数并填充地址一样。
extern some_type some_variable;

源文件1:

void UseSomeVariable()
{
    x = some_variable;
}

源文件2:

some_type some_variable = some_value;

/* possible also uses some_variable */

如果您只想在一个地方声明变量并在另一个地方定义它,请不要使用“static”关键字。这样做的缺点是,您不能在不同的编译单元(.c文件)中使用相同的全局变量,并且您不能在头文件中使用它。


0

你需要预先声明变量吗?如果不需要,那么将初始化程序放在唯一的声明上。如果你的初始化程序不是一个常量(这需要 C++ 而不是 C,如果我没记错的话),那么我可以理解为什么你需要在一些使用它的函数之前预先声明它。但是,你需要在初始化程序之前预先声明所有需要的内容。

因此,将常量定义和静态变量放在每个文件的顶部,这样静态变量的初始化程序就可以紧接着常量后面了。然后你就不需要单独的初始化行了。

在任何其他情况下,caf 是正确的:值得让你的代码编译通过 gcc,以获得其警告的好处。我曾经为 C++ 中的 MFC GUI 编写过 g++ 的编译代码。(编译而非运行!)

据我所知,C 没有一种方法来编写弱定义,如果没有带有初始化程序的定义,则会产生错误。因为变量进入 BSS 部分时总是有一个隐式的 0。


如果您的初始化器不是常量(这需要C++,而不是C,如果我没记错的话) 并非完全如此。它可以是链接时间常量,例如函数指针。例如,如果函数需要访问此变量(交叉引用变量和函数),则可能需要“预声明”。尽管C不支持该语法。 - Robin Hsu

0
在C++中(而不是在C中),可以使用未命名命名空间来代替static来实现。声明如下:
namespace {
    extern some_type some_name;
}

extern使得这个非定义但是,由于未命名的命名空间,some_name仍然具有内部链接,就像声明为static一样(参考:https://en.cppreference.com/w/cpp/language/storage_duration#Linkage)。

相应的定义只是这样:

namespace {
    some_type some_name = initialization;
}

-1

如果我理解你的问题,也许你只是没有以正确的方式面对它。

使用extern SomeType someVar_;可能更好,它告诉你的程序,我知道这个变量会被知道,但现在我不想告诉你它是什么。

因此,在另一个文件中,你可以声明你的变量,比如说

static SomeType SomeVar_;

在你的文件中,放入

extern SomeType SomeVar_

然后在任何你想要初始化的地方进行初始化。


3
如果将一个具有static定义的变量声明为extern,那么会导致连接错误,因为该变量的内部链接性使得它无法被声明在另一个文件中的extern访问到。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接