宏定义与预处理、函数和函数库

以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。


一、C语言预处理理论

1、由源码到可执行程序的过程

  • 源码.c->(编译)->elf可执行程序
  • 源码.c->(编译)->目标文件.o->(链接)->elf可执行程序
  • 源码.c->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序
  • 源码.c->(预处理)->预处理过的.i源文件->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序
  • 预处理用预处理器,编译用编译器,汇编用汇编器,链接用链接器,这几个工具再加上其他一些额外的会用到的可用工具,合起来叫编译工具链。
  • gcc就是一个编译工具链。

2、预处理的意义

(1)编译器本身的主要目的是编译源代码,将C的源代码转化成.S的汇编代码。编译器聚焦核心功能后,就剥离出了一些非核心的功能到预处理器去。

(2)预处理器帮编译器做一些编译前的杂事。

3、编程中常见的预处理

(1)头文件包含:#include(#include <>和#include ""的区别);

(2)注释;

(3)条件编译:#if #elif #endif, #ifdef

(4)宏定义

4、gcc中只预处理不编译的方法

  • gcc编译时可以给一些参数来做一些设置。
  • 譬如gcc xx.c -o xx可以指定可执行程序的名称;
  • 譬如gcc xx.c -c -o xx.o可以指定只编译不连接,也可以生成.o的目标文件。
  • 譬如gcc -E xx.c -o xx.i可以实现只预处理不编译。一般情况下没必要只预处理不编译,但有时候这种技巧可以用来帮助我们研究预处理过程,帮助debug程序。

5、总结

  • 宏定义被预处理时的现象有:第一,宏定义语句本身不见了(可见编译器根本就不认识#define,编译器根本不知道还有个宏定义);第二,typedef重命名语言还在,说明它和宏定义是有本质区别的(说明typedef是由编译器来处理而不是预处理器处理的);


二、C语言预处理代码实战

1、头文件包含

(1)#include <> 和 #include""的区别

  • <>专门用来包含系统提供的头文件(就是系统自带的,不是程序员自己写的),""用来包含自己写的头文件;
  • 更深层次来说:<>的话C语言编译器只会到系统指定目录(编译器中配置的或者操作系统配置的寻找目录,譬如在ubuntu中是/usr/include目录,编译器还允许用-I来附加指定其他的包含路径)去寻找这个头文件(隐含意思就是不会找当前目录下),如果找不到就会提示这个头文件不存在。
  • ""包含的头文件,编译器默认会先在当前目录下寻找相应的头文件,如果没找到然后再到系统指定目录去寻找,如果还没找到则提示文件不存在。
  • 规则虽然允许用双引号来包含系统指定目录,但是一般的使用原则是:如果是系统指定的自带的用<>,如果是自己写的在当前目录下放着用"",如果是自己写的但是集中放在了一起专门存放头文件的目录下将来在编译器中用-I参数来寻找,这种情况下用<>。

(2)头文件包含的真实含义就是:在#include<xx.h>的那一行,将xx.h这个头文件的内容原地展开替换这一行#include语句。过程在预处理中进行。

2、注释

  • 注释是给人看的,不是给编译器看的。
  • 在预处理阶段,预处理器会拿掉程序中所有的注释语句,到了编译器编译阶段程序中其实已经没有注释了。

3、条件编译

  • 条件编译中用的两种条件判定方法分别是#ifdef 和 #if



三、宏定义

1、宏定义的规则和使用解析

(1)宏定义的解析规则:在预处理阶段由预处理器进行替换,这个替换是原封不动的替换。

(2)宏可以带参数,称为带参宏。

  • 在定义带参宏时,每一个参数在宏体中引用时都必须加括号,最后整体再加括号,括号缺一不可。

2、宏定义示例1:MAX宏

  • #define MAX(a, b) (((a)>(b)) ? (a) : (b))

(1)要想到使用三目运算符来完成。

(2)注意括号的使用

3、宏定义示例2:SEC_PER_YEAR

  • #define SEC_PER_YEAR (365*24*60*60UL)

(1)当一个数字直接出现在程序中时,它的是类型默认是int;

(2)一年有多少秒,这个数字刚好超过了int类型存储的范围.


4、带参宏和带参函数的区别(宏定义的缺陷)

(1)宏定义是在预处理期间处理的,而函数是在编译期间处理的。

  • 这个区别带来的实质差异是:宏定义最终是在调用宏的地方把宏体原地展开,而函数是在调用函数处跳转到函数中去执行,执行完后再跳转回来。
  • 宏定义是原地展开,因此没有调用开销;而函数是跳转执行再返回,因此函数有比较大的调用开销。所以宏定义和函数相比,优势就是没有调用开销,没有传参开销,所以当函数体很短(尤其是只有一句话时)可以用宏定义来替代,这样效率高。

(2)带参宏和带参函数的一个重要差别

  • 宏定义不会检查参数的类型,返回值也不会附带类型。用宏的时候程序员必须很注意实际传参和宏所希望的参数类型一致,否则可能编译不报错但是运行有误。
  • 函数有明确的参数类型和返回值类型。当我们调用函数时编译器会帮我们做参数的静态类型检查,如果编译器发现我们实际传参和参数声明不同时会报警告或错误。用函数的时候程序员不太用操心类型不匹配因为编译器会检查,如果不匹配编译器会叫。
  • 宏和函数各有千秋,各有优劣。总的来说,如果代码比较多用函数适合而且不影响效率;但是对于那些只有一两句话的函数开销就太大了,适合用带参宏。但是用带参宏又有缺点:不检查参数类型。


5、内联函数和inline关键字

(1)内联函数通过在函数定义前加inline关键字实现。

(2)“内联函数就是带了参数静态类型检查的宏。”

  • 内联函数本质上是函数,所以有函数的优点(内联函数是编译器负责处理的,编译器可以帮我们做参数的静态类型检查);
  • 但是同时也有带参宏的优点(不用调用开销,而是原地展开)。
  • 当我们的函数内函数体很短(譬如只有一两句话)的时候,希望利用编译器的参数类型检查来排错,又希望没有调用开销时,最适合使用内联函数。


6、宏定义来实现条件编译(#define #undef #ifdef)

  • 程序有DEBUG版本和RELEASE版本,区别就是编译时有无定义DEBUG宏。


三、函数库

1、什么是函数库?

  • 函数库就是一些事先写好的函数的集合,给别人复用。

2、函数库的提供形式:动态链接库与静态链接库

(1)早期的函数共享都是以源代码的形式进行的。

  • 这种方式共享是最彻底的(后来这种源码共享的方向就形成了我们现在的开源社区)。
  • 缺点是无法以商业化形式来发布函数库。

(2)以库(主要有2种:静态库和动态库)的形式来提供。

  • 比较早出现的是静态链接库。静态库其实就是商业公司将自己的函数库源代码经过只编译不连接形成.o的目标文件,然后用ar工具将.o文件归档成.a的归档文件(.a的归档文件又叫静态链接库文件)。商业公司通过发布.a库文件和.h头文件来提供静态库给客户使用;客户拿到.a和.h文件后,通过.h头文件得知库中的库函数的原型,然后在自己的.c文件中直接调用这些库文件,在连接的时候链接器会去.a文件中拿出被调用的那个函数的编译后的.o二进制代码段链接进去形成最终的可执行程序。
  • 动态链接库比静态链接库出现的晚一些,效率更高一些,是改进型的。现在我们一般都是使用动态库。静态库在用户链接自己的可执行程序时就已经把调用的库中的函数的代码段链接进最终可执行程序中了,这样好处是可以执行,坏处是太占地方了。尤其是有多个应用程序都使用了这个库函数时,实际上在多个应用程序最后生成的可执行程序中都各自有一份这个库函数的代码段。当这些应用程序同时在内存中运行时,实际上在内存中有多个这个库函数的代码段,这完全重复了。而动态链接库本身不将库函数的代码段链接入可执行程序,只是做个标记。然后当应用程序在内存中执行时,运行时环境发现它调用了一个动态库中的库函数时,会去加载这个动态库到内存中,然后以后不管有多少个应用程序去调用这个库中的函数都会跳转到第一次加载的地方去执行(不会重复加载)。

3、函数库中库函数的使用

(1)gcc中编译链接程序默认是使用动态库的,要想静态链接需要显式用-static来强制静态链接。

(2)库函数的使用需要注意4点。

  • 第一,包含相应的头文件;
  • 第二,调用库函数时注意函数原型;
  • 第三,有些库函数链接时需要额外用-lxxx来指定链接;
  • 第四,如果是动态库,要注意-L指定动态库的地址。

4、字符串函数

(1)C库中字符串处理函数包含在string.h中,这个文件在ubuntu系统中在/usr/include中。

(2)常见字符串处理函数及作用:

memcpy 确定src和dst不会overlap,则使用memcpy效率高
memmove 确定会overlap或者不确定但是有可能overlap,则使用memove比较保险
memset
memcmp
memchr
strcpy
strncpy
strcat
strncat
strcmp
strncmp
strdup
strndup
strchr
strstr
strtok

5、数学库函数(math.h)

  • 真正的数学运算的函数定义在:/usr/include/i386-linux-gnu/bits/mathcalls.h;
  • 使用数学库函数的时候,只需要包含math.h即可。

6、C链接器的工作特点

  • 因为库函数有很多,链接器去库函数目录搜索的时间比较久。为了提升速度想了一个折中的方案:链接器只是默认的寻找几个最常用的库,如果是一些不常用的库中的函数被调用,需要程序员在链接时明确给出要扩展查找的库的名字。链接时可以用-lxxx来指示链接器去到libxxx.so中去查找这个函数。
  • 链接时加-lm,就是告诉链接器到libm中去查找用到的函数。


五、自己制作静态链接库并使用

(1)第一步:自己制作静态链接库
首先使用gcc -c只编译不连接,生成.o文件;然后使用ar工具进行打包成.a归档文件
库名不能随便乱起,一般是lib+库名称,后缀名是.a表示是一个归档文件

注意:制作出来了静态库之后,发布时需要发布.a文件和.h文件。

(2)第二步:使用静态链接库

把.a和.h都放在我引用的文件夹下,然后在.c文件中包含库的.h,然后直接使用库函数。
第一次,编译方法:gcc test.c -o test
报错信息:test.c:(.text+0xa): undefined reference to `func1'
test.c:(.text+0x1e): undefined reference to `func2'
第二次,编译方法:gcc test.c -o test -laston
报错信息:/usr/bin/ld: cannot find -laston
collect2: error: ld returned 1 exit status
第三次,编译方法:gcc test.c -o test -laston -L.

无报错,生成test,执行正确。

除了ar名另外,还有个nm命令也很有用,它可以用来查看一个.a文件中都有哪些符号


六、自己制作动态链接库并使用

(1)动态链接库的后缀名是.so(对应windows系统中的dll),静态库的扩展名是.a

(2)第一步:创建一个动态链接库。

gcc aston.c -o aston.o -c -fPIC
gcc -o libaston.so aston.o -shared 
-fPIC是位置无关码,-shared是按照共享库的方式来链接。

注意:做库的人给用库的人发布库时,发布libxxx.so和xxx.h即可。

(3)第二步:使用自己创建的共享库。

第一步,编译方法:gcc test.c -o test
报错信息:test.c:(.text+0xa): undefined reference to `func1'
test.c:(.text+0x1e): undefined reference to `func2'
collect2: error: ld returned 1 exit status

第二步,编译方法:gcc test.c -o test -laston
报错信息:/usr/bin/ld: cannot find -laston
collect2: error: ld returned 1 exit status

第三步,编译方法:gcc test.c -o test -laston -L.
编译成功

但是运行出错,报错信息:

error while loading shared libraries: libaston.so: cannot open shared object file: No such file or directory

错误原因:动态链接库运行时需要被加载(运行时环境在执行test程序的时候发现他动态链接了libaston.so,于是乎会去固定目录尝试加载libaston.so,如果加载失败则会打印以上错误信息。)


解决方法一:
将libaston.so放到固定目录下就可以了,这个固定目录一般是/usr/lib目录。
cp libaston.so /usr/lib即可

解决方法二:

使用环境变量LD_LIBRARY_PATH。操作系统在加载固定目录/usr/lib之前,会先去LD_LIBRARY_PATH这个环境变量所指定的目录下去寻找,如果找到就不用去/usr/lib下面找了,如果没找到再去/usr/lib下面找。所以解决方案就是将libaston.so所在的目录导出到环境变量LD_LIBRARY_PATH中即可。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/Winshare/s5pv210/AdvancedC/4.6.PreprocessFunction/4.6.12.sharedobject.c/sotest


解决方案三:

ubuntu中用ldconfig

(4)ldd命令

  • 作用是可以在一个使用了共享库的程序执行之前解析出这个程序使用了哪些共享库,并且查看这些共享库是否能被找到,能被解析(决定这个程序是否能正确执行)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461075.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PictureBox

代码添加图片&#xff1a; pictureBox1.Image Image.FromFile("F:\360wallpaper_dt.jpg"); http://msdn.microsoft.com/query/dev10.query?appIdDev10IDEF1&lZH-CN&kk(SYSTEM.WINDOWS.FORMS.PICTUREBOX);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERS…

C++的const修饰

2019独角兽企业重金招聘Python工程师标准>>> C的const修饰 ‍const的两个用途‍ &#xff08;1&#xff09;可以定义 const 常量 &#xff08;2&#xff09;const 可以修饰函数的参数、返回值. const的好处 &#xff08;1&#xff09;便于进行类型检查&#xff0c;…

链表(单链表、双链表、内核链表)

以下内容源于朱有鹏嵌入式课程的学习&#xff0c;如有侵权&#xff0c;请告知删除。 一、链表的引入 1、从数组的缺陷说起 数组有2个缺陷&#xff0c;一个是数组中所有元素的类型必须一致&#xff1b;第二个是数组的元素个数必须事先制定并且一旦指定之后不能更改。数组的第一…

心率变异性 matlab,心率变异性好的功率谱分析方面的问题

本帖最后由 天路 于 2018-2-25 21:16 编辑本人正在学习心率变异性方面的内容&#xff0c;但是按照文献上的方法做出来的结果并不是很理想&#xff0c;文献上说的是心率变异性的频率的范围是0.4以内&#xff0c;但是我做的功率谱上显示频率分布在整个频域内&#xff0c;试了很多…

C#获取Windows下光标位置(转)

使用C#获取光标相对于显示器屏幕的位置&#xff1a; 方式一&#xff1a; 1 [csharp] view plaincopyprint?2 using System; 3 using System.Drawing; 4 using System.Runtime.InteropServices; 5 6 namespace ColorPicker 7 { 8 /// <summary> 9 /// win…

[Android Studio] Android Studio如何提示函数用法

Eclipse有一个很好的功能&#xff0c;就是当你代码调用某个android API时&#xff0c;鼠标移到对应的函数或者方法上&#xff0c;就会自动有一个悬 浮窗提示该函数的说明&#xff08;所包含的参数含义&#xff0c;该方法功能&#xff09;。迁移到Android Studio后&#xff0c;这…

uboot源码——链接脚本u-boot.lds分析

以下内容源于朱有鹏嵌入式的学习与整理&#xff0c;如有侵权&#xff0c;请告知删除。 uboot的链接脚本u-boot.lds&#xff0c;位于uboot根目录下的/board/samsung/x210目录。 脚本内容与分析如下&#xff1a; /** (C) Copyright 2002* Gary Jennejohn, DENX Software Engi…

php 调用 com,PHP调用COM函数

用PHP调用windows中的COM相关函数&#xff1a;函数列表&#xff1a;com_load — 创建一个COM组件的引用。com_invoke — 调用一个COM组件的方法。com_propget — 取一个COM组件的属性的值。com_get — 同上&#xff0c;取一个COM组件的属性的值。com_propput — 赋一个值给一个…

struts2--java.lang.IllegalAccessException: Class ognl.OgnlRuntime can not access a member of

这个问题是我碰到的一个比较纠结的问题&#xff0c;如果第一次碰到肯定能让你也很纠结&#xff0c;哈哈 [c-sharp] view plaincopyprint? 01.2010-10-19 18:27:22 com.opensymphony.xwork2.util.logging.jdk.JdkLogger warn 02.警告: Error setting expression nnew.nTitle …

情怀真的可以吃~致文青

说实在的&#xff0c;这首歌名如其实的平凡。最标准的Trip-Hop节奏&#xff0c;最普通的四个和弦&#xff0c;最没有惊喜的合成器音色&#xff0c;歌词也是那山那山的波澜不惊。就是这么一首平平无奇的歌&#xff0c;却在首发的7个小时内&#xff0c;狂刷一百万次试听点击。 …

决策树php,决策树模型组合之随机森林与GBDT

前言&#xff1a;决策树这种算法有着很多良好的特性&#xff0c;比如说训练时间复杂度较低&#xff0c;预测的过程比较快速&#xff0c;模型容易展示(容易将得到的决策树做成图片展示出来)等。但是同时&#xff0c;单决策树又有一些不好的地方&#xff0c;比如说容易over-fitti…

关于uboot的简介——uboot的目录结构

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 不同版本的uboot&#xff0c;或者同一版本不同人员移植的uboot&#xff0c;目录结构和文件内容都会有所不同&#xff0c;也就是说&#xff0c;可以根据需要去添加、删除或者更改目录结构。在以三星的…

为何不精通C? 03 深入剖析声明

对于复杂的C函数声明&#xff0c;或者被typedef别名后的声明&#xff0c;很多人往往一头雾水。本文主要解析下C语言中声明过程所遵循的原则。 声明 引用《C专家编程》的第三章内容&#xff0c;说明下声明的优先级规则&#xff1a; 声明从它的名字开始读&#xff0c;然后按照优先…

Photoshop绘制植物大战僵尸中的食人花

本人意见&#xff1a;我打算使用本文中的食人花形象作为我的一次讲课中的主要参考。我想说明的是&#xff0c;通过FLASH和PS都能够轻松地绘制出如本文所描述的食人花卡通效果。独立游戏开发者如果安排好时间&#xff0c;完全可以通过1-2个月掌握FLASH和PS绘制&#xff08;想精通…

ios手机 php无法上传文件,【已解决】uni.uploadFile 苹果ios图片上传不成功

安卓正常上传&#xff0c;苹果里我到相册里选了一张20k的图片&#xff0c;服务器都得不到数据&#xff0c;服务器端是PHP&#xff0c;$_FILES居然是空的&#xff0c;下面是简单的代码openPic(){var that this;uni.chooseImage({count: 1,sourceType: [album, camera],sizeType…

uboot源码——uboot启动内核过程总结

总结的思维导图&#xff0c;其下载地址&#xff1a;Uboot启动.mmap_免费高速下载|百度网盘-分享无限制 第一阶段&#xff1a;汇编阶段&#xff0c;即start.S文件的工作。 第二阶段&#xff1a;C代码阶段&#xff0c;即start_armboot函数的工作。 值得一提的是&#xff0c;star…

虚拟化运行[OpenStack] VMWare产品介绍

最近使用开发的过程中出现了一个小问题&#xff0c;顺便记录一下原因和方法--虚拟化运行 世界上最早研制虚拟化软件的厂商之一。目前是是寰球桌面到数据中心虚拟化解决方案的引导厂商。中文名“威睿”&#xff0c;纽约证券交易所代码&#xff1a;VMW。总部设在加利福尼亚州的帕…

oracle模拟重叠事务,ORACLE的事务读一致性与语句读一致性

SET TRANSACTION READ ONLY来实现事物级别的一致性。一个事物所有语句读到的数据都是一致的。我们开始试验一&#xff0c;模拟语句级别读一致性。第一个session使用显示打开一个游标模拟数据读&#xff0c;同时在游标读数据的过程中&#xff0c;启动另外一个session更改数据&am…

内核源码——kernel启动过程的思维导图

参考博客 内核源码——汇编阶段的head.S文件_天糊土的博客-CSDN博客___head汇编 内核源码——C语言阶段的start_kernel函数_天糊土的博客-CSDN博客_start_kernel 思维导图 kernel启动过程的思维导图下载地址&#xff1a;内核启动过程.mmap

能力=知识+技能+经验

知识少则眼光不够深远&#xff1b;技能不精则难以服众经验太少则容易应变不灵。那么如果想让老板提升你&#xff0c;最“靠谱”的方法就是用这几方面检验自己&#xff0c;寻找机会进行自我提升&#xff0c;全面而优质的能力自然会让HR对你另眼相看。 然而这种理想的状态并非短期…