1、C语言,C语言概述,C语言及标准化,最早由Dennis Ritchie 于1973年为 Unix设计并实现,从贝尔实验室到世界各地。 标准化 C89 ANSI(美国 国家标准委员会)于89年12月正式通过,1990年被ISO批准为国际标准 C99 1999年ISO通过了新版的C标准,包含了一些重要的改变 基于C的语言 C+ Java C# Perl ,C语言的特点,C语言是一种底层语言 C语言是一种小型语言 有限的语言特性 依赖标准函数库 C语言是一种包容性语言 更广阔的自由度 不强制进行详细的错误检查 C语言不是一个强类型的语言。但类型检查机制已经得到了加强。,C语言的优点,高效 可移植 功
2、能强大 灵活 从嵌入式到商业数据处理 限制少 标准库 与Unix系统集成,C语言的缺点,更容易隐藏错误 有时会难以理解 有时会难以修改,C语言基本概念,编写一个简单的C程序,一个简单的C程序 #include int main() printf(“Hello World!”); return 0; 保存为xxx.c,编译和连接,预处理 预处理器执行#开头的命令(通常称为指令) 编译 编译器会将程序翻译成机器指令(目标代码) 连接 连接器把目标代码和所需的其他附加代码整合在一起,产生可执行的程序。,GCC,GNU C Compiler的简称,现指GNU Compiler Collection 新
3、版本gcc可以编译ada, C, C+,fortran,java和Objective-C GNU 为自由软件基金会的一个项目 GCC可以免费获取,还可以在多个操作系统中运行 GCC是许多 基于Unix系统(Linux, BSD, Mac OS X)的主要编译器,并广泛应用于商业软件开发,GCC使用,选项 -c 编译 -o 自定义目标名 -std=c89或-std=c99 -pedantic 根据C标准产生警告消息 -Wall 编译器检测到可能的错误时生成警告消息 -O0 不优化 -O1 缺省优化 -O3最高优化 -E 仅预处理 -S 产生汇编 .,Hello World中的语言特性,指令 #i
4、nclude 函数 自定义函数 库函数 main函数 语句 显示字符串 printf();,注释,/* 注释 */ 注释的习惯使用方式 盒型注释 简化盒型 翼型注释 注释的嵌套是不合法的 忘记终止注释可能导致严重问题 C99提供的新注释形式 /,变量和赋值,变量 类型 int float 声明 int x; float y, z; 必须把声明放在语句之前 C99中,声明可以不在语句之前 赋值 给float变量的赋值需要在数值后加f 混合赋值是可以的,但不一定安全,变量的初始化,用printf显示变量的值 printf(“%d”, x); printf(“%f”, y); printf(“%d,
5、 %.2f”, x, y); 变量初始化 变量未初始化时访问 初始化,标识符和关键字,标识符,变量、函数、宏、其他实体的名称 以字母、下划线开头 可以包含字母、下划线、数字 大小写敏感 不能和关键字冲突 标识符长度有限制 吗? C89声称可以任意长,但编译器只记住前31个 C99中是63个 关键字 有特殊含义的一串字符 全部小写 标准库中的函数名全部小写,C语言书写规范,语句 可以分开放在任意多行内 空格使我们更容易区分 缩进有助于轻松识别程序嵌套 空行可以把程序划分成逻辑单元 驼峰命名方式和下划线区分方式,基本类型,整数类型,C中,基本类型只有char 、int、float、double四种
6、 有符号和无符号 Singed (默认) Unsinged Short和long 16位机和32位机上的整数类型的表示范围 表示范围并非C标准定义,根据编译器的不同而不同 C99中增加的整数类型 Long long int Unsiged long long int 有些编译器支持非标准的128位整数,字面值和常量,整数常量(字面值) 3被认为是int 3.5被认为是double 3.5f被认为是float 3.5L被认为是long double 35L被认为是long int 35LL被认为是long long 35u被认为是unsigned int 35UL是unsigned long 八
7、进制和十六进制 0377 0x12ab,浮点类型,C提供三种浮点类型 Float:单精度 Double:双精度 Long double :扩展双精度 C标准没有说明精度的范围有多少 IEEE提供了精度: Float 1.17548*10(-38)3.40282*10(38) Double 2.22507*10(-308)1.79769*10(308) 浮点常量 以下浮点常量有效 57.0 57. 57.0e0 57E0 5.7e1 5.7e+1 .57e2 570e-1,字符类型,char 各ASCII A 65 a 97 0 48 字符操作 Char c = a; C = 65; C = c
8、 + 1; C+; 有符号和无符号char C标准没有说明普通char是有符号还是无符号 C语言对字符类型视为整数类型,转义序列,响铃 a 退格 b 换行 n 回车 r 水平制表 t 垂直制表 v 特殊字符 ? ”,使用printf()输出各种类型的变量值,printf(“格式字符串“, .); %i/%d int %f/%lf float/double %hd/%ld short int/long int %u unsigned int %c char %s string %p address %o 八进制 %x/%X 十六进制 %g float/double去尾零 %e/%E 科学计数法
9、% %,Scanf()从键盘输入,scanf(“格式串“, 地址表); 格式串后的每个参数都必需是一个地址(指针) 函数返回实际被转换并赋值的输入项的数目 格式符 d 十进制整数型 i 整数型,可以是八进制,十进制,十六进制 o 八进制整数 u 无符号十进制整数 x 十六进制整数 c 字符 s 非空白符组成的字符串 e f g 浮点数 p 地址,Sizeof(),sizeof(类型) sizeof(变量名) sizeof(表达式) sizeof只关心类型,只会分析括号中的类型,不会对括号中的内容进行运算。如: int i = 5; sizeof(i=10); 计算的结果,i是多少?,运算符和表
10、达 式,运算符,() - . ! + - + - * & (type) sizeof 从右向左 * / % + - = = != & | & | ?: = += -= *= /= %= &= = |= = 从右向左 ,算术运算符,+ - * / % 运算符/可能产生意外 的结果 运算符%要求操作数是整数 把0做为/或%的右操作数会出现未定义的行为 当运算符/和%用于操作负数,其结果未定义 C89中,操作数的其中之一是负数,结果可以向上取整,也可以向下取整。如-9/7的结果可以是-1,也可以是-2。 如果i和j其中一个是负数,i%j的结果依赖具体实现 C99中, 除法的结果总是向零截取,因此 -
11、9/7的结果是-1,i%j的结果符号与i相同。,练习,将一个二位数逆序打印 如 29 -92 提示:n%10获得个位数,n/10获取十位数 试想如果是三位数呢?,练习,EAN 13通用商品条形码一般由前缀部分、制造厂商代码、商品代码和校验码组成。条形码的前几位数,00-09代表美国,加拿大,4549代表日本,690-693代表中国大陆,471代表我国台湾地区,489代表香港特区。校验码就是条形码中最后一位,具体计算规则如下: 比如说某个商品的条形码是: 6 9 2 3 0 7 6 2 1 3 1 9 5 1、把奇数位的数字加起来,除了最后一位校验码,这里是 62061116 这个A 2、把偶数
12、位的数字加起来,这里是 93723933 这个B 3、A3*B-1114,对10取余=4 4、945 这就是最后一位那个校验码 5的来历,赋值运算,一般的运算符不会改变操作数的值 但赋值会改变操作数的值 赋值的右结合性 i = j = k = 0;是合法的 k = 1 + (j = i);是合法的 左值 左值 表示存储在计算机内存中的对象 ,而不是常量或计算结果 赋值运算要求左操作数必须是一个左值 10 = I;/错 i + j = 0;/错 -i = j;/错 复合赋值,自增和自减,+ - 前+和后+ 前- -和后- - 同一表达式多次出现+和- -运算,结果往往很难理解 i = 1; j
13、= 2; K = +I + j+; - I = 1; J = 2; K = i+ + j+;,逻辑表达式,关系运算符 = 判等 = != 逻辑运算符 ! & | &和|的短路特征 逻辑运算的结果,真或假,0或1,位运算,& 按位与 经常用来屏蔽某些二进制位(置0)。也可以知道一个数的某一位是1还是0 | 按位或 经常用来将某些二进制位置1, 也可以知道某个二进制位是1还是0 按位异或 可以用来将指定的位反转 按位取反 右移 左移右边空位补零,右移左边空们补符号位,无符号数补零 移位运算经常用来拆字节和凑字节。,地址运算符,& 取地址 * 根据地址取变量 printf中的%p用来输出地址。 取地
14、址取得的是一个变量的开始地址。 地址是按字节编号的。,表达式计算时的类型转换,if(任何一个数为long double) 将另一个数转成long double) else if(任何一个数为double) 将另一个数转成double else if(任何一个数为float) float else if(任何一个数是unsigned long int) unsigned long int ,表达式计算时的类型转换,else if(一个是long int而另一个是unsigned int) if(long int可以表示所有的unsigned int的值) 将unsigned int 转成long
15、 int; else 将两个数都转成unsigned long int; else if(一个是long int) 另一个转成long int else if(一个是unsigned int) 另一个转成unsigned int; else 两个数都转成int类型。 ,流程控制语句,If语句,if(表达 式)语句 if(0=n) 复合语句 多条语句 else子句 级联式if语句 悬空else if(y!=0) if(x!=0) r = x / y; else printf(“Error: y is equal to 0n“);,练习,判断闰年 计算股票经纪人的佣金 当股票通过经纪人进行买卖时,
16、经纪人的佣金通过交易额来计算 计算表:,条件表达式,表达式1?表达式2:表达式3,Switch语句,switch(控制表达式) case 常量表达式 :语句; default : 语句; 控制表达式被当做整数处理,可以是字符,但不能是浮点数事项字符串 常量表达式必须是常量,如 3 A 2+5 语句 可以是零到多条 不允许重复分支,default不一定在最后 break,练习,将数字分级 输入一个1100的数,输出级别 输入 年,月,输出 这个月有多少天?,循环,while(表达式)语句 无限循环 while(1) 练习 显示平方表 1 4 9 16 数列求和 输入一些数据,输出这些数之和,do
17、语句,do 语句 while(表达式); 练习 计算整数的位数,for 语句,for(表达式1;表达式2;表达式3) 语句; for的惯用法 for(i=0; i0;i-) 在for循环中省略表达式 C99中的for语句 for(int i=0; in; i+) 逗号运算符,空语句,经常用来编写空循环体的循环 C程序员习惯性地把空语句单独放置在一行 不小心在if, while, for中的圆括号后放置分号会创建空语句 if(d=0); fhile(i0); for(.);,退出循环,break continue goto,练习,账簿结算 开发一个程序用来维护账簿的余额,程序将为用户提供选择菜单
18、 清空账户余额 存钱 取钱 显示当前余额,数组,数组,含有多个数据值的数据结构 每个数据值具有相同的数据类型,这些数据称为元素 数组下标 从0开始 C语言不检查下标的范围,当越界访问时,结果不可预知 对于某些编译器,以下代码会产生莫明其妙的结果 int a10, i; for(i=0; i=10; i+) ai = 0; 练习: 用户输入一串数放入数组中,然后反向输出出来,数组初始化,数组初始化 用大括号括起来的常量表达式列表初始化数组很常见 如果初始化数组短,那么剩余元素赋值为0 利用以上特性,很容易把数组初始化为全0: int a10 = 0; 初始化完全为空是非法的 如果给定了初始化式,
19、可以省略掉数组长度 C99中的指定初始化式 int a15=2=10, 5=20, 14=40; int a15=5=20, 2=10, 14=40; int a = 5=20,2=10,14=40;,练习,检查数中重复出现的数字 用户输入一个数字,程序显示是否有重复数字 试着使用头文件中的bool,true, false,对数组使用sizeof()运算符,sizeof(数组名)可以确定数组的大小 以下表达式可以确定数组的长度 sizeof(数组名)/sizeof(数组名0) 这种用法比较灵活,但麻烦,可以使用宏来简化,练习,计算利息 在几年内100元投资在不同利率下的价值,由用户输入最低利率
20、和投资年数 Years 6% 7% 8% 9% 10% 1 106.00 107.00 108.00 109.00 110.00 2 112.36 114.49 116.64 118.81 121.00 3 119.10 122.50 125.97 129.50 133.10 4 126.25 131.08 136.05 141.16 146.41 5 133.82 140.26 146.93 153.86 161.05,多维数组,数组可以有任意维数 C语言是按照行主序存储数组的,多维数组一样如此 多维数组的初始化 初始化式没有足够的数填满数组,则补0 甚至可以省掉内层花括号,练习,发牌 程序
21、负责发一副标准纸牌,每张标准纸牌都在一个花色(梅花、方块、红桃或黑桃)和一个等级(2、3、4、5、6、7、8、9、10、J、Q、K、A) 。程序需要用户输入 手里应该握有几张牌。,C99中的变长数组,int n; scanf(“%d”, n); int an;/c99 only ,函数,函数,函数是一连串语句,这些语句 组合起来,并被指定了一个名字 函数返回值,形式参数,函数体和实际参数 不带返回值的函数 函数的定义 函数不能返回数组,除此之外没有其他限制 指定返回类型是void代表函数没有返回值 如果省略返回类型,C89会假定返回int.C99中不合法 函数体中可以出现变量声明和语句 ,但在
22、C89中,声明必须的语句之前,C99中没有此限制 。 练习 判定素数,函数声明,隐式声明 函数声明和函数原型 声明没有函数体 声明可以不写参数名 C99规则: 在调用一个函数之前, 必须先对其进行声明或定义。,形式参数和实际参数,形式参数和实际参数的区别 void形参和空形参 函数调用 中的值传递 实际参数的转换 编译器在调用前遇到原型 编译器在调用前没有遇到原型 floatdouble char,shortint 数组型参数 形式参数可以不用说明数组的长度 函数中无法确定数组长度,只能用第二个参数传入 形参是多维数组时,可以不指定数组的长度,但一定要指定数组的列数。,return,retur
23、n 表达 式; 如果表达式类型与函数返回类型不匹配,会隐式转换 如果没有给出表达式,return可以出现在void函数中 在void函数中的最后一句加return没有意义,因为函数即使没有return也会返回 在非void函数中如果到达函数体的末尾也未遇到return,如果程序试图返回一个值,其行为未定义。,程序终止,main函数中的return将导致程序终止 C99中省略函数的返回类型是不合法的 Main函数中终止程序中的另一个方法是调用exit函数 exit(0); exit(EXIT_SUCCESS); exit(EXIT_FAILURE); 需要#include 头文件 Main中的r
24、eturn 和exit没有差异 Exit可以在任何地方 终止 程序,递归(recursive),一个函数调用本身,叫做递归调用 递归函数的特征: 一定有退出条件 每次递归调用,会让解决的问题向已知条件靠近 练习: 求阶乘 汉诺塔,变量和作用域,局部变量和全局变量,局部变量 自动存储期限 auto 块作用域 C99中的作用域可能更小 静态局部变量 静态局部变量具体有静态存储期限 静态局部变量依然是块作用域 形式参数 形式参数和局部变量唯一的不同是,形式参数在每次函数调用时会被自动初始化。 全局变量(外部变量) 静态存储期限 文件作用域,练习,用全局变量实现栈,全局变量的的利弊,少数几个函数共享大
25、量时,外部变量很有用 但一般我们尽量避免使用 维护代码时,改变全局变量需要检阅所有使用过该变量的函数 外部变量值赋值错误时,很验证确定是哪个函数出错 使用了外部变量的函数很难复用。,作用域,规则: 当程序块内的声明命名一个标识符,如果此标识符已经是可见的,新的声明会临时“隐藏”旧的声明,标识符获得了新的含义。 示例。,指针,指针变量,内存被分为字节,每人字节有唯一的地址 指针就是地址 指针变量可以存储指针 指针变量的声明 int *p; int* q; int * k; int i, j, a10, *p, *q; 每个指针变量只能指向一种特定类型的对象 。 取地址和间接寻址 & *,指针的基
26、本应用,指针赋值 指针作为参数 练习: 写一个函数,找出一个数组中的最大值和最小值 指针做为返回值 函数可以返回一个指针 永远不要返回指向自动变量的指针,指针和数组,指针的算术运算 加上整数 减去整数 两个指针相减 指针比较 用指针处理数组 *p+或*(p+) (*p)+ *+p或*(+p) +*p或+(*p) 用指针名作为数组 指针和多维数组,字符串,字符串字面量,双引号 延续字符串字面量 “I am string and go on.” “a string”another string” 存储字符串字面量 字符串以“空字符”结束.0 编译器会把字面量看做是char*类型的指针 根据指针和数
27、组的关系,对字符串可以取下标 char c = “abc”1; 字符串字面量和字符常量,字符串变量,C中,字符串由一维数组存储,但必需在结尾加0 初始化字符串变量 char date8 = “July 10”; char date = “July 10”; 字符数组和字符指针 char* date = “July 10”; char date = “July 10”;,字符串的输入输出,输出 printf(“%sn”, str); printf(“%.6sn”, str); printf(“%10.5sn”, str); puts(str); 输入 scanf(“%s”, str);/注意不需
28、要加& scanf永远不会读入空白符,而会跳过,换行、空格、制表等空白符会使scanf结束 gets()可以读取一行,而且不会在开始读字符之前跳过空白 gets()会持续读入直到换行符停止 gets()会忽略掉换行符,读取字符注意事项,scanf和puts读入字符串时无法判断数组是否已经填满 可以用%ns代替%s使用scnaf更安全 自定义函数逐个字符读字符串 int readLine(char str, int n); 使用getchar() const 和 字符串,使用C语言的字符串库,直接复制或比较字符串字符会失败 char str110, str210; str1 = “abc”;/E
29、rror str2 = str1;/Error char str310 = “abc”;/Ok #include strcpy 超出数组长度后的结果无法预料 strncpy strlen strcat strcmp,练习,显示一个月的提醒列表 用户输入一系列提醒,每条提醒都要有一个前缀来说明是一个月中的哪一天。当用户输入0时,程序显示出录入的全部提醒列表,并按日期排序。 程序运行如下: 输入一个提醒:24 莎莎的生日 输入一个提醒:5 8:00 和丽丽约好去晨练 输入一个提醒:26 和莎莎看电影盗梦空间 输入 一个提醒:5 晚上上英语课剑桥国际英语2 输入一个提醒:0 程序输出 : 日期 提醒
30、 8:00 和丽丽约好去晨练 晚上上英语课剑桥国际英语2 ,char remindersMAX_REMINDMSG_LEN+3; char day_str3, msg_strMSG_LEN+1; int day, i, j, num_remind = 0; for (;) if (num_remind = MAX_REMIND) printf(“- No space left -n“); break; printf(“Enter day and reminder: “); scanf(“%2d“, ,if (day = 0) break; sprintf(day_str, “%2d“, day
31、); read_line(msg_str, MSG_LEN); for (i = 0; i i; j-) strcpy(remindersj, remindersj-1); strcpy(remindersi, day_str); strcat(remindersi, msg_str); num_remind+; ,字符串惯用法,搜索字符串尾 strlen的实现 while(*s) s+; while(*s+); 复制字符串 strcat的实现 while(*p+ = *s+);,字符串数组和命令行参数,char strs8= ; char *strs = ; 命令行参数 int main(i
32、nt argc, char *argv); 练习: 从命令输入人名,判断是否是会员。,预处理器,预处理器,预处理器是一个小软件,它可以在编译前处理C程序 预处理器的行为是由预处理指令控制的 #include #define,预处理指令,宏定义 #define 文件包含 #include 条件编译 #if #ifdef #ifndef #elif #else #endif #undef,预处理指令使用规则,指令都以#开始 #不要求必须是行首,只要前面有空白符就行 指令的符号之间可以插入任意数量的空格或制表符 指令总是在第一个换行符处结束,除非明确地指出要延续 #define TEST aaa b
33、bb 指令可以出现在程序中的任何地方 注释可以与指令放在同一行,宏定义,简单的宏(对象式宏) #define 标准符 替换列表 简单宏定义主要用来定义那些“明示常量” #define STE_LEN 80 #define TRUE 1 #define FALSE 0 #define PI 3.1415926 #define CR r #define EOS 0 常见的错误定义 #define N = 10 #define N 100;,宏定义带来的好处,程序会更易读 程序会更易于修改 可以避免前后不至或键盘输入错误 可以对C语法做小的修改 #define BEGIN #defing END #
34、define LOOP for(;) 对类型重命名 #define BOOL int 控制条件编译 不要滥用宏-千奇百怪的helloworld,带参数的宏,宏函数(带参数的宏,函数式宏) #define 标识符(x1, x2) 替换列表 特别注意,标识符和(之间不能有空格 宏函数的替换 #define MAX(x, y) (x)(y)?(x):(y) #define IS_EVEN(n) (n)%2=0) #define TOUPPER(c) (a(c)y=t #define getchar() getc(stdin) 圆括号是必须的吗? 是的,练习,写一个宏函数,用它来验证一个日期是否合法,
35、#define ISLEAP(y) (y)%4=0&(y)%100!=0|(y)%400=0) #define ISSMALL(m) (m)=4|(m)=6|(m)=9|(m)=11) #define NORMAL(m) (ISSMALL(m)?30:31) #define DAYS(y,m) (m)=2?28+ISLEAP(y):NORMAL(m) #define IN(x,from,to) (x)=(from)&(x)1600&IN(m,1,12)&IN(d,1,DAYS(y,m),宏函数的优缺点,优点 程序可能会稍微快一点 宏更“通用” 宏函数中的参数不检查类型 缺点 编译后的代码通常会
36、变大 宏参数没有类型检查 无法用一个指针指向宏 宏可能会不止一次地计算它的参数 n = MAX(i+, j);,#和#运算符,#运算符 只能出现在宏函数中,其作用是将参数字符串化 #define PTINT_INT(n) printf(#n “=%dn”, n); #运算符 将两个记号“粘合”在一起,成为一个记号,其中一个记号一般为宏参数 #define ID(n) i#n int ID(1), ID(2), ID(3) Define GEN_MAC(type) type type#_max(type x, type y) return x y ? x : y;,预定义宏,_LINE_ 被编译
37、文件行号 _FILE_ 被编译文件名 _DATE_ 编译的日期 _TIME_ 编译的时间 _STDC_ 如果编译器是标准的,那么值为1,条件编译,#if和#endif #define DEBUG #if DEBUG printf(“Value of i: %dn”, i); printf(“Value ofj: %dn”, j); #endif #if 常量表达式 常量表达式为0时,预处理器删除#if和#endif中间的代码 #if会把没定义过的标准符视做为0,如果没有定义DEBUG,测试#if DEBUG会失败,但#if !DEBUG会成功,#ifdef #ifndef #elif #els
38、e #endif,编写大型程序,多个源文件和头文件,#include 在系统 位置找头文件 #include “文件名” 从当前目录找,找不到再到系统位置找 可以用宏来定义文件名 #if defined(IA32) #define CPU_FILE “ia32.h” #elif defined(IA64) #define CPU_FILE “ia64.h” #elif defined(ADM64) #define CPU_FILE “amd64.h” #endif #include CPU_FILE,使用头文件,共享宏定义 #include 共享函数原型 共享变量声明 Extern 保护头文件
39、 防止多次包含头文件 #ifndef BOOLEAN_H #define BOOLEAN_H #endif,练习,构建自己的工具库,构建多文件程序,编译和链接 Makefile,结构、联合和枚举,结构,结构变量 struct int number; char name10; int onHand; part1, part2; 初始化 112, “Disk”, 10 C99中的指定初始化 .number=112, .name=“Disk”, .onHand=10 操作结构 part1.number = 113; strcpy(part1.name, “Disk2”); part2=part1;,
40、结构类型,结构标记声明 struct part int number; char name10; int onHand; ; 用标记声明变量 struct part part1, part2; 使用typedef简化声明,练习,定义结构点,再定义结构rect(矩形),由两个点组成,计算每个点离(0,0)的距离,再计算矩形的面积 定义一个包含一年中各个月份的名字和每个月天数的表格,输出这个表格。用一个结构的数组保存结构,在每个结构中保存一个月的名字和天数。 January February March April May June July August September December,结
41、构作为参数和返回值,结构作为参数 结构变量的值传递 结构指针 结构作为 返回值 练习,结构的对齐与补齐,由于内存分配会将结构中的变量分配到内存的边界上,以方便访问。所以每个成员放的位置是从本身长度的倍数位开始放。但本身长度超过4时,以4计。此称为对齐。 char 1倍 short 2倍 int 4倍 double 4倍 整个结构变量的长度要保持内部最长成员(超过4以4计)的倍数。如果不够,则补齐。,位段(位域),用整个一个字节(char)去表示一个二进制变量(如一个on/off开关)看起来有点铺张浪费,而char已经是c中的可以独立分配与寻址的最小单位了。此时可使用域 MS-DOS操作系统中存
42、储日期的方式 struct file_date unsigned int day : 5; unsigned int month:4; unsigned int year : 7; ; 位域类型必须是int ,unsigned int或signed int,int 有些编译器上会有二义性。 不能取得域的地址。除此之外,域完全可以像其他变量一样使用。,联合,联合的操作几乎和结构相同 union int i; double d; u; 用联合提供数据的不同视角,枚举,枚举是一个常量整形值的列表。其中的值都是枚举常量,默认从零开始。可以定义时指定值,也可以多个枚举名表示同一个值。枚举常量可看成字面量
43、。 enum escapes BELL=a, BACKSPACE=b,TAB=t, NEWLINE=n, VTAB=v, RETURN=r; enum months JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, OCT, NOV, DEC; enum suit CLUBS, DIAMONDS, HEARTS,SPADES;,高级指针应用,关于指针,指针变量,指针是一个变量 野指针 防止野指针, 空指针,初始化为0,或NULL 打印时输出nil 没有办法检查一个指针是否是野指针,但可以检测出空指针 宁可用空指针,不用野指针 指针的加法,加一个整数 指针的
44、减法,减去一个整数,或减去一个指针 悬空指针,不要在函数中返回普通局部变量的地址。 返回一个字符串字面量是安全的,因为字符串字面量是静态分配的。 int* p, q是定义了一个指针,一个int型的变量,关于指针,我们总是通过操作指针来操作目标,比如swap函数 值传递和地址传递,本质上是值传递 数组做为参数传递时传的是指针 常量指针 const char* p = NULL;/指向的对象是常量 char const *p = NULL;/指针自己是常量 const char* const p = NULL;/到const的const指针 常量指针指不会通过指针修改目标数据,但并不代表这个指针中
45、的值不可改变。当然,也没有办法阻止别人修改目标数据。 在函数的参数中使用常量指针特别有用 int a = 1; const int c = 2; const int* p1 = /试图修改c的值,双指针,指向指针的指针 地址指针,指针的指针,二级指针 想间接地修改指针的指向时,可以用二级指针 示例 int str2int(const char* str, const char* q) int r = 0; while(isdigit(*str) r = r*10+(*str-0); +str; *q = str; return r; ,void指针,代表任何类型的指针,对此不能用*取变量,可以
46、用类型转换还原类型,函数指针,指向函数的指针 利用函数指针将函数做为参数传递 示例一、对一组数据做不同的处理 示例二、利用不同的排序规则排序,动态存储分配,在程序执行期间进行内存分配 动态内存分配适用于所有类型,但多数情况下是对字符串、数组、结构的分配 内存分配函数 malloc 分配但不对内存进行初始化 calloc 分配且对内存进行清零 realloc 调整先前分配的内存大小 以上内存分配函数声明在 头文件中 函数无法知道计划存储在内存块中的数据是什么类型,所以内存分配函数都返回void*类型 当内存分配失败时,函数返回空指针 if(p=NULL),动态分配字符串,分配空间 char* p
47、 = (char*)malloc(n+1) 赋值 strcpy(p, “abc”); 在函数中的动态分配 char* concat(char*s1, char*s2) char* r = malloc(strlen(s1)+strlen(s2)+1); strcpy(r, s1); strcat(r, s2); return r; ,动态分配数组,使用malloc分配 int* a=malloc(n*sizeof(int); 使用calloc分配 int* a = calloc(n, sizeof(int); struct pointint x;int y; *p; p = calloc(1,
48、 sizeof(struct point); realloc函数调整分配后的大小 当扩展内存时,不会对增加的内存初始化 失败时返回空指针,原内存中的数据不会改变 第一个参数是空指针时,如同调用malloc 如果以0作为第二个参数,会释放原内存块 小心 ,realloc可能会将内存块移到别的地方 去。,释放存储空间,动态分配的空间都来自于称为“堆”的内存池中。 不可再访问而又没释放的内存称为“垃圾”,存在垃圾的程序存在“内存泄漏”现象 free函数 每个c程序员都需要自己收回各自的不用内存。 参数为空指针不起作用,如非分配函数返回的地址结果未定义 可能会造成悬空指针问题 试图访问或修改释放过的内存结果未定义,输入输出,格式化输入,printf,fprintf fprintf(stderr, “Error: data file cant be opened.n”