返回信息流RT,求解。
这是一条镜像帖。来源:北邮人论坛 / cpp / #87518同步于 2015/6/12
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
函数前加Extern声明有什么作用?
dajingling
2015/6/12镜像同步17 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
和不加extern有什么区别吗?是不加extern的头文件必须被引用,加了extern头文件可以不被引用吗?
【 在 FromMars 的大作中提到: 】
: 一方面是定义 外部/全局 变量,给其他地方引用
: 我觉得主要是让连接器知道函数在什么地方,毕竟连接器比较笨
嗯 用得少还是不说了,免得误人子弟
win上编程基本都是AFX_EXT_CLASS或者__declspec(dllexport)申明的动态库类,我也很少用这个关键字了
【 在 dajingling 的大作中提到: 】
: 和不加extern有什么区别吗?是不加extern的头文件必须被引用,加了extern头文件可以不被引用吗?
其实对于函数来说,你不加extern,它默认就是extern。
函数的linkage只能是extern或者static。
如果是static,那么一个编译单元(.c文件)不能直接通过函数名来引用另一个编译单元里定义的函数。
如果是extern,那么所有的编译单元里,都可以用这个名称来引用这个函数。
【 在 FromMars 的大作中提到: 】
: 嗯 用得少还是不说了,免得误人子弟
: win上编程基本都是AFX_EXT_CLASS或者__declspec(dllexport)申明的动态库类,我也很少用这个关键字了
这个标注是关于(运行时的)动态链接的,不是(编译之后马上做的)静态链接。C语言对如何装载、动态链接、动态装载没有任何规定。所以只能依靠操作系统的接口了。
是吗
我觉得模块功能上来说 动态库技术太强大了
几乎完全不需要什么extern关键字,至于模块内部的静态链接,好像C++也不怎么需要(可以用其他方式实现一样的功能),C没有封装或许用的多一点
【 在 nuanyangyang 的大作中提到: 】
:
: 这个标注是关于(运行时的)动态链接的,不是(编译之后马上做的)静态链接。C语言对如何装载、动态链接、动态装载没有任何规定。所以只能依靠操作系统的接口了。
知道了,就是加了Static的函数没法被别的文件include了,即使include了别的文件也可以定义相同的函数名。是吧?
【 在 nuanyangyang 的大作中提到: 】
: 其实对于函数来说,你不加extern,它默认就是extern。
: 函数的linkage只能是extern或者static。
: 如果是static,那么一个编译单元(.c文件)不能直接通过函数名来引用另一个编译单元里定义的函数。
: ...................
【 在 dajingling 的大作中提到: 】
: 知道了,就是加了Static的函数没法被别的文件include了,即使include了别的文件也可以定义相同的函数名。是吧?
include是另一回事。
C语言的每个.c文件都是分别编译的。如果你想在一个.c文件里调用另一个.c文件里定义的函数,你只要声明就可以了。比如:
// foo.c
// 这里定义函数
int square(int n) {
return n * n;
}
// bar.c
// 这里声明
int square(int n); // 即使不说,默认就是extern。
int square_sum(int x, int y) {
int x2 = square(x); // 在这里调用
int y2 = square(y); // 在这里调用
return x2 * y2;
}
编译的时候,分别编译两个文件,然后链接
cc -c -o foo.o foo.c # 编译foo.c,生成foo.o
cc -c -o bar.o bar.c # 编译bar.c,生成bar.o
cc -o main foo.c bar.c # 链接,生成可执行文件main
编译器看bar.c的时候,发现声明了但没有定义的square函数,会给调用的地方(就是square_sum里的两个地方)留个记号;链接的时候,找到真正定义square的模块(foo.o),然后把这些记号换成square函数真正的地址。
当然,如果要求每次使用都要自己声明,就太麻烦了。想象一下,如果不只是bar.c要用这个square函数,而是几十个.c文件都要使用它,那么每个文件里都要写一遍声明,太麻烦了。
所以,如果写一个模块(比如foo.c),而且里面有可以在外部调用的函数(比如square),那么就可以单独写一个.h文件,里面写入所有的外部可见的函数的声明。比如这样:
// foo.h
int square(int n); // 声明
// foo.c
#include "foo.h"
int square(int n) {
return n*n;
}
C语言在编译foo.c的时候,会先预处理。编译器真正看到的代码是:
// 编译器看到的代码
int square(int n); // 同一个文件里既声明又定义是没有问题的。
int square(int n) {
return n*n;
}
真正带来方便的是用户:bar.c。现在有了foo.h,那么bar.c的作者可以这样写:
// bar.c
#include "foo.h"
int square_sum(int x, int y) {
int x2 = square(x); // 在这里调用
int y2 = square(y); // 在这里调用
return x2 * y2;
}
这样,编译器编译bar.c的时候,实际看到的就是:
// 编译器看到的代码
int square(int n); // 这句是#include "foo.h"得到的。
int square_sum(int x, int y) {
int x2 = square(x); // 在这里调用
int y2 = square(y); // 在这里调用
return x2 * y2;
}
这也就相当于,只要有#include "foo.h",就有了对foo.c里的公共函数的声明。这样简化了开发。
以上就是include的真正来由。
(其实所谓#include <stdio.h>,做的也就是声明标准库里定义的函数。)
include本身有自己的问题:编译器每次都要看一遍.h里的所有内容。如果.h文件很多,很大,又被大量的.c文件include,那么编译就会变得非常慢。很多新的语言,比如java, go, rust等,都有更完善的模块系统,而不是用朴素的include。