在外部声明中出现警告

23
#include<stdio.h>
#include<stdlib.h>
#define GREY 1
#define BLACK 0
#define WHITE 2
typedef struct node * graph;
typedef struct stack * snode;

graph cnode(int data);          //cnode is to create a node for graph
void cgraph(void);
struct node {
        int data, color;
        struct node *LEFT, *RIGHT, *TOP, *DOWN;
};//this structure defines a node of the graph

struct stack {
struct stack *priv;
struct cgraph *graph_node;
};// this is to define a structure which should hold node of a structure

    extern snode sroot;

我定义了一个头文件(declaration.h),如上所示,以下是一个c程序(stack.c), 我正在开发这个库并将使用它。

#include<declarations.h>
void cstack (graph temp);
void stackpush(snode stemp);
extern int stack_counter = 0;

sroot=NULL;
void cstack (graph gtemp) //cstack is to create stack
{
   snode spriv,some;
  if (stack_counter==0)
  {
   sroot=stackpush(gtemp);
    spriv=sroot;
   stack_counter++;
   }
   else{
   some=cstacknode(gtemp);
    some->priv=spriv;
    spriv=some;
  }

}

//struct stack is representing a stack
//struct node is representing a node in graph

snode  cstacknode (graph gtemp)
//this function should create a node of the stack which should be storing the graph node as a pointer
{
 snode an;
 an=(snode)malloc(sizeof(snode));
 an->graph_node=gtemp;
 an->priv=NULL;
 return an;
}

void stackpush(snode stemp)
{

}

这两个文件都在同一个目录中。 我编译了上述的文件stack.c,使用如下命令:

cc -I ./ stack.c

编译时出现了以下警告:

stack.c:4: warning: ‘stack_counter’ initialized and declared ‘extern’
stack.c:6: warning: data definition has no type or storage class
stack.c:6: error: conflicting types for ‘sroot’
./declarations.h:21: note: previous declaration of ‘sroot’ was here
stack.c:6: warning: initialization makes integer from pointer without a cast
stack.c: In function ‘cstack’:
stack.c:12: warning: passing argument 1 of ‘stackpush’ from incompatible pointer type
stack.c:3: note: expected ‘snode’ but argument is of type ‘graph’
stack.c:12: error: void value not ignored as it ought to be
stack.c:13: warning: assignment makes pointer from integer without a cast
stack.c:17: warning: assignment makes pointer from integer without a cast
stack.c: At top level:
stack.c:27: error: conflicting types for ‘cstacknode’
stack.c:17: note: previous implicit declaration of ‘cstacknode’ was here
stack.c: In function ‘cstacknode’:
stack.c:32: warning: assignment from incompatible pointer type

我想知道为什么当我将变量声明为extern时,我会收到警告,我已经将其加粗,请问有何想法?如果有人想分享其他关于剩余错误的内容,请告诉我。


请参考更近期的问题如何在声明时定义extern变量? - Jonathan Leffler
我相信nmichaels的答案(在撰写本文时排名第二)是正确的答案:在头文件中声明您的extern,在c文件中不使用extern定义它。如果其他文件想要使用此变量,它们将不得不包含头文件并链接c文件。这样,您还可以有一个单独的地方来声明类型,编译器可以检查不匹配(我不确定链接器是否会注意到两个文件以不同方式声明相同的extern)。 - Thomas Guyot-Sionnest
4个回答

54

虽然你的代码存在许多相当严重和明显的错误(已在其他答案中涵盖),但你放在问题标题中的警告是完全无关紧要的、毫无意义的警告。GCC编译器以发出无用的警告著称。许多这样的警告似乎源于某人无能和完全毫无根据的信念,认为做某事在某种程度上是“错误”的,而实际上却没有任何问题。

在你的情况下,警告是由以下代码触发的:

extern int stack_counter = 0;

声明。 显然,这个警告的“作者”认为extern限定符应该保留用于非定义声明。 在这种情况下,初始化程序= 0的存在将声明转换为定义(因此正式地使extern可选)。 尽管如此,它没有错误,实际上,在这里extern可能是非常受欢迎的,以强调stack_counter旨在成为全局变量的事实。

同样,您是否需要全局变量是一个不同的问题,并且,同样,您的代码包含大量其他错误。 但是,您似乎关注的这个警告并不值得。 只需在编译器设置中禁用此警告(并请向GCC团队写一封粗鲁的信件),就可以了。


19
公平地说,这是一个警告,可以预示(并帮助解决)连接器错误(多重定义)。例如,在TU1中,创建一个全局变量并将其赋值,然后在TU2中复制声明并在该声明前加入“extern”。初始化与extern一起出现使得extern的含义不同,从而导致创建两个此变量的实例并使连接器出错。这并不是说您的观点没有价值,但这可能是警告存在的原因。 - Steven Lu
6
@StevenLu:那么,GCC在错误发生之前会发出警告,然后无论如何链接阶段仍会发生错误。太棒了。 - alecov
5
好的,像if (x=1) { ...}这样的代码是完全合法的,但在这里加上一个警告通常是有用的。 - fwyzard
2
@alecov 这就是为什么你应该始终使用“-Werror”的原因。GCC在使您的无效代码编译方面非常出色,但最好防止不正确的代码根本无法编译。 - yyny
3
旨在使用一个文件中的单个外部“定义”(例如 int a;)以及其他文件中的一个或多个“声明”(extern int a;等)。使用“extern”可以表示变量在某个其他位置被定义(可能是同一文件中,但很可能是在不同的源文件中)。 extern int a = 5 违反了这种惯用法。 - Andrey Portnoy
显示剩余11条评论

8
你的头文件中的extern声明允许除定义变量的模块外的其他模块使用它。如果它应该被定义为int stack_counter = 0并存在stack.c中,则需要这样定义并在头文件中放置extern stack_counter
在stack.c的第6行,你没有为sroot定义存储类。由于它在头文件中是externed,我假设你的意思是snode sroot=NULL
解决这些问题,然后实现stackpush(确保它不返回void)并按顺序处理其余的警告。请注意,在C语言中,你必须使用函数的前向声明(带有原型)或在使用它们之前定义函数。函数cstack可能应该是文件中的最后一个函数。

哦,好的,谢谢你指出这些错误。我已经修改了我的文件declarations.h。我想在这里发布我的两个程序,但是我无法看到任何代码标记,以便我可以发布修改后的内容。请告诉我如何进一步讨论,如何将C程序放入我的回复中,以便代码在stackoverflow上可读。我尝试添加反引号`和',但都没有起作用。 - reality displays
1
@Bond:无法在评论中完成。你最好的选择是更新问题以反映更改。或者,如果你在其他地方被卡住了,可以提一个新问题。 - nmichaels

5
我刚用gcc遇到了这个警告,而且它很容易修复。stack_counter不应该在头文件中初始化,而是应该在包含它的源文件中初始化。因此,头文件应该只使用以下内容:
extern int stack_counter;

然后在一个包含该头文件的源代码文件的全局范围内声明并初始化stack_counter:

int stack_counter = 0;

0

Clang 仍然会对此发出警告。该行

extern int counter = 0;

将会触发警告:

警告:'extern'变量有一个初始值[-Wextern-initializer]

这个警告并不重要,因为使用定义语句给变量赋值即可。

int counter = 0;

默认情况下仍然产生静态持续性和外部链接。事实上,如果未提供存储类别说明符,则默认值如下:

  • 对于所有函数,使用 extern
  • 对于文件范围内的对象,使用 extern
  • 对于块范围内的对象,使用 auto

还有一种称为“试探性定义”的东西,它是一个没有初始化程序的外部声明,要么没有存储类别说明符,要么具有 static 说明符。

“试探性定义”是可能或可能不充当定义的声明。如果在同一翻译单位中较早或较晚找到了实际的外部定义,则试探性定义仅充当声明。

因此,以下行:

int counter;

是一个试探性定义,它声明并定义了一个counter,隐含地初始化为= 0(或者对于数组、结构体和联合类型,= {0})。


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