小弟我学C的那些年[ch02]:宏,结构体,typedef
c语言的编译过程:
- 预处理
- 编译
- 汇编
- 链接
而预处理中有三种情况:
- 文件包含( #include )
- 条件编译(#if,#ifndef,#endif)
宏定义( #define )
宏就是预处理中的一种情况. 其实,宏的概念就是文本替换
宏的作用:
1.可维护性
2.可读性
宏还有其他作用比如:程序调试跟踪 等, 因为我也没试过那些,这里不写那些了
宏定义:
1.不带参数的宏定义
#define 标识符 字符串
2.带参数的宏定义
#define 标识符(参数表) 字符串
注意:标识符一般大写 ,
这里将不会解答宏中包含特殊符号:#、##的问题.
1.不带参数的宏:
源文件:C:\Users\Zero\Desktop\c\temp\macro.c
<stdio.h> #define PI 3.1415 //定义常量PI void main(){ printf("PI常量的值:%f",PI); }
结果:
原因:
宏是预编译中的一种情况(我在前面也有说到), 而预编译主要就是替换,宏就是文本替换
我们可以生成预编译后的文件来查看:
执行dos命令:
打开文件预编译后的文件macro.i文件:
由于加载预编译后的文本很长,这里我主要显示main主函数这部分:
wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fgetwchar (void); wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fputwchar (wint_t); int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) getw (FILE*); int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) putw (int, FILE*); # 2 "macro.c" 2 void main(){ printf("PI常量的值:%f",3.1415); }
我们可以看到原来的”PI”不见了,被替换成了3.1415
2.带参数的宏:
源文件:C:\Users\Zero\Desktop\c\temp\macro2.c
#include <stdio.h> #define AREA(x,y) x*y void main(){ int a=6; int b=5; int result; result=AREA(a+b,a-b); printf("result:%d",result); }
结果:
原因:
也许有人会认为”result=AREA(a+b,a-b)”的运算过程是这样的:
result = (a+b)*(a-b) --> result= (6+5)(6-5) --> result=11
不过,很遗憾它并不是函数,AREA(x,y) 不是函数 ,它不是函数!!!
开始就说过,宏就是替换 ,追其本质, 这里也是替换.
查看预编译后的文件:
执行dos命令:
打开文件预编译后的文件macro2.i , 主要显示main函数部分:
wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fgetwchar (void); wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fputwchar (wint_t); int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) getw (FILE*); int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) putw (int, FILE*); # 2 "macro2.c" 2 void main(){ int a=6; int b=5; int result; result=a+b*a-b; printf("result:%d",result); }
我们发现原来的”AREA(a+b,a-b)”被替换成了“a+b*a-b”
result = (a+b)*(a-b) --> result= (6+5)(6-5) --> result=11
result=a+b*a-b –> result=6+5*6-5 –> result=31
因为是替换,并不是函数,所以最后的结果是31
关于宏的作用:
1.可维护性
源文件:C:\Users\Zero\Desktop\c\temp\effect.c
#include <stdio.h> #define PI 3.14 void main(){ printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); }
比如:我们在控制台输出10次PI的值, PI=3.14 , 后来,我发现3.14并不是我想要的结果,
我想要修改原来3.14的值, 改成3.1415 ,这个时候 我只需要修改PI这个常量就可以了.并不需要修改10次
#include <stdio.h> #define PI 3.1415 void main(){ printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); printf("控制台输出PI:%f\n",PI); }
2.可读性
假如,一个常量为3.1415926, 你的代码也很长的时候,去找3.1415926是非常麻烦的,
#define PI=3.1415926 , 你只需要找到PI就可以了
结构体
在实际问题中,一组数组往往具有不同的数据类型,这个时候就可以用到结构体
结构体:结构体是基本数组类型组成,里面可以存在不同类型的变量.像java中对象
结构体定义的是一个类型, 就好像自己定义了整形(int)一样
结构体的定义:
显示声明结构体:
struct 结构体名{
类型 变量名;
类型 变量名;
…
};
匿名声明结构体:
struct {
类型 变量名;
类型 变量名;
…
}结构体变量;
下面我将演示,及说明结构体的声明:
显示声明结构体:
源文件:C:\Users\Zero\Desktop\c\temp\struct.c
<stdio.h> #include <string.h> //导入strcpy()函数的头文件 //显示声明结构体 struct stu{ char name[20]; //学生姓名 int age; //学生年龄 }; void main(){ //声明结构体变量 struct stu student; //struct stu: 这里是一个类型, student是一个变量 //赋值 strcpy(student.name,"张二"); //student.name="张二",写法是错误的 //因为字符数组定义之后,是不能直接赋值的 ,不过,可以用strcpy()方法赋值 student.age=13; //控制台输出 printf("student.name:%s\n",student.name); printf("student.name:%d\n",student.age); }
编译文件:
结构体声明还是比较简单的,把”struct 结构体名”看成一个整体就可以了, 如下:
int 变量; //定义整形
struct 结构体名 变量; //定义结构体
两者的定义都是差不多的。关于访问结构体里的变量 ,一般都是通过 “结构体变量.变量” 访问
匿名声明结构体:
源文件:C:\Users\Zero\Desktop\c\temp\struct2.c
<stdio.h> #include <string.h> //导入strcpy()函数的头文件 //匿名声明结构体 struct{ char name[20]; //学生姓名 int age; //学生年龄 }student; void main(){ //赋值 strcpy(student.name,"张三"); student.age=23; //控制台输出 printf("student.name:%s\n",student.name); printf("student.age:%d\n",student.age); }
编译文件:
我们发现匿名声明和显示声明还是有一些不同的,在void main()主函数里,我并没有定义student变量, 但是却可以正常执行, 原因是在匿名声明结构体的时候,我就声明了student变量, 创建匿名结构体的时候,是可以同时声明变量的,因为它是匿名的, 匿名结构体通常只能用一次。
typedef
typedef, 我感觉还是挺好用的,typedefine的简写, 顾名思义就是类型定义,它可以给类型取一个别名.
下面我们可以通过代码来观察:
源文件:C:\Users\Zero\Desktop\c\temp\typedef.c
<stdio.h> typedef int Integer; //给int类型取个别名Integer typedef double Double; //给double类型取个别名Double void main(){ Integer i=1; //定义整形变量 int j=10; //定义整形变量 Double mark=23.3; //定义双精度浮点变量 printf("Integer i:%d\n",i); printf("int j:%d\n",j); printf("Double mark:%lf\n",mark); }
即便是取了别名,原来的类型还是可以用的
编译文件:
typedef还可以用于结构体上:
源文件:C:\Users\Zero\Desktop\c\temp\typedef.c
显示声明:
<stdio.h> #include <string.h> //导入strcpy函数的函数库 //显示定义学生结构体,别名stu_type typedef struct stu{ char name[20];//学生姓名 int age;//学生年龄 }stu_type; void main(){ stu_type student; //声明结构体变量student //赋值 strcpy(student.name,"赵七"); student.age=17; //控制台输出 printf("student.name:%s\n",student.name); printf("student.age:%d\n",student.age); }
结果:
匿名声明:
源文件:C:\Users\Zero\Desktop\c\temp\typedef3.c
#include <stdio.h> #include <string.h> //导入strcpy函数的函数库 //匿名定义学生结构体,别名stu_type typedef struct{ char name[20];//学生姓名 int age;//学生年龄 }stu_type; void main(){ stu_type student; //声明结构体变量student //赋值 strcpy(student.name,"赵九"); student.age=19; //控制台输出 printf("student.name:%s\n",student.name); printf("student.age:%d\n",student.age); }
结果:
其实,匿名声明结构体加上typedef之后,就有名字 - -!,就可以使用多次了
好了,文章到了这里,也该结束了. 欢迎阅读我学C的那些年