C语言学习(12)
宏定义
1. 宏定义: 理解为字符的替换
语法: #define PI 3.14 //定义了一个宏,名字叫做PI,代表3.14这个小数
1 #include <stdio.h> 2 3 //定义一个宏 4 #define PI 3.14 5 #define N 88 6 #define PATH "/mnt/hgfs/share" 7 8 int main() 9 { 10 int r=5; 11 printf("%f\n",PI); 12 printf("%d\n",N); 13 printf("%s\n",PATH); 14 printf("%f\n",PI*r*r); 15 }
注意:宏定义的名字一般都写成大写
优点: 宏定义的执行效率很高(宏定义在预处理的时候就已经被替换了,而变量等到程序运行的时候才会加载到内存种参与运算),宏定义不是变量
宏定义总共两种表现形式:
形式1: 普通的宏定义 #define PI 3.14 不要理解为变量了
形式2: 宏函数 #define ADD(a,b) (a)+(b)
1 #include <stdio.h> 2 3 //定义一个宏函数 4 #define ADD(a,b) a+b 5 int myadd(int a,int b) 6 { 7 return a+b; 8 } 9 int main() 10 { 11 int m=12; 12 int n=13; 13 printf("m+n is:%d\n",ADD(m,n)); //迷惑初学者,跟函数调用类似(错误理解,宏函数不是真正的函数) 14 printf("m+n is:%d\n",myadd(m,n)); 15 }
注意: 宏函数不是真正意义上的函数
宏函数一定加上圆括号(为了防止出现优先级的问题)
宏定义如果有多行(一行写不下),必须加上续行符
多行必须添加续行符
1 #include <stdio.h> 2 3 #define CMP(a,b) if(a>b) printf("a>b\n");\ 4 else printf("a<=b\n") 5 6 int main() 7 { 8 int m=78; 9 int n=76; 10 CMP(m,n); 11 }
C语言中printf和scanf的工作原理
1. scanf工作原理
返回值:表示输入的符合要求的数据个数
scanf和printf在系统中有个IO缓冲区,键盘输入的内容先进入到缓冲区,然后scanf从缓冲区里面读取符合要求的数据
int getchar(void) 一次从缓冲区中读取一个字符(并把字符从缓冲区里面剔除
1 #include <stdio.h> 2 3 int main() 4 { 5 int n1,n2; 6 int ret; 7 int ret1; 8 label: 9 printf("请输入1个整数!\n"); 10 ret=scanf("%d",&n1); //hello\n 11 //判断返回值 12 if(ret!=1) //你输入的不是整数 13 { 14 //清除掉io缓冲区中错误的字符 15 while(getchar()!='\n'); 16 goto label; 17 } 18 else 19 printf("n1 is:%d\n",n1); 20 }
2. printf工作原理
先将输出的内容存放到IO缓冲区里面,遇到\n或者return 0 主函数退出会自动刷新缓冲区(把缓冲区里面的内容显示在液晶屏上面)
1 #include <stdio.h> 2 3 int main() 4 { 5 printf("hello world"); //液晶屏上没有输出 6 //printf("hello world\n"); //输出了 7 8 while(1); //不让程序退出 9 }
C语言中的结构体,共用体,枚举
1. 结构体:
定义: struct 结构体的名字
{
结构体成员;
};
比如: struct student //把不同类型的数据打包成一个整体,程序员自定义了一种新的数据类型
{
char name[10];
int age;
};
1 #include <stdio.h> 2 #include <string.h> 3 struct student 4 { 5 char name[10]; 6 int age; 7 }; 8 int main() 9 { 10 //一个班级10个学生 11 struct student array[10]; 12 strcpy(array[0].name,"张三");; 13 array[0].age=18; 14 15 strcpy(array[1].name,"李四");; 16 array[1].age=18; 17 18 struct student *p=array; //&array[0] 19 printf("p->name is:%s\n",p->name); 20 printf("p->age is:%d\n",p->age); 21 22 p++; //跳到下一个结构体 23 24 printf("p->name is:%s\n",p->name); 25 printf("p->age is:%d\n",p->age); 26 27 28 }
简化书写 typedef struct student
{
char name[10];
int age;
}stu,*pstu;
stu就是 struct student 类型名 stu stu1; //stu1是个普通结构体变量
pstu就是struct student *类型名 pstu stu4; //stu4是个结构体指针变量
结构体的初始化和使用:
struct student stu1={“张三”,18};
struct student stu1;
stu1.name="张三";
stu1.age=18;
struct student stu1={.name="张三";.age=18;}
struct student *stu1;
stu1->name="张三";
stu1->age=18;
struct student *stu1;
(*stu1).name="张三";
(*stu1).age=18;
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 //定义一个结构体 5 struct student 6 { 7 char name[10]; 8 int age; 9 }; 10 11 int main() 12 { 13 //写法一:定义结构体变量并初始化 14 struct student stu1={"张三",18}; //int a; 15 //普通结构体采用小数点引用成员 16 printf("学生姓名:%s\n",stu1.name); 17 printf("学生年龄: %d\n",stu1.age); 18 19 //写法二:定义结构体变量没有立马初始化,后面才赋值 20 struct student stu2; 21 //stu2.name="张三"; //数组赋值只能用strcpy() 22 strcpy(stu2.name,"李四"); 23 stu2.age=18; 24 25 //写法三:linux内核代码中很常见 26 //优点:可以不按照结构体定义的顺序赋值 27 struct student stu3={ 28 .age=19, 29 .name="王五" //小数点一定要写上 30 }; 31 32 33 //写法四:定义结构体指针 34 struct student *stu4=malloc(sizeof(struct student)); //野指针 Segmentation fault (core dumped) 35 strcpy(stu4->name,"赵六"); 36 stu4->age=18; 37 printf("学生姓名:%s\n",stu4->name); 38 printf("学生年龄: %d\n",stu4->age); 39 40 //写法五:定义结构体指针 41 struct student *stu5=malloc(sizeof(struct student)); 42 strcpy((*stu5).name,"赵七"); 43 (*stu5).age=18; 44 45 free(stu4); 46 free(stu5); 47 }
普通结构体变量使用小数点引用成员:struct student stu1 stu1.age;
结构体指针使用->引用成员: struct student *p; p->age;‘
2. 结构体大小规则
32位系统
第一步:找到结构体成员中数据类型最大的成员
第二步:如果数据类型最大成员>=4字节,那么整个结构体按照4字节对齐
如果数据类型最大成员<4字节,那么整个结构体按照最大数据成员的大小对齐
第三步:如果char和short连续挨在一起,需要合并
64位系统
第一步:找到结构体成员中数据类型最大的成员
第二步:如果数据类型最大成员>=8字节,那么整个结构体按照8字节对齐
如果数据类型最大成员<8字节,那么整个结构体按照最大数据成员的大小对齐
第三步:如果char和short连续挨在一起,需要合并(小心这一步跟32位系统的区别)
3. 联合体(共用体)
定义: union 联合体的名字 //跟结构体写法类似
{
};
union sex
{
char a;
char b;
};
普通联合体变量: union sex sex1; sex1.a='M';
联合体指针: union sex *p=&sex1; p->b='W';
联合体数组: union sex array[10];
跟结构体的区别,特点:
联合体中所有的成员变量占用同一块内存区域(成员变量是互斥)
联合体大小由最大成员的大小决定,也要满足字节对齐
1 #include <stdio.h> 2 union yy //1<8 1字节对齐 3 { 4 char a; //1 5 char b; //1 6 }; 7 8 union tt 9 { 10 char a; 11 int b; 12 }; 13 14 union uu // 16字节 15 { 16 char a[11]; //11 17 double b; //8 18 int c; 19 }; 20 21 int main() 22 { 23 //定义联合体变量 24 union yy y1; 25 y1.a='@'; //只给a赋值了,b没有赋值 26 y1.b='#'; //由于a和b共用同一块内存,b覆盖掉前面a的值 27 28 printf("sizeof yy is:%lu\n",sizeof(union yy)); //1 29 printf("sizeof tt is:%lu\n",sizeof(union tt)); //4 30 printf("sizeof uu is:%lu\n",sizeof(union uu)); //16 31 union tt t1; 32 t1.b=567; 33 //请问t1.a是多少???? %d打印 34 printf("t1.a is:%c ASCII is:%d\n",t1.a,t1.a); 35 printf("y1.a is:%c\n",y1.a); 36 printf("y1.a is:%p\n",&(y1.a)); 37 printf("y1.b is:%c\n",y1.b); 38 printf("y1.b is:%p\n",&(y1.b)); 39 }
实际用途:
用来表示互斥的概念--》用联合体
union sex
{
char man; // 'M' --》男 'W' --》女
char woman;
}
4. 枚举:
定义: enum 枚举的名字 {枚举数据}
enum day {mon,tues,wen};
enum color {red, green blue};
1 #include <stdio.h> 2 3 //enum day {mon,tues,wen}; //默认情况下三个枚举变量值:分别是 0 1 2 4 5 //enum day {mon,tues=7,wen}; // 0 7 8 6 enum color {red,green,blue,yellow,black}; 7 //0 1 2 3 4 5 6 一个星期的七天 可读性差 8 enum day {mon,tues=7,wen,tir,fri=2,sat}; // 0 7 8 9 2 3 9 10 struct car //小汽车 11 { 12 char name[10]; //品牌 13 enum color carcolor; //车的颜色 14 float price; 15 }; 16 int main() 17 { 18 //定义一台车 19 struct car car1={"奔驰s600",black,2000000.0}; 20 21 printf("mon is:%d\n",mon); 22 printf("tues is:%d\n",tues); 23 printf("wen is:%d\n",wen); 24 printf("tir is:%d\n",tir); 25 printf("fri is:%d\n",fri); 26 printf("sat is:%d\n",sat); 27 }
C语言中的头文件
作用: 包含其它头文件
结构体,联合体,枚举的定义
全局变量,函数的声明
内联函数定义
宏定义
头文件的标准写法:
参考系统中标准头文件:
#ifndef _XXXX_H 目的为了防止重复包含同一个头文件
#define _XXXX_H
#endif
头文件<>和“”区别:
<> --》多用于系统自带的头文件,编译器会自动去系统的环境变量中寻找这个头文件
环境变量:系统的一个默认路径/usr/include
“” --》多用于自定义的头文件,编译器会自动从当前路径下寻找这个头文件,如果当前路径下面没有,就去环境变量中找
C语言程序编译的四个过程
hello.c源文件 --->hello 可执行程序 经历四个过程: 预处理--》编译--》汇编--》链接生成可执行程序
C语言的条件编译(跟宏定义配合使用)
1. 作用: 满足条件就会帮你编译代码,不满足,就不编译(代码写了跟没写一样,被注释掉了)
2. 写法一:
#if 宏定义 //判断宏定义的值是真还是假
代码;
#else
代码;
#endif
变形形式: #if #elif #else #endif
写法二:
#ifdef 宏定义 //宏定义定义了就执行,不是判断真假
代码;
#else //没有定义
代码;
#endif
写法三:
#ifndef 宏定义 // 宏定义没有定义就执行
代码;
#else //定义了就执行
代码;
#endif
注意:以上都是完整的标准写法, #else可以写也可以不写的
作业:
1. scanf("%d",&n); // 34hhh 不正确的整数
#include <stdio.h> int main() { int n; int ret; label: printf("请输入整数!\n"); ret=scanf("%d",&n); //34hhh if(ret!=1) { //清空缓冲区 while(getchar()!='\n'); goto label; } else if(ret==1&&getchar()=='\n') //正确的整数 34\n printf("正确的整数:%d\n",n); else if(ret==1&&getchar()!='\n') //数字后面有字符 34yut\n { printf("你输入的数字后面还有字符!!!!\n"); //清空缓冲区 while(getchar()!='\n'); goto label; } }