目录
一:C语言的构成特点
二: 数据类型
三: 常量、变量、运算符及表达式
(一)标识符
(二)常量
(三)变量:其值可以改变的量
(四)各种类型数据混合运算
(五)运算符和表达式
(六) 理解记忆运算符表
四: 顺序结构
五. 选择结构
六:循环结构
七: 数组
八: 函数
九: 指针
十:指针和数组
十一:编译预处理
十二:结构体和共用体
一:C语言的构成特点:
1、一个C程序可以由一个或多个源文件(.c)构成,一个源文件是一个编译单位
2、C程序是由函数构成的(即C程序的基本构成单位是函数),一个C程序可以有多个函数,但必须有且只能有一个主函数(即main函数)
#include <stdio.h> 文件包含 int main(){ 主函数......return 0; }
3、一个函数由两部分构成:函数首部和函数体;
4、一个C程序总是从main函数开始执行的,结束于主函数,而与主函数的位置无关;5、主函数能调用用户函数或系统函数,用户函数可以调用彼此不能调用主函数。
6、注释。注释不被编译执行 多行注释:/* */ 单行注释:// 注释,不能嵌套7、语句以分号结尾;一行可以写多条语句;
8、区分大小写;9、c语言中字母的ascll码:A~Z:65-90, a~z:97-122
小写字母-32=大写字母 大写字母+32=小写字母
'0'=48
10、ASCII 码对照表:
二: 数据类型:
不同的数据类型在内存中占用不同大小的存储单元
不同的数据类型所表示的数据取值范围不同
不同的数据类型可以参与的运算种类也不相同
便于在程序中按不同的方式和要求进行处理
三: 常量、变量、运算符及表达式
(一)标识符
1、定义:用来标识变量、常量、函数等的字符序列(符号化的名字)
2、组成:字母,数字,下划线的组合开头:字母,下划线
注意:
1. C语言的关键字和12个预处理命令不能用作标识符,C语言中有32个关键字(保留字)
32个关键字: short:声明短整型变量或函数 int: 声明整型变量或函数 long:声明长整型变量或函数 float:声明浮点型变量或函数 double:声明双精度变量或函数 char:声明字符型变量或函数 struct:声明结构体变量或函数 union:声明共用数据类型 enum:声明枚举类型 typedef:用以给数据类型取别名 const:声明只读变量 unsigned:声明无符号类型变量或函数 signed:声明有符号类型变量或函数 auto:声明自动变量 extern:声明变量是在其他文件中声明 register:声明寄存器变量 static:声明静态变量 volatile:说明变量在程序执行中被隐含地改变 void:声明函数无返回值或无参数 if:条件语句 else:条件语句否定分支 switch :用于开关语句 case:开关语句分支 for:一种循环语句 do :循环语句的循环体 while :循环语句的循环条件 goto:无条件跳转语句 continue:结束当前循环,开始下一轮循环 break:跳出当前循环 default:开关语句中的“其他”分支 sizeof:计算数据类型长度 return :子程序返回语句
2. C语言中大小写敏感
(二)常量
程序运行时其值不能改变的量
1、整型常量:10(十进制)、15L(长整型)、010(八进制)、0X3D(十六进制)、25U(无符号整 型)
(1)三种表示形式:八进制、十进制、十六进制
(2)后缀:L或1长整型 u或U无符号整型
(3)存储:补码
注意:c语言中没有二进制的书写形式大于等于0小于等于255的整型数据可以和字符型数据通用。
int类型取值范围:-32768 - 32767
unsigned int 型数据(无符号整形)取值范围:-65535 - 65535
2、实型常量:也称浮点数或实数
(1)十进制形式:8.14 -78.96 .12
(2)指数形式:1.23E-2 43.5e+4 2e7表示2乘10的七次幂注意:e前e后需有数,e后必为整数,e和数字之间不能有空格
3、字符常量:
用单引号括起来的单个普通字符或转义字符。
'A'、'b'、'c'、'\n'、'\101'(1)可视字符常量: 'A'、'b'
(2)转义字符: 无法从键盘输入或者另有他用以反斜线 \ 开头,放在一对单引号内:'\n'表示换行、'\\'表示反斜线....
转义字符表示一个字符,占用一个字节长度(3)存储
字符常量占用一个字节长度,存储该字符的ASCII码值(4)整型与字符型在[0,255]范围通用
转义字符对照表:
4、字符串常量:
用双引号("")括起来的字符序列。
如:"sum"、"A"、"123"
存储:每个字符串尾自动加一个 '\0' 作为字符串结束标志
注意:字符常量与字符串常量不同
5、符号常量(宏定义):
用标识符来代表常量(标识符形式的常量)。
如:#define PI 3.14
(三)变量:其值可以改变的量
1、变量代表内存中具有特定属性的一个存储单元,它用来存放数据,也就是变量的值,
在程序运行期间,这些值是可以改变的。
2、定义格式:先定义,后使用.
数据类型 变量名1,变量名2例如:int a;a=3;
3、变量类型:
整型变量(int(默认类型)、long、short,unsigned)实型变量(float,double(默认类型))
字符变量(char)4、变量初始化:定义变量的同时赋初值
int a=3;
float b=5.3;
而 int a;a=3不叫初始化,叫赋值
变量定义就是申请数据的存储空间,登记数据的各种存储特性。
例如:
int ;占2个字节或4个字节,vc6.0中为4字节,没有指定编译系统则为默认为2字节
long;占4个字节
short ; 占2个字节
char ;占1个字节
float ;占4个字节
double;占8个字节
指针类型:默认为2字节,VC6.0中为4字节
整型数据可以和字符型数据通用
(四)各种类型数据混合运算
在不同类型数据之间运算时会发生自动类型转换(包括赋值)
(五)运算符和表达式
C语言一共有34个运算符,记这些运算符时从以下三方面记:
- 运算符的书写形式
- 运算符的运算规则
- 优先级和结合性(从左往右算还是从右往左)
1、算术运算符:
+ - * / %
说明:
(1)两个整数相除的结果为整数。
5/3=1,1.0/2=0.5
(2)%要求参加运算的运算对象为整数结果也是整数。%的运算结果与第一个数的符号相同
8%3,结果为2 -5%3=-2 -5%-3=-2(3)*不能省略
(4)优先级与结合性:同数学一样,可加括号改变。2、强制类型转换运算符
(类型名)表达式
注意:
(float)22/5 与(float) (22/5)不同3、赋值运算符及表达式
(1) 简单赋值运算符 =:
变量名=表达式
int x=3
注意:左侧必须是变量,不能是常量或表达式
结合性:右结合 例如:int i=3;int m;m=i++ + i++ + i++
可以连续赋值但不能连续初始化:
例如:int x,y,z; x=y=z=3;这是对的
int x=y=z=3;这是错的
(2)复合赋值:
a+= 表达式 等价于 a = a +(表达式)(3)赋值运算的类型转换:
赋值运算符两侧的数据类型不一致时,赋值运算符右端的表达式计算结果要转
换为赋值运算符左端变量的类型。
将取值范围小的类型转换为取值范围大的类型是安全的,而反之则是不安全
4、 自增、自减运算符及表达式:
(1) 变量名++ i++
变量名-- i-- 先加减再赋值 ,加减完了再用++变量名 ++i
--变量名 --i 先赋值在加减 ,赋值完了再加减
(2)注意:
右结合性
++ -- 运算符的运算数也可以是浮点型
自增自减运算符只能对变量操作,不能对常量和表达式操作。
5、逗号运算符及表达式
(1) 一般形式:
表达式1,表达式2,......,表达式n注意:
求解过程为:先求解表达式1,再求解表达式2,.,最后求解表达式n。
逗号表达式的值是最后一个表达式的值优先级最低,具有左结合性。
6、关系运算符及表达式
(1) > < >= <= == !=
(2)优先级:< <= > >= (高) == != (低)
(3) 关系表达式的值为一个逻辑值,即"真"用"1"表示或"假"用"0"表示。
两个等价形式:
e!=0 等价于 e e==0 等价于 !e
7、逻辑运算符及表达式:
! && ||
优先级: ! > && > || ! > 算数元素算符>关系运算符>&&>||>赋值>逗号
计算时:所有非0值(包括负数)均"真”,0表示“假”
结果:真为1,假为0
短路规则: 当有足够条件确定表达式的值后,剩余部分将不再被计算。
8、条件运算符与表达式:
一般形式:表达式1?表达式2:表达式3
执行过程:如果表达式1结果为真则执行2否则执行3
结合方向:右结合 例如:a>b?a:(c>d?c:d)
9、位运算符
对字节或字内的二进制位进行的操作
位运算符的运算对象只能是整型数据或字符型数据,不能是实型数据
运算对象一律按二进制补码参加运算,并按位进行运算
位运算的结果是一个整型数据
C语言提供6种位运算符:& 、 | 、 ^ 、 ~ 、 << 、 >>
复合赋值运算符:
&= 、 |= 、^= 、<<= 、 >>=
1、按位与运算(&):同一为一,其余为零1&1=11&0=00&1=00&0=0作用:清零。 2、按位或运算 ( | ):有一说一,无一为零1|1=11|0=10|1=10|0=0作用:将特定位置1。3、按位异或运算(^)相同为零,相异为一1^1=01^0=10^1=10^0=0 4、按位取反运算(~)单目运算符。按位取反后0变1,1变0。对一个数按位取反得到的值为该数+1后再乘以-1例如:int a=12; ~a=-(12+1)=-13 5、按位左移运算(<<)格式:数<<n功能:将二进制位按位依序左移n位。对一个十进制数左移n位后得到的值为该数乘以2的n次方的积。例如:int n=4;n<<2=4*2*2=16 6、按位右移运算(>>)格式:数>>n功能:将二进制位按位依序右移n位对一个十进制数右移n位后得到的值为该数除以2的n次方的商。若该数为一个负数并且不能被2的n次方整除则得到的数为商加-1例如:int a=-13; a<<2=a/(2*2)-1=-4
10. sizeof运算符
用于计算数据类型或表达式所占内存的字节数
sizeof是C关键字,不是函数名一般形式:
sizeof(表达式)或sizeof(数据类型)
例:sizeof(1.5+10)= 8
sizeof(int)= 2
(六) 理解记忆运算符表
C 运算符的优先级与结合性:
其中,2、3、14 的结合性为从右至左,其余为从左至右。
四: 顺序结构
C语句的分类(共5类):
1. 控制语句:完成一定的控制功能
2. 函数调用语句:完成函数调用功能
3. 表达式语句:a=b+c
4. 空语句:只有分号的语句
5. 复合语句: {a=5;b=a%2;}
语句以;结束
C不提供输入输出语句,输入输出是由函数完成的,都是函数调用语句
#include <stdio.h>
在调用C语言库函数时,需要使用编译预处理命令#include <相关的头文件>,使相应的头文件包含到用户源程序中
数据输出:
1. 字符输出函数putchar():
putchar(ch)
功能:在标准输出设备(即显示器屏幕)上输出一个字符。例加:
putchar('b'); putchar('\n'); putchar('\101'); putchar(st);
注意:只能用单引号,如果是多个字符只会输出第一个字符
2. printf()函数(用于控制输出格式)
一般形式:printf(格式控制字符串,输出项表列)
也可以直接输出:printf("xxx")
功能:将输出项表列内容按照控制字符串要求输出。控制字符串:包含三种信息。
①普通字符原样输出。
②转义字符按转变后的字符输出。③由%和输出格式符组成的输出格式控制符。
输出表列:可以是常量、变量、表达式、函数等,多个用逗号隔开例如: printf("a =%d b =%f",a,b)
a=和b=表示普通字符串(原样输出) %d和%f表示格式说明
注意:输出列表中填 "\t" 是水平制表符,作用为补全前面字符串的位数到8的整数倍。
若\t前面没有字符/字符串,则在前面直接补8个空格;
若前面有3个字符\字符串,则补5个空格;
若有10个字符\字符串,则补上6个空格;如果已经满8个,则补上8个空格
格式说明符:
在%和格式符之间可以使用附加说明符:
例如:
%ld、%lo--输出十进制、八进制长整型数
%lf、%le——输出双精度实型数
%mf——输出m位数(域宽,右对齐,不够原样输出)对float和double型指整数位数+小数点+小数位数
%-mf——输出m位数(域宽,左对齐)
对float和double型指整数位数+小数点+小数位数
float a=3.1415; printf("%0.2f",a); 输出3.14 四舍五入
int k=1234; printf("%06d\n",k); 输出 001234
int k=1234; printf("%-06d\n",k); 输出 1234
%.nf—输出的n位小数宽度与精度同时指定时,要先满足精度,再满足宽度。
注意:
1. printf函数格式控制中的格式说明符与输出参数的个数和类型必须一一对应。
输出格式少于输出项时,多余的输出项不予输出;
2. 格式说明符的%和后面的描述符之间不能有空格。除%X、%E、外类型描述符必 须是小写字母。
3. 长整型数应该用%ld(或%lo、%lx、%lu)格式输出,否则会出现输出错误。
4. printf函数的参数可以是常量、变量或表达式。在计算各参数值时,从右至左的顺 序求值。
数据输入:
1. 字符输入函数
函数调用的一般形式 getchar()
功能:从标准输入设备(即键盘)上交互输入一个字符。
例如:getchar(); c= getchar(); printf("%c \n",getchar());getchar()只能接受一个字符,以回车结束;
连续输入多个字符时,,中间不能有其他字符
2. 格式输入函数
一般形式: scanf(格式控制字符串,参数地址列表)
功能:从键盘上输入各类型数据存入参数地址列表中
scanf( "a=%d, b=%f", &a, &b);
以%s形式输入时,若对象为数组名,不加&(因为本身就是地址)
a=和b=表示普通字符(一定要原样输入,不然不对),%d和%f表示格式说明符
输入时不能对精度有要求,但可以对宽度有要求
输入格式说明:
在%和格式符之间可以使用附加说明符:
对float和double型指整数位数+小数点+小数位数
注意:
格式符之间若无普通字符,则:输入的数值型数据用空白符分隔,输入的char型数据不能分隔
scanf不能控制浮点数的小数精度
五. 选择结构
1. if 语句
单分支控制的if条件语句:
if(表达式)单条语句;
注意:语句A可以是单条语句,也可以是用一对{}括起来的复合语句。
双分支控制的if条件语句:
if(表达式)
语句A;
else
语句B;
注意:语句A、B可以是单条语句,也可以是用一对{}括起来的复合语句。
多分支控制的if条件语句:
if(表达式1){
一条或多条语句}
else if(表达式2){一条或多条语句
}
else if(表达式n){一条或多条语句
}else{
一条或多条语句
}
选择结构的嵌套:
在if语句中又包含一个或多个if语句称为if语句的嵌套
if()
if()语句1
else语句2
else
if()语句3
else语句4
注意:else总是与它前面最近的且未曾配对的if配对。
2. 多路选择的switch语句
switch语句的作用是根据表达式的值,使流程跳转到不同的语句
switch(表达式){
case 常量:语句;
break;
case 常量:语句;
break;
case 常量:语句;
break;
default: 语句
}
注意:
switch后的表达式只能是整型或者字符型。
case后常量表达式的值不能相同。
case后允许多条语句,不需要大括弧号。case后面可以没有分句(自然也没有分号)
如果不添加break语句,需要特别注意执行顺序。
case和default子句的先后顺序可以自行变动。若找不到匹配的case值,则执行default后语句并且连带向下
无论default在什么位置都是找不到匹配的case才执行,如果default在最上面而且
没有break,那么也会连带向下
default 子句可以省略。
六:循环结构
1. while语句(先判断表达式,再执行循环体)
while(表达式){
循环体语句
}
当表达式为“真”时执行循环体语句“假”时不执行
2. do while语句(先无条件地执行循环体,然后判断循环条件是否成立)
do{
循环体语句
}while(表达式)
注意:do-while语句的循环体至少被执行一次,如果while表达式为“真”再从do开始执行
while和do---while循环的区别:
当while后面的表达式的第一次的值为“真”时,两种循环得到的结果相同;否则不相同
do-while的循环体至少执行一次,while的循环体可能一次也不执行
3. for 语句
for(表达式1;表达式2;表达式3){
循环体语句;
}
for循环中的表达式1,2,3都可以省略,但是;不能省略
break和continue:
continue可以结束本次循环
break可以结束整个循环
continue 语句只用于循环语句中,break语句还可以用switch语句中
七: 数组
数组是具有相同类型的变量的集合,这些变量在内存中占有连续的存储单元,
一维数组:
数据类型说明符 数组名[数组常量表达式]
例如:int a[5],c[6]; 这里5和6表示数组长度
数组名是数组的首地址,是地址常量
数组元素的引用:
数组名[下标]
例如:int a[5]; a[0] a[1] a[2] a[3] a[4] a[i] i=0,1,2,3,4
数组所有元素在内存中连续存放
一维数组初始化:
1. 对全部数据元素初始化:
int a[5]={1,2,3,4,5}或 int a[]={1,2,3,4,5}
2. 对部分数据元素初始化:
int a[8]={1,2,3}; {}里的数量如果多于[]的数量会出错,少会补零
3. 字符数组两种初始化方法:
char a[3]={'b','o','y'};char a[4]=" boy";
4. 若未对数组初始化,则数组元素的值是随机数。
例如:int a[2]; 那么这不叫初始化,里面的两个元素是随机的
注意:
char类型数组里面可以存整型数据 例如: char str[]={1,2,3};
整型在字符数组中的存储,就是将值转化成ASCII值进行存储,类似于哈希表的
方式,一个键对应一个值,我们只需要找到它的键,进而取出它的值即可使用 。
所以我们不用担心整型存储到字符数组中会变成字符型,因为我们存储的是整型
所对应的ASCII值,而不是整型本身。
二维数组:
二维数组的定义:
类型说明符 数组名[整型常量表达式1][表达式2]
例如:int a[3][4] char ch[5][6] 里面数字代表长度
存储方式:
数组元素的存放顺序:按行序优先连续
二维数组引用:
数组名[下标表达式][下标表达式]
二维数组初始化:
1. 对全部元素初始化
int a[3]3]={1,2,3,4,5,6,7,8,9}
int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}}
int a[][3] = {{1,2,3),{4,5,6},{7,8,9}} 第一维长度数可以省略,第二维不可以2. 对部分元素初始化
int a [3] [4] ={{1}, {5}, {9}}
int a [3] [4]={{1},{0,6},{0,0,11}} 多则出错,少则补零
字符串数组:
字符串的本质:字符串是一种以‘\0'结尾的字符数组
例如: "HELLO"
字符串结束标志:'\0',(既无动作,又不显示)
字符串的长度:第一个 '\0' 以前字符的个数
在字符型数组或字符串中遇 '\0' 即认为该字符串结束。
系统对字符串常量自动加一个'\0'作为结束符。1. 一维字符数组
定义:char c[5];通常存放一个字符串
2.初始化
char c[5]={ 'C','h' } 部分赋值时其它默认\0
char c[5]={'c', 'h','r' ,'n', 'a' } ,如果最后一位是\0则存放的是字符串,不是\0则是字符
3. 用字符串常量
char c[6]= {"china"};
或:char c[6]= "china"; 这里下标不能是5,因为后面默认有一个\0
把一个字符串常量赋值给一维字符数组时,数组的元素个数要大于字符串的实际长度
字符串输入函数:
1. scanf("%s",字符数组)
输入的字符串里不能包含空格,因为字符串末尾自动加'\0'
以空格或回车结束输入串长度应小于字符数组长度
scanf("%s,%s",a,b);这里的逗号不能正确读取数据。
2. gets()
例如:char str[10]; gets(str)
输入的字符串里可以包含空格;
末尾自动加'\0';
只有回车才能结束
输入串长度应小于字符数组长度字符串输出函数:
1. printf("%s",字符串地址)
依次输出字符串中的字符直到遇到'\0' ,不会自动换行
2. puts(字符串地址)
puts(str) 输出完后自动换行,会将"\0"转成"\n"
如果字符串中包含"\n",那么读到\n时会换行再接着输出
字符串连接函数:
strcat(字符数组1,字符数组2)
把字符串2接到字符串1的后面
字符数组1必须足够长
字符数组1的\0去掉,字符数组2的\0保留注意:用此函数在开头必须写#include <string.h>
字符串复制函数
strcpy(字符数组1,字符串2)
将字符串2复制到字符数组1字符数组1必须足够长
字符数组1必须是数组名,字符数组2是数组名或字符串常量错误演示:
str1="computer";
str1=str2; 不能用赋值语句直接给字符数组赋值。只能用strcpy函数处理。
注意:用此函数在开头必须写#include <string.h>
字符串长度函数:
strlen(字符串)
到\0结束,实际长度,不包括'\0'在内,
空格也算一个长度
注意:用此函数在开头必须写#include <string.h>
字符串比较函数:
strcmp(字符串1,字符串2)
函数返回值相同位置不同字符的ASCII码差值。
不能用关系运算符直接进行字符串大小或字符数组大小的比较
字符串1=字符串2,函数的返回值为0
字符串1>字符串2,返回值为一正整数
字符串1<字符串2,返回值为一负整数
注意:用此函数在开头必须写#include <string.h>
八: 函数
库函数: #include <stdio.h> 输入输出函数 #include <math.h> 数学函数 #include <string.h> 字符串函数数学函数: 平方根函数 sqrt(x) 幂函数 pow(x,n) 绝对值函数 fabs(x) 指数函数 exp(x) 以e为底的对数函数 log(x) 以10为底的对数函数 log10(x)
函数的概念:
函数是程序的基本单位;
必须有一个main函数,位置在哪儿都行;
一个源程序文件可由一个或多个函数组成
一个C语言程序可以由一个或多个源程序文件组成C程序执行总是从main(开始,结束于main);可调用其它函数
所有函数定义是并列的;不能嵌套定义,可以嵌套调用,不能调用main()
函数分类:
从函数定义角度:标准库函数、用户自定义函数
有无返回值:有返回值的函数、无返回值的函数
从主调函数和被调函数数据传送:有参函数、无参函数
从函数作用范围:内部函数、外部函数函数的定义与调用:
1. 无参函数定义:
类型说明符 函数名(){......}
2. 有参函数定义:
类型说明符 函数名(形参列表){......}
形参:例如int x,int y;如果形参类型一样也要分别定义数据类型
函数名: 函数名命名规则和变量名一样,同一程序函数不能同名
类型说明符:表示函数类型,绝定函数返回值类型,如果省略不写就默认为int型
函数的调用:
1. 主调函数: 主动去调用其它函数
被调函数:被其它函数所调用2. 函数必须先定义或声明后才能被调用
若被调用函数的定义在调用函数之后,必须先声明
声明方法:
方法1:被调函数首部加分号;int max(int a,int b);
方法2:int max(int,int);
3、函数调用的一般形式
(1)有返回值的函数调用例如:x=函数名(实参表列);
z=max(x,y);
printf("%d",max(x,y));
x=max(a,max(b,c));
(2)无返回值的函数调用
例如:
函数名(实参表列);
函数名();
函数定义与函数声明的区别:
函数定义:
指函数功能的确立
指定函数名、函数类型、形参及类型、函数体等
是完整独立的单位
函数声明:
是对函数名、返回值类型、形参类型的说明
不包括函数体
是一条语句,以分号结束,只起一个声明作用函数的形式参数和实际参数:
通常有值传递方式和地址传递方式两种注意:函数的实参时从右往左算的
例如:func(func(x++,y++),func(--x--y));
要先算func(--x--y)再算func(x++,y++)
一、值传递方式
1、实参可以是常量、普通变量、表达式、带有返回值的函数调用,数组元素;
2、实参与形参在数量、顺序要一致。类型应相同或满足赋值相容;3、没有函数调用时,形参不占内存;函数调用时,为形参分配内存,形参
与实参占用不同的内存单元,调用函数执行完后,形参释放内存;
(形参是随用随分配,用完就释放)
4、单向数值传递,只能实参的值传递给形参,不能将形参的值传递给实参;
5、形参只能是变量
6. 由于仅仅“值传递”,对于简单变量,形参的变化不会影响实参
二、 地址传递
1、函数调用时,将数据的存储地址作为参数传递给形参
2、形参与实参占用“同样”的存储单元3、“双向”传递
4、实参和形参必须是地址常量或变量数组名作为函数参数,传递的是地址;
1、实参数组和形参数组类型,维数一致;2、形参数组可以不指明大小;
3、二维数组作为形参时,只有第一维大小可省略,第二维大小必须指定;
函数的返回值:
1、函数的返回值最高只能有一个;
2、返回值的类型可以是除数组以外的任意类型,与函数类型一致;
3、函数中的return语句可以有多个,一般在if-else语句中,但是只执行一次;
4、函数定义类型和返回值类型不一致时,会转成函数值类型,如果不能实现转换则报错5、void类型函数没有返回值,有的编译器用void报错是因为会自动给函数return;
函数的嵌套与递归调用:
不允许函数嵌套定义,但允许函数的嵌套调用和函数的递归调用。
函数间的关系是平行的、独立的递归调用:
调用该函数本身
用if语句空控制:条件成立进行递归,条件不成立结束递归
if(递归终止条件成立){
return 递归公式初值
}else{
return 递归函数调用返回的结果值
}
变量的存储类别,作用域:
变量的作用域:存在的空间性,指变量在程序中的有效范围
1. 局(内)部变量:
在函数内(复合语句内)定义的变量,它只在本函数内(复合语句内)有效。
(1)生存期是该语句块,进入语句块时获得内存,仅能由语句块内语句访问,退出语 句块时释放内存,不再有效;
(2)定义时不会自动初始化,除非程序员指定初值;
(3)并列语句块各自定义的同名变量互不干扰,不同函数可以有相同变量名;
(4)形参和实参可以同名;(5)形参也是局部变量
(6)主函数里的变量也是局部变量;
(7)复合语句里定义的变量也是局部变量;复合语句的局部变量和本函数的局部变量 同名时,本函数的局部变量不起作用。2. 全(外)局变量:
在所有函数之外定义的变量
(1)生存期是整个程序,从程序运行起占内存,程序退出时释放内存;
(2)有效范围是从定义变量的位置开始到本程序结束;
(3)全局变量必须在所有函数之外定义,且只能定义一次
(4)如在其作用域内的函数中定义了同名局部变量,则在局部变量的作用域内,同名全局变量暂时不起作用。
(5)可以通过声明一个extern的全局变量扩展全局变量的作用域,也可以通过定
义一个static的全局变量限制这种扩展。
(6)全局变量增加函数间数据联系,函数只能返回一个值,可用影响全局变量的
方式带回给主调函数多个值;
(7)全局变量会降低程序的清晰性;因为所有函数都能影响变量的值;变量的生存期:存在的时间性
1. 动态存储变量:(auto)
auto 数据类型 变量名;一般情况auto省略不写
(1)进入语句块时自动申请内存,退出时自动释放内存
(2)未赋初值时,其值不确定,每次调用重新赋值。
(3)局部变量(不含静态局部变量)、形参2、静态存储变量:(static)
static 数据类型 变量名;
(1)局部静态变量编译时赋初值一次,以后每次调用不再重新赋初值,而是保留上次函数调用结束时的值;
(2)局部静态变量定义时不赋初值则自动赋初值0。
(3)全局变量、静态局部变量C程序的存储类别
1、auto型(自动变量)auto类别变量用完后释放所占用空间。
局部变量默认为auto类别,无初始化时,初值为随机值。
使用时间短,一般都为auto类别变量
2、static型(静态变量)static修饰的局部变量会改变它的生命周期,延长了它的生命周期,
以至于当这个函数调用完了,修饰的局部变量并没有销毁,到下次
调用此函数不会产生新的而是接着上次的使用
static类别从定义到程序运行结束均占用存储空间。
全局变量默认为static类别,无初始化时,初值为0。static类别变量只进行一次初始化。
生存期为整个源程序。
3、register型(寄存器变量):为了提高某些变量和函数形参的存取速度,C允许将局部变量的值存在
cpu寄存器中,不用去内存取数据;
只有局部自动变量和形参能作为寄存器变量,静态变量不可以;
寄存器变量数目有限,不能定义任意多个寄存器变量;
只有int,char和指针类型变量可以定义为寄存器型。
一般不能直接使用。
4、extern型(外部变量):如果需要用其它文件中的全局变量,则需在本文件中使用extern int x;说明一下。
extern类别变量可以加大变量的作用范围。
两格说明格式:
格式1:定义同时说明类别:存储类别 类型名 变量名;格式2:分别定义、说明:类型名 变量名;存储类别 变量名;
九: 指针
指针与指针变量的概念:
内存区的每一个字节有一个编号,这就是“地址”,它相当于旅馆中的房间号。
在地址所标识的内存单元中存放数据,这相当于旅馆房间中居住的旅客一样。
由于通过地址能找到所需的变量单元,我们可以说,地址指向该变量单元。将地址形象化地称为“指针”
指针类型默认为2字节,VC6.0中为4字节
指针变量的定义:
1、定义:基类型 *变量名;例如:int *p1,*p2; float *q;
2、注意:
(1)指针变量也是变量,在内存中也要占用一定的内存单元,但所有类型的指针变量 都占用同样大小的内存。
(2)指针变量前面的 "*" ,表示该变量的类型为指针型变量。变量的两个物理意义:
变量值和变量地址
1、指针:一个变量的地址
2、指针变量:专门存放变量地址的变量指针变量的赋值:
1. 初始化赋值
基类型 * 指针名 = 初始地址值;例如:int i; int *p=&i;
2. 赋值
例如: int i,*p; p=&i;
3. 在定义指针变量时必须指定基类型。使用指针变量时,要与基类型一致。下面
的赋值是错误的:
float a; int * p1; p1=&a;
4. 指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量。
5. 相同基类型的指针变量可以相互赋值
6. 可用NULL对指针变量赋值,使其不指向任何变量.
指针变量的引用:
格式:*指针变量
int a;int *p=&a; *p=10
两个有关的运算符:
1. &:取地址运算符。
2. *:指针运算符(或称“间接访问”运算符)。注意:
1. 指针变量必须先定义,后赋值,最后才能使用!没有赋值的指针变量是没有
任何意义的,也绝对是不允许使用的。(野指针)
2. 指针变量只能指向定义时所规定类型的变量。3. int a=3,*p;p=&a;
则:p与&a等价,*p与a等价
*&a=*p=a &*p=&a
十:指针和数组
数组名:
表示存放数组元素的连续空间的首地址
可以看作是一个“常量”指针,但不能修改其指向
数组元素可以使用下标法来引用,也可以使用指针来引用
同样的,指针也可以当成一个数组名来使用一维数组与指针变量:
1、数组的指针:
数组名表示数组的首地址,是一个指针常量,不能修改其指向。
2、指向一维数组的指针变量int a[10],*p;p=a;(或p=&a[0])
(*p)[5]表示指向含有五个元素的一维数组指针变量
C规定:数组名a是数组的首地址,a+1代表下一个元素地址(即&a[1]),.....,a+i代表第i个元素地址(即&a[i])
所以: 地址:&a[i]==a+i 元素:a[i]==*(a+i)
指向数组的指针运算规则:
(1)p+n:向后移动n个元素(不是n个字节)(2)p-n:向前移动n个元素(不是n个字节)
(3)int * p=&a[1],*q=&a[5];q-p就是4,q+p无意义。必须指向相同数组的指针减法。
(4)关系运算,p>q,表示p高地址,p<q,表示p低地址
(5)指针可以与0比较,if(p!=0)。。。#define NULL O int*p=NULL注意:
(1)数组名是地址常量,且不可对其赋值,也不可做++或--运算。
int a[10];a++,a--都是错误的。
但是可以进行a+n
(2)指针变量可以做++或--运算。明白几个式子的含义:
*p++等价于*(p++)
(*p)++等价于a++(int a,*p=&a);
*(p--)等价于a[i--]*(++p)等价于a[++i]
*(--p)等价于a[--i]数组名作为函数参数:
1. 一维数组名可以作为函数参数,二维数组名也可作函数参数。传递的是地址。
2. 用指针变量作形参,来接受实参数组名传递来的地址。
可以有两种方法:
①用指向变量的指针变量
②用指向一维数组的指针变量字符串的指针:
1. 存放访问字符串的两种方法:
(1)字符数组放字符串
(2)用字符串指针指向一个字符串。2. 两者区别:
(1) 字符串指针存放的是字符串的首地址。字符数组名存放的数组的首地址。
(2) 字符串指针可以自增,自减,字符数组名不可以指向函数的指针:
指向函数的指针变量
函数名与数组名一样,是起始地址,而且是一个地址常量。
定义指向函数的指针变量的方式:类型名 (*指针变量名)();
在定义指向函数的指针变量时,要注意有两个小括号必须要有。
单独的函数名代表该函数的首地址(函数的入口地址)。
函数的指针变量只能指向函数的入口处(函数的首地址),不能指向函数中的某条指令。(另对指向函数的指针变量加1是没有意义的)。
给指向函数的指针变量赋值时,只写函数名即可。返回指针的函数:
类型名 *函数名(形参列表){ }
多维数组与指针变量:
例:int a[3][4];
a+1是跳过一行。因为二维数组名是行指针,加1是跳过一行不是一个元素注意:
1,只有列指针才是"真正”指向元素。即指向某一个元素的存储单元。
2,一维数组名表示的是列指针;二维数组名表示的是行指针。二维数组可以横向越界
若a是一个二维数组,则有:
1. a+i是行指针,即指向的是一整行。若对它加1则指向下一行。
2. *(a+i)和a[i]一样,都是一个列指针即指向的是一个元素,3. *(a+i)+j和a[i]+j一样,都表示元素a[i][j]的地址。即与&a[i][j]等价。
4. (*(a+i))[j]和a[i][j]一样 都表示元素a[i][j]
指针数组:
一个数组,若其元素均为指针类型数据,称为指针数组。也就是说,指针数组中
的每一个元素都存放一个地址,相当于一个指针变量。
定义:
类型说明符 *数组名[数组长度] 例如:int *pa[3]指针数组的使用:
# include <stdio.h> void main(){int a=5,b=3; int *p[2]; p[0]=&a;p[1]=&b;printf("%d,%d",*p[0],*p[1]); }
指向指针的指针:
用来存放指针变量地址的指针变量称为指向指针的指针变量。
1、定义:
类型 **指针名
2、引用:
int a=3,*p,**pa;p=&a,pa=&p;
则:a=*p=**pa
十一:编译预处理
预处理命令概念:
“编译预处理”是在进行编译第一次扫描前系统自动先对程序的预处理部分进行编译,然后才对源程序进行编译。
C提供的预处理功能主要包括:
1、宏定义2、文件包含
3、条件编译
格式:
“#”开头 占单独书写行 语句尾不加分号宏定义:
第一种:无参宏定义
格式:
#define 宏名 宏内容没有分号;如有分号,则带分号一起替换,往往替换后有语法错误
功能:用一个指定的标识符(宏名)来代表一串字符(宏内容)
宏名我们一般用大写字母表示,遵守用户自定义标识符命名规则。
#define可以在函数外定义也可以在函数内定义,只能放在一行。
#undef命令可以提前终止宏名的作用域。宏替换时,先替换完所有的宏后再运算,替换过程中不能乱加括号。
在进行宏定义时,可以引用已经定义的宏名嵌套置换。例如:
#define PI 3.1415926
#define P50
#define SIZE 20第二种:带参宏定义
#define 宏名(参数列表) 宏内容功能:提供了一种更加灵活的替换方式,有时候能起到函数的作用。
在定义有参宏时,参数列表必须用一对小括号括起且小括号和宏名之间不能有 空格
对有参宏名进行替换时,需要将形参改成相应的实参,并且注意分清形参和实 参的对应关系。
如:
#define s(a,b)a*b+5
#define f(x,y) x*y文件包含
#include命令把另外一个源文件的全部内容嵌入到源程序中来。(.h和.c)
1、#include <文件名>
只检索C语言编译系统所确定的标准目录2、#include "文件名"
首先对使用包含文件的源文件所在的目录进行检索,若没有找到指定的文件,再在标准目录中检索。
十二:结构体和共用体
结构体:
一种构造数据类型
用途:把不同类型的数据组合成一个整体结构体类型的定义:
struct 结构体名{成员表列 };struct student1{int num;char name[20];char sex;int age;float score;char address[30]; };结构体的成员可以是另一个结构体: struct date {int year;int month;int day; } struct student2{int num; char name[20];struct date birthday;char sex;int age; float score; };
结构体变量的定义
定义结构体变量后,系统分配内存单元个数为所有成员所占单元个数之和。
用sizeof测量字节数,得到的是大于等于真正字节数的4的整倍数
1. 三种定义方法:1. 先定义结构体类型再定义变量名struct student {int num;char name[20];float score;};struct student stu1,stu2;2. 在定义结构体类型的同时定义变量struct student {int num;char name[20]; float score;}stu1, stu2;3. 直接定义结构体类型变量(不出现结构体名)struct {int num;char name[20]; float score;}stu1, stu2 ;
结构体变量的初始化同结构体变量定义相同
若初值个数多于成员个数则出错,若初值个数少于成员个数,则多余成员自动赋0 若没赋初始值就是随机数 1. struct student {int num;struct date birthday;char name[20];float score;};struct student stu1={1,{1999,11,12},"liming",90}2. struct student {int num;char name[20]; float score;}stu1, stu2={1,"liming",90};各成员与值之间依次一一对应;(类型和数量对应);3. struct {int num;char name[20]; float score;}stu1, stu2={1,"liming",90};
结构体变量的引用(访问)
1、结构体变量名.成员名 结构体变量不能整体引用,只能引用它的成员。stu.num;(普通变量怎么用,它就怎么用);
2、结构体变量的成员还是结构体,则层层访问;stu.birthday.year
3、对结构体变量的成员可以像普通变量一样进行各种运算(根据成员的类型决定可以进行的运算);
结构体变量的赋值,输出和输入
struct student {int num;struct date birthday;char name[20];float score; }; struct student stu;1. 赋值: stu.num=101; Strcpy(stu.name,"xiaoming"); 这里不能直接赋值,要用Strcpy stu.birthday.month=11; stu.birthday.year=1999; stu.birthday.day=12;2. 相同类型的结构体变量可以互相赋值: struct student stu1,stu2; 可以 stu1=stu2;3. 不能将一个结构体变量作为一个整体进行输出。必须一个一个成员按其类型输出; struct student stu; printf("%d,%d-%d-%d,%s\n", stu.num; stu.birthday.year, stu.birthday.month, stu.birthday.day, stu.name);4. 不能将一个结构体变量作为一个整体进行输入。必须一个一个成员按其类型输入; struct student stu; scanf("%d,%d-%d-%d,%s\n",&stu.num;&stu.birthday.year,&stu.birthday.month, &stu.birthday.day,stu.name);
结构体数组:
1. 定义:(1)struct student {int num;struct date birthday;char name[20];float score;};struct student stu[3];(2)struct student {int num;struct date birthday;char name[20];float score;}stu[3];(3) struct {int num;struct date birthday;char name[20];float score;}stu[3];2. 初始化:struct student {int num;char name[20];float score;}stu[2]={{101,"liming","99"},{102,"xiaoming","88"}};
指向结构体变量的指针:
当指针变量指向结构体变量时,对指针变量加1则跳过整个结构体而不是跳过一个成员。 指向结构体数组元素时与指向普通数组的指针一样,可以进行算术运算: “加”表示向后移动一个元素地址,“减”表示向前移动一个元素地址 1. 定义:struct student {long num;char name[20];float score;};struct student stu1,*p;2. 初始化 p=&stu1;3. 成员的引用(*指针名).成员名 指针名->成员名以下3种形式等价(1)结构体变量.成员名(2)(*p).成员名(3)p->成员名
共用体:
共用体与结构体类似 共用体中的所有成员共用同一段内存,起始地址一样。 共用体类型变量所占的字节等于该共用体类型占用字节数最多的成员所占的字节数。 格式:union 共用体名{成员列表; }; 成员列表为定义该共用体的成员,成员定义的方式与普通变量的方式一样。 成员列表必须用一对花括号括起。 共用体名可以省略 共用体只能单个某一成员进行初始化 共用体所有成员占有同一段地址空间,即共用体变量的地址和它的各成员的地址都是同一地址。 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用。 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖
typedef:
用typedef定义新类型名
在编程中可以用typedef定义新的类型名来代替已有的类型名格式;格式:
typedef 已有类型名 新的类型名;
例如: typedef int INTEGER;以后在定义变量时int和INTERGER是等价的。
INTEGER a[10],b == int a[10],b;1. typedef可用于定义各种类型名但不能定义变量
typedef语句最后的标识符必定是一个类型名而不是变量名。
2. typedef只能对已经存在的类型新增一个别名,不是创造新类型。
typedef后必须是一个已有的类型。枚举类型:
定义格式:enum 枚举名 {枚举元素列表}; 作用:将所有可能的值一一列出 例:enum Weekday{sum,mon,tue,wed,thu,fri,sat);→常量 注:习惯性将枚举类型的第一个字母大写,来和系统自带类型名区别 引用格式: 例:enum Weekday workday,weekday;