金华网站建设解决方案,wordpress文章名称,wp网站模板,想成为网站设计师要怎么做文章目录 引言1. 第一个C语言程序#xff1a;HelloWorld1.1 编写C语言代码#xff1a;hello.c1.2 代码分析 2. 数据类型2.1 常量与变量2.1.1 关键字2.1.2 数据类型2.1.3 常量2.1.4 变量2.1.5 使用示例 2.2 整型#xff1a;int2.2.1 整型变量的定义和输出2.2.2 整型变量的输入… 文章目录 引言1. 第一个C语言程序HelloWorld1.1 编写C语言代码hello.c1.2 代码分析 2. 数据类型2.1 常量与变量2.1.1 关键字2.1.2 数据类型2.1.3 常量2.1.4 变量2.1.5 使用示例 2.2 整型int2.2.1 整型变量的定义和输出2.2.2 整型变量的输入2.2.3 short、int、long、long long2.2.4 有符号数和无符号数区别 2.3 sizeof关键字2.4 字符型char2.4.1 字符变量的定义和输出2.4.2 字符变量的输入2.4.3 ASCII对照表2.4.4 转义字符 2.5 实型(浮点型)float、double2.6 C语言如何表示相应进制数2.7 类型限定符2.8 printf函数和putchar函数2.9 scanf函数与getchar函数 3. 运算符与表达式3.1 常用运算符分类3.2 算术运算符3.3 赋值运算符3.4 比较运算符3.5 逻辑运算符3.6 类型转换3.6.1 隐式转换3.6.2 强制转换 4. 程序流程结构4.1 概述4.2 选择结构4.2.1 if语句4.2.2 if…else语句4.2.3 if…else if…else语句4.2.4 三目运算符4.2.5 switch语句 4.3 循环结构4.3.1 while语句4.3.2 do…while语句4.3.3 for语句4.3.4 嵌套循环 4.4 跳转语句break、continue、goto4.4.1 break语句4.4.2 continue语句4.4.3 goto语句(无条件跳转尽量少用) 5. 数组和字符串5.1 概述5.2 一维数组5.2.1 一维数组的定义和使用5.2.2 一维数组的初始化5.2.3 数组名 5.3 二维数组5.3.1 二维数组的定义和使用5.3.2 二维数组的初始化5.3.3 数组名 5.4 多维数组(了解)5.5 字符数组与字符串5.5.1 字符数组与字符串区别5.5.2 字符串的初始化5.5.3 字符串的输入输出 6. 函数6.1 概述6.1.1 函数分类6.1.2 函数的作用6.1.3 函数的调用产生随机数 6.2 函数的定义6.2.1 函数定义格式6.2.2 函数名字、形参、函数体、返回值 6.3 函数的调用6.3.1 函数执行流程6.3.2 函数的形参和实参6.3.3 无参函数调用6.3.4 有参函数调用6.3.5 函数返回值 6.4 函数的声明6.5 main函数与exit函数6.6 多文件(分文件)编程6.6.1 防止头文件重复包含 7. 指针7.1 概述7.1.1 内存7.1.2 物理存储器和存储地址空间7.1.3 内存地址7.1.4 指针和指针变量 7.2 指针基础知识7.2.1 指针变量的定义和使用7.2.2 通过指针间接修改变量的值7.2.3 指针大小7.2.4 野指针和空指针7.2.5 万能指针void *7.2.6 const修饰的指针变量 7.3 指针和数组7.3.1 数组名7.3.2 指针操作数组元素7.3.3 指针加减运算7.3.4 指针数组 7.4 多级指针7.5 指针和函数7.5.1 函数形参改变实参的值7.5.2 数组名做函数参数7.5.3 指针做为函数的返回值 7.6 指针和字符串7.6.1 字符指针7.6.2 字符指针做函数参数7.6.3 const修饰的指针变量7.6.4 指针数组做为main函数的形参7.6.5 字符串处理函数 7.7 指针小结 8. 内存管理8.1 作用域8.1.1 局部变量8.1.2 静态(static)局部变量8.1.3 全局变量8.1.4 静态(static)全局变量8.1.5 extern全局变量声明8.1.6 全局函数和静态函数8.1.7 总结 8.2 内存布局8.2.1 内存分区8.2.2 存储类型总结8.2.3 内存操作函数8.2.4 堆区内存分配和释放 8.3 内存分区代码分析 9. 复合类型(自定义类型)9.1 结构体9.1.1概述9.1.2 结构体变量的定义和初始化9.1.3 结构体成员的使用9.1.4 结构体数组9.1.5 结构体套结构体9.1.6 结构体赋值9.1.7 结构体和指针9.1.8 结构体做函数参数 9.2 共用体(联合体)9.3 枚举9.4 typedef 引言
C语言自从其诞生于1972年一直是编程世界中不可或缺的一部分。由Dennis Ritchie和Ken Thompson在贝尔实验室创建C语言凭借其强大的功能和灵活性迅速成为了操作系统、嵌入式系统甚至是其他编程语言的开发基石。如果你对计算机编程有浓厚的兴趣那么掌握C语言几乎是一项必备技能。
C语言应用领域
C语言可以写网站后台程序C语言可以专门针对某个主题写出功能强大的程序库C语言可以写出大型游戏的引擎C语言可以写出另一个语言来C语言可以写操作系统和驱动程序并且只能用C语言编写任何设备只要配置了微处理器就都支持C语言。从微波炉到手机都是由C语言技术来推动的
1. 第一个C语言程序HelloWorld
1.1 编写C语言代码hello.c
#include stdio.hint main()
{//这是第一个C语言代码 printf(hello world\n);return 0;
}注意 C语言的源代码文件是一个普通的文本文件但扩展名必须是.c。
1.2 代码分析 include头文件包含 #include的意思是头文件包含#include stdio.h代表包含stdio.h这个头文件使用C语言库函数需要提前包含库函数对应的头文件如这里使用了printf()函数需要包含stdio.h头文件 main函数 一个完整的C语言程序是由一个、且只能有一个main()函数(又称主函数必须有)和若干个其他函数结合而成可选。main函数是C语言程序的入口程序是从main函数开始执行。 {} 括号程序体和代码块 {}叫代码块一个代码块内部可以有一条或者多条语句C语言每句可执行代码都是;分号结尾所有的#开头的行都代表预编译指令预编译指令行结尾是没有分号的所有的可执行语句必须是在代码块里面 注释 //叫行注释注释的内容编译器是忽略的注释主要的作用是在代码中加一些说明和解释这样有利于代码的阅读/**/叫块注释块注释是C语言标准的注释方法行注释是从C语言借鉴过来的 printf函数 printf是C语言库函数功能是向标准输出设备输出一个字符串printf(“hello world\n”);// \n的意思是回车换行 return语句 return代表函数执行完毕返回return代表函数的终止如果main定义的时候前面是int那么return后面就需要写一个整数如果main定义的时候前面是void那么return后面什么也不需要写在main函数中return 0代表程序执行成功return -1代表程序执行失败int main()和void main()在C语言中是一样的但C只接受int main这种定义方式 2. 数据类型
2.1 常量与变量
2.1.1 关键字 2.1.2 数据类型 2.1.3 常量
在程序运行过程中其值不能被改变的量常量一般出现在表达式或赋值语句中 2.1.4 变量
在程序运行过程中其值可以改变变量在使用前必须先定义定义变量前必须有相应的数据类型
标识符命名规则
标识符不能是关键字标识符只能由字母、数字、下划线组成第一个字符必须为字母或下划线标识符中字母区分大小写
变量特点
变量在编译时为其分配相应的内存空间可以通过其名字和地址访问相应内存
声明和定义区别
声明变量不需要建立存储空间如extern int a;定义变量需要建立存储空间如int b;
#include stdio.hint main()
{//extern 关键字只做声明不能做任何定义后面还会学习这里先了解//声明一个变量aa在这里没有建立存储空间extern int a;a 10; //err, 没有空间就不可以赋值int b 10; //定义一个变量bb的类型为intb赋值为10return 0;
}从广义的角度来讲声明中包含着定义即定义是声明的一个特例所以并非所有的声明都是定义
2.1.5 使用示例
#include stdio.h
#define MAX 10 //声明了一个常量名字叫MAX值是10常量的值一旦初始化不可改int main()
{int a; //定义了一个变量其类型为int名字叫aconst int b 10; //定义一个const常量名为叫b值为10//b 11; //err,常量的值不能改变//MAX 100; //err,常量的值不能改变a MAX;//将abc的值设置为MAX的值a 123;printf(%d\n, a); //打印变量a的值return 0;
}2.2 整型int
2.2.1 整型变量的定义和输出 #include stdio.hint main()
{int a 123; //定义变量a以10进制方式赋值为123int b 0567; //定义变量b以8进制方式赋值为0567int c 0xabc; //定义变量c以16进制方式赋值为0xabcprintf(a %d\n, a);printf(8进制b %o\n, b);printf(10进制b %d\n, b);printf(16进制c %x\n, c);printf(16进制c %X\n, c);printf(10进制c %d\n, c);unsigned int d 0xffffffff; //定义无符号int变量d以16进制方式赋值printf(有符号方式打印d %d\n, d);printf(无符号方式打印d %u\n, d);return 0;
}2.2.2 整型变量的输入
#include stdio.hint main()
{int a;printf(请输入a的值);//不要加“\n”scanf(%d, a);printf(a %d\n, a); //打印a的值return 0;
}2.2.3 short、int、long、long long 2.2.4 有符号数和无符号数区别
有符号数 有符号数是最高位为符号位0代表正数1代表负数。
#include stdio.hint main()
{signed int a -1089474374; //定义有符号整型变量aprintf(%X\n, a); //结果为 BF0FF0BA//B F 0 F F 0 B A//1011 1111 0000 1111 1111 0000 1011 1010return 0;
}无符号数 无符号数最高位不是符号位而就是数的一部分无符号数不可能是负数。
#include stdio.hint main()
{unsigned int a 3236958022; //定义无符号整型变量aprintf(%X\n, a); //结果为 C0F00F46return 0;
}有符号和无符号整型取值范围
2.3 sizeof关键字
sizeof不是函数所以不需要包含任何头文件它的功能是计算一个数据类型的大小单位为字节
#include stdio.hint main()
{int a;int b sizeof(a);//sizeof得到指定值占用内存的大小单位字节printf(b %d\n, b);size_t c sizeof(a);printf(c %u\n, c);//用无符号数的方式输出c的值return 0;
}2.4 字符型char
2.4.1 字符变量的定义和输出
字符型变量用于存储一个单一字符在 C 语言中用 char 表示其中每个字符变量都会占用 1 个字节。在给字符型变量赋值时需要用一对英文半角格式的单引号(’ )把字符括起来。
字符变量实际上并不是把该字符本身放到变量的内存单元中去而是将该字符对应的 ASCII 编码放到变量的存储单元中。char的本质就是一个1字节大小的整型。
#include stdio.hint main()
{char ch a;printf(sizeof(ch) %u\n, sizeof(ch));printf(ch[%%c] %c\n, ch); //打印字符printf(ch[%%d] %d\n, ch); //打印‘a’ ASCII的值char A A;char a a;printf(a %d\n, a); //97printf(A %d\n, A); //65printf(A %c\n, a - 32); //小写a转大写Aprintf(a %c\n, A 32); //大写A转小写ach ;printf(空字符%d\n, ch); //空字符ASCII的值为32printf(A %c\n, a - ); //小写a转大写Aprintf(a %c\n, A ); //大写A转小写areturn 0;
}2.4.2 字符变量的输入
#include stdio.hint main()
{char ch;printf(请输入ch的值);//不要加“\n”scanf(%c, ch);printf(ch %c\n, ch); //打印ch的字符return 0;
}2.4.3 ASCII对照表 2.4.4 转义字符 注意红色字体标注的为不可打印字符。
#include stdio.hint main()
{printf(abc);printf(\refg\n); //\r切换到句首 \n为换行键printf(abc);printf(\befg\n);//\b为退格键 \n为换行键printf(%d\n, \123);// \123为8进制转义字符0123对应10进制数为83printf(%d\n, \x23);// \x23为16进制转义字符0x23对应10进制数为35return 0;
}2.5 实型(浮点型)float、double
实型变量也可以称为浮点型变量浮点型变量是用来存储小数数值的。在C语言中 浮点型变量分为两种 单精度浮点数(float)、 双精度浮点数(double) 但是double型变量所表示的浮点数比 float 型变量更精确。
由于浮点型变量是由有限的存储单元组成的因此只能提供有限的有效数字。在有效位以外的数字将被舍去这样可能会产生一些误差。
不以f结尾的常量是double类型以f结尾的常量(如3.14f)是float类型。
#include stdio.hint main()
{//传统方式赋值float a 3.14f; //或3.14Fdouble b 3.14;printf(a %f\n, a);printf(b %lf\n, b);//科学法赋值a 3.2e3f; //3.2*1000 3200e可以写Eprintf(a1 %f\n, a);a 100e-3f; //100*0.001 0.1printf(a2 %f\n, a);a 3.1415926f;printf(a3 %f\n, a); //结果为3.141593return 0;
}2.6 C语言如何表示相应进制数 #include stdio.hint main()
{int a 123; //十进制方式赋值int b 0123; //八进制方式赋值 以数字0开头int c 0xABC; //十六进制方式赋值//如果在printf中输出一个十进制数那么用%d八进制用%o十六进制是%xprintf(十进制%d\n,a );printf(八进制%o\n, b); //%o,为字母o,不是数字printf(十六进制%x\n, c);return 0;
}2.7 类型限定符 2.8 printf函数和putchar函数
printf是输出一个字符串putchar输出一个char。 printf格式字符
#include stdio.h
int main()
{int a 100;printf(a %d\n, a);//格式化输出一个字符串printf(%p\n, a);//输出变量a在内存中的地址编号printf(%%d\n);char c a;putchar(c);//putchar只有一个参数就是要输出的charlong a2 100;printf(%ld, %lx, %lo\n, a2, a2, a2);long long a3 1000;printf(%lld, %llx, %llo\n, a3, a3, a3);int abc 10;printf(abc %6d\n, abc);printf(abc %-6d\n, abc);printf(abc %06d\n, abc);printf(abc %-06d\n, abc);double d 12.3;printf(d \ %-10.3lf \\n, d);return 0;
}2.9 scanf函数与getchar函数
getchar是从标准输入设备读取一个char。scanf通过%转义的方式可以得到用户通过标准输入设备输入的数据。
#include stdio.hint main()
{char ch1;char ch2;char ch3;int a;int b;printf(请输入ch1的字符);ch1 getchar();printf(ch1 %c\n, ch1);getchar(); //测试此处getchar()的作用printf(请输入ch2的字符);ch2 getchar();printf(\ch2 %ctest\\n, ch2);getchar(); //测试此处getchar()的作用printf(请输入ch3的字符);scanf(%c, ch3);//这里第二个参数一定是变量的地址而不是变量名printf(ch3 %c\n, ch3);printf(请输入a的值);scanf(%d, a);printf(a %d\n, a);printf(请输入b的值);scanf(%d, b);printf(b %d\n, b);return 0;
}3. 运算符与表达式
3.1 常用运算符分类 3.2 算术运算符 3.3 赋值运算符 3.4 比较运算符 C 语言的比较运算中 “真”用数字“1”来表示 “假”用数字“0”来表示。
3.5 逻辑运算符 3.6 类型转换
数据有不同的类型不同类型数据之间进行混合运算时必然涉及到类型的转换问题。
转换的方法有两种
自动转换(隐式转换)遵循一定的规则,由编译系统自动完成强制类型转换把表达式的运算结果强制转换成所需的数据类型
类型转换的原则占用内存字节数少(值域小)的类型向占用内存字节数多(值域大)的类型转换以保证精度不降低。
3.6.1 隐式转换
#include stdio.hint main()
{int num 5;printf(s1%d\n, num / 2);printf(s2%lf\n, num / 2.0);return 0;
}3.6.2 强制转换
强制类型转换指的是使用强制类型转换运算符将一个变量或表达式转化成所需的类型其基本语法格式如下所示 (类型说明符) (表达式)
#include stdio.hint main()
{float x 0;int i 0;x 3.6f;i x; //x为实型, i为整型直接赋值会有警告i (int)x; //使用强制类型转换printf(x%f, i%d\n, x, i);return 0;
}4. 程序流程结构
4.1 概述
C语言支持最基本的三种程序运行结构顺序结构、选择结构、循环结构。
顺序结构程序按顺序执行不发生跳转选择结构依据是否满足条件有选择的执行相应功能循环结构依据条件是否满足循环多次执行某段代码
4.2 选择结构
4.2.1 if语句 #include stdio.hint main()
{int a 1;int b 2;if (a b){printf(%d\n, a);}return 0;
} 4.2.2 if…else语句 #include stdio.h
int main()
{int a 1;int b 2;if (a b){printf(%d\n, a);}else{printf(%d\n, b);}return 0;
}4.2.3 if…else if…else语句 #include stdio.hint main()
{unsigned int a;scanf(%u, a);if (a 10){printf(个位\n);}else if (a 100){printf(十位\n);}else if (a 1000){printf(百位\n);}else{printf(很大\n);}return 0;
}4.2.4 三目运算符
#include stdio.hint main()
{int a 10;int b 20;int c;if (a b){c a;}else{c b;}printf(c1 %d\n, c);a 1;b 2;c ( a b ? a : b );printf(c2 %d\n, c);return 0;
}4.2.5 switch语句
#include stdio.hint main()
{char c;c getchar();switch (c) //参数只能是整型变量{case 1:printf(OK\n);break;//switch遇到break就中断了case 2:printf(not OK\n);break;default://如果上面的条件都不满足那么执行defaultprintf(are u ok?\n);}return 0;
}4.3 循环结构
4.3.1 while语句 #include stdio.hint main()
{int a 20;while (a 10){scanf(%d, a);printf(a %d\n, a);}return 0;
}4.3.2 do…while语句 #include stdio.hint main()
{int a 1;do{a;printf(a %d\n, a);} while (a 10);return 0;
}4.3.3 for语句
#include stdio.hint main()
{int i;int sum 0;for (i 0; i 100; i){sum i;}printf(sum %d\n, sum);return 0;
}4.3.4 嵌套循环
循环语句之间可以相互嵌套
#include stdio.hint main()
{int num 0;int i, j, k;for (i 0; i 10; i){for (j 0; j 10; j){for (k 0; k 10; k){printf(hello world\n);num;}}}printf(num %d\n, num);return 0;
}4.4 跳转语句break、continue、goto
4.4.1 break语句
在switch条件语句和循环语句中都可以使用break语句:
当它出现在switch条件语句中时作用是终止某个case并跳出switch结构。当它出现在循环语句中作用是跳出当前内循环语句执行后面的代码。当它出现在嵌套循环语句中跳出最近的内循环语句执行后面的代码。
#include stdio.hint main()
{int i 0;while (1){i;printf(i %d\n, i);if (i 10){break; //跳出while循环}}int flag 0;int m 0;int n 0;for (m 0; m 10; m){for (n 0; n 10; n){if (n 5){flag 1;break; //跳出for (n 0; n 10; n)}}if (flag 1){break; //跳出for (m 0; m 10; m)}}return 0;
}4.4.2 continue语句
在循环语句中如果希望立即终止本次循环并执行下一次循环此时就需要使用continue语句。
#includestdio.hint main()
{int sum 0; //定义变量sumfor (int i 1; i 100; i){if (i % 2 0) //如果i是一个偶数执行if语句中的代码{continue; //结束本次循环}sum i; //实现sum和i的累加}printf(sum %d\n, sum);return 0;
}4.4.3 goto语句(无条件跳转尽量少用)
#include stdio.hint main()
{goto End; //无条件跳转到End的标识printf(aaaaaaaaa\n);End:printf(bbbbbbbb\n);return 0;
}5. 数组和字符串
5.1 概述
在程序设计中为了方便处理数据把具有相同类型的若干变量按有序形式组织起来——称为数组。
数组就是在内存中连续的相同类型的变量空间。同一个数组所有的成员都是相同的数据类型同时所有的成员在内存中的地址是连续的。
数组属于构造数据类型:
一个数组可以分解为多个数组元素这些数组元素可以是基本数据类型或构造类型。按数组元素类型的不同数组可分为数值数组、字符数组、指针数组、结构数组等类别。
通常情况下数组元素下标的个数也称为维数根据维数的不同可将数组分为一维数组、二维数组、三维数组、四维数组等。通常情况下我们将二维及以上的数组称为多维数组。
5.2 一维数组
5.2.1 一维数组的定义和使用
数组名字符合标识符的书写规定(数字、英文字母、下划线)数组名不能与其它变量名相同同一作用域内是唯一的方括号[]中常量表达式表示数组元素的个数定义数组时[]内最好是常量使用数组时[]内即可是常量也可以是变量
#include stdio.hint main()
{int a[10];//定义了一个数组名字叫a有10个成员每个成员都是int类型//a[0]…… a[9]没有a[10]//没有a这个变量a是数组的名字但不是变量名它是常量a[0] 0;//……a[9] 9;int i 0;for (i 0; i 10; i){a[i] i; //给数组赋值}//遍历数组并输出每个成员的值for (i 0; i 10; i){printf(%d , a[i]);}printf(\n);return 0;
}5.2.2 一维数组的初始化
在定义数组的同时进行赋值称为初始化。全局数组若不初始化编译器将其初始化为零。局部数组若不初始化内容为随机值。 int a[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组同时初始化所有成员变量int a[10] { 1, 2, 3 };//初始化前三个成员后面所有元素都设置为0int a[10] { 0 };//所有的成员都设置为0//[]中不定义元素个数定义时必须初始化int a[] { 1, 2, 3, 4, 5 };//定义了一个数组有5个成员5.2.3 数组名
数组名是一个地址的常量代表数组中首元素的地址。
#include stdio.hint main()
{int a[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组同时初始化所有成员变量printf(a %p\n, a);printf(a[0] %p\n, a[0]);int n sizeof(a); //数组占用内存的大小10个int类型10 * 4 40int n0 sizeof(a[0]);//数组第0个元素占用内存大小第0个元素为int4int i 0;for (i 0; i sizeof(a) / sizeof(a[0]); i){printf(%d , a[i]);}printf(\n);return 0;
}5.3 二维数组
5.3.1 二维数组的定义和使用
二维数组定义的一般形式是 类型说明符 数组名[常量表达式1][常量表达式2] 其中常量表达式1表示第一维下标的长度常量表达式2 表示第二维下标的长度。 int a[3][4]; 命名规则同一维数组定义了一个三行四列的数组数组名为a其元素类型为整型该数组的元素个数为3×4个即 二维数组a是按行进行存放的先存放a[0]行再存放a[1]行、a[2]行并且每行有四个元素也是依次存放的。
二维数组在概念上是二维的其下标在两个方向上变化对其访问一般需要两个下标。在内存中并不存在二维数组二维数组实际的硬件存储器是连续编址的也就是说内存中只有一维数组即放完一行之后顺次放入第二行和一维数组存放方式是一样的
#include stdio.hint main()
{//定义了一个二维数组名字叫a//由3个一维数组组成这个一维数组是int [4]//这3个一维数组的数组名分别为a[0],a[1],a[2]int a[3][4];a[0][0] 0;//……a[2][3] 12;//给数组每个元素赋值int i 0;int j 0;int num 0;for (i 0; i 3; i){for (j 0; j 4; j){a[i][j] num;}}//遍历数组并输出每个成员的值for (i 0; i 3; i){for (j 0; j 4; j){printf(%d, , a[i][j]);}printf(\n);}return 0;
}5.3.2 二维数组的初始化
//分段赋值 int a[3][4] {{ 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }};int a[3][4] { { 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }};//连续赋值int a[3][4] { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };//可以只给部分元素赋初值未初始化则为0int a[3][4] { 1, 2, 3, 4 };//所有的成员都设置为0int a[3][4] {0};//[]中不定义元素个数定义时必须初始化int a[][4] { 1, 2, 3, 4, 5, 6, 7, 8};5.3.3 数组名
数组名是一个地址的常量代表数组中首元素的地址。
#include stdio.hint main()
{//定义了一个二维数组名字叫a//二维数组是本质上还是一维数组此一维数组有3个元素
//每个元素又是一个一维数组int[4]int a[3][4] { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };//数组名为数组首元素地址二维数组的第0个元素为一维数组//第0个一维数组的数组名为a[0]printf(a %p\n, a);printf(a[0] %p\n, a[0]);//测二维数组所占内存空间有3个一维数组每个一维数组的空间为4*4//sizeof(a) 3 * 4 * 4 48printf(sizeof(a) %d\n, sizeof(a));//测第0个元素所占内存空间a[0]为第0个一维数组int[4]的数组名4*416printf(sizeof(a[0]) %d\n, sizeof(a[0]) );//测第0行0列元素所占内存空间第0行0列元素为一个int类型4字节printf(sizeof(a[0][0]) %d\n, sizeof(a[0][0]));//求二维数组行数printf(i %d\n, sizeof(a) / sizeof(a[0]));// 求二维数组列数printf(j %d\n, sizeof(a[0]) / sizeof(a[0][0]));//求二维数组行*列总数printf(n %d\n, sizeof(a) / sizeof(a[0][0]));return 0;
}5.4 多维数组(了解)
多维数组的定义与二维数组类似其语法格式具体如下 数组类型修饰符 数组名 [n1][n2]…[nn]; int a[3][4][5]; 定义了一个三维数组数组的名字是a数组的长度为3每个数组的元素又是一个二维数组这个二维数组的长度是4并且这个二维数组中的每个元素又是一个一维数组这个一维数组的长度是5元素类型是int。
#include stdio.hint main()
{//int a[3][4][5] ;//定义了一个三维数组有3个二维数组int[4][5]int a[3][4][5] { { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } } };int i, j, k;for (i 0; i 3; i){for (j 0; j 4; j){for (k 0; k 5; k){//添加访问元素代码printf(%d, , a[i][j][k]);}printf(\n);}}return 0;
}5.5 字符数组与字符串
5.5.1 字符数组与字符串区别
C语言中没有字符串这种数据类型可以通过char的数组来替代字符串一定是一个char的数组但char的数组未必是字符串数字0(和字符‘\0’等价)结尾的char数组就是一个字符串但如果char数组没有以数字0结尾那么就不是一个字符串只是普通字符数组所以字符串是一种特殊的char的数组。
#include stdio.hint main()
{char c1[] { c, , p, r, o, g }; //普通字符数组printf(c1 %s\n, c1); //乱码因为没有’\0’结束符//以‘\0’(‘\0’就是数字0)结尾的字符数组是字符串char c2[] { c, , p, r, o, g, \0}; printf(c2 %s\n, c2);//字符串处理以‘\0’(数字0)作为结束符后面的h, l, l, e, o不会输出char c3[] { c, , p, r, o, g, \0, h, l, l, e, o, \0};printf(c3 %s\n, c3);return 0;
}5.5.2 字符串的初始化
#include stdio.h// C语言没有字符串类型通过字符数组模拟
// C语言字符串以字符‘\0’, 数字0
int main()
{//不指定长度, 没有0结束符有多少个元素就有多长char buf[] { a, b, c };printf(buf %s\n, buf); //乱码//指定长度后面没有赋值的元素自动补0char buf2[100] { a, b, c };
char buf[1000]{“hello”};printf(buf2 %s\n, buf2);//所有元素赋值为0char buf3[100] { 0 };//char buf4[2] { 1, 2, 3 };//数组越界char buf5[50] { 1, a, b, 0, 7 };printf(buf5 %s\n, buf5);char buf6[50] { 1, a, b, 0, 7 };printf(buf6 %s\n, buf6);char buf7[50] { 1, a, b, \0, 7 };printf(buf7 %s\n, buf7);//使用字符串初始化编译器自动在后面补0常用char buf8[] agjdslgjlsdjg;//\0后面最好不要连着数字有可能几个数字连起来刚好是一个转义字符//\ddd八进制字义字符\xdd十六进制转移字符// \012相当于\nchar str[] \012abc;printf(str %s\n, str);return 0;
}5.5.3 字符串的输入输出
由于字符串采用了’\0’标志字符串的输入输出将变得简单方便。
#include stdio.hint main()
{char str[100];printf(input string1 : \n);scanf(%s, str);//scanf(“%s”,str)默认以空格分隔printf(output:%s\n, str);return 0;
}6. 函数
6.1 概述
6.1.1 函数分类
C 程序是由函数组成的我们写的代码都是由主函数 main()开始执行的。函数是 C 程序的基本模块是用于完成特定任务的程序代码单元。
从函数定义的角度看函数可分为系统函数和用户定义函数两种
系统函数即库函数这是由编译系统提供的用户不必自己定义这些函数可以直接使用它们如我们常用的打印函数printf()。用户定义函数用以解决用户的专门需要。
6.1.2 函数的作用
函数的使用可以省去重复代码的编写降低代码重复率
// 求两数的最大值
int max(int a, int b)
{if (a b){return a;}else{return b;}
}int main()
{// 操作1 ……// ……int a1 10, b1 20, c1 0;c1 max(a1, b1); // 调用max()// 操作2 ……// ……int a2 11, b2 21, c2 0;c2 max(a2, b2); // 调用max()// ……return 0;
}函数可以让程序更加模块化从而有利于程序的阅读修改和完善
假如我们编写一个实现以下功能的程序读入一行数字对数字进行排序找到它们的平均值打印出一个柱状图。如果我们把这些操作直接写在main()里这样可能会给用户感觉代码会有点凌乱。但假如我们使用函数这样可以让程序更加清晰、模块化
#include stdio.hint main()
{float list[50];// 这里只是举例函数还没有实现readlist(list, 50);sort(list, 50);average(list, 50);bargraph(list, 50);return 0;
}这里我们可以这么理解程序就像公司公司是由部门组成的这个部门就类似于C程序的函数。默认情况下公司就是一个大部门( 只有一个部门的情况下 )相当于C程序的main()函数。如果公司比较小( 程序比较小 )因为任务少而简单一个部门即可( main()函数 )胜任。但是如果这个公司很大( 大型应用程序 )任务多而杂如果只是一个部门管理( 相当于没有部门没有分工 )我们可想而知公司管理、运营起来会有多混乱不是说这样不可以运营只是这样不完美而已如果根据公司要求分成一个个部门( 根据功能封装一个一个函数 )招聘由行政部门负责研发由技术部门负责等这样就可以分工明确结构清晰方便管理各部门之间还可以相互协调。 6.1.3 函数的调用产生随机数
当调用函数时需要关心5要素
头文件包含指定的头文件函数名字函数名字必须和头文件声明的名字一样功能需要知道此函数能干嘛后才调用参数参数类型要匹配返回值根据需要接收返回值
#include stdio.h
#include time.h
#include stdlib.hint main()
{time_t tm time(NULL);//得到系统时间srand((unsigned int)tm);//随机种子只需要设置一次即可int r rand();printf(r %d\n, r);return 0;}6.2 函数的定义
6.2.1 函数定义格式
函数定义的一般形式: 返回类型 函数名(形式参数列表) { 数据定义部分; 执行语句部分; } 6.2.2 函数名字、形参、函数体、返回值
函数名 理论上是可以随意起名字最好起的名字见名知意应该让用户看到这个函数名字就知道这个函数的功能。注意函数名的后面有个圆换号()代表这个为函数不是普通的变量名。形参列表 在定义函数时指定的形参在未出现函数调用时它们并不占内存中的存储单元因此称它们是形式参数或虚拟参数简称形参表示它们并不是实际存在的数据所以形参里的变量不能赋值。
void max(int a 10, int b 20) // error, 形参不能赋值
{
}在定义函数时指定的形参必须是类型变量的形式
//1: right, 类型变量
void max(int a, int b)
{
}//2: error, 只有类型没有变量
void max(int, int)
{
}//3: error, 只有变量没有类型
int a, int b;
void max(a, b)
{
}在定义函数时指定的形参可有可无根据函数的需要来设计如果没有形参圆括号内容为空或写一个void关键字
// 没形参 圆括号内容为空
void max()
{
}// 没形参 圆括号内容为void关键字
void max(void)
{
}函数体 花括号{ }里的内容即为函数体的内容这里为函数功能实现的过程这和以前的写代码没太大区别以前我们把代码写在main()函数里现在只是把这些写到别的函数里。返回值 函数的返回值是通过函数中的return语句获得的return后面的值也可以是一个表达式。 如果函数带返回值return后面必须跟着一个值如果函数没有返回值函数名字的前面必须写一个void关键字这时候我们写代码时也可以通过return中断函数(也可以不用)只是这时return后面不带内容( 分号“”除外)。
void max()// 最好要有void关键字
{return; // 中断函数这个可有可无
}6.3 函数的调用
定义函数后我们需要调用此函数才能执行到这个函数里的代码段。这和main()函数不一样main()为编译器设定好自动调用的主函数无需人为调用我们都是在main()函数里调用别的函数一个 C 程序里有且只有一个main()函数。
6.3.1 函数执行流程
#include stdio.hvoid print_test()
{printf(this is for test\n);
}int main()
{print_test(); // print_test函数的调用return 0;
}进入main()函数调用print_test()函数
它会在main()函数的前寻找有没有一个名字叫“print_test”的函数定义如果找到接着检查函数的参数这里调用函数时没有传参函数定义也没有形参参数类型匹配开始执行print_test()函数这时候main()函数里面的执行会阻塞( 停 )在print_test()这一行代码等待print_test()函数的执行。
print_test()函数执行完( 这里打印一句话 )main()才会继续往下执行执行到return 0, 程序执行完毕。
6.3.2 函数的形参和实参
形参出现在函数定义中在整个函数体内都可以使用离开该函数则不能使用。实参出现在主调函数中进入被调函数后实参也不能使用。实参变量对形参变量的数据传递是“值传递”即单向传递只由实参传给形参而不能由形参传回来给实参。在调用函数时编译系统临时给形参分配存储单元。调用结束后形参单元被释放。实参单元与形参单元是不同的单元。调用结束后形参单元被释放函数调用结束返回主调函数后则不能再使用该形参变量。实参单元仍保留并维持原值。因此在执行一个被调用函数时形参的值如果发生改变并不会改变主调函数中实参的值。
6.3.3 无参函数调用
如果是调用无参函数则不能加上“实参”但括号不能省略。
// 函数的定义
void test()
{
}int main()
{// 函数的调用test(); // right, 圆括号()不能省略test(250); // error, 函数定义时没有参数return 0;
}6.3.4 有参函数调用
a) 如果实参表列包含多个实参则各参数间用逗号隔开。
// 函数的定义
void test(int a, int b)
{
}int main()
{int p 10, q 20;test(p, q); // 函数的调用return 0;
}b) 实参与形参的个数应相等类型应匹配(相同或赋值兼容)。实参与形参按顺序对应一对一地传递数据。
c) 实参可以是常量、变量或表达式无论实参是何种类型的量在进行函数调用时它们都必须具有确定的值以便把这些值传送给形参。所以这里的变量是在圆括号( )外面定义好、赋好值的变量。
// 函数的定义
void test(int a, int b)
{
}int main()
{// 函数的调用int p 10, q 20;test(p, q); // righttest(11, 30 - 10); // righttest(int a, int b); // error, 不应该在圆括号里定义变量return 0;
}6.3.5 函数返回值
a如果函数定义没有返回值函数调用时不能写void关键字调用函数时也不能接收函数的返回值。
// 函数的定义
void test()
{
}int main()
{// 函数的调用test(); // rightvoid test(); // error, void关键字只能出现在定义不可能出现在调用的地方int a test(); // error, 函数定义根本就没有返回值return 0;
}b如果函数定义有返回值这个返回值我们根据用户需要可用可不用但是假如我们需要使用这个函数返回值我们需要定义一个匹配类型的变量来接收。
// 函数的定义, 返回值为int类型
int test()
{
}int main()
{// 函数的调用int a test(); // right, a为int类型int b;b test(); // right, 和上面等级char *p test(); // 虽然调用成功没有意义, p为char *, 函数返回值为int, 类型不匹配// error, 必须定义一个匹配类型的变量来接收返回值// int只是类型没有定义变量int test(); // error, 必须定义一个匹配类型的变量来接收返回值// int只是类型没有定义变量int test();return 0;
}6.4 函数的声明
如果使用用户自己定义的函数而该函数与调用它的函数即主调函数不在同一文件中或者函数定义的位置在主调函数之后则必须在调用此函数之前对被调用的函数作声明。
所谓函数声明就是在函数尚在未定义的情况下事先将该函数的有关信息通知编译系统相当于告诉编译器函数在后面定义以便使编译能正常进行。
注意一个函数只能被定义一次但可以声明多次。
#include stdio.hint max(int x, int y); // 函数的声明分号不能省略
// int max(int, int); // 另一种方式int main()
{int a 10, b 25, num_max 0;num_max max(a, b); // 函数的调用printf(num_max %d\n, num_max);return 0;
}// 函数的定义
int max(int x, int y)
{return x y ? x : y;
}函数定义和声明的区别 1定义是指对函数功能的确立包括指定函数名、函数类型、形参及其类型、函数体等它是一个完整的、独立的函数单位。 2声明的作用则是把函数的名字、函数类型以及形参的个数、类型和顺序(注意不包括函数体)通知编译系统以便在对包含函数调用的语句进行编译时据此对其进行对照检查例如函数名是否正确实参与形参的类型和个数是否一致。
6.5 main函数与exit函数
在main函数中调用exit和return结果是一样的但在子函数中调用return只是代表子函数终止了在子函数中调用exit那么程序终止。
#include stdio.h
#include stdlib.hvoid fun()
{printf(fun\n);//return;exit(0);
}int main()
{fun();while (1);return 0;
}6.6 多文件(分文件)编程
把函数声明放在头文件xxx.h中在主函数中包含相应头文件在头文件对应的xxx.c中实现xxx.h声明的函数 6.6.1 防止头文件重复包含
当一个项目比较大时往往都是分文件这时候有可能不小心把同一个头文件 include 多次或者头文件嵌套包含。
为了避免同一个文件被include多次C/C中有两种方式一种是 #ifndef 方式一种是 #pragma once 方式。
7. 指针
7.1 概述
7.1.1 内存
内存含义
存储器计算机的组成中用来存储程序和数据辅助CPU进行运算处理的重要部分内存内部存贮器暂存程序/数据——掉电丢失 SRAM、DRAM、DDR、DDR2、DDR3。外存外部存储器长时间保存程序/数据—掉电不丢ROM、ERRROM、FLASHNAND、NOR、硬盘、光盘。
内存是沟通CPU与硬盘的桥梁
暂存放CPU中的运算数据暂存与硬盘等外部存储器交换的数据
7.1.2 物理存储器和存储地址空间
有关内存的两个概念物理存储器和存储地址空间。
物理存储器实际存在的具体存储器芯片。
主板上装插的内存条显示卡上的显示RAM芯片各种适配卡上的RAM芯片和ROM芯片
存储地址空间对存储器编码的范围。我们在软件上常说的内存是指这一层含义。
编码对每个物理存储单元一个字节分配一个号码寻址可以根据分配的号码找到相应的存储单元完成数据的读写
7.1.3 内存地址
将内存抽象成一个很大的一维字符数组编码就是对内存的每一个字节分配一个32位或64位的编号与32位或者64位处理器相关这个内存编号我们称之为内存地址内存中的每一个数据都会分配相应的地址char:占一个字节分配一个地址int: 占四个字节分配四个地址float、struct、函数、数组等 7.1.4 指针和指针变量
内存区的每一个字节都有一个编号这就是“地址”。如果在程序中定义了一个变量在对程序进行编译或运行时系统就会给这个变量分配内存单元并确定它的内存地址(编号)指针的实质就是内存“地址”。指针就是地址地址就是指针指针是内存单元的编号指针变量是存放地址的变量。通常我们叙述时会把指针变量简称为指针实际他们含义并不一样。 7.2 指针基础知识
7.2.1 指针变量的定义和使用
指针也是一种数据类型指针变量也是一种变量指针变量指向谁就把谁的地址赋值给指针变量“*”操作符操作的是指针变量指向的内存空间
#include stdio.hint main()
{int a 0;char b 100;printf(%p, %p\n, a, b); //打印a, b的地址//int *代表是一种数据类型int*指针类型p才是变量名//定义了一个指针类型的变量可以指向一个int类型变量的地址int *p;p a;//将a的地址赋值给变量pp也是一个变量值是一个内存地址编号printf(%d\n, *p);//p指向了a的地址*p就是a的值char *p1 b;printf(%c\n, *p1);//*p1指向了b的地址*p1就是b的值return 0;
}注意可以取得一个变量在内存中的地址。但是不能取寄存器变量因为寄存器变量不在内存里而在CPU里面所以是没有地址的。
7.2.2 通过指针间接修改变量的值 int a 0;int b 11;int *p a;*p 100;printf(a %d, *p %d\n, a, *p);p b;*p 22;printf(b %d, *p %d\n, b, *p);7.2.3 指针大小
使用sizeof()测量指针的大小得到的总是4或8sizeof()测的是指针变量指向存储地址的大小在32位平台所有的指针地址都是32位(4字节)在64位平台所有的指针地址都是64位(8字节) int *p1;int **p2;char *p3;char **p4;printf(sizeof(p1) %d\n, sizeof(p1));printf(sizeof(p2) %d\n, sizeof(p2));printf(sizeof(p3) %d\n, sizeof(p3));printf(sizeof(p4) %d\n, sizeof(p4));printf(sizeof(double *) %d\n, sizeof(double *));7.2.4 野指针和空指针
指针变量也是变量是变量就可以任意赋值不要越界即可32位为4字节64位为8字节但是任意数值赋值给指针变量没有意义因为这样的指针就成了野指针此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)。所以野指针不会直接引发错误操作野指针指向的内存区域才会出问题。 int a 100;int *p;p a; //把a的值赋值给指针变量pp为野指针 ok不会有问题但没有意义p 0x12345678; //给指针变量p赋值p为野指针 ok不会有问题但没有意义*p 1000; //操作野指针指向未知区域内存出问题err但是野指针和有效指针变量保存的都是数值为了标志此指针变量没有指向任何变量(空闲可用)C语言中可以把NULL赋值给此指针这样就标志此指针为空指针没有任何指针。
7.2.5 万能指针void *
void *指针可以指向任意变量的内存空间 void *p NULL;int a 10;p (void *)a; //指向变量时最好转换为void *//使用指针变量指向的内存时转换为int **( (int *)p ) 11;printf(a %d\n, a);7.2.6 const修饰的指针变量 int a 100;int b 200;//指向常量的指针//修饰*指针指向内存区域不能修改指针指向可以变const int * p1 a; //等价于int const *p1 a;//*p1 111; //errp1 b; //ok//指针常量//修饰p1指针指向不能变指针指向的内存可以修改int * const p2 a;//p2 b; //err*p2 222; //ok在编辑程序时指针作为函数参数如果不想修改指针对应内存空间的值需要使用const修饰指针数据类型。
7.3 指针和数组
7.3.1 数组名
数组名字是数组的首元素地址但它是一个常量 int a[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };printf(a %p\n, a);printf(a[0] %p\n, a[0]);//a 10; //err, 数组名只是常量不能修改7.3.2 指针操作数组元素
#include stdio.hint main()
{int a[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int i 0;int n sizeof(a) / sizeof(a[0]);for (i 0; i n; i){//printf(%d, , a[i]);printf(%d, , *(ai));}printf(\n);int *p a; //定义一个指针变量保存a的地址for (i 0; i n; i){p[i] 2 * i;}for (i 0; i n; i){printf(%d, , *(p i));}printf(\n);return 0;
}7.3.3 指针加减运算
加法运算
指针计算不是简单的整数相加如果是一个int *1的结果是增加一个int的大小如果是一个char *1的结果是增加一个char大小
#include stdio.hint main()
{int a;int *p a;printf(%d\n, p);p 2;//移动了2个intprintf(%d\n, p);char b 0;char *p1 b;printf(%d\n, p1);p1 2;//移动了2个charprintf(%d\n, p1);return 0;
}通过改变指针指向操作数组元素
#include stdio.hint main()
{int a[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int i 0;int n sizeof(a) / sizeof(a[0]);int *p a;for (i 0; i n; i){printf(%d, , *p);p;}printf(\n);return 0;
}减法运算 示例1
#include stdio.hint main()
{int a[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int i 0;int n sizeof(a) / sizeof(a[0]);int *p an-1;for (i 0; i n; i){printf(%d, , *p);p--;}printf(\n);return 0;
}示例2
#include stdio.hint main()
{int a[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int *p2 a[2]; //第2个元素地址int *p1 a[1]; //第1个元素地址printf(p1 %p, p2 %p\n, p1, p2);int n1 p2 - p1; //n1 1int n2 (int)p2 - (int)p1; //n2 4printf(n1 %d, n2 %d\n, n1, n2);return 0;
}7.3.4 指针数组
指针数组它是数组数组的每个元素都是指针类型。
#include stdio.hint main()
{//指针数组int *p[3];int a 1;int b 2;int c 3;int i 0;p[0] a;p[1] b;p[2] c;for (i 0; i sizeof(p) / sizeof(p[0]); i ){printf(%d, , *(p[i]));}printf(\n);return 0;
}7.4 多级指针
C语言允许有多级指针存在在实际的程序中一级指针最常用其次是二级指针。二级指针就是指向一个一级指针变量地址的指针。 int a 10;int *p a; //一级指针*p 100; //*p就是aint **q p;//*q就是p//**q就是aint ***t q;//*t就是q//**t就是p//***t就是a7.5 指针和函数
7.5.1 函数形参改变实参的值
#include stdio.hvoid swap1(int x, int y)
{int tmp;tmp x;x y;y tmp;printf(x %d, y %d\n, x, y);
}void swap2(int *x, int *y)
{int tmp;tmp *x;*x *y;*y tmp;
}int main()
{int a 3;int b 5;swap1(a, b); //值传递printf(a %d, b %d\n, a, b);a 3;b 5;swap2(a, b); //地址传递printf(a2 %d, b2 %d\n, a, b);return 0;
}7.5.2 数组名做函数参数
数组名做函数参数函数的形参会退化为指针
#include stdio.hvoid printArrary(int *a, int n)
{int i 0;for (i 0; i n; i){printf(%d, , a[i]);}printf(\n);
}int main()
{int a[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };int n sizeof(a) / sizeof(a[0]);//数组名做函数参数printArrary(a, n); return 0;
}7.5.3 指针做为函数的返回值
#include stdio.hint a 10;int *getA()
{return a;
}int main()
{*( getA() ) 111;printf(a %d\n, a);return 0;
}7.6 指针和字符串
7.6.1 字符指针
#include stdio.hint main()
{char str[] hello world;char *p str;*p m;p;*p i;printf(%s\n, str);p mike jiang;printf(%s\n, p);char *q test;printf(%s\n, q);return 0;
}7.6.2 字符指针做函数参数
#include stdio.hvoid mystrcat(char *dest, const char *src)
{int len1 0;int len2 0;while (dest[len1]){len1;}while (src[len2]){len2;}int i;for (i 0; i len2; i){dest[len1 i] src[i];}
}int main()
{char dst[100] hello mike;char src[] 123456;mystrcat(dst, src);printf(dst %s\n, dst);return 0;
}7.6.3 const修饰的指针变量
#include stdio.h
#include stdlib.h
#include string.hint main(void)
{//const修饰一个变量为只读const int a 10;//a 100; //err//指针变量 指针指向的内存 2个不同概念char buf[] aklgjdlsgjlkds;//从左往右看跳过类型看修饰哪个字符//如果是* 说明指针指向的内存不能改变//如果是指针变量说明指针的指向不能改变指针的值不能修改const char *p buf;// 等价于上面 char const *p1 buf;//p[1] 2; //errp agdlsjaglkdsajgl; //okchar * const p2 buf;p2[1] 3;//p2 salkjgldsjaglk; //err//p3为只读指向不能变指向的内存也不能变const char * const p3 buf;return 0;
}7.6.4 指针数组做为main函数的形参
int main(int argc, char *argv[]);main函数是操作系统调用的第一个参数标明argc数组的成员数量argv数组的每个成员都是char *类型argv是命令行参数的字符串数组argc代表命令行参数的数量程序名字本身算一个参数
#include stdio.h//argc: 传参数的个数包含可执行程序
//argv指针数组指向输入的参数
int main(int argc, char *argv[])
{//指针数组它是数组每个元素都是指针char *a[] { aaaaaaa, bbbbbbbbbb, ccccccc };int i 0;printf(argc %d\n, argc);for (i 0; i argc; i){printf(%s\n, argv[i]);}return 0;
}7.6.5 字符串处理函数
strcpy()
#include string.h
char *strcpy(char *dest, const char *src);
功能把src所指向的字符串复制到dest所指向的空间中\0也会拷贝过去
参数dest目的字符串首地址src源字符首地址
返回值成功返回dest字符串的首地址失败NULL注意如果参数dest所指的内存空间不够大可能会造成缓冲溢出的错误情况。 char dest[20] 123456789;char src[] hello world;strcpy(dest, src);printf(%s\n, dest);strncpy()
#include string.h
char *strncpy(char *dest, const char *src, size_t n);
功能把src指向字符串的前n个字符复制到dest所指向的空间中是否拷贝结束符看指定的长度是否包含\0。
参数dest目的字符串首地址src源字符首地址n指定需要拷贝字符串个数
返回值成功返回dest字符串的首地址失败NULL char dest[20] ;char src[] hello world;strncpy(dest, src, 5);printf(%s\n, dest);dest[5] \0;printf(%s\n, dest);strcat()
#include string.h
char *strcat(char *dest, const char *src);
功能将src字符串连接到dest的尾部‘\0’也会追加过去
参数dest目的字符串首地址src源字符首地址
返回值成功返回dest字符串的首地址失败NULLchar str[20] 123;char *src hello world;printf(%s\n, strcat(str, src));strncat()
#include string.h
char *strncat(char *dest, const char *src, size_t n);
功能将src字符串前n个字符连接到dest的尾部‘\0’也会追加过去
参数dest目的字符串首地址src源字符首地址n指定需要追加字符串个数
返回值成功返回dest字符串的首地址失败NULLchar str[20] 123;char *src hello world;printf(%s\n, strncat(str, src, 5));strcmp()
#include string.h
int strcmp(const char *s1, const char *s2);
功能比较 s1 和 s2 的大小比较的是字符ASCII码大小。
参数s1字符串1首地址s2字符串2首地址
返回值相等0大于0 在不同操作系统strcmp结果会不同 返回ASCII差值小于0char *str1 hello world;char *str2 hello mike;if (strcmp(str1, str2) 0){printf(str1str2\n);}else if (strcmp(str1, str2) 0){printf(str1str2\n);} else{printf(str1str2\n);}strncmp()
#include string.h
int strncmp(const char *s1, const char *s2, size_t n);
功能比较 s1 和 s2 前n个字符的大小比较的是字符ASCII码大小。
参数s1字符串1首地址s2字符串2首地址n指定比较字符串的数量
返回值相等0大于 0小于 0char *str1 hello world;char *str2 hello mike;if (strncmp(str1, str2, 5) 0){printf(str1str2\n);}else if (strcmp(str1, hello world) 0){printf(str1str2\n);}else{printf(str1str2\n);}sprintf()
#include stdio.h
int sprintf(char *str, const char *format, ...);
功能根据参数format字符串来转换并格式化数据然后将结果输出到str指定的空间中直到出现字符串结束符 \0 为止。
参数str字符串首地址format字符串格式用法和printf()一样
返回值成功实际格式化的字符个数失败 - 1char dst[100] { 0 };int a 10;char src[] hello world;printf(a %d, src %s, a, src);printf(\n);int len sprintf(dst, a %d, src %s, a, src);printf(dst \ %s\\n, dst);printf(len %d\n, len);sscanf()
#include stdio.h
int sscanf(const char *str, const char *format, ...);
功能从str指定的字符串读取数据并根据参数format字符串来转换并格式化数据。
参数str指定的字符串首地址format字符串格式用法和scanf()一样
返回值成功参数数目成功转换的值的个数失败 - 1char src[] a10, b20;int a;int b;sscanf(src, a%d, b%d, a, b);printf(a:%d, b:%d\n, a, b);strchr()
#include string.h
char *strchr(const char *s, int c);
功能在字符串s中查找字母c出现的位置
参数s字符串首地址c匹配字母(字符)
返回值成功返回第一次出现的c地址失败NULLchar src[] ddda123abcd;char *p strchr(src, a);printf(p %s\n, p);strstr()
#include string.h
char *strstr(const char *haystack, const char *needle);
功能在字符串haystack中查找字符串needle出现的位置
参数haystack源字符串首地址needle匹配字符串首地址
返回值成功返回第一次出现的needle地址失败NULLchar src[] ddddabcd123abcd333abcd;char *p strstr(src, abcd);printf(p %s\n, p);strtok()
#include string.h
char *strtok(char *str, const char *delim);
功能来将字符串分割成一个个片段。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时, 则会将该字符改为\0 字符当连续出现多个时只替换第一个为\0。
参数str指向欲分割的字符串delim为分割字符串中包含的所有字符
返回值成功分割后字符串首地址失败NULL在第一次调用时strtok()必需给予参数s字符串往后的调用则将参数s设置成NULL每次调用成功则返回指向被分割出片段的指针 char a[100] adc*fvcv*ebcy*hghbdfg*casdert;char *s strtok(a, *);//将*分割的子串取出while (s ! NULL){printf(%s\n, s);s strtok(NULL, *);}atoi()
#include stdlib.h
int atoi(const char *nptr);
功能atoi()会扫描nptr字符串跳过前面的空格字符直到遇到数字或正负号才开始做转换而遇到非数字或字符串结束符(\0)才结束转换并将结果返回返回值。
参数nptr待转换的字符串
返回值成功转换后整数类似的函数有
atof()把一个小数形式的字符串转化为一个浮点数atol()将一个字符串转化为long类型 char str1[] -10;int num1 atoi(str1);printf(num1 %d\n, num1);char str2[] 0.123;double num2 atof(str2);printf(num2 %lf\n, num2);7.7 指针小结 8. 内存管理
8.1 作用域
C语言变量的作用域分为
代码块作用域(代码块是{}之间的一段代码)函数作用域文件作用域
8.1.1 局部变量
局部变量也叫auto自动变量(auto可写可不写)一般情况下代码块{}内部定义的变量都是自动变量它有如下特点
在一个函数内定义只在函数范围内有效在复合语句中定义只在复合语句中有效随着函数调用的结束或复合语句的结束局部变量的声明声明周期也结束如果没有赋初值内容为随机
#include stdio.hvoid test()
{//auto写不写是一样的//auto只能出现在{}内部auto int b 10;
}int main(void)
{//b 100; //err 在main作用域中没有bif (1){//在复合语句中定义只在复合语句中有效int a 10;printf(a %d\n, a);}//a 10; //err离开if()的复合语句a已经不存在return 0;
}8.1.2 静态(static)局部变量
static局部变量的作用域也是在定义的函数内有效static局部变量的生命周期和程序运行周期一样同时staitc局部变量的值只初始化一次但可以赋值多次static局部变量若未赋以初值则由系统自动赋值数值型变量自动赋初值0字符型变量赋空字符
#include stdio.hvoid fun1()
{int i 0;i;printf(i %d\n, i);
}void fun2()
{//静态局部变量没有赋值系统赋值为0而且只会初始化一次static int a;a;printf(a %d\n, a);
}int main(void)
{fun1();fun1();fun2();fun2();return 0;
}8.1.3 全局变量
在函数外定义可被本文件及其它文件中的函数所共用若其它文件中的函数调用此变量,须用extern声明全局变量的生命周期和程序运行周期一样不同文件的全局变量不可重名
8.1.4 静态(static)全局变量
在函数外定义,作用范围被限制在所定义的文件中不同文件静态全局变量可以重名,但作用域不冲突static全局变量的生命周期和程序运行周期一样同时staitc全局变量的值只初始化一次
8.1.5 extern全局变量声明
extern int a;声明一个变量这个全局变量在别的文件中已经定义了这里只是声明而不是定义。
8.1.6 全局函数和静态函数
在C语言中函数默认都是全局的使用关键字static可以将函数声明为静态函数定义为static就意味着这个函数只能在定义这个函数的文件中使用在其他文件中不能调用即使在其他文件中声明这个函数都没用。
对于不同文件中的staitc函数名字可以相同。
注意
允许在不同的函数中使用相同的变量名它们代表不同的对象分配不同的单元互不干扰同一源文件中,允许全局变量和局部变量同名在局部变量的作用域内全局变量不起作用所有的函数默认都是全局的意味着所有的函数都不能重名但如果是staitc函数那么作用域是文件级的所以不同的文件static函数名是可以相同的
8.1.7 总结 8.2 内存布局
8.2.1 内存分区
C代码经过预处理、编译、汇编、链接4步后生成一个可执行程序。 在 Windows 下程序是一个普通的可执行文件以下列出一个二进制可执行文件的基本情况
通过上图可以得知在没有运行程序前也就是说程序没有加载到内存前可执行程序内部已经分好3段信息分别为代码区text、数据区data和未初始化数据区bss3 个部分有些人直接把data和bss合起来叫做静态区或全局区。
代码区 存放 CPU 执行的机器指令。通常代码区是可共享的即另外的执行程序可以调用它使其可共享的目的是对于频繁被执行的程序只需要在内存中有一份代码即可。代码区通常是只读的使其只读的原因是防止程序意外地修改了它的指令。另外代码区还规划了局部变量的相关信息。全局初始化数据区/静态数据区data段 该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量包括全局静态变量和局部静态变量和常量数据如字符串常量。未初始化数据区又叫 bss 区 存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空NULL。
程序在加载到内存前代码区和全局区(data和bss)的大小就是固定的程序运行期间不能改变。然后运行可执行程序系统把程序加载到内存除了根据可执行程序的信息分出代码区text、数据区data和未初始化数据区bss之外还额外增加了栈区、堆区。
代码区text segment 加载的是可执行文件代码段所有的可执行代码都加载到代码区这块内存是不可以在运行期间修改的。未初始化数据区BSS 加载的是可执行文件BSS段位置可以分开亦可以紧靠数据段存储于数据段的数据全局未初始化静态未初始化数据的生存周期为整个程序运行过程。全局初始化数据区/静态数据区data segment 加载的是可执行文件数据段存储于数据段全局初始化静态初始化数据文字常量(只读)的数据的生存周期为整个程序运行过程。栈区stack 栈是一种先进后出的内存结构由编译器自动分配释放存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放因此局部变量的生存周期为申请到释放该段栈空间。堆区heap 堆是一个大容器它的容量要远远大于栈但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放若程序员不释放程序结束时由操作系统回收。
8.2.2 存储类型总结 #include stdio.h
#include stdlib.hint e;
static int f;
int g 10;
static int h 10;
int main()
{int a;int b 10;static int c;static int d 10;char *i test;char *k NULL;printf(a\t %p\t //局部未初始化变量\n, a);printf(b\t %p\t //局部初始化变量\n, b);printf(c\t %p\t //静态局部未初始化变量\n, c);printf(d\t %p\t //静态局部初始化变量\n, d);printf(e\t %p\t //全局未初始化变量\n, e);printf(f\t %p\t //全局静态未初始化变量\n, f);printf(g\t %p\t //全局初始化变量\n, g);printf(h\t %p\t //全局静态初始化变量\n, h);printf(i\t %p\t //只读数据(文字常量区)\n, i);k (char *)malloc(10);printf(k\t %p\t //动态分配的内存\n, k);return 0;
}8.2.3 内存操作函数
1) memset()
#include string.h
void *memset(void *s, int c, size_t n);
功能将s的内存区域的前n个字节以参数c填入
参数s需要操作内存s的首地址c填充的字符c虽然参数为int但必须是unsigned char , 范围为0~255n指定需要设置的大小
返回值s的首地址int a[10];memset(a, 0, sizeof(a));memset(a, 97, sizeof(a));int i 0;for (i 0; i 10; i){printf(%c\n, a[i]);}2) memcpy()
#include string.h
void *memcpy(void *dest, const void *src, size_t n);
功能拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数dest目的内存首地址src源内存首地址注意dest和src所指的内存空间不可重叠可能会导致程序报错n需要拷贝的字节数
返回值dest的首地址int a[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int b[10];memcpy(b, a, sizeof(a));int i 0;for (i 0; i 10; i){printf(%d, , b[i]);}printf(\n);//memcpy(a[3], a, 5 * sizeof(int)); //err, 内存重叠3) memmove() memmove()功能用法和memcpy()一样区别在于dest和src所指的内存空间重叠时memmove()仍然能处理不过执行效率比memcpy()低些。
4) memcmp()
#include string.h
int memcmp(const void *s1, const void *s2, size_t n);
功能比较s1和s2所指向内存区域的前n个字节
参数s1内存首地址1s2内存首地址2n需比较的前n个字节
返回值相等0大于0小于0int a[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int b[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int flag memcmp(a, b, sizeof(a));printf(flag %d\n, flag);8.2.4 堆区内存分配和释放
1)malloc()
#include stdlib.h
void *malloc(size_t size);
功能在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域用来存放类型说明符指定的类型。分配的内存空间内容不确定一般使用memset初始化。
参数size需要分配内存大小(单位字节)
返回值
成功分配空间的起始地址
失败NULL#include stdlib.h
#include stdio.h
#include string.hint main()
{int count, *array, n;printf(请输入要申请数组的个数:\n);scanf(%d, n);array (int *)malloc(n * sizeof (int));if (array NULL){printf(申请空间失败!\n);return -1;}//将申请到空间清0memset(array, 0, sizeof(int)*n);for (count 0; count n; count) /*给数组赋值*/array[count] count;for (count 0; count n; count) /*打印数组元素*/printf(%2d, array[count]);free(array);return 0;
}2)free()
#include stdlib.h
void free(void *ptr);
功能释放ptr所指向的一块内存空间ptr是一个任意类型的指针变量指向被释放区域的首地址。对同一内存空间多次释放会出错。
参数
ptr需要释放空间的首地址被释放区应是由malloc函数所分配的区域。
返回值无8.3 内存分区代码分析
1) 返回栈区地址
#include stdio.h
int *fun()
{int a 10;return a;//函数调用完毕a释放
}int main(int argc, char *argv[])
{int *p NULL;p fun();*p 100; //操作野指针指向的内存,errreturn 0;
}2) 返回data区地址
#include stdio.hint *fun()
{static int a 10;return a; //函数调用完毕a不释放
}int main(int argc, char *argv[])
{int *p NULL;p fun();*p 100; //okprintf(*p %d\n, *p);return 0;
}3) 值传递1
#include stdio.h
#include stdlib.hvoid fun(int *tmp)
{tmp (int *)malloc(sizeof(int));*tmp 100;
}int main(int argc, char *argv[])
{int *p NULL;fun(p); //值传递形参修改不会影响实参printf(*p %d\n, *p);//err操作空指针指向的内存return 0;
}4) 值传递2
#include stdio.h
#include stdlib.hvoid fun(int *tmp)
{*tmp 100;
}int main(int argc, char *argv[])
{int *p NULL;p (int *)malloc(sizeof(int));fun(p); //值传递printf(*p %d\n, *p); //ok*p为100return 0;
}5) 返回堆区地址
#include stdio.h
#include stdlib.hint *fun()
{int *tmp NULL;tmp (int *)malloc(sizeof(int));*tmp 100;return tmp;//返回堆区地址函数调用完毕不释放
}int main(int argc, char *argv[])
{int *p NULL;p fun();printf(*p %d\n, *p);//ok//堆区空间使用完毕手动释放if (p ! NULL){free(p);p NULL;}return 0;
}9. 复合类型(自定义类型)
9.1 结构体
9.1.1概述
数组描述一组具有相同类型数据的有序集合用于处理大量相同类型的数据运算。
有时我们需要将不同类型的数据组合成一个有机的整体如一个学生有学号/姓名/性别/年龄/地址等属性。显然单独定义以上变量比较繁琐数据不便于管理。
语言中给出了另一种构造数据类型——结构体。
9.1.2 结构体变量的定义和初始化
定义结构体变量的方式
先声明结构体类型再定义变量名在声明类型的同时定义变量直接定义结构体类型变量无类型名 结构体类型和结构体变量关系
结构体类型指定了一个结构体类型它相当于一个模型但其中并无具体数据系统对之也不分配实际内存单元。结构体变量系统根据结构体类型内部成员状况为之分配空间。
//结构体类型的定义
struct stu
{char name[50];int age;
};//先定义类型再定义变量常用
struct stu s1 { mike, 18 };//定义类型同时定义变量
struct stu2
{char name[50];int age;
}s2 { lily, 22 };struct
{char name[50];int age;
}s3 { yuri, 25 };9.1.3 结构体成员的使用
#includestdio.h
#includestring.h//结构体类型的定义
struct stu
{char name[50];int age;
};int main()
{struct stu s1;//如果是普通变量通过点运算符操作结构体成员strcpy(s1.name, abc);s1.age 18;printf(s1.name %s, s1.age %d\n, s1.name, s1.age);//如果是指针变量通过-操作结构体成员strcpy((s1)-name, test);(s1)-age 22;printf((s1)-name %s, (s1)-age %d\n, (s1)-name, (s1)-age);return 0;
}9.1.4 结构体数组
#include stdio.h//统计学生成绩
struct stu
{int num;char name[20];char sex;float score;
};int main()
{//定义一个含有5个元素的结构体数组并将其初始化struct stu boy[5] {{ 101, Li ping, M, 45 }, { 102, Zhang ping, M, 62.5 },{ 103, He fang, F, 92.5 },{ 104, Cheng ling, F, 87 },{ 105, Wang ming, M, 58 }};int i 0;int c 0;float ave, s 0;for (i 0; i 5; i){s boy[i].score; //计算总分if (boy[i].score 60){c 1; //统计不及格人的分数}}printf(s%f\n, s);//打印总分数ave s / 5; //计算平均分数printf(average%f\ncount%d\n\n, ave, c); //打印平均分与不及格人数for (i 0; i 5; i){printf( name%s, score%f\n, boy[i].name, boy[i].score);// printf( name%s, score%f\n, (boyi)-name, (boyi)-score);}return 0;
}9.1.5 结构体套结构体
#include stdio.hstruct person
{char name[20];char sex;
};struct stu
{int id;struct person info;
};int main()
{struct stu s[2] { 1, lily, F, 2, yuri, M };int i 0;for (i 0; i 2; i){printf(id %d\tinfo.name%s\tinfo.sex%c\n, s[i].id, s[i].info.name, s[i].info.sex);}return 0;
}9.1.6 结构体赋值
#includestdio.h
#includestring.h//结构体类型的定义
struct stu
{char name[50];int age;
};int main()
{struct stu s1;//如果是普通变量通过点运算符操作结构体成员strcpy(s1.name, abc);s1.age 18;printf(s1.name %s, s1.age %d\n, s1.name, s1.age);//相同类型的两个结构体变量可以相互赋值//把s1成员变量的值拷贝给s2成员变量的内存//s1和s2只是成员变量的值一样而已它们还是没有关系的两个变量struct stu s2 s1;
//memcpy(s2, s1, sizeof(s1));printf(s2.name %s, s2.age %d\n, s2.name, s2.age);return 0;
}9.1.7 结构体和指针
1)指向普通结构体变量的指针
#includestdio.h//结构体类型的定义
struct stu
{char name[50];int age;
};int main()
{struct stu s1 { lily, 18 };//如果是指针变量通过-操作结构体成员struct stu *p s1;printf(p-name %s, p-age%d\n, p-name, p-age);printf((*p).name %s, (*p).age%d\n, (*p).name, (*p).age);return 0;
}2)堆区结构体变量
#includestdio.h
#include string.h
#include stdlib.h//结构体类型的定义
struct stu
{char name[50];int age;
};int main()
{struct stu *p NULL;p (struct stu *)malloc(sizeof(struct stu));//如果是指针变量通过-操作结构体成员strcpy(p-name, test);p-age 22;printf(p-name %s, p-age%d\n, p-name, p-age);printf((*p).name %s, (*p).age%d\n, (*p).name, (*p).age);free(p);p NULL;return 0;
}3)结构体套一级指针
#includestdio.h
#include string.h
#include stdlib.h//结构体类型的定义
struct stu
{char *name; //一级指针int age;
};int main()
{struct stu *p NULL;p (struct stu *)malloc(sizeof(struct stu));p-name malloc(strlen(test) 1);strcpy(p-name, test);p-age 22;printf(p-name %s, p-age%d\n, p-name, p-age);printf((*p).name %s, (*p).age%d\n, (*p).name, (*p).age);if (p-name ! NULL){free(p-name);p-name NULL;}if (p ! NULL){free(p);p NULL;}return 0;
}9.1.8 结构体做函数参数
1)结构体普通变量做函数参数
#includestdio.h
#include string.h//结构体类型的定义
struct stu
{char name[50];int age;
};//函数参数为结构体普通变量
void set_stu(struct stu tmp)
{strcpy(tmp.name, mike);tmp.age 18;printf(tmp.name %s, tmp.age %d\n, tmp.name, tmp.age);
}int main()
{struct stu s { 0 };set_stu(s); //值传递printf(s.name %s, s.age %d\n, s.name, s.age);return 0;
}2)结构体指针变量做函数参数
#includestdio.h
#include string.h//结构体类型的定义
struct stu
{char name[50];int age;
};//函数参数为结构体指针变量
void set_stu_pro(struct stu *tmp)
{strcpy(tmp-name, mike);tmp-age 18;
}int main()
{struct stu s { 0 };set_stu_pro(s); //地址传递printf(s.name %s, s.age %d\n, s.name, s.age);return 0;
}3)结构体数组名做函数参数
#includestdio.h//结构体类型的定义
struct stu
{char name[50];int age;
};//void set_stu_pro(struct stu tmp[100], int n)
//void set_stu_pro(struct stu tmp[], int n)
void set_stu_pro(struct stu *tmp, int n)
{int i 0;for (i 0; i n; i){sprintf(tmp-name, name%d%d%d, i, i, i);tmp-age 20 i;tmp;}
}int main()
{struct stu s[3] { 0 };int i 0;int n sizeof(s) / sizeof(s[0]);set_stu_pro(s, n); //数组名传递for (i 0; i n; i){printf(%s, %d\n, s[i].name, s[i].age);}return 0;
}4)const修饰结构体指针形参变量
//结构体类型的定义
struct stu
{char name[50];int age;
};void fun1(struct stu * const p)
{//p NULL; //errp-age 10; //ok
}//void fun2(struct stu const* p)
void fun2(const struct stu * p)
{p NULL; //ok//p-age 10; //err
}void fun3(const struct stu * const p)
{//p NULL; //err//p-age 10; //err
}9.2 共用体(联合体)
联合union是一个能在同一个存储空间存储不同类型数据的类型联合体所占的内存长度等于其最长成员的长度倍数也有叫做共用体同一内存段可以用来存放几种不同类型的成员但每一瞬时只有一种起作用共用体变量中起作用的成员是最后一次存放的成员在存入一个新的成员后原有的成员的值会被覆盖共用体变量的地址和它的各成员的地址都是同一地址
#include stdio.h//共用体也叫联合体
union Test
{unsigned char a;unsigned int b;unsigned short c;
};int main()
{//定义共用体变量union Test tmp;//1、所有成员的首地址是一样的printf(%p, %p, %p\n, (tmp.a), (tmp.b), (tmp.c));//2、共用体大小为最大成员类型的大小printf(%lu\n, sizeof(union Test));//3、一个成员赋值会影响另外的成员//左边是高位右边是低位//低位放低地址高位放高地址tmp.b 0x44332211;printf(%x\n, tmp.a); //11printf(%x\n, tmp.c); //2211tmp.a 0x00;printf(short: %x\n, tmp.c); //2200printf(int: %x\n, tmp.b); //44332200return 0;
}9.3 枚举
枚举将变量的值一一列举出来变量的值只限于列举出来的值的范围内。
枚举类型定义
enum 枚举名
{枚举值表
};在枚举值表中应列出所有可用值也称为枚举元素。枚举值是常量不能在程序中用赋值语句再对它赋值举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为012 …
#include stdio.henum weekday
{sun 2, mon, tue, wed, thu, fri, sat
} ;enum bool
{flase, true
};int main()
{enum weekday a, b, c;a sun;b mon;c tue;printf(%d,%d,%d\n, a, b, c);enum bool flag;flag true;if (flag 1){printf(flag为真\n);}return 0;
}9.4 typedef
typedef为C语言的关键字作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字不能创建新类型。
与#define不同typedef仅限于数据类型而不是能是表达式或具体的值#define发生在预处理typedef发生在编译阶段
#include stdio.htypedef int INT;
typedef char BYTE;
typedef BYTE T_BYTE;
typedef unsigned char UBYTE;typedef struct type
{UBYTE a;INT b;T_BYTE c;
}TYPE, *PTYPE;int main()
{TYPE t;t.a 254;t.b 10;t.c c;PTYPE p t;printf(%u, %d, %c\n, p-a, p-b, p-c);return 0;
}