转载自:https://blog.csdn.net/shanghairuoxiao/article/details/72876248
目录
1.[extern关键字作用]
http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html)
- 当它与"C"一起连用时,如:
extern "C" void fun(int a, int b);
则告诉编译器在编译fun
这个函数名时按着C
的规则去翻译相应的函数名而不是C++
的,C++
的规则在翻译这个函数名时会把fun
这个名字变得面目全非,可能是fun@aBc_int_int#%$
也可能是别的,不同的编译器采用的方法不一样.
为什么要extern "C"
呢???
因为,C++
语言支持函数重载,C
语言不支持函数重载,函数被C++
编译器编译后在库中的名字与C
语言的不同。参考:https://blog.csdn.net/u014783685/article/details/84973067- 当
extern
不与"C
"在一起修饰变量或函数时,如在头文件中:extern int g_Int
,它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或其他模块中使用。extern
以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。
切记:对于变量,只在头文件中做声明。即extern char g_str[];
,不要在头文件中定义:char g_str[] = "123456";
因为,变量可以声明多次,但只能定义一次。如果有2个.cpp
包含了这个头文件,那么这个变量就会被定义两次,那么就会出错。
1.0 关于_声明与定义_:
extern int a;//声明一个全局变量a
int a; //定义一个全局变量a
extern int a =0 ;//定义一个全局变量a 并给初值。
int a =0;//定义一个全局变量a,并给初值,与上一行不能连起来使用,会出现重定义。
1.1 extern"C"的使用
参考:https://blog.csdn.net/big_bit/article/details/51595714?utm_source=copy
- 可以是单一语句
extern "C" double sqrt(double)`;
- 可以是复合语句,相当于复合语句中的声明都加了
extern"C"
extern "C" { double sqrt(double); int min(int, int); }
3.可以包含头文件,相当于头文件中的声明都加了extern"C"
extern"C" { #include <cmath> }
不可以将
extern"C"
添加在函数内部如果函数有多个声明,可以都加
extern"C"
,也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则。除extern"C",还有extern"FORTRAN"等。
1.2 extern修饰变量时的方法:
- 使用头文件,然后声明它们,然后其他文件去包含头文件。
注:B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在链接时从模块A生成的目标代码中找到此函数。例:/*a.h*/ #include<iostream> extern int i; /*a.cpp*/ #include"a.h" int i=4; /*main.cpp*/ #include"a.h" int main(){ cout<<i<<endl; return 0; }
- 在其他文件中直接extern。 例:
/*a.h*/ #include<iostream> extern int i; /*a.cpp*/ #include"a.h" int i=4; /*main.cpp*/ #include<iostream> extern int i; int main(){ cout<<i<<endl; return 0; }
http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html
2.头文件里面的ifndef /define/endif的作用
参考:https://blog.csdn.net/yapingmcu/article/details/8027473
例如:要编写头文件test.h
在头文件开头写上两行:
#ifndef _TEST_H
#define _TEST_H//一般是文件名的大写
············
············
头文件结尾写上一行:
#endif
为什么要用这些呢?
#ifndef
是if not define
的简写,是一种宏定义判断,作用是防止多重定义,对同一文件进行多次编译报错。所以最好是将每一个头文件都使用上这个方法,将头文件的内容都放在#ifndef
和#endif
中。
一般格式为:
#ifndef <标识 >
#define <标识 >
......
......
#endif <标识 >
命名规则
但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H_ #define _STDIO_H_ ...... #endif
详细解释:
1.比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。加上ifndef/define/endif,就可以防止这种重定义错误。
例:假设你的工程里面有4个文件,分别是a.cpp,b.h,c.h,d.h
。
a.cpp
的头部是:#include "b.h " #include "c.h "
b.h
和c.h
的头部都是:#include "d.h "
而d.h
里面有class D
的定义。 这样一来, 编译器编译a.cpp
的时候,先根据#include "b.h "
去编译b.h
这个问题,再根据b.h
里面的#include "d.h"
,去编译d.h
的这个文件,这样就把d.h
里面的class D
编译了; 然后再根据a.cpp
的第二句#include "c.h "
,去编译c.h
,最终还是会找到的d.h
里面的class D
,但是class D
之前已经编译过了,所以就会报重定义错误。
2…在#ifndef中定义变量出现的问题(一般变量不定义在#ifndef中)。#ifndef AAA #define AAA ... int i; ... #endif
里面有一个变量定义 在
vc
中链接时就出现了i重复定义的错误,而在c
中成功编译。
结论:
(1).当你第一个使用这个头的.cpp
文件生成.obj
的时候,int i
已经在里面定义了。当另外一个使用这个的.cpp
再次[单独]生成.obj
的时候,int i
又被定义。然后两个obj
被另外一个也include
这个头的.cpp
连接在一起,就会出现重复定义.
(2).把源程序文件扩展名改成.c
后,VC
按照C
语言的语法对源程序进行编译,而不是C++
。在C
语言中,若是遇到多个int i
,则自动认为其中一个是定义,其他的是声明。
(3).C
语言和C++
语言连接结果不同,可能(猜测)是在进行编译的时候,C++
语言将全局变量默认为强符号,所以连接出错。C
语言则依照是否初始化进行强弱的判断的。(参考)
解决方法:
(1).把源程序文件扩展名改成.c。
(2).推荐解决方案:.h
中只声明 extern int i;在.cpp
中定义<x.h> #ifndef __X_H__ #define __X_H__ extern int i; #endif //__X_H__ <x.c> int i;
相同作用:#pragma once
#pragma once方式却不受一些较老版本的编译器支持,一些支持了的编译器又打算去掉它,所以它的兼容性可能不够好。
3.volatile关键字
- 访问寄存器要比访问内存要块,因此CPU会优先访问该数据在寄存器中的存储结果,但是内存中的数据可能已经发生了改变,而寄存器中还保留着原来的结果。(如汇编语句改变了内存中的值,而不让编译器知道),为了避免这种情况的发生将该变量声明为volatile,告诉CPU每次都从内存去读取数据。
在多线程中,两个线程使用到同一个变量,而且这个变量的值可能会发生改变时,应该用volatile声明,让编译器每次操作该变量时一定要从内存中真正取出,当一个变线程改变了该变量的值时,对另外一个线程是可见的。 - 一个参数可以即是const又是volatile的吗?
答案:如果一个变量不会被本程序改变,通常可能给它加上const,但如果该变量可能被其他程序或外部端口改变,而本程序又在检测这个变量的值,就需要给它加上volatile。于是变量就同时有volatile和const了,这个时候该变量具有const和volatile的双重属性。该变量不可以在编译过程中被程序代码修改,同时编译器不得对i进行优化编译。