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++中详细细微操作不 ...
随机推荐
- 解决ERR Client sent AUTH, but no password is set
在搭建cookies池时,需要将账号密码保存到redis,保存时报错:ERR Client sent AUTH, but no password is set 报错原因:Redis服务器没有设置密码, ...
- jmeter压测参数设定(转)
jmeter压测参数设定 一.基本公式 线程数 = QPS * time: 注:QPS--每秒完成请求的个数:time--每个请求响应完成平均需要时间: 故QPS * time就是所有请求完成响应所需 ...
- asp.net core2.0中异常的处理
最近在开发中遇到一些关于如何抛出异常的困惑,在qq群里进行了讨论,有些人认为抛出异常是有理由的,可以对业务流程进行控制,而有些认为抛出异常会导致程序性能低下,我写一些自己的心得吧. 异常的全局处理 a ...
- 模态框 modal data-toggle data-target
模态框 modal data-toggle data-target 1. Data-*属性 模态框(modal) 触发事件(data-toggle) 触发对象data-target(ID 或类) ...
- 机顶盒webview开发调试
安装node的anywhere插件 启动本地服务器后 使用chrome的DevTool-----> chrome://inspect/#devices 点击inspect 第一次需要FQ ...
- Java8新特性之Collectors
参考:Java8新特性之Collectors 在第二天,你已经学习了Stream API能够让你以声明式的方式帮助你处理集合.我们看到collect是一个将管道流的结果集到一个list中的结束操作.c ...
- freemarker 设置中文
在web中添加一段代码 <servlet> <servlet-name>freemarker</servlet-name> <servlet-class> ...
- mybatis:数据持久层框架
mybatis是一个持久层的框架,是Apache下的顶级项目. mybatis托管到goolecode下,再后来托管到GitHub下:https://github.com/mybatis/mybati ...
- JS--bom对象:borswer object model浏览器对象模型
bom对象:borswer object model浏览器对象模型 navigator获取客户机的信息(浏览器的信息) navigator.appName;获得浏览器的名称 window:窗口对象 a ...
- EChart.js 笔记二
交互组件 Echart.js 中交互组件比较多.例如: legend(图例).title(标题组件).visualMap(视觉映射组件).dataZoom(数据缩放组件).timeline(时间线组件 ...