目录
第一章 C语言概述
第二章 数据类型 运算符 表达式
第三章 简单的C程序设计
第四章 选择循环结构
第五章 数组
第六章 函数
第七章 编译预处理
第八章 指针
第九章 结构体与共用体
第一章 C语言概述
C语言的特点:
- 语言简洁、紧凑;使用方便、灵活; (37个关键字,9种控制语句)
- 运算符丰富; (34种运算符)
- 具有丰富的数据结构;
- 具有结构化的控制语句;
- 语法限制不太严格,程序设计自由度大;(对数组下标越界不进行检查;整型量与字符型数据以及逻辑型数据可以通用)
- C语言允许直接访问物理地址,能进行位(bit)操作,能实现汇编语言的大部分功能,可以直接访问硬件;
- 用C语言写的程序可移植性好;
- 生成代码质量高,程序执行效率高。
37个关键字
数据类型 | int(基本整型) short(短整型) long(长整型) signed(有符号) unsigned(无符号) char(字符型) float(单精度实型) double(双精度实型) struct(结构体) union(共用体) enum(枚举类型) void(空类型) |
控制语句 | 条件语句:if、 else、goto 循环语句:for、do、while、break、continue 开关语句:switch、case、default、return |
存储类型 | auto、extern、register、static |
其它关键字 | const、sizeof、typedef volatile、inline、restrict、_bool、_Complex、_Imaginary |
9种控制语句
if()…else、for()、while()、do…while()、continue、break、switch、return、goto
34种运算符
……
② #include<stdio.h>为头文件,<stdio.h>为标准输入输出库。
③ /* */(多行注释)、//(单行注释),对编译和运行不起作用,可加在程序的任意位置;在程序进行编译预处理时将每个注释替换成一个空格;在字符串中不做为注释使用。
④ C语言的基本结构和常见特性:
- C程序是由函数构成的。
- 一个函数由两部分构成:函数头和函数体。函数头:函数的第一行,函数体:{}的部分。
- 每个语句和定义的最后必须有分号,分号是C语句必要的组成部分,函数首部后面不能跟分号,因为不是C语句。
- 一个C程序总是从main函数开始执行的,而不论main函数在整个程序中的位置如何。
- C程序的构成要素:函数和变量。
⑤ C语言的字符集:
- 字母:包括26个小写字母和26个大写字母(52个字符)
- 数字:0-9(10个字符)
- 空字符:空格符、制表符、换行符统称为空字符。只在字符常量和字符串常量中起作用,在其他地方不起作用,可增强程序的清晰性和可读性。
- 标点和特殊字符:逗号、分号等。
⑥ C语言的标识符:
- 命名规则:以字母或下划线开头,后面可以跟字母、数字、下划线。
- 分类:关键字、预定义标识符、用户标识符。
- 标识符应先定义后使用,且遵循见名知意的原则。
⑦ C语言的关键字:特定含义的标识符,由C语言集成环境定义和使用的,用户不能更改,只能使用。
⑧ 程序:一组计算机能够识别和执行的指令;
程序设计:给出解决特定问题的程序,是软件构造活动中的重要部分;
计算机语言:为使人与计算机之间传递信息;
计算机语言的发展史:机器语言=>汇编语言=>高级语言;
程序设计的任务:问题分析、设计算法、编写程序、对源程序编译链接、运行程序分析结果、写文档;
程序对数据的描述:数据结构、对操作的描述:算法;
计算机算法分为:数值运算算法和非数值运算算法;
算法的表示:自然语言、传统流程图、结构化流程图、伪代码;
流程图的组成部分:
表示相应操作的框;
带箭头的流程线;
框内外必要的文字说明;
第二章 数据类型 运算符 表达式
一、数据类型
① 基本含义:
- 数据类型规定了该类型中数据的取值范围;
- 数据类型定义了一个运算集,即不同类型的数据拥有不同的运算关系;
- 数据类型定义了数据在计算机内的存储形式以及在书写中的表示方式。
② C语言规定,任何数据都必须有明确的数据类型。(基于VC 6.0)
数据 类型 | 基本类型 | 数值类型 | 整型 | 短整型(short) (2个字节) |
基本整型(int) (4个字节) | ||||
长整型(long) (4个字节) | ||||
有符号整型(signed int) (4个字节) | ||||
无符号整型(unsigned int) (4个字节) | ||||
实型 | 单精度实型(float) (4个字节) | |||
双精度实型(double) (8个字节) | ||||
长双精度实型(long double)(16个字节) | ||||
字符类型 | 字符型 (char) (1个字节) | |||
无符号字符型(unsigned char)(1个字节) | ||||
构造类型 | 数组([]) | |||
结构体(struct) | ||||
共用体(union) | ||||
枚举类型(enum) | ||||
指针类型(*) | ||||
空类型(void) |
二、常量与变量
① 常量定义:其值不能被改变的量。
常量分类:整型常量、实型常量、字符常量(普通字符和转义字符)、字符串常量、符号常量;
注意:常量并不占用内存,在程序运行时它作为操作对象直接出现在运算器的各种寄存器中,使用时可以不需要类型说明。
② 变量定义:其值可以改变的量(先定义后使用)。
常变量:在变量定义的前面加上const,常变量有类型、占存储单元、可以被引用、但其值不可变。
变量名:变量名实际上是以一个名字代表的一个存储地址。在对程序进行编译链接时由编译系统给每一个变量分配对应的内存地址。从变量中取值,实际上是通过变量名找到相应的内存地址,从该存储单元中读取数据。
变量的定义形式:(类型名+变量名);
- 变量需要初始值;
- 变量名表可以是一个或多个标识符名,中间用逗号隔开。
1、整型数据
① 整型常量:(整数的末尾加上大写字母L或小写字母l,表示长整型;加上U或者u代表无符号整型)
- 十进制->有效的十进制数字串;
- 八进制->以0开头的八进制数字串;
- 十六进制->以0X或0x开头的十六进制数字串。
② 整型变量:
变量在内存中的存储形式:
- 整型 --> 补码(负数补码:数的绝对值的二进制进行按位取反后加1)
- 实型 --> 指数
- 字符型-> ASCII码
③有符号的整型数据存储单元中的最高位代表符号位(0为正,1为负)
2、实型数据
①实型常量:(实数的末尾加上大写字母F或小写字母f,表示float类型)
表达形式:十进制小数形式和指数形式。
合法形式为:e前e后必有数,e后必为整数。
②实型变量:
实型变量有一定的有效位数,不同的类型有效位数不同,计算机不能准确无误的表示实型变量。
3、字符型数据
① 字符常量的特点:
- 字符常量只能用单引号括起,不能用双括号或其他括号;
- 字符常量只能是单个字符,不能是字符串;
- 字符可以是字符集中的任意字符。
② 字符变量:字符型变量(char)用来存放字符常量,注意一个字符变量只能存放一个字符常量。
③ C语言规定字符型数据和整型数据之间可以通用。且可以进行算术运算。
4、 字符串常量
- 字符串常量用双引号括起;
- 字符串常量可以含有一个或多个字符;
- 可以把一个字符常量赋给一个字符变量,但不能把一个字符串常量赋给一个字符变量;
- 字符串常量占的内存字节数等于字符串中的字节数加1。
- 字符串结束标志 \0
5、符号常量
#define 将符号常量定义为其后的常量值,使用前必须先定义。(含义清楚、一概全改)
一般形式为 : #define 符号常量名 常量
一般情况下,符号常量名大写,变量名小写。符号常量的值不能被改变,也不能被赋值。
三、运算符与表达式
1、运算符:
①分类:
按功能:算术运算符(+、-、*、/、%、++、--)、关系运算符(>、<、==、>=、<=、!=)、
逻辑运算符(&&、||、!)、逗号运算符(,)、赋值运算符(=)、位运算符(<<、>>、~、|、^、&)。
按操作数的个数:单目运算符、双目运算符、三目运算符。
2、表达式:
类型:算术表达式、赋值表达式、关系表达式、逻辑表达式、条件表达式、逗号表达式。
注意:sizeof ()是计算对象所占的字节个数的运算符;通常用来查看变量或结构体等所占的字节个数。
第三章 简单的C程序设计
一、数据的输出与输入
1、转义字符及其表示的作用
字符形式 | 含义 | ASCII代码 |
\ddd | 1到3位八进制数所代表的字符(范围0-0377) | 0-255 |
\xhh | 1到2位十六进制数所代表的字符(范围0-0xff) | 0-255 |
\n | 换行,将当前位置移到下一行的开头(字符无法显示) | 10 |
\t | 水平制表(字符无法显示) | 9 |
\r | 回车,将当前位置移到本行的开头(字符无法显示) | 13 |
\f | 换页,将当前位置移到下一页的开头(字符无法显示) | 12 |
\b | 退格,将当前位置移到前一列(字符无法显示) | 8 |
\0 | 字符串的结束标志(字符无法显示) | 0 |
\\ | 反斜杠字符,输出\ | 92 |
\` | 单引号字符,输出` | 39 |
\`` | 双引号字符,输出`` | 34 |
2、printf()的修饰符
修饰符 | 意义 |
标志 | 五种标志:+、-、空格、#、0 |
digit(s) 数字 | 字段宽度的最小值。如果该字段不能容纳要打印的数或字符串,系统自动扩展 (系统自动扩展到相应的字段,不是出错)。 |
.digit(s) 小数点后的数字 | %f :将要在小数点右边打印的数字的位数(小数位数); %s :打印的字符的个数; 整型格式:是将要打印的数字的最小位数,如果必要,用前导零达到这个位数 |
3、printf ()的标志
标志 | 意义 |
+与- | “+”右对齐,“-”左对齐。 |
+与- | 有符号数若为正数,则显示加号,若为负号,则显示减号。 |
(空格) | 有符号数若为正数,则显示时带前导空格(不显示正号),若为负,则显示负号。 |
# | %#o:输出八进制前导0 %#x/%#X:输出十六进制前导0x/0X |
0 | 对所有数字格式,用前导零填充字符宽度,如果出现-标志或指定了精度(对于整数)则忽略该标志。 |
4、scanf()格式修饰符
格式修饰符 | 意义 |
* | 本输入项读入后略去 实例:scanf("%2d%*3d%2d",&a,&b);输入1234567 则a的值为12,b的值为67,中间的345被略去。 |
digit(s) | 最大字段宽度。在达到最大字段宽度或遇到第一个空格字符时停止对输入项的读取。 |
5、字符数据的输入与输出
① getchar()从键盘读取一个字符和putchar() 向显示屏显示一个字符,读取时要按回车键才能将字符显示在显示屏上。两个函数一次只能对一个字符进行操作。
(putchar()参数为字符变量或常量、整型变量或常量,其值要在ASCII码范围内,getchar()无参数)
② getch()与getche()函数也是从键盘读取一个字符,但是不需等待回车键确认,getch()读入的字符不回显到显示屏上,而getche()要回显。
③ puts()和gets()函数对多个字符进行操作。
6、注意事项
① 如果在字符控制串中除了格式说明符以外还有其他字符,则在输入数据时应输入与这些字符相同的字符。
② 用%c格式输入字符时,空格和转义字符都作为有效字符输入。
③ 输入数据时,遇到以下情况时认为当前数据输入结束:
- 遇到空格键、字符回车键、制表键;
- 遇到满足最大字段宽度时认为结束;
- 遇到非法输入时;
- 当使用多个scanf()函数给多个字符型变量赋值时,注意输入的方法。
第四章 选择循环结构
一、选择结构
1、选择结构形式
if | if(表达式) 语句; |
if else | if(表达式) 语句1; else 语句2; |
if-else if | if(表达式) 语句1; else if(表达式) 语句2; else 语句3; |
switch-default | switch(表达式) { case 常量表达式 1:语句1;break; case 常量表达式 2:语句2;break; default: 语句3;break; } |
2、算术、逻辑、关系、赋值运算符的优先级:由高到低
! | +、-、*、/、% | >、<、>=、<=、==、!- | && | || | = |
一般情况下,单目运算符的优先级要高于双目运算符。
与(&&)遇0后不再计算,或(||)遇1后不再计算。(重点)
3、注意事项:
① if语句:
- 在3种形式的if语句中,在if关键字之后均为表达式;
- 在if语句中条件判断表达式必须用括号()括起来,在语句之后必须加分号。
- 在if语句的3种形式中,所有语句应为单个语句,如果要想在满足条件时执行多个语句,则必须把多个语句用{}括起来组成一个复合语句,在 } 之后一定不能有分号。
② switch语句:
- 表达式应为整型、字符型、枚举型表达式。
- 在case后各常量表达式的值不能相同,否则会出现错误。
- 在case后允许有多个语句且不用{}括起来。
- 若每个分支后都有break语句,则各case和default子句的先后顺序可以变动,而不会影响程序执行结果。
- default子语句可以省略不写。
③ 在if语句的嵌套中:C语言规定,else总是与它前面最近的未配对的if配对。
二、循环结构
1、循环结构形式
While 当型 循环结构 | while(表达式) { 循环体语句; } | 特点:先判断,后执行 |
do while 直到型 循环结构 | do { 循环体语句; } while(表达式); | 特点:先执行,后判断 |
for | for(表达式1;表达式2;表达式3) { 循环体语句; } | 表达式1:赋值表达式 表达式2:循环条件 表达式3:循环变量的变化方式 |
2、注意事项:
① while语句:
- while语句中的表达式一般是关系表达式或逻辑表达式,只要表达式的值为真(非0)即可继续循环。
- 在语法上,循环体应该是一条语句。如需要包括有一个以上的语句,则必须用{}括起来,组成复合语句。
- 在循环体中应包含使循环趋于结束的语句,以避免出现死循环。
- 允许while语句的循环体又是while语句,从而形成循环的嵌套。
② do-while语句:
- 在if语句和while语句中,表达式后面都不能加分号,而在do-while语句的表达式后面则必须加分号,因为这是一条完整语句结束的标志。
- 在do和while之间的循环体由多个语句组成时,也必须用{}括起来组成一个复合语句。
- do-while和while语句相互替换时,要注意修改循环条件。
- do-while语句也可以组成多重循环,而且也可以和while语句相互嵌套。
③ for语句:
- 在循环变量已赋值时,可省略表达式1。
- 如省去表达式2,则不对循环条件进行判断,将造成无限循环。一般可以采用在循环体内设置转移语句break来跳出循环。
- 如省去表达式3,则循环变量的值在for语句本身不变化,也会造成死循环。这次应在循环体内设法修改循环变量的值,以结束循环。
- for语句中的各表达式都可省略,但分号间隔符不能少。
- 循环体可以是空语句。
- for语句也可以与while,do while 语句相互嵌套,构成多重循环。
3、转移语句:break、continue、goto、return.
① break:
- break语句用在switch语句或循环语句中,其作用是跳出switch语句或跳出本层循环,转去执行后面的程序。
- 转移方向:执行后面的程序,由于转移方向明确,所以不需要语句标号与之配合。
② continue:
- continue语句只能用在循环体中,其作用是结束本次循环,即不再执行循环体中continue之后的语句,转入下一个循环。
- continue语句只能结束本层本次循环体的执行,并不跳出循环。
③ goto:
- 无条件转移语句。(一般情况下不使用)
④ return:
- 只能出现在被调函数中,用于返回主调函数
第五章 数组
一、一维数组
1、定义:只有一个下标的数组元素所组成的数组(先定义后使用)。
一般形式: 类型说明符 数组名[常量表达式]
类型说明符:任意一种基本数据类型或构造数据类型;
数组名:用户定义的标识符,表示数组在内存中的首地址,是地址常量,不能改变;
常量表达式:数组元素个数(数组的长度);
[]:数组运算符,是单目运算符,优先级别最高。
定义数组注意事项:
- 数组名的命名规则和变量名相同,遵循标识符规则,但不能与其他变量名相同(易错点);
- 常量表达式可以是符号常量或常量,但不允许为变量;且必须用[]括起;
- 常量表达式表示元素的个数,从0开始计算,即下标变量比数组的常量表达式少1;
- C语言不能对数组进行动态定义。
2、引用:
一般表达式:数组名[下标]
下标只能是整型常量或整型表达式,若为小数,系统自动取整。
C语言规定只能逐个引用下标变量,而不能一次引用整个数组。
3、初始化:见小本笔记
二、二维数组
1、定义:有两个下标的数组元素所组成的数组(先定义后使用)。
一般形式: 类型说明符 数组名[常量表达式1][ 常量表达式2]
常量表达式1:第一维(称为行)下标的长度;
常量表达式2:第二维(称为列)下标的长度。
C语言中二维数组是按行排列的,允许使用多维数组,其排列顺序:第一维的下标变量最慢,最右边的下标变化最快。
2、引用:
一般表达式: 数组名[下标][下标]
由于下标变量和数组引用的形式是一样的,使用时应特别注意。
3、初始化:见小本笔记
4、存储方式:按行存储。
三、字符数组
1、定义:一种用来存放和处理字符型数据的数组变量,字符数组中一个元素存放一个字符。
一般形式: char 数组名[常量表达式];
char 数组名[常量表达式1][ 常量表达式2];
2、初始化:
① 逐个给数组中的各元素赋初值: 全部赋值:可省略数组长度
部分赋值:后面的数组元素自动赋空字符 \0
注意:若括号中提供的初值个数大于数组长度,系统按语法错误处理。
② 用字符串直接给字符数组赋初值:
C语言中,将字符串用字符数组来存放;
C语言中,规定了一个“字符串结束标志”,以字符‘\0’代表。
注意:字符串方式赋初值比逐个字符赋初值要多占一个字节,用来存放字符串结束标志‘\0’。
例如: char ch[10]={"C program"}; 或 char ch[]={"C program"};
char ch[10]= "C program"; 或 char ch[]= "C program";
3、字符串的输入与输出
- 单个字符: “%c”;
- 整个字符串:“%s”; %.ns:输出字符的个数
注意:
- 用scanf()函数输入字符串时,字符串中不能包含空格,否则,系统将空格作为输出时字符串的结束标志;输入项是字符数组名时,不需要加地址符&;
- 用printf()函数输出字符不包括结束符‘\0’;如果数组长度大于字符串的实际长度,也只输出到遇‘\0’结束;
- 如果一个字符串数组中包含一个以上‘\0’,则遇到第一个‘\0’时输出就结束。
- 用%s格式符输出字符串时,printf()函数中的输出项是字符数组名。
4、字符串处理函数
用于输入输出的字符串函数应包含在头文件“stdio.h”中;
用于比较、拷贝、合并、测长度等用途的字符串函数则包含在头文件“string.h”.
- 字符串输出函数:
调用形式: puts (字符数组名);
- 字符串输入函数:
调用形式:gets(字符数组名); 只有回车是字符串结束标志。
- 字符串长度测量函数:(string length)
调用形式: strlen(字符串);
函数功能:测量指定字符串的函数(字符串结束标志前的所有字符的个数,不包括 \0)。
返回值:字符串的长度(不包括\0)。
- 字符串连接函数:(string catenate)
调用形式: strcat (字符数组名1,字符数组名2);
函数功能:取消字符数组1中的字符串结束标志‘\0’,把字符数组2中的字符串连接到字符数组1中的字符串后面。
返回值:字符数组1的首地址。字符数组1的长度要足够大。
- 字符串拷贝函数:(string copy)
调用形式: strcpy(字符数组名,字符串);
字符串为字符串常量或已赋值的字符数组名;
函数功能:将字符串存入到字符数组;
返回值:字符数组的首地址。
- 字符串比较函数:(string compare)
调用形式: strcmp (字符串1,字符串2);
字符串1和字符串2为字符串常量或已赋值的字符数组名。
函数功能:按照ASCII码的顺序比较两个字符串(自左至右逐个比较),并由函数返回值返回比较结果。
返回值:若字符串1<字符串2, 返回值为负整数
若字符串1=字符串2,返回值为0;
若字符串1>字符串2,返回值为正整数;
- 字符串小写转换成大写函数: (string uppercase)
调用形式: strupr (字符串);
字符串为字符串常量或已赋值的字符数组名。
函数功能:将字符串中所有小写字母均转换成大写字母;
返回值:替换后字符串的首地址。
- 字符串大写转换成小写函数:(string lowercase)
调用形式: strlwr (字符串);
字符串为字符串常量或已赋值的字符数组名。
函数功能:将字符串中所有大写字母均转换成小写字母;
返回值:替换后字符串的首地址。
第六章 函数
一、函数的定义及调用
1、函数定义:
包含内容:指定函数的名字,以便以后按名调用;
指定函数的类型,即函数返回值的类型;
指定函数参数的名字和类型,以便在调用时向它们传递参数;
指定函数应当完成什么操作,即函数的功能。
注意:①库函数由C语言系统提供,用户无需定义,在调用函数之前也无需声明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。
②用户自定义的函数,即用户自己编写出来实现特定功能的函数,使用前需要声明。
③函数不可以嵌套定义,但是可以嵌套调用。
2、函数调用:
① 函数调用的三种方式:函数语句、函数表达式、函数参数。
函数语句: printstart();
函数表达式: c=2*max(a, b);
函数参数: m=max (a, max (b, c));
②调用过程:
- 在定义函数中指定的形参,在未出现函数调用时,它们并不占用存储单元,在发生函数调用时,函数的形参才被临时分配内存单元;
- 将实参的值传递给对应的形参;
- 在执行函数期间,由于形参已经有值,就可以利用形参进行有关的运算;
- 通过return语句将函数值带回主调函数;
- 调用结束,形参单元被释放。
③ 调用即在定义后面加上;号。
④ 主调函数调用被调函数的条件:
1:被调用的函数必须是已经存在的;
2:如果调用库函数,一般还应该在本程序开头用#include 命令将调用有关库函数时用到的信息“包含”到本程序中。
3: 在调用自定义函数时,若该函数与调用它的函数在同一个文件中,如果主调函数在被调函数之前,一般还应该在主调函数对被调函数的原型作相应的声明。如果被调函数的定义出现在主调函数之前,可以不加声明。如果已在所有函数定义之前且在函数外部做了声明,则在各个主调函数不必对所有的函数进行声明。
⑤嵌套调用的定义:在调用一个函数的过程中,该函数又调用另一函数。
⑥递归调用的定义:直接或间接的调用自己。
二、函数参数和函数值
1、函数参数:
①实际参数:在调用一个函数时,也就是真正使用一个函数时,函数名后面括号中的参数;
形式参数:在定义函数时,函数名后面括号中的变量名称为“形式参数”;在函数调用之后,传递给函数的值将被复制到这些形式参数中。
② 在调用有参函数时,主调函数与被调函数间涉及数据的传递,C语言中数据的传递是通过实际参数与形式参数来实现的。(单向值传递,双向址传递)
③ 形参变量在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。形参只有在函数内部有效,函数调用结束后则不能再使用该形参变量。
④ 实参可以是常量、变量、表达式,无论实参是何种类型的量,在进行函数调用时,必须有明确的值,以便把值赋给形参。形参和实参在数量、类型、顺序上必须保持一致,否则系统会显示出错信息。实参与形参之间进行单向值传递,由实参赋给形参,而不能由形参赋给实参,在函数调用过程中,形参的值发生变化,而实参的值不会发生变化。在进行双向址传递时,形参数据的改变可能会导致实参的改变。
2、函数的返回值:
① 函数的返回值是通过函数中的return语句来实现的,return语句将被调函数中的一个确定的值带回主调函数中去。
② 如果函数需要返回值,则使用return语句,若不需要返回值,则不需要。
③ 返回值类型应与函数定义中函数值的类型一致。(不一致时函数值类型决定返回值类型)
④ 不返回函数值的函数,可明确定义为“空类型”,类型说明符为void.即void函数无返回值。
⑤ 如果函数值为整型,在函数定义时可省去类型说明。
三、数组作为函数参数
1、数组元素:(值传递)
数组元素做函数参数采用值传递的方式,形参值的改变不会影响实参值的变化。
2、数组名:(址传递)
① 用数组名做函数参数时,应该在主调函数和被调函数中分别定义数组。
② 实参数组与形参数组类型应该一致。
③ 在函数调用时系统对形参数组的大小不做检查,只是将实参数组的首地址传给形参数组。不指定数组大小时,数组名后面需要一个方括号。
④ 数组名做函数参数时传递的是数组的首地址,所以形参数组的值发生变化会影响实参数组的值。
四、局部变量与全局变量
1、作用域的定义:变量的作用范围。
2、变量的作用域分类:局部变量和全局变量。
3、局部变量:
① 定义:在一个函数内部定义,只有在本函数内才能使用,在本函数外使用是非法的。
② 注意事项:
- 主函数中定义的变量也只在主函数中有效,而不是因为在主函数中定义而在整个程序中有效,主函数也不能使用其它函数中定义的变量。
- 不同函数中可以使用相同名字的局部变量,它们代表不同的变量,互不干扰。
- 形参也是局部变量。
- 在一个函数的内部,可以在复合语句中定义变量,定义的变量只在本复合语句中有效。
4、全局变量:
① 定义:在函数的外部定义,属于一个源程序文件,其作用域是整个源程序。在函数中使用全局变量一般应做全局变量声明。
② 注意事项:
- 全局变量的作用是增加函数间数据联系的渠道。
- 不在必要时不要使用全局变量。
- 全局变量在程序的执行过程中始终都占据内存单元,而不是在使用时才开辟内存单元。
- 使函数间的通用性降低,因函数在执行时要依赖于其所在的外部变量。
③ 在同一文件中,全局变量与局部变量同名,全局变量被屏蔽。
五、动态存储变量和静态存储变量
局部变量 | 全局变量 | ||||
存储类别 | auto | register | 局部static | 全局static | 外部extern |
作用域 | 定义变量的函数或复合语句内 | 本文件 | 本文件或其它文件 | ||
存储方式 | 动态存储方式 | 静态存储方式 | |||
存储区 | 动态存储区 | 寄存器 | 静态存储区 | ||
生存期 | 函数调用开始至结束 | 程序整个运行期间 | |||
赋初值 | 每次函数调用时 | 编译时赋值,只赋一次 | |||
未赋初值 | 不确定 | 自动赋初值0(数值型)或空字符‘\0’(字符型) |
1、变量的存储方式:静态存储方式和动态存储方式。(生存期不同)
2、静态存储方式定义:指在程序运行期间分配固定的存储空间的方式。在定义变量时就分配存储单元并一直保持不变。
动态存储方式定义:在程序运行期间根据需要进行动态地分配存储空间的方式。
3、自动变量(auto)的特点:
- 自动变量作用域仅限于定义该变量的结构内。在函数中定义的自动变量只在该函数内有效。
- 自动变量属于动态存储方式,只有定义它的函数被调用时才给它分配存储单元,开始它的生存期。函数调用结束时,释放存储空间。
- 自动变量的作用域与生存周期都限于本结构内,所以不同的结构中允许使用同名的自动变量而不会混淆。
4、动态寄存器变量(register)的特点:
- 只有局部自动变量和形式参数可以作为寄存器变量。在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束释放寄存器。
- 不能一次定义太多的寄存器变量,当寄存器个数不够时,会把寄存器变量转换为自动变量。
- 局部静态变量不能定义为寄存器变量。
5、静态局部变量(static)的特点:
- 在函数内定义,属于静态存储类别,在静态存储区内分配存储单元。在整个程序运行期间都不释放。
- 只在编译时赋一次初值,以后每次调用时都不再重新赋值而只保留上次函数调用结束时的值。
- 在定义局部变量时不赋初值,对静态局部变量来说,编译时自动赋初值(数值赋0,字符赋空字符\0)。而对自动变量不赋值,其值是不确定的。
- 虽然静态局部变量在函数调用结束后仍然存在,但其它函数不能引用,但若再次调用该函数时,它又可继续使用,而且保存了前次被调用后留下的值。
6、静态外部变量(static)的特点:
- 在全局变量前加上static可以使此全局变量只在本文件中有效而不能被其它文件引用,使程序的通用性提高。
7、静态外部变量(extern)的特点:
- 用extern来声明外部变量,以扩展外部变量的作用域。在一个文件中定义外部变量,在其它文件中可以引用。
六、内部函数与外部函数
1、分类依据:能否被其它源程序文件所调用。
2、内部函数:
① 定义:一个函数只能被本文件中的其它函数所调用,而不能被其它源文件中的函数所调用。
② 形式:
static 类型标识符 函数名(形参表)
③特点:
使用内部函数,可以使函数只局限于所在文件,如果在不同的文件中有同名的内部函数,互不干扰。
3、外部函数:
① 定义:可以被其它的文件所调用。
② 形式:
extern 类型标识符 函数名(形参表)
③ 特点:
可供其他文件所调用,声明的形式即在函数原型前面加上关键字extern。
第七章 编译预处理
一、宏定义
分类:无参宏定义和有参宏定义。
1.无参宏定义:
① 一般形式:
#define 标识符 字符串
#: 代表其后为预处理命令;
#define 为宏定义命令,会在编译预处理时将宏名替换成字符串
② 无参宏定义说明:
- 使用宏名代替一个字符串,在宏展开时又以该字符串代替宏名;
- 宏定义不是C语句,不必在行末加分号;
- #define 命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束,通常放在开头部分;
- 可以用#undef命令终止宏定义的作用域;
- 在进行宏定义时,可以引用已定义的宏名,可以层层置换;
- 对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换;
- 宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只做字符替换,不分配内存空间。
2.有参宏定义:
① 一般形式:
#define 宏名(参数表) 字符串
② 有参宏定义说明:
- 对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参;
- 在宏定义时,在宏名与带参数的括弧之间不能有空格出现,否则将空格以后的字符都作为替代字符串的一部分;
- 在宏定义中的形参是标识符,而宏调用中的实参可以是表达式;
- 在宏定义中,字符串内的形参通常要用括号括起来,以避免出错;
- 带参的宏和带参的函数很相似,但有本质上的不同,把同一表达式用宏处理和函数处理两者的结果有可能是不同的;
- 宏定义也可以用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。
二、文件包含处理
1.定义:指一个源文件可以将另外一个源文件的全部内容包含进来。
C语言中使用#include命令来实现文件包含的操作。
2.文件包含说明:
- 一个#include命令只能指定一个被包含文件,如果要包含多个文件,要用多个#include命令。
- 在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。
- #include命令中,文件名可以用双撇号或尖括号括起来。
- 被包含文件与其所在的文件,在预编译后已成为同一文件,因此,被包含文件中含有全局静态变量,在所在的文件中也有效,不必用extern声明。
三、条件编译
1.定义:实现对源程序中的一部分文件在满足一定条件才进行编译称为条件编译。
2.形式:
①作用: 当指定的标识符已经被#define命令定义过,则在编译时编译程序段1,否则编译程序段2。
②作用:当指定的标识符未被#define命令定义过,则在编译时编译程序段1,否则编译程序段2。
③作用:当指定表达式值为真时就编译程序段1,否则编译程序段2.
第八章 指针
一、变量的指针和指向变量的指针
1、相关概念
① 内存单元:内存的基本单元是字节,把一个字节看作是一个内存单元,不 同的数据类型所占的内存单元不同。
② 地址:内存中每一个字节都有一个编号,就是“地址”。 对每一个变量,它在内存中都有一个存储位置,这个位置就是该变量的地址,对变量值的存取是通过地址进行。在C语言中这个地址被形象化地称为“指针”。
③ 指针: 指针:一个变量的地址(是地址常量)。
指针变量:存放另一变量的地址(即指针)。
2、指针变量的相关操作
(1).定义: 类型说明符 *变量名;
类型说明符:用来指定指针变量可以指向的变量的类型;
*:表示该变量为指针类型;
变量名:定义的指针变量名;
(2).赋值: (指针变量必须先赋值后使用)
指针变量初始化时:(int a,*p=&a;)
使用赋值语句: (int a,*p; p=&a;)
(3).引用: *指针变量;
指针变量说明中:“*”是类型说明符,表示其后的变量是指针变量;
表达式中:“*”是一个运算符,表示取指针所指向的值;
(4).运算:
赋值运算:赋的值可以是指针变量的值、数组的首地址、字符串的首地址、函数的入口地址;
加减运算:(只能用于数组,且自身运算)即只能对指向数组的指针变量进行;
指向同一数组的两个指针变量相减有意义,相加无意义。
关系运算:指向同一数组的两个指针变量进行关系运算可表示它们所代表的地址间的关系;
空运算:对指针变量赋空值和不赋值是不同的,不赋值时指针为野指针,不能使用;赋空值 后则可以使用,只是它不指向具体的变量。
二、数组的指针和指向数组的指针
1.数组的指针:指数组的起始地址。
数组元素的指针:指数组元素的地址。
2.引用数组元素:即使用数组中的元素。
例:int a[10], *p; p=a;
下标法:a[i];
指针法:*(p+i);(占内存少,运行速度快);
数组名法:*(a+i);
指针下标法:p[i];
3.使用指针变量时要注意的问题:(int a[10], *p; p=a;)
⑴ p++: 合法,因为p是指针变量,++只能用于变量。
a++: 不合法,因为a是数组名,其值是数组元素的首地址(常量),程序运行期间值固定不变。
⑵ 指针变量使用时要注意当前指向。
⑶ 指针变量p可以指向数组以后的内存单元而编译不作检查。
⑷ 指针变量运算时要注意的几个问题:
① p++; *p; 使p指向下一元素a[1],则*p值即为a[1]。
② ++ 和 * 优先级同为2,结合性从右向左,则*p++等价于*(p++),即先得到p指向的变量的值*p,再使p=p+1。
③ *(p++)与*(++p):
*(p++)先取*p值,再使p加1; 若p初值&a[0],则*(p++)得a[0]值;
*(++p)是p先加1,再取*p; 若p初值&a[0],则*(++p)得a[1]值。
④ (*p)++ 表示p所指向的元素的值加1。
⑤ 如果p指向a[i]元素,则:
*(p++)相当于a[i++],先对p进行 * 运算,再使p自加。
*(p--)相当于a[i--],先对p进行 * 运算,再使p自减。
*(++p)相当于a[++i],先使p自加,再作 * 运算。
*(--p)相当于a[--i],先使p自减,再作 * 运算。
3.数组与指针的关系:
① 指针:一个单独变量,只是指向了其他变量的地址;
数组:一串元素序列并且真实的存储元素内容,它的数组名可以相当一个指针,代表数组的首地址;
sizeof解析:p是一个指针变量,其内容存储的是4个字节的地址;而数组名a并不是一个变量,它是一个常量的地址,sizeof将其视为整个数组的代表,因此计算的时候会计算整个数组的大小。
4.指向二维数组的指针变量
说明形式: 类型说明符 (*指针变量名)[长度] (数组指针)
含义:int (*p)[n]; p为指向含n个元素的一维数组的指针变量
类型说明符:所指数组的数据类型;
*:表示其后的变量是指针类型;
长度:二维数组的列数。
例:int (*ptr)[M] : 一个指向具有M个整型元素的一维数组的指针
int *prt [M] : 具有M个指针元素的一维指针数组,每个元素都只能指向整型量
5. 数组元素为指针类型的指针数组
说明形式: 类型说明符 *指针变量名[长度] (指针数组)
含义:int *p[n];定义由n个指向整型数据的指针元素组成的指针数组p
类型说明符:所指数组的数据类型;
*[]:表示其后的变量是指针数组类型;
长度:指向数组的长度。
6.用数组做函数参数有如下四种情况:
7.二维数组
三、字符串的指针和指向字符串的指针
1.可以用字符数组来存放字符串,字符数组的每个元素存放一个字符,且以字符串结束标志‘\0’结尾,所以可以通过字符数组名输入输出一个字符串。字符串指针变量的定义说明与指向字符变量的指针变量说明是相同的,只能按照对指针变量的赋值不同来区别。
2.字符指针与字符数组的区别
(1)字符数组由若干个元素组成,每个元素中存放字符串的一个字符,而字符指针变量中存放的是字符串的首地址。
(2)初始化方式:char *a=”I love China”; => char *a; a=”I love China”;
char str[13]= ”I love China”; ≠> char str[13]; str[]=”I love China”;
(3)赋值方式:对字符数组不能整体赋值,只能对单个元素进行。而字符指针变量赋值可整体进行。
char *a; a=”I love China”; (正确)a是一个指针变量,可以用来赋值。
char str[13]; str=”I love China”; (错误)str数组名常量,不可以赋值。
(4)存储单元的内容:字符数组分配若干存储单元,以存放各元素的值,而对于字符指针变量,只分配一个存储单元。
char *a; scanf(“%s”,a); (错误)
char *a,str[10]; a=str; scanf(“%s”,a); (正确)
(5)字符指针变量的值可以改变,字符数组名是一个常量,不能改变。
char *str=”I love China”; str=str+7; (正确)
char str[13]=”I love China”; str=str+7; (错误)
(6)字符数组中各元素的值是可以改变的,但字符指针变量所指向的字符串常量是不可以改变的。
char a[]=”house”; a[2]=’r’; (正确)
char *b =”house”; b[2]=’r’; (错误)
(7)用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
char *format; format=”a=%d,b=%d\n”; printf(format,a,b);
四、指针函数与函数指针
1.指针作为函数的参数
① 使用指针类型做函数的参数,实际向函数传递的是变量的地址。变量的地址在调用时作为实参,被调函数使用指针变量作为形参接收传递的地 址。这里的实参数据类型要与形参的指针所指向的对象数据类型一致。
② 数组名做函数参数时,在函数调用前形参数组并不占用内存单元,在函数调用时,形参数组并不分配新的存储单元,而是以实参数组的首地址作为形参数组的首地址,共占同一段内存。所以传递的是数组的首地址,形参数组的元素改变,实参数组也会发生改变。
2.指向函数的指针
①一般形式: 类型说明符 (*指针变量名)(); (函数的指针)
类型说明符:被指函数的返回值类型;
(*指针变量名):表示*后面的变量是定义的指针变量;
():指针变量所指的是一个函数。
②用函数指针形式调用函数的步骤:
- 先定义指针变量;
- 把被调函数的入口地址赋予该函数指针变量;
- 用函数指针变量形式调用函数:
一般形式: (*指针变量名)(实参表);
注意事项:
函数指针变量不能进行算术运算,这与数组指针变量不同。
函数调用时(*指针变量名)两边的括号不能省略,且*只是一种标志。
3.指针型函数
一般形式: *函数名(形参表) (指针的函数)
第九章 结构体与共用体
一、结构体类型
1.结构体类型的定义:能集中不同的数据类型于一体的数据类型。
struct 结构体名
{
类型标识符 成员1;
类型标识符 成员2;
}变量名表;
2.结构体类型变量的定义(3种)
① 先声明结构体类型,再定义该类型的变量:
② 在声明类型的同时定义变量: ③ 不指定类型名而直接定义结构体类型变量:
注意:
- 结构体类型与结构体类型变量不同。只能对变量赋值、存取或运算,而不能对一个类型这么做,编译时,对类型是不分配空间的,只对变量分配空间。
- 结构体类型中成员名可以与程序中的变量名相同,但二者不代表同一对象。
- 结构体变量的成员可以单独使用,它的作用与地位相当于普通变量。
- 在定义结构体变量时可以对他的成员初始化,初始化列表是用花括号括起来的一些常量,注意是对结构体变量初始化,而不是结构体类型。
3.结构体类型变量的引用
引用形式:
结构体类型变量名.成员名
结构体类型变量名->成员名
引用时注意:
- C语言允许将一个结构体变量直接赋值给另一个具有相同结构的结构体变量。
- C语言中若成员本身属于结构体类型,要用若干个.,只能对最低级的成员进行赋值或存取以及运算。
- 结构体变量的成员可以像普通变量一样进行运算,同类型结构体变量可以相互赋值,可以引用结构体变量的地址。
<>、结构体数组
1.一般形式:
struct 结构体名 结构体数组名[整型常量表达式];
对结构体数组初始化是在定义数组的后面加上:={初值列表};
引用方式和变量相同
<>、结构体指针变量
1.定义:指向结构体变量的指针变量称为结构体指针变量。结构体指针变量的值是所指向的结构体 变量的首地址。
2.一般形式:
struct 结构体名 *结构体指针变量名;
注意:说明结构体指针变量后,必须让结构体指针变量指向同类型的结构体变量或数组,然后才能通过指针引用所指对象的成员项。结构体指针变量主要用于对结构体数组操作。
3.引用:
(*结构体指针变量名).成员项名
结构体指针变量名->成员项名
注意:
- (++p)->num先使p自加1,然后得p指向的元素中的num成员值
(p++)->num先求得p->num的值,再使p自加1指向stu[1]
- 如果要将某一成员的地址赋给p,可以使用强制类型转换p=(struct Student *)stu[0].name。
二、共用体类型
1.一般形式: union 共用体名
{
类型标识符 成员1;
类型标识符 成员2;
}变量名表;
注意:
- 先定义,后使用,不能引用共用体变量,只能引用共用体变量中的成员。
- 共用体类型的每个成员项存放在同一段内存单元中。
- 共用体变量所占的内存长度是成员中的最大长度。
2.引用:
共用体类型变量名.成员名
共用体类型变量名->成员名
可以对共用体变量初始化,但初始化表中只能有一个变量。
共用体变量中起作用的成员是最后一次被赋值的成员。
三、枚举类型
1.一般形式:
enum 枚举类型名{枚举元素表}枚举变量表;
注意:枚举元素如果不给值,自动取0~n-1整数值(n是枚举元素的个数)。
在定义枚举元素表时,可以对某个枚举元素赋值,且其后的元素的值会自动加一。
2.枚举变量的定义(3种)
① 先定义后说明:
enum week{Sun,Mou,Tue,Wed,Thu,Fri,Sat};
enum week day1,day2,day3;
② 定义时说明:
enum week{ Sun,Mou,Tue,Wed,Thu,Fri,Sat}day1,day2,day3;
③ 直接说明:
enum { Sun,Mou,Tue,Wed,Thu,Fri,Sat}day1,day2,day3;
3.枚举变量的引用
- 枚举元素是常量,不是变量。不能在程序中用赋值语句再对它赋值。
- 只能把枚举元素名赋给枚举变量,不能把枚举元素的数值直接赋给枚举变量。如要赋值需要强制转换。
- 枚举元素可以用来做判断,其比较的规则是:按其在定义时的顺序号比较大小。
- 枚举常量是可以引用和输出的printf(“%d”,workday);
<>、用户自定义类型
一般形式: typedef 原类型名 用户自定义类型名;
注意与#define之间的区别:#define是在预编译时处理的,它只能作简单的字符串替换, 而typedef是在编译阶段处理的。实际上并不是作简单的字符串替换。