extern C小结
名词解释
1.extern
extern翻译为外部的,用在变量或函数之前,使其可见范围拓宽,这就是为啥叫extern吧。那它是用来干嘛的呢?当用在变量或函数之前,提示编译器到其他模块寻找其定义。
举个例子:
在源文件A里定义的函数,在其它源文件里是看不见的(即不能访问)。为了在源文件B里能调用这个函数,应该在B的头部加上一个外部声明:
(extern) 函数原型;
这样,在源文件B里也可以调用那个函数了。
2.声明与定义
声明(declaration)仅仅是声明有这么个东西,但是并没有涉及到内存分配等动作,这些动作在其他地方做,那么它是不是就没什么作用了呢,相反,它还是扮演了很重要的角色,首先它声明了一个变量或函数的类型,让程序知道某个变量的类型,或者一个函数的参数及返回值类型。
定义(definition)可以理解为声明的超集,也可以把声明理解为定义的子集,定义的同时进行了内存的分配,所以声明可以进行多次,但定义就只能做一次。
要点讲解
1.extern 函数
这个会比较简单,当你定义一个函数时,如
Int fun(void)
编译器会默认它为
extern int fun(void)
所以在函数之前加或者不加extern区别不大。
从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。
附上实例:
//file_01.h int add(int, int);//显然这里是一个全局函数,这样才具有被extern调用的属性。 //file_01.c #include "file_01.h"
int add(int a, int b)
{
return a + b;
} //main.c #include <stdio.h>
int main(int argc, char** argv)
{
int a = ;
int b = ;
extern int add(int, int);//这一行可以放在调用之前的任何地方,extern也是可以省略的
int c = add(a, b);
printf("%d\n", c);
}
2.extern 变量
简单点讲:
extern int var;//这个就是一个声明,可以进行多次
int var;//这个就是一个定义
extern int var = 0;//这个不仅是一个声明,同时也是一个定义
举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern int v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。
附上实例:
//file_01.h extern int g_var;
/*此处就是一个声明,定义放在.c文件中实现,这样的做法就类似于函数了,只不过因为函数是默认带extern的,
所以平时并没有注意而已,extern如果忽略,那么就相当于在.h文件就定义了,那么后面就直接g_var = 9,相当于赋值(不是初始化),
不过有warning。*/ //file_01.c #include "file_01.h" int g_var = ;//此处是实际的定义了,并且初始化(应该不是赋值)。 //main.c
#include <stdio.h> extern int g_var;
/*此处extern可以省略会比较神奇,所以省略之后仍然是声明
可以做个简单验证,如果此处int g_var = 0;则会出现重定义错误。
*/ int main(int argc, char** argv)
{
printf("%d\n", g_var);
}
3.extern 数组& extern 指针
一个源文件里定义了extern char a[] = “abcd”;
可以在另一个文件里进行声明:extern char* a;吗?
附上实例:
//file_01.h extern char g_var[]; //file_01.c #include "file_01.h" char g_var[] = "abcd"; //main.c #include <stdio.h> //extern char* g_var;
//如果用指针形式,运行时会出现段错误
extern char g_var[];
/*这里如果两行同时有效,则报错误:
main.c:7: error: conflicting types for ‘g_var’
main.c:6: error: previous declaration of ‘g_var’ was here
从错误上来看,显然两次声明出现了类型冲突,说明编译器并没有把char*和char[]等同看待,
不信把两行都声明成char*或者char[]就能顺利通过编译了。
*/ int main(int argc, char** argv)
{
printf("%s\n", g_var);
}
那么上面用指针形式报段错误到底是怎么回事呢?
上面用指针形式,直接段错误,说明连接器根本就没把这里的g_var和file_01.c中定义的g_var当成一回事,
它们是两种类型,奇怪的是,如果上述用指针形式,编译能通过,说明编译器认为g_var是已经定义过的变量,
就是file_01.c中定义的那个,但是运行的时候段错误,到底咋回事呢,原来g_var直接被赋予了一个由数组中内容组成的值,
因为原来这里的值是abcd,是被当成char[]来解释的,即g_var等于数组的首地址,而现在要被当成char*来解释了,所以很显然,
那就等价于g_var = “abcd”,即g_var = 0x64636261,否则如果按正常的思路来讲应该是先对g_var初始化,然后再对其赋值,
值为数组首地址地址,但是现在没有单独初始化变量g_var的动作,或者说数组初始化的时候这个变量就已经有了,
说到底是数组和指针的内部结构不一样导致的。如果非要用这种指针形式来打印的话,也最多能打印数组中的前四个字节,
因为指针变量的大小就是四个字节,可以这样打印 unsigned db = (unsigned)g_var;
char* ptr = (char*)(&db);
printf("%s\n", ptr);
4.extern “C”
在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同。
extern C小结的更多相关文章
- C语言extern关键字使用
在chinaunix上看见一篇转载的文章,觉得特别好,关于extern使用的解释: 参考链接:http://doc.chinaunix.net/CPP/201206/2248432.shtml 在C语 ...
- C++中extern关键字用法小结
总结C++中关于extern关键字的用法. 1.变量的生明和定义中 C++语言支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译.为了将程序分为许多文件,则需要在文件中共享代码 ...
- static和extern的用法小结
以前写程序是,基本不管static和extern,一个工程文件也只有一个c文件.今天尝试用多个文件来写,自然就涉及到这两个关键词的使用,自己查了些资料,并且做了些实验,总结如下. extern的用法 ...
- 转:C++项目中的extern "C" {}
引言 在用C++的项目源码中,经常会不可避免的会看到下面的代码: #ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __c ...
- C++项目中的extern "C" {}
from:http://www.cnblogs.com/skynet/archive/2010/07/10/1774964.html C++项目中的extern "C" {} 20 ...
- C和C++混合编程中的extern "C" {}
引言 在用C++的项目源码中,经常会不可避免的会看到下面的代码: 1 2 3 4 5 6 7 8 9 #ifdef __cplusplus extern "C" { #endif ...
- extern extern “C”用法详解
1.extern 修饰一个变量,告诉编译器这个变量在其他地方定义,编译器不会给出变量未定义的警告. extern tells the compiler that the variable is def ...
- C++ typedef用法小结 (※不能不看※)
C++ typedef用法小结 (※不能不看※) 第一.四个用途 用途一: 定义一种类型的别名,而不只是简单的宏替换.可以用作同时声明指针型的多个对象.比如:char* pa, pb; // 这多数不 ...
- static使用方法小结
static使用方法小结 statickeyword是C, C++中都存在的keyword, 它主要有三种使用方式, 当中前两种仅仅指在C语言中使用, 第三种在C++中使用(C,C++中详细细微操作不 ...
随机推荐
- JS 将值插入数组中
使用 push 方法 1.var arr = [1,2,3] arr.push(数值) 或者 arr.push({xxx:数值}) 2.输出数组中的最后一个 console.log(arr.[arr. ...
- flutter开发vscode用模拟器调试
android studio的太重,我装的是android sdk,使用avd的模拟器启动黑屏 启动夜神模拟器(已卸载) 建立连接: adb connect 127.0.0.1:62001 (夜 ...
- Netty ByteBuf 和 String 转换
参考https://blog.csdn.net/o1101574955/article/details/81024102 参考http://youyu4.iteye.com/blog/2361959 ...
- Python 版百度站长平台链接主动推送脚本
如果自己的网站需要被百度收录,可以在搜索结果中找到,就需要将网站的链接提交给百度.依靠百度的爬虫可能无法检索到网站所有的内容,因此可以主动将链接提交给百度. 在百度的站长平台上介绍了链接提交方法,目前 ...
- python数学第六天【指数族】
- IBM rational rose画时序图软件破解安装
上边这个链接是开头的安装步骤,照着链接中的步骤安装完之后,接下来看下边. 1.然后安装完成打开软件“IBM Rational License Keyadministrator”.出现下图:选中第二项“ ...
- vue監聽屬性
使用$watch,就是監聽到某個值發生變化,執行回調函數.
- Spring Boot 构建电商基础秒杀项目 (十) 交易下单
SpringBoot构建电商基础秒杀项目 学习笔记 新建表 create table if not exists order_info ( id varchar(32) not null defaul ...
- docker 搭建简易仓库registry
下载仓库镜像: docker pull registry:2 运行仓库库镜像: docker run -d -p 5000:5000 -v /usr/local/registry:/var/li ...
- [Codeforces261D]Maxim and Increasing Subsequence——树状数组+DP
题目链接: Codeforces261D 题目大意:$k$次询问,每次给出一个长度为$n$的序列$b$及$b$中的最大值$maxb$,构造出序列$a$为$t$个序列$b$连接而成,求$a$的最长上升子 ...