`
envy2002
  • 浏览: 153819 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

linux extern关键字的作用

阅读更多

extern 的作用是起到一个链接作用!

 

    1.简单的例子。

 

     //包含头文件是为了声明这个函数,表示这个函数已经被定义过了,已经生产过二进制代码了。

#include <stdio.h>

int main(int argc, char **argv)
{
    printf("hello world!");
	return 0;
}

 

 

           

 

  假设现在是一个大工程,A.c include B.h  C.h,现在又有B.c  C.c文件。编译过程如何呢?

 

  编译是单个文件进行的。这句话如何理解呢?

 

   计算机最终执行的是二进制代码,我们用的库都是二进制机器码,CPU不停地吃,来执行指令。假设我们写好了许多

 

 许多好用的函数,从文本文件(main.c)编译成了二进制机器码文件(main.so,即库文件)。现在我需要利用这些基础的

 

函数形成重新组合调用,获得一些新的功能。比方说,我又添加了extra.c 来调用main.c中的函数。这时候就有了两种办法来生成新的二进制文件。

 

一种就是把main.c extra.c作为一个整体的whole.c来编译,生成whole.so二进制文件。从方法学的角度讲,这是整体

 

法。当然这是个不好的方法。聪明的办法分而治之,划分,化整体为局部。先把后来的extra.c编译好了之后,再整体化处

 

理,这个额外的过程就是链接。因为编译好的二进制代码是没必要重新生成的,只需要新的二进制代码执行后,跳入到编译

 

好的二进制代码那里执行就好了。

 

    又回到我们假设的工程,现在有三个文件,A.c, B.c, C.c,我们可以一个个编译成A.o, B.o, C.o,因为A调用了B,C,

 

所以最后的连接过程是不会丢失这个调用的关系的。链接的过程就像是声明一种边界一样。我在这里声明:这个是编译好的

 

文件,你不需要编译,你自己管你自己的就行了。最后连接器会把这个边界给缝合起来的。

 

 

  在例子中,我们看到,我们include 了<stdio.h>,这里声明了好多函数,就是告诉编译器,操作系统中这些函数都已经

 

实现了,(至于在哪里,我们最后再谈。),所以这个printf是合法的,然后我们用gcc -c file.c ,可以发现生成了file.o。

 

file.o 包含了printf的一个符号地址,用链接程序再替换一下,换成物理地址,gcc file.c -o file.exe,file.exe就包含

 

真正的物理地址了,就可以去call库文件中的函数了。这样程序就会执行了。

 

    同理,我们再试试这样写:

 

   extern int printf (__const char *__restrict __format, ...);

//printf这个函数是在库文件(libc.so.6 => /lib/tls/i686/cmov/libc.so.6)
//中已经被编译成了二进制代码
int main(int argc, char **argv)
{
    printf("hello world!");
	return 0;
}
 

   我们用gcc -c file.c可以看到命令是可以成功的。这里的extern也就是相当于#include <stdio.h>一样,告诉编译器一个

 

边界,这个,我是有二进制代码的!!(在某某二进制的库文件里面) 放过他,请继续编译。如果我们既没有头文件,也没

 

声明这个是个"已经编译好的二进制函数“(extern 可以看成是个 ”二进制函数“,我自己发明的,不正规)。编译一下如何?

 

结果是这个: 

 

    file.c:7: warning: incompatible implicit declaration of built-in function ‘printf’

 

编译会给个警告,自作聪明地给你反推出一个”二进制“函数,自己假设这个函数已经有了,然后连接搜索库,结果真找了,

 

呵呵。但是它的反推功能如何,我不知道。

 

    再假设,我们自己的函数,调用了一个sayhello(),结果sayhello()这个函数根本就没变成”二进制“函数,所以肯定会爆

 

一个函数没有的错误。

 

    看到这里,我们已经大概明白了编译的过程。这是一个万丈高楼平地起的过程,一个个函数被”二进制“化,一个个新的再

 

依赖这些”二进制函数“ 。库的过程就是这样,常用的所有基本功能编译好了,打成包,就成了库文件。新的开发依赖这个库

 

,所以我必须有这些库,而后,我自己再把自己的函数编译,打包,归档,形成了新的库,这样就形成了库的依赖关系。和

 

java的jar包是一个道理。

 

 从这里,我们也可以得出一个结论:头文件是开发的非必要条件。因为可以不include,直接手工声明,也是可以编译,

 

链接成功了。(头文件只是起到了一个方便的作用)。

 

如果你要用C++开发mysql, 如果有合适的库文件,不需要它的头文件,也是可以开发的呦,不过我没试,

 

所以不知道能不能成功,呵呵。注:(完整版的mysql,都是由开发包的,头文件,库文件一应俱全)。

 

最用我们用ldd明来看看都依赖了哪些库?

 

 

  bard@bard-desktop:~/sharing$ ldd file.exe

	linux-gate.so.1 =>  (0x002fe000)
	libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00a5d000)
	/lib/ld-linux.so.2 (0x00c56000)
bard@bard-desktop:~/sharing$ 
 

最后,我们做一下拓展训练:

 

  首先只有一个文件:

 

int main(int argc, char **argv)
{
    sayHello();
	return 0;
}
 

 

    gcc -c file.c ,我们看到是不成功的。

 

 

    第二个文件

       #include <stdio.h>

int one=1;
int two=2;

void sayHello()
{
	printf("love you");
	
}
 

 

    gcc -c constant.c  是可以成功的,生成了constant.o(似乎sayHello函数已经二进制化了)

 

 

    但是这时gcc file.c -o file.exe,还是不成功,为什么呢?

 

 

    看来这个constant.o 和libc.so库文件是有区别的啊,如果我们把这个constant.o打包到libc.so中就可以了。呵呵

 

   正确做法是

 

   gcc constant.o file.c -o file.exe

 

   看来这个命令的含义是:  编译file.c 并和constant.o中的”二进制“函数sayHello进行链接。

 

   不知道,各位兄弟姐妹们是如何理解编译过程的,如果您有好的想法,请给我留言。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    gcc主要关键字介绍

    此外,熟悉这些关键字对于理解和编写shell脚本也至关重要,尤其是在Linux环境下进行系统管理或自动化任务时。希望本文能够为初学者提供一个清晰的学习路径,帮助他们更快地掌握gcc的关键字并应用于实际编程中。

    c/c++中如何实现动态库动态加载的完整工程示例代码

    在动态库实现方面,提供一个虚拟元类MetaObject,然后在库的cpp文件中建立子类继承该类,实现其具体功能,并在cpp文件中直接提供函数API,这些API函数不在头文件中声明,需要extern关键字修饰。

    嵌入式linux工程师面试题目及知识点

    11. 如何在两个.c文件中引用对方的变量:extern关键字; 12. 使用malloc之前需要做的准备工作:检查返回值; 13. realloc函数在使用上要注意的问题:避免内存泄露; 14. strtok函数在使用上要注意的问题:避免缓冲区...

    经典嵌入式Linux面试题

    13. 如何引用一个已经定义过的全局变量:可以用引用头文件的方式,也可以用extern关键字。 14. 全局变量可不可以定义在可被多个.C文件包含的头文件中:可以,在不同的C文件中以static形式来声明同名全局变量。 编程...

    linux c语言面试宝典

    * 可以用引用头文件的方式,也可以用 extern 关键字。 15. 全局变量可不可以定义在可被多个.C 文件包含的头文件中: * 可以,在不同的 C 文件中以 static 形式来声明同名全局变量。 16. 语句 for( ;1 ;) 的...

    linux c学习笔记

    EXTERN关键字在C语言中用于声明全局变量,表明该变量在其他源文件中已被定义。这有助于解决多文件项目中变量的可见性和生命周期问题。 七、浅谈动态内存 动态内存管理是C语言中的一大特性,通过malloc()、calloc()...

    Linux知识体系复习

    - **Extern关键字**:用于声明一个在其他源文件中定义的变量或函数,使得可以在当前文件中使用它们。 - **Volatile关键字**:表示变量的值可能会被意外改变,阻止编译器对其进行优化。通常用于中断处理、多任务环境...

    extern()和 flock()函数

    ### `extern` 关键字详解 #### 一、`extern` 的基本概念 在 C/C++ 编程语言中,`extern` 是一个非常重要的关键字,主要用于声明一个变量或函数是在其他地方定义的。通常情况下,当我们需要在不同的源文件之间共享...

    c语言与c++基础知识点(必看)

    2、extern关键字:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。 也就是说extern有两个作用,第一...

    华三linuxC语言面笔试精选

    可以通过extern关键字或引用头文件的方式来引用已定义的全局变量,前者在编译时不会检查变量名是否正确,后者则会在编译阶段进行检查。 #### 全局变量在多文件中的定义 全局变量可以在多个.C文件中声明,但只能在...

    你可能不知道的,定义,声明,初始化

    extern 关键字用于在头文件中声明变量或函数,然后在源文件中定义它们。例如,extern int a; 是对变量 a 的声明,而不是定义。 struct 的声明、定义和初始化 struct 是一种复杂的数据结构,可以包含多个变量和函数...

    嵌入式LinuxC语言面试试题1.pdf

    答:可以使用extern关键字来声明变量; 二、 指针数组和字符数组 1. 如何定义一个有10个元素的整数型指针数组?答:可以使用指针数组的定义方式,如int *(*p)[10]; 2. 如何初始化一个字符数组?答:可以使用字符...

    Linux C编程一站式学习

    最后,书中详细探讨了链接过程,解释了多目标文件的链接、声明和定义的区别、extern和static关键字的作用,以及静态库的使用。此外,还提到了动态链接和装载过程,帮助读者理解程序在Linux系统中的实际运行方式。 ...

    linux下C++动态链接C++库示例

    C++有个特定的关键字用来声明采用C binding的函数: extern "C" 。 用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。 因此,只有非成员函数才能被声明为extern "C",并且不能被重载。 尽管限制...

    linux qt 动态链接库 静态链接库.docx

    在上面的代码中,我们使用 `extern "C"` 关键字来调用 C 函数 `hello`。 五、结论 动态链接库和静态链接库是 Qt 开发者必备的技能。了解它们的使用可以帮助我们更好地掌握 Qt 框架,提高应用程序的可重用性和灵活...

    linux模块间函数调用通讯

    - 在模块B中,使用`extern`关键字声明需要调用的函数。 示例: ```c // 模块A.c EXPORT_SYMBOL(my_function); // 模块B.c extern int my_function(); ``` #### 三、问题排查与解决方案 在实际开发中,可能会遇到...

    Linux将多个C源文件编译进一个KO模块驱动示例

    1. **创建主C源文件**:这个文件通常被称为`main.c`或`driver.c`,它会包含上述的基本模块结构,并通过`extern`关键字引用其他源文件中的函数。 2. **组织源文件**:其他C源文件(如`function1.c`、`function2.c`)...

    2002年嵌入式或LINUX相关研发面试题目

    11. 在两个.c文件中引用对方的变量可以使用extern关键字,例如`extern int x;`。 12. 使用malloc之前需要做准备工作是检查返回值是否为空指针,例如`if (!p) { ... }`。 13. realloc函数在使用上要注意的问题是避免...

Global site tag (gtag.js) - Google Analytics