extern 关键字说明

extern 关键字说明

  • 1. 作用1:extern "C"
    • 1.1 关于extern “C“(详细剖析) 历史:
    • 1.1. 扩展: 一个标准的C头文件格式(extern "C")
    • 1.3 extern "C" 详细说明
  • 2. 作用2:extern 变量/函数
    • 2.1 背景: 局部变量和外部(全局)变量说明
      • 2.1.1 局部变量
      • 2.1.2 全局变量
      • 2.1.3 全局变量/函数如何被其他文件使用 - extern
    • 2.2 extern 作用2/定义:
      • 2.2.1 注意事项1. extern int g_Int; 它是一个声明不是定义
      • 2.2.2 注意事项2. 在使用extern时候要严格对应声明时的格式
      • 2.2.3 注意事项3. extern变量声明使用规范:在*.c文件中定义了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明
      • 2.2.4 注意事项4. extern 函数声明:仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。
      • 2.2.5 注意事项5. 请记住,只在头文件中做声明,源文件做定义
  • 3. 例子说明
    • 3.1 没有extern 报错
      • 3.1.1 失败原因解析(注意:定义和声明的区别):
      • 3.1.2 解决: 请记住, 只在头文件中做声明,源文件做定义;真理总是这么简单。
    • 3.2 有extern ok
  • 4. 例子说明:a.c中定义了一个结构体数组,b.c文件想用,则 extern xxx 一下;也可以在.h中extern
    • 4.1 问题:结构体数组定义在.h 中报重复定义
      • 4.1.1 注意:定义和声明的区别
    • 4.2 解决:在.h中extern 一下即可
    • 4.3 解释了为什么头文件中声明结构体,然后其他.c文件包含.h,定义一个结构体对象不会报错。
  • 参考:

extern 关键字的两个作用: extern “C” 和 extern 变量

1. 作用1:extern “C”

1.1 关于extern “C“(详细剖析) 历史:

https://blog.csdn.net/zjy1175044232/article/details/121354194

1.1. 扩展: 一个标准的C头文件格式(extern “C”)

https://blog.csdn.net/lqy971966/article/details/89192268

***************************************
-----------------hello.h---------------------
产品名:hello
模块名:hello
日期:2019/4/10
作者:hani
文件描述:hello.c 的头文件
***************************************
#ifndef  _HELLO_H_ //防止头文件被重复包含
#define _HELLO_H_

#ifdef __cplusplus // __cplusplus是一个C++规范规定的预定义宏
				  //如果在c++编译器中编译链接,则执行下列代码,直到最近endif结束
extern "C" { // 表示告诉编译器,这是c写的库文件,请用c约定编译链接,
#endif       //因为c++的函数重载会改变编译后的函数名称,而c不支持函数重载
			//C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的

// 变量,数据结构,函数声明等
void printHello();

#ifdef __cpluscplus
}
#endif

#endif  //end _HELLO_H_

1.3 extern “C” 详细说明

__cplusplus是一个C++规范规定的预定义宏。你可以信任的是:所有的现代C++编译器都预先定义了它;而所有C语言编译器则不会
所以,如果上述代码被C语言程序引用的话,它的内容就等价于下列代码。

#ifndef  _HELLO_H_ //防止头文件被重复包含
#define _HELLO_H_

// 变量,数据结构,函数声明等
void printHello();

#endif  //end _HELLO_H_

C语言却是一门单一名字空间的语言,也不允许函数重载,所以,C语言编译器不需要对任何名字进行复杂的处理

C++的缔造者Bjarne Stroustrup在最初就把——能够兼容C,能够复用大量已经存在的C库——列为C++语言的重要目标。
但两种语言的编译器对待名字的处理方式是不一致的,这就给链接过程带来了麻烦。

为了解决这一问题,C++引入了链接规范(linkage specification)的概念,表示法为extern"language string",
C++编译器普遍支持的"language string"有"C"和"C++",分别对应C语言和C++语言。

链接规范的作用是告诉C++编译:对于所有使用了链接规范进行修饰的声明或定义,应该按照指定语言的方式来处理,
比如名字,调用习惯(calling convention)等等。
extern 有两个作用,第一个,当它与"C"一起连用时,如: extern “C” void fun(int a, int b);
则告诉编译器extern “C” {…balabala… }中的这些函数名时按着C的规则去翻译相应的函数名而不是C++的编译规则编译。因为c++的函数重载会改变编译后的函数名称,而c不支持函数重载。

2. 作用2:extern 变量/函数

2.1 背景: 局部变量和外部(全局)变量说明

2.1.1 局部变量

  1. 在函数内定义的变量是局部变量;
  2. 局部变量的生存周期是:该函数使用期间;函数结束即销毁;
  3. 局部变量的作用范围是:定义至函数结束

2.1.2 全局变量

1. 在函数之外定义的变量则称为外部变量,又叫做全局变量;
2. 它的存储方式为静态存储,其生存周期:为整个程序的生存周期。
3. 全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。
	其他文件也可见。
	因为所有未加static修饰的函数、变量都具有全局可见性,即其他文件可见。
4. 如果全局变量不在文件的开头定义,有效的作用范围将只限于其定义处到文件结束。
5. 如果在定义点之前的函数想引用该全局变量,则应该在引用之前用关键字 extern 对该变量作“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
6. 因为全局变量的作用范围是只限于其定义处到文件结束,其他文件也可见,但是其他文件应该怎么使用呢?
	那就extern 声明一下即可

参考:
http://c.biancheng.net/view/404.html

通俗易懂说static
https://blog.csdn.net/lqy971966/article/details/88988844

2.1.3 全局变量/函数如何被其他文件使用 - extern

1.对变量而言:

1. 因为全局变量不能在头文件中定义,如果定义会导致各个.c文件包含的这个头文件重复定义;
2. 全局变量可以在.c中定义,只要在.h中声明即可,那么如何声明呢?
	加了extern就是声明;
	如果没有extern关键字,就是定义;

2.对于函数而言:

有没有extern 是一个意思 都是声明
1. 函数可以再头文件中声明,加与不加extern都是一样的意思。都是声明。

2.2 extern 作用2/定义:

当extern不与"C"在一起,而修饰变量或函数时。extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或其他模块中使用。

2.2.1 注意事项1. extern int g_Int; 它是一个声明不是定义

extern int g_Int;

2.2.2 注意事项2. 在使用extern时候要严格对应声明时的格式

如:

test1.c 
char a[32];

test1.h 
extern char *a;

这样不可以,程序运行时会告诉你非法访问。
原因在于,指向类型char的指针并不等价于类型char的数组。
extern char *a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。
应该将声明改为extern char a[ ]。

2.2.3 注意事项3. extern变量声明使用规范:在*.c文件中定义了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明

2.2.4 注意事项4. extern 函数声明:仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。

下述两个函数声明没有明显的区别:

extern int f(); 
int f();

2.2.5 注意事项5. 请记住,只在头文件中做声明,源文件做定义

3. 例子说明

3.1 没有extern 报错

main.c 
#include
#include"test1.h"
#include"test2.h"

void main(){
	fun1();
	fun2();
	return;
}
	
test1.h
#ifndef TEST1H
#define TEST1H

//extern char g_str[];
extern char g_str[]="abcde"; // 这个时候相当于没有extern, 等价于 char g_str[]="abcde";
void fun1();

#endif

test1.c
#include
#include "test1.h"
void fun1() { 
	printf("str=%s\n",g_str); 
}

test2.h
#ifndef TEST2H
#define TEST2H
void fun2();
#endif

test2.c
#include
#include "test1.h"
void fun2() { 
	printf("str=%s\n",g_str); 
}

[root@localhost extern-test]# !gcc
gcc main.c test1.c test2.c
/tmp/ccPfBpf2.o:(.data+0x0): multiple definition of `g_str'
/tmp/ccKGoLlw.o:(.data+0x0): first defined here
/tmp/cca1rEcy.o:(.data+0x0): multiple definition of `g_str'
/tmp/ccKGoLlw.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
[root@localhost extern-test]#

3.1.1 失败原因解析(注意:定义和声明的区别):

multiple definition of `g_str'

因为你把全局变量 g_str 的定义放在了头文件之后,test1.cpp这个模块包含了test1.h所以定义了一次g_str,
而test2.cpp也包含了test1.h所以再一次定义了g_str,这个时候连接器在连接test1和test2时发现两个g_str。

3.1.2 解决: 请记住, 只在头文件中做声明,源文件做定义;真理总是这么简单。

3.2 有extern ok

main.c 
#include
#include"test1.h"
#include"test2.h"

void main(){
	fun1();
	fun2();
	return;
}
	
test1.h
#ifndef TEST1H
#define TEST1H
extern char g_str[]; //声明
void fun1();
#endif

test1.c
#include
#include "test1.h"

char g_str[]="abcde"; //定义

void fun1() { 
	printf("str=%s\n",g_str); 
}

test2.h
#ifndef TEST2H
#define TEST2H
void fun2();
#endif

test2.c
#include
#include "test1.h"

void fun2() { 
	printf("str=%s\n",g_str); 
}

[root@localhost extern-test]# !gcc
gcc main.c test1.c test2.c
[root@localhost extern-test]# ./a.out 
str=123456
str=123456
[root@localhost extern-test]#

4. 例子说明:a.c中定义了一个结构体数组,b.c文件想用,则 extern xxx 一下;也可以在.h中extern

4.1 问题:结构体数组定义在.h 中报重复定义

原因:因为把全局变量 数组的定义放在了头文件之后,test1.cpp这个模块包含了test1.h所以定义了一次数组,
而test2.cpp也包含了test1.h所以再一次定义了数组,这个时候连接器在连接test1和test2时发现两个数组。

解决:定义放.c文件中,然后头文件中 extern一下。

4.1.1 注意:定义和声明的区别

4.2 解决:在.h中extern 一下即可

a.c 文件中定义了 结构体数组 g_astTESTFileType
b.c 文件要使用它

a.c

/* filetype */
typedef struct tagTEST_FILETYPE
{
	CHAR *pcFileName;
	CHAR *pcFileDes;
	UINT uiFileId;
}TEST_FILETYPE_S;

/* file type */
typedef enum tagTEST_FILETYPE_E
{
	TEST_FILETYPE_DOC = 0, 
	TEST_FILETYPE_PPT,
	TEST_FILETYPE_TXT,
	TEST_FILETYPE_MAX,
}TEST_FILETYPE_E;

TEST_FILETYPE_S  g_astTESTFileType[] =
{
	{"DOC",  "DOC file type",  TEST_FILETYPE_DOC},
	{"PPT",  "PPT file type",  TEST_FILETYPE_PPT},
	{"TXT",  "TXT file type",  TEST_FILETYPE_TXT},
	/* add to here */
};

b.c

extern TEST_FILETYPE_S  g_astTESTFileType[];

//……使用
g_astWafTPFileType[uiLoop].pcFileName
……

或者在 .h 中 extern TEST_FILETYPE_S g_astTESTFileType[]; 也可以

4.3 解释了为什么头文件中声明结构体,然后其他.c文件包含.h,定义一个结构体对象不会报错。

定义和声明的区别!!!

参考:

https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html

你可能感兴趣的:(C语言开发,extern)