如果js 全局变量量如果没被用到,是不是不参与编译

c++ extern及其对struct的使用
extern是一个关键字,它告诉编译器存在着一个变量或者一个函数,如果在当前编译语句的前面中没有找到相应的变量或者函数,也会在当前文件的后面或者其它文件中定义,来看下面的例子。能够使用extern引用其它cpp文件中定义的函数说明了一个问题:如果一个工程现编译cpp文件,在把多个目标文件链接成为可执行文件,而两个或多个文件中,定义了相同的全局变量,那么,程序编译的时候不会报错,因为编译器单独编译每个文件,在链接可执行文件的时候,由于多个目标文件中含有相同的全局变量,而生成可执行文件的时候,任何文件中定义的全局变量对其它目标文件都是可见的,此时由于变量定义冲突而发生错误。PS:定义在.h文件中的函数和变量不能使用extern变量声明,原因是#include &filename&在预编译的时候将.h文件中的内容插入了cpp文件中,因此编译器找得到在其它.h文件中定义的变量或者函数。编译的时候,只编译cpp文件的内容,.h文件时不参与编译,如果使用extern声明在.h文件中定义的变量或者函数,那么声明为extern的变量和函数在其它.cpp文件中找不到,因此程序编译的时候就发生了错误。下面摘录UE4里使用extern的一处源码:/**
* A macro to declare a logging category as a C++ "extern"
* @param CategoryName, category to declare
* @param DefaultVerbosity, default run time verbosity
* @param CompileTimeVerbosity, maximum verbosity to compile into the code
#define DECLARE_LOG_CATEGORY_EXTERN(CategoryName, DefaultVerbosity, CompileTimeVerbosity) \
extern struct FLogCategory##CategoryName : public FLogCategory&ELogVerbosity::DefaultVerbosity, ELogVerbosity::CompileTimeVerbosity& \
FORCEINLINE FLogCategory##CategoryName() : FLogCategory(TEXT(#CategoryName)) {} \
} CategoryN
* A macro to define a logging category, usually paired with DECLARE_LOG_CATEGORY_EXTERN from the header.
* @param CategoryName, category to define
#define DEFINE_LOG_CATEGORY(CategoryName) FLogCategory##CategoryName CategoryN
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!全局变量在编译时怎么分配空间?_百度知道
全局变量在编译时怎么分配空间?
哪些文件中定义的全局变量会分配内存?难道所有文件中的全局变量都要分配内存?那样好用静态存储区中的空间不是相当大吗
我有更好的答案
如果不需要使用就不要定义全局变量,因为只要定义了,代码又参与编译就会分配内存,局部变量定义了编译程序还帮着分析变量有无被使用并提示未被使用的变量,而全局变量则不会被分析是否有被使用,所以要小心使用,少做无谓的浪费,全局变量能占用的总空间数是有限的,超过限度时编译器会提示且拒绝链接,不能生成最终执行文件
谢谢啊 那库函数里面定义的全局变量会不会在运行时分配内存呢
采纳率:39%
为您推荐:
其他类似问题
您可能关注的内容
全局变量的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。不积跬步无以至千里,不积小流无以成江河。
C语言全局变量那些事儿(深入C中最隐秘的地带)
【虽然自认为对C的角角落落都有所了解,但直到看到这篇文章,才知道C中的一些隐秘的坑,是自己之前不知道的。
关于全局变量的链接问题,之前在我博客的某文章中有介绍过,一般对C有些了解的程序员都知道这个问题,但本文最后所提到的使用动态链接库所出现的问题,着实让我震惊。
(心急的朋友,可跳过前面的,直接读“第4个例子”)
所以,以后大家在使用全局变量的时候要留点心,良好的命名规范,在一定程度上可以避免文中所提到的隐秘Bug。
文章略长,请大家耐心读完,一定会有收获的! 】
以下为转载内容(额,为了能暂时放到显示,让更多人看到,把文章类型设为了“原创”,自打30大板。):原文链接:http://coolshell.cn/articles/10115.html
------------------------------------------------割------------------------------------------------
作为一名程序员,如果说沉迷一门编程语言算作一种乐趣的话,那么与此同时反过来去黑一门编程语言就是这种乐趣的升华。今天我们就来黑一把C语言,好好展示一下这门经典语言令人抓狂的一面。
我们知道,全局变量是C语言语法和语义中一个很重要的知识点,首先它的存在意义需要从三个不同角度去理解:对于程序员来说,它是一个记录内容的变量(variable);对于编译/链接器来说,它是一个需要解析的符号(symbol);对于计算机来说,它可能是具有地址的一块内存(memory)。其次是语法/语义:从作用域上看,带static关键字的全局变量范围只能限定在文件里,否则会外联到整个模块和项目中;从生存期来看,它是静态的,贯穿整个程序或模块运行期间(注意,正是跨单元访问和持续生存周期这两个特点使得全局变量往往成为一段受攻击代码的突破口,了解这一点十分重要);从空间分配上看,定义且初始化的全局变量在编译时在数据段(.data)分配空间,定义但未初始化的全局变量暂存(tentative
definition)在.bss段,编译时自动清零,而仅仅是声明的全局变量只能算个符号,寄存在编译器的符号表内,不会分配空间,直到链接或者运行时再重定向到相应的地址上。
我们将向您展现一下,非static限定全局变量在编译/链接以及程序运行时会发生哪些有趣的事情,顺便可以对C编译器/链接器的解析原理管中窥豹。以下示例对ANSI C和GNU C标准都有效,笔者的编译环境是Ubuntu下的GCC-4.4.3。
第一个例子
#ifndef _H_
#define _H_
/* foo.c */
#include &stdio.h&
#include "t.h"
} 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 main()
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);
Makefile如下:
test: main.o foo.o
gcc-otestmain.o
main.o: main.c
foo.o: foo.c
运行情况:
sizeof(b)=8
这个项目里我们定义了四个全局变量,t.h头文件定义了一个整型a,main.c里定义了两个整型b和c并且未初始化,foo.c里定义了一个初始化了的结构体,还定义了一个main的函数指针变量。由于C语言每个源文件单独编译,所以t.h分别包含了两次,所以int a就被定义了两次。两个源文件里变量b和函数指针变量main被重复定义了,实际上可以看做代码段的地址。但编译器并未报错,只给出一条警告:
/usr/bin/ld: Warning: size of symbol'b'changed from
4 inmain.o to 8
运行程序发现,main.c打印中b大小是4个字节,而foo.c是8个字节,因为sizeof关键字是编译时决议,而源文件中对b类型定义不一样。但令人惊奇的是无论是在main.c还是foo.c中,a和b都是相同的地址,也就是说,a和b被定义了两次,b还是不同类型,但内存映像中只有一份拷贝。我们还看到,main.c中b的值居然就是foo.c中结构体第一个成员变量b.a的值,这证实了前面的推断——即便存在多次定义,内存中只有一份初始化的拷贝。另外在这里c是置身事外的一个独立变量。
为何会这样呢?这涉及到C编译器对多重定义的全局符号的解析和链接。在编译阶段,编译器将全局符号信息隐含地编码在可重定位目标文件的符号表里。这里有个“强符号(strong)”和“弱符号(weak)”的概念——前者指的是定义并且初始化了的变量,比如foo.c里的结构体b,后者指的是未定义或者定义但未初始化的变量,比如main.c里的整型b和c,还有两个源文件都包含头文件里的a。当符号被多重定义时,GNU链接器(ld)使用以下规则决议:
像上面这个例子中,全局变量a和b存在重复定义。如果我们将main.c中的b初始化赋值,那么就存在两个强符号而违反了规则一,编译器报错。如果满足规则二,则仅仅提出警告,实际运行时决议的是foo.c中的强符号。而变量a都是弱符号,所以只选择一个(按照目标文件链接时的顺序)。
事实上,这种规则是C语言里的一个大坑,编译器对这种全局变量多重定义的“纵容”很可能会无端修改某个变量,导致程序不确定行为。如果你还没有意识到事态严重性,我再举个例子。
第二个例子
/* foo.c */
#include &stdio.h&;
} b = { 2, 4 };
int main();
void foo()
printf("foo:\t(&b)=0x%08x\n\tsizeof(b)=%d\n
\tb.a=%d\n\tb.b=%d\n\tmain:0x%08x\n",
&b, sizeof b, b.a, b.b, main);
/* main.c */
#include &stdio.h&
int main()
if (0 == fork()) {
printf("child:\tsleep(1)\n\t(&b):0x%08x\n
\t(&c)=0x%08x\n\tsizeof(b)=%d\n\tset b=%d\n\tc=%d\n",
&b, &c, sizeof b, b, c);
printf("parent:\t(&b)=0x%08x\n\t(&c)=0x%08x\n
\tsizeof(b)=%d\n\tb=%d\n\tc=%d\n\twait child...\n",
&b, &c, sizeof b, b, c);
printf("parent:\tchild over\n\t(&b)=0x%08x\n
\t(&c)=0x%08x\n\tsizeof(b)=%d\n\tb=%d\n\tc=%d\n",
&b, &c, sizeof b, b, c);
运行情况如下:
sizeof(b)=8
parent: (&b)=0x
sizeof(b)=4
wait child...
child: sleep(1)
sizeof(b)=4
sizeof(b)=8
parent: child over
sizeof(b)=4
(说明一点,运行情况是直接输出到stdout的打印,笔者曾经将./test输出重定向到log中,结果发现打印的执行序列不一致,所以采用默认输出。)
这是一个多进程环境,首先我们看到无论父进程还是子进程,main.c还是foo.c,全局变量b和c的地址仍然是一致的(当然只是个逻辑地址),而且对b的大小不同模块仍然有不同的决议。这里值得注意的是,我们在子进程中对变量b进行赋值动作,从此子进程本身包括foo()调用中,整型b以及结构体成员b.a的值都是1,而父进程中整型b和结构体成员b.a的值仍是2,但它们显示的逻辑地址仍是一致的。
个人认为可以这样解释,fork创建新进程时,子进程获得了父进程上下文“镜像”(自然包括全局变量),虚拟地址相同但属于不同的进程空间,而且此时真正映射的物理地址中只有一份拷贝,所以b的值是相同的(都是2)。随后子进程对b改写,触发了操作系统的写时拷贝(copy on write)机制,这时物理内存中才产生真正的两份拷贝,分别映射到不同进程空间的虚拟地址上,但虚拟地址的值本身仍然不变,这对于应用程序来说是透明的,具有隐瞒性。
还有一点值得注意,这个示例编译时没有出现第一个示例的警告,即对变量b的sizeof决议,笔者也不知道为什么,或许是GCC的一个bug?
第三个例子
这个例子代码同上一个一致,只不过我们将foo.c做成一个静态链接库libfoo.a进行链接,这里只给出Makefile的改动。
test: main.o foo.o
ar rcs libfoo.a foo.o
gcc-static -otestmain.o
main.o: main.c
foo.o: foo.c
rm-f *.otest
运行情况如下:
(&b)=0x080ca008
sizeof(b)=8
parent: (&b)=0x080ca008
(&c)=0x080cc084
sizeof(b)=4
wait child...
child: sleep(1)
(&b):0x080ca008
(&c)=0x080cc084
sizeof(b)=4
(&b)=0x080ca008
sizeof(b)=8
parent: child over
(&b)=0x080ca008
(&c)=0x080cc084
sizeof(b)=4
从这个例子看不出有啥差别,只不过使用静态链接后,全局变量加载的地址有所改变,b和c的地址之间似乎相隔更远了些。不过这次编译器倒是给出了变量b的sizeof决议警告。
到此为止,有些人可能会对上面的例子嗤之以鼻,觉得这不过是列举了C语言的某些特性而已,算不上黑。有些人认为既然如此,对于一切全局变量要么用static限死,要么定义同时初始化,杜绝弱符号,以便在编译时报错检测出来。只要小心地使用,C语言还是很完美的嘛~对于抱这样想法的人,我只想说,请你在夜深人静的时候竖起耳朵仔细聆听,你很可能听到Dennis Richie在九泉之下邪恶的笑声——不,与其说是嘲笑,不如说是诅咒……
第四个例子
/* foo.c */
#include &stdio.h&
const struct {
} b = { 3, 3 };
int main();
void foo()
printf("foo:\t(&b)=0x%08x\n\tsizeof(b)=%d\n
\tb.a=%d\n\tb.b=%d\n\tmain:0x%08x\n",
&b, sizeof b, b.a, b.b, main);
/* t1.c */
#include &stdio.h&
int b = 1;
int c = 1;
int main()
int count = 5;
while (count-- & 0) {
printf("t1:\t(&b)=0x%08x\n\t(&c)=0x%08x\n
\tsizeof(b)=%d\n\tb=%d\n\tc=%d\n",
&b, &c, sizeof b, b, c);
/* t2.c */
#include &stdio.h&
printf("t2:\t(&b)=0x%08x\n\t(&c)=0x%08x\n
\tsizeof(b)=%d\n\tb=%d\n\tc=%d\n",
&b, &c, sizeof b, b, c);
Makefile脚本:
exportLD_LIBRARY_PATH:=.
test: t1.o t2.o
gcc-shared -fPIC -o libfoo.so foo.c
gcc-otestt1.o
t2.o -L. -lfoo
t1.o: t1.c
t2.o: t2.c
.PHONY:clean
rm-f *.o *.sotest*
执行结果:
t2: (&b)=0x0804a01c
sizeof(b)=4
(&b)=0x0804a01c
sizeof(b)=8
t1: (&b)=0x0804a01c
sizeof(b)=4
t2: (&b)=0x0804a01c
sizeof(b)=4
(&b)=0x0804a01c
sizeof(b)=8
t1: (&b)=0x0804a01c
sizeof(b)=4
其实前面几个例子只是开胃小菜而已,真正的大坑终于出现了!而且这次编译器既没报错也没警告,但我们确实眼睁睁地看到作为main()中强符号的b被改写了,而且一旁的c也“躺枪”了。眼尖的读者发现,这次foo.c是作为动态链接库运行时加载的,当t1第一次调用t2时,libfoo.so还未加载,一旦调用了foo函数,b立马中弹,而且c的地址居然还相邻着b,这使得c一同中弹了。不过笔者有些无法解释这种行为的原因,有种说法是强符号的全局变量在数据段中是连续分布的(相应地弱符号暂存在.bss段或者符号表里),或许可以上报GNU的编译器开发小组。
另外笔者尝试过将t1.c中的b和c定义前面加上const限定词,编译器仍然默认通过,但程序在main()中第一次调用foo()时触发了Segment fault异常导致奔溃,在foo.c里使用指针改写它也一样。推断这是GCC对const常量所在地址启用了类似操作系统写保护机制,但我无法确定早期版本的GCC是否会让这个const常量被改写而程序不会奔溃。
至于volatile关键词之于全局变量,自测似乎没有影响。
怎么样?看了最后一个例子是否有点“不明觉厉”呢?C语言在你心目中是否还是当初那个“纯洁”、“干净”、“行为一致”的姑娘呢?也许趁着你不注意的时候她会偷偷给你戴顶绿帽,这一切都是通过全局变量,特别在动态链接的环境下,就算全部定义成强符号仍然无法为编译器所察觉。而一些IT界“恐怖分子”也经常将恶意代码包装成全局变量注入到root权限下存在漏洞的操作序列中,就像著名的栈溢出攻击那样。某一天当你傻傻地看着一个程序出现未定义的行为却无法定位原因的时候,请不要忘记Richie大爷那来自九泉之下最深沉的“问候”~
或许有些人会偷换概念,把这一切归咎于编译器和链接器身上,认为这同语言无关,但我要提醒你,正是编译/链接器的行为支撑了整个语言的语法和语义。你可以反过来思考一下为何C的胞弟C++推出“命名空间(namespace)”的概念,或者你可以使用其它高级语言,对于重定义的全局变量是否能通过编译这一关。
所以请时刻谨记,C是一门很恐怖的语言!
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!GCC编译C语言时,如果加的.a库里面和C源文件同时有某个函数或者某个全局变量,GCC会用哪个? - 知乎49被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答21 条评论分享收藏感谢收起63 条评论分享收藏感谢收起C++如何声明全局变量?_百度知道
C++如何声明全局变量?
必须写extern吗?
我有更好的答案
有两个类都需要使用共同的变量,将这些变量定义为全局变量。比如,res.h和res.cpp分别来声明和定义全局变量,类ProducerThread和ConsumerThread来使用全局变量。/**********res.h声明全局变量************/&&#pragma&once&&&#include&&QSemaphore&&&&const&int&g_nDataSize&=&1000;&//&生产者生产的总数据量&&const&int&g_nBufferSize&=&500;&//&环形缓冲区的大小&&extern&char&g_szBuffer[];&//&环形缓冲区&&extern&QSemaphore&g_qsemFreeB&//&控制环形缓冲区的空闲区(指生产者还没填充数据的区域,或者消费者已经读取过的区域)&&extern&QSemaphore&g_qsemUsedB&//&控制环形缓冲区中的使用区(指生产者已填充数据,但消费者没有读取的区域)
网络工程师
对,必须用extern声明,因为要涉及多个文件,不用extern的话会导致编译链接出现问题。不过不建议用全局变量,面向对象要求低耦合
本回答被提问者采纳
extern是声明只有涉及多个文件的全局变量才需要使用extern比如,当文件a,b都需要访问一个全局变量val时,就要在文件a,b中声明如下如果只是在同一文件中使用这个全局变量就不需要extern了。
如果要是你只编写一个C文件的话,全局变量在main 函数和其他定义的函数之外定义就行啦;但如果涉及到两个或者两个以上的C文件,定义全局变量要用到extern
要是在一个文件当中,可以不用,要是在不同的文件当中,那就必须用
就是通过 extern声明啊。
其他5条回答
为您推荐:
其他类似问题
您可能关注的内容
全局变量的相关知识
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。

我要回帖

更多关于 c 全局变量 的文章

 

随机推荐