前面学习了c语言的基本语法特性,本节进行更深入的学习。

  • 预处理程序。 编译指令: 预处理, 宏定义,

  • 建立自己的数据类型:结构体,联合体,动态数据结构

  • c语言表达式工具 逻辑运算符: & | ^ ~ << >>

  • 函数的递归调用方法

什么是预处理

vim helloworld.c1

helloworld.c:

#include int main()
{    printf("hello,world!\n");    return 0;
}1234567

编译的目的:

从c语言.c源文件变成可执行文件

gcc helloworld.c -o helloworld.out./helloworld.out12

编译的四个步骤:

.c文件->.i文件->.s文件->.o文件->可执行文件(可运行)

下面我们来查看预处理中要做的事情:

gcc -o helloworld.i helloworld.c -E1

-E表示只让gcc执行预处理。

// 查看helloworld.i文件cat helloworld.i12

vim跳到整个文档底部,命令: :$

可以看到代码的底端是我们的main函数

对比一下.i文件和.c文件的区别

首先:它们都是c的语法。其次.c文件main函数上面是#include

.i 文件中这行代码不见了,变成了上面这些东西。

所以预处理所做的第一件事情就是展开头文件

#includestdio.h展开,将未注释的内容直接写入.i文件。

在预处理步骤中,除了展开头文件,还要进行宏替换。

宏是什么

c语言常量分为直接常量和符号常量:

#define 标识符 常量值 (注意:没有分号)1

helloMacro.c源代码:

#include #define R 10int main()
{    int a =R;    printf("a=%d\n");    printf("hello,world!\n");    return 0;
}12345678910
gcc -o helloMacro.i helloMacro.c -E1

预处理过之后的代码

# 4 "helloworld.c"int main()
{    int a =10;    printf("a=%d\n");    printf("hello,world!\n");    return 0;
}12345678

可以看到10是直接当做一个字符串来替换原本的宏定义R。

宏的本质是发生在预处理阶段单纯的字符串替换(宏替换), 在预处理阶段,宏不考虑语法;

示例代码2:
vim helloMacro2.c

#include #define R 10#define M int main(M){    printf("hello,world!\n");    return 0;
}12345678
gcc helloMacro2.c -o helloMacro2.out./helloMacro2.out12

预处理是没有问题的,可以成功的编译执行。宏不考虑C语言的语法。它很单纯,字符串替换。

  • 宏用于大量反复使用的常量、数组buffer的大小,为了便于修改定义成宏。

通常定义数组我们这样写:

int a[10];int b[10];12

定义两个相同大小的数组,这里我们就可以改为下面代码。

#define R 10int a[R];int b[R];123

一次修改,可以修改两份。

宏也是可以传递参数的,可以做一些函数可以做的事情

宏函数

vim helloMacroFunction.c
源代码:

#include #define R 10#define M int main(#define N(n) n*10M){    int a = R;    int b = N(a);    printf("b = %d\n",b);    printf("a =%d\n",a);    printf("hello,world!\n");    return 0;
}123456789101112131415
gcc helloMacroFunction.c -o helloMacroFunction.out./helloMacroFunction.out12

这里的处理过程: 首先将参数a替换到上面的宏中,上面就变成了N(a) a*10,之后再用a*10替换下面的N(a)

int b = N(a); //变成了 int b =a*10;1
gcc -o helloMacroFunction.i helloMacroFunction.c -E1

预处理之后:

# 8 "hello.c"int main(){    int a = 10;    int b =a*10;    printf("b = %d\n",b);    printf("a =%d\n",a);    printf("hello,world!\n");    return 0;
}123456789

先不考虑宏实现,先来写一个正常的求和函数。

vim helloAdd.c1
#include #define R 20#define M int main(#define N(n) n*10int add(int a,int b){    return a b;
}M){    int a = R;    printf("a =%d\n",a);    printf("hello,world!\n");    int b =N(a);    printf("b = %d\n",b);    int c =add(a,b);    printf("c =%d\n",c);    return 0;
}1234567891011121314151617181920212223
gcc helloAdd.c -o helloAdd.out./helloAdd.out12

使用宏函数实现求和。

vim helloAddMacro.c1
#include #define R 20#define M int main(#define N(n) n*10#define ADD(a,b) a bint add(int a,int b){    return a b;
}M){    int a = R;    printf("a =%d\n",a);    printf("hello,world!\n");    int b =N(a);    printf("b = %d\n",b);    int c =add(a,b);    printf("c =%d\n",c);    int d =ADD(a,b);    printf("d =%d\n",d);    return 0;
}1234567891011121314151617181920212223242526
gcc helloAddMacro.c -o helloAddMacro.out./helloAddMacro.out12

可以看到使用宏函数和普通函数的求和效果是一致的。结果与简单的字符串替换一致。

ADD(a,b) 被替换成 a b 因此式子变成int d = a b;

gcc -o helloAddMacro.i helloAddMacro.c -E
vim helloAddMacro.i12

版本3,宏定义中优先级问题。

#include #define R 20#define M int main(#define N(n) n*10#define ADD(a,b) a bint add(int a,int b){    return a b;
}M){    int a = R;    printf("a =%d\n",a);    printf("hello,world!\n");    int b =N(a);    printf("b = %d\n",b);    int c =add(a,b);    printf("c =%d\n",c);    int d =ADD(a,b);    printf("d =%d\n",d);    int e =ADD(a,b) * ADD(a,b);    printf("e =%d\n",e);    return 0;
}1234567891011121314151617181920212223242526272829

预测一下e的输出为: a b*a b ab先乘起来,a=20,b=200,ab=4000,然后加上a,b:得到结果(4220)

gcc helloAddMacroPrecedence.c -o helloAddMacroPrecedence.out./helloAddMacroPrecedence.out12

运算是等我们编译完了,执行的时候才会运行的。预处理阶段不会进行运算操作。

  • 宏定义时由于本质是字符串的替换

真正运算的时候,会按照运算符号的优先级来进行

解决方案:

#define ADD(a,b) (a b)1
gcc helloAddMacroPrecedence.c -o helloAddMacroPrecedence2.out./helloAddMacroPrecedence2.out12

加个括号,保证优先级更高一点。

宏函数和正常函数的优势?

正常的add函数需要返回值类型,需要传递进来的参数有类型要求。

讲传入的a,b 类型进行改变,如变为两个浮点型数,程序就会自动类型转换。

但是宏函数就没有这种要求可以不用考虑输入值的类型,这与普通的函数定义不同。

int c =add(10.5,20.4);printf("c =%d\n",c);float d =ADD(10.5,20.4);printf("d =%f\n",d);12345
gcc helloAddMacroPrecedenceCompare.c -o helloAddMacroPrecedenceCompare.out./helloAddMacroPrecedenceCompare.out12

普通函数例如int add(int a,int b)除了在开头要声明值的类型,还要设置返回值,因此在定义过程与调用过程相对复杂。若能用宏定义实现的情况应优先考虑宏定义.

宏是不考虑数据类型,不考虑c语言的语法的。只是简单的字符串的处理。

预处理阶段,除了宏之外,还提供了一个叫做mtianyan:条件编译的功能。

可以按照不同的条件,编译不同的程序部分,从而产生不同的目标代码文件。对于程序的移植和调试都是很有用的。

下集预告: 和宏比较相近的功能,typedef

Linux C预处理之typedef

严格来讲,typedef和预处理是没

Linux C语言结构体相关推荐

  1. linux中c语言结构体详解,Linux C语言结构体-学习笔记

    Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习. 预处理程序. 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 ...

  2. linux c语言结构体初始化,Linux c中 结构体初始化方式

    某日在看Linux底层驱动的介绍时,发现在Linux C中结构体的有多种初始化方式,这激起了我的好奇心,亲自实践,今日便来总结一下. 首先定义一个结构体:struct test{ int a; cha ...

  3. Linux+c语言结构体对齐,C语言中结构体struct的对齐问题解析

    一:struct和union的区别 struct,相互关联的元素的集合,每个元素都有自己的内存空间:每个元素在内存中的存放是有先后顺序的,就是定义时候的顺序:一个struct所占的总的内存大小,并不是 ...

  4. python展开 c函数中的宏预处理_Linux C语言结构体-学习笔记

    Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习. 预处理程序. 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 ...

  5. c语言2个字符串可以相互赋值吗,c语言结构体2之变量赋值于字符串

    #include #include struct dangdang { char email[]; char name[]; char addr[]; int num; int bugnum; cha ...

  6. c语言结构体和联合体,C语言结构体和联合体

    1.单链表插入 #include #include #define FALSE 0 #define TRUE 1 typedef struct NODE{ STRUCT NODE *link; int ...

  7. linux windows 结构体,Linux下C语言——结构体对齐

    结构体对齐的步骤: 1.结构体各成员对齐 2.整个结构体圆整 结构体对齐的特定对齐值: 1.自身对齐值: 自身对齐值就是结构体变量里每个成员的自身大小; 2.指定对齐值: 指定对齐值是由宏#pragm ...

  8. linux c 结构体参数,C语言结构体类型定义

    C语言结构体类型定义 结构体的定义形式如下: struct 结构体名 { 结构体成员 }: 结构体变量的定义方式有三种: 1.先定义结构体,再定义变量: eg. struct student{ cha ...

  9. 关于c语言结构体偏移的一点思考

    注:此处只是利用了编译器的特性来计算结构体偏移 这句话就一笔带过,说得有点牵强附会.以后有时间自己再详细了解一下编译器的特性... more exceptional c++ 中文版 26页 https ...

最新文章

  1. Windows7 64bit VS2013 Caffe test MNIST操作步骤
  2. 新一代(New Generation)测试框架TestNG
  3. Aggregate累加器
  4. 指纹识别开发包 SourceAFIS
  5. 放出php压缩HTML函数,轻松压缩html、js和Css
  6. 传感器应用的demo自动录音器
  7. JavaScript基础事件(6)
  8. 用html5交换两个变量的值,Python判断两个对象相等的原理 python交换两个变量的值为什么不用中间变量...
  9. cdgb调试linux崩溃程序
  10. 1. BeeGo 介绍与项目的创建,启动
  11. Android 开发 学习网站
  12. 一台电脑能装两个版本的cad吗_同一台电脑装多个cad 电脑怎么装cad软件步骤
  13. autocad.net 画多段线_VB.net 在AutoCAD中绘制矩形云线
  14. msm8953 LCD移植详解
  15. Cobalt Strike上线微信机器人提醒
  16. 2014 c语言程序设计形成性考核册,C语言程序设计形成性考核册参考答案
  17. 为什么普遍使用Linux做服务器?
  18. 什么是大数据架构?需要学什么内容?
  19. 如何参加活动拿到华为实践证书?一起来吧~
  20. 补单平台-淘宝天猫补单平台-手工补单平台-靠谱的补单平台是什么样的

热门文章

  1. q7goodies事例_Java 8 Friday Goodies:Lambda和排序
  2. JMetro版本4.8已发布
  3. permgen_什么是PermGen泄漏?
  4. javaone_JavaOne 2012覆盖率
  5. ejb jsf jpa_完整的WebApplication JSF EJB JPA JAAS –第1部分
  6. 本周Java技巧#7 – Maven慢吗?
  7. uibinder表单提交_使用UIBinder的GWT自定义按钮
  8. 存根类 测试代码 java_为旧版代码创建存根-测试技术6
  9. Java 8 Friday:Java 8将彻底改变数据库访问
  10. 监控整页,非AJAX,要求通知