1数组
数组定义方式:
类型说明符 数组名[常量表达式];
说明
1 命名规则
2 定义必须制定个数
3 常量表达式可以是常量或符号常量
数组必须先定义后使用
Int a[10] // 定义数组长度为10
a[6] 引用下标为6的数
赋值
Int a[10]={1,2,3}
Int a[]={1,2,3};
二维数组
类型说明符 数组名[常量表达式][常量表达式];
定义维数和各维大小
Int A[3][4]
元素表示形式
a[下标][下标]
取值
B=a[3][4]
赋值
1分行赋值
A[2][2]={{1,2},{1,2}}
2用所有数据赋值
A[2][2]={1,2,3,4}
3全部元素赋值,可以第一维度不赋值
A[0][2]={1,2,3,4}
用来存放字符数据的数组是字符数组
字符串:
字符串结束标志“\0”;
系统会在字符串常量会自动加上结束符号,多占一个字节内存。
字符数组输入
%c格式符号 逐个输入
%c 格式符号 整个输入
注意:
1 输出字符不包含’\0’
2 输出项是数组名
3 遇到’\0’结束
可用scanf输入字符串
数组名代表数组起始地址
%o八进制输出字符数组起始地址
Scanf(“%s”,a) 输出字符串实质,是从起始地址开始直到’\0’结束字符的字符串。
1 Puts() 输出字符串
2 Gets() 从终端获取字符串
3 strcat(str1,str2) 连接两个字符串 ,把第二个连接到第一个后面
- 第一个字符数组足够大,容纳两个数组
- 第一个’\0’ 取消,只保留一个
strcpy(str1,str2) 把字符串2复制到字符串1中。
- 第一个字符数组足够大,容纳两个数组
- 字符数组1必须写成数组名
- 如果未对字符数组1赋值,结果无法预料。会将第二个字符数组加’\0’复制进第一个,原有保留
- 不能用赋值语句直接将字符串常量或字符数组赋值给字符数组。
- 可以将字符串前两个字符复制进去
strcpy(str1,str2,2)
Strcmp(字符串1,字符串2) 比较两个字符串
英文字母,位置后面的大,小写大于大写。
- 字符串1与2相同,等于0
- 1>2,返回正数
- 1<2,返回负数
strlen(字符数组)
返回字符数组实际长度,不包括’\0’
strlwr(字符数组)将字符数组大写转化为小写
StringLoweRrcase
Strupr(字符数组) 将小写转化为大写
StringUpperCase
2函数
概念:
C语言可由一个主函数和其他若干函数组成
1 C语言由多个程序模块组成,每个程序模块有个源文件
2 源文件由一个多个函数或其他文件组成
3 C程序由main函数开始,最后回到main函数
4 函数平行,即定义函数是分别进行
5 用户来看函数两种,1 标准函数,库函数,由系统提供 2自定义函数
6 函数形式 有参函数,无参函数
类型为void,不返回值
无参函数一般形式
类型标识符 函数名()
{
声明部分
语句部分
}
有参函数
类型标识符 函数名()
{
声明部分
语句部分
}
空函数
类型标识符 函数名()
{
}
空函数用于功能扩展,占一个位置
定义函数,函数后面跟的参数(变量名)是形惨
调用函数,函数后面跟的参数(可以是表达式)是实参
实参和形参数说明:
1 形参未出现调用,不占用内存
2 实参可以是常量,变量,表达式
3 函数形参必须指定类型
4 实参和形惨 类型兼容或相同
5 实参向形参是值传递。
函数返回值,通过函数调用使主调函数获得一个值
1函数值使用retrun
2 定义函数时定义返回类型
3 定义值应该与返回值一致。
4 不带返回值的,设置为void.
函数一般调用形式
函数名(参数名)
1 函数语句 f()
2 表达式 f()*2;
3 作为函数实参 z(f())
调用函数
1 调用函数必须已存在
2 如果是库函数,必须include
3 如果是自定义函数,调用函数必须在后面。
函数调用前对函数进行声明
函数定义 函数功能的确立,包括函数名,函数值类型,参数,函数体,是一个函数单位
函数声明则是把名字 类型 参数 通知系统 编译时候检查。
函数声明两种
1 函数名(参数类型1 参数名1,参数类型2 参数名2)
2 函数名(参数类型1 ,参数类型2 )
a(int a,int b)
a(int,int);
说明:
1 在8.5之前 可以只要函数类型 函数名
2 如果被调用函数在调用函数之前,可以不加声明
函数嵌套调用
在调用函数过程中,又调用另一个函数。
函数递归调用
在调用函数过程直接或间接调用函数本身
一个递归问题 分为回推和递推两个阶段
数组元素可以作为函数参数。
数组名可以作为函数参数,传递的数组首元素的地址。
说明
1在调用函数和被调用分别声明函数
2 声明函数,函数大小不起作用,编译不会检查
3 实参和形惨类型一致
4 形参可以不指定大小
参数定义
可以把第一维数组大小省略
Int a[][3] 合法
局部变量
在一个函数内部定义的变量是局部变量,只在本函数使用
说明:
1 主函数定义变量只在主函数使用
2 不同函数可以使用不同变量
3 形式参数也是局部变量
4 在一个函数内部可以使用复合函数定义变量
在函数外面定义的变量是全局变量,可以为其他函数共用,适用范围在函数定义到源文件结束。
- 设置全局变量增加函数间联系的渠道
- 在不必要的时候不要使用全局变量
不使用原因:
1 执行过程占用存储单元
2 通用性降低
3 降低了清晰度
4 内部与外部同名,屏蔽外部
变量的存储类别
1 分为 静态存储方式 和动态存储方式
程序中用户使用存储空间
1 程序区
2 静态存储区
3 动态存储区
全局变量在静态存储区,程序开始分配,结束释放,占用固定内存空间
动态存储区
1 函数形式参数
2 自动变量(未加static)
3 函数调用时的现场保护和返回地址。
每个变量和函数有两个类别:数据类型 和数据的存储类别
数据类型 如整形
存储类别 数据在内存的存储方式
1 自动的 auto
2 静态的 static
3 寄存器的 register
4 外部的 extern
1 auto
默认存储类别,函数调用时分配,函数结束释放
形参和函数中变量都属于它
2 static
函数调用结束不消失保留原值,下次调用已有值,就是上次调用值
说明:
1 静态存储区分配内存
2 在编译时赋初值,
3 如果没赋值,默认会赋初值
4 虽然调用完毕存在,但其他函数不能调用
register变量
静态和动态变量放在内存中。
寄存器变量,放在寄存器中,存取速度比内存块。
说明
1 只有局部变量和形式参数可以作为寄存器变量
2 寄存器数目有限,不同变量处理方法不同
3 局部静态变量不能作为寄存器变量
外部变量 extern
外部变量是在函数外部声明的变量,作用域在函数定义开始,文件结束。
此作用域,所有函数都可以享用。
分配在静态存储区
如果在定义点之前使用,必须使用外部变量声明
Int main()
{
extern A,B
}
Int A=2,B=3;
多文件使用
在一个文件函数外部定义,另一个文件进行外部变量声明 extern Num
实质:
遇到extern,先从本文件寻找定义,找不到,从其他文件,再找不到,报错。
static声明外部变量
1 用static声明的外部变量只能在本文件使用,其他文件使用外部声明想使用会报错
变量的声明和定义
函数由声明部分与执行语句组成
声明部分是对有关标识符(变量,函数,结构体,共用体)的属性进行说明
对于函数,
函数的声明是函数的原型,函数的定义是函数本身
被调函数的声明在调用函数的声明部分。
对于变量
声明分为定义性声明,需要创建存储空间
引用性声明,不需要创建存储空间。
广义的 声明包括定义
比如 int a
但是并非所有 对于 extra 只是声明
狭义的 叙述方便 需要创建存储空间 定义
不需要创建存储空间 声明。
外部变量的定义与声明不同,定义只能有一次,在所有函数之外,声明可以有很多次。
函数声明:
1 以前函数可以只用函数类型和函数名
2 如果被调用函数在主调函数之前,可以不加声明。
3 文件开头已经对函数进行,各函数就不用进行声明
4 如果被调用函数原型为整形,可以不必作函数原型声明
但是在visual studio c+= 通不过
函数调用
函数是互相独立,平行。定义函数,一个函数不能包含另一个函数
不能嵌套定义函数,可以嵌套调用函数。
外部变量的定义只能有一次,函数之外
外部变量的声明有很多次,可以在函数之内。
根据定义分配存储空间,
声明是声明一个已经定义的变量,扩展作用范围。
Static声明两个作用:
1 对局部变量声明,该变量在程序执行期间不释放
2 全局使用static声明,该变量仅限在本文件
使用auto register static 是在定义变量基础上加上关键字
作用域分
局部变量{
动态局部变量,离开函数消失,静态:离开函数不消失,寄存器,离开函数消失
}
全局变量{
静态:仅限本文件,动态,其它文件可以使用};
变量存在时间:动态存储,调用函数分配,静态存储,整个程序运行期间
动态存储{
自动变量,寄存器变量,形式参数
}
静态存储{
静态局部变量,静态全局变量,外部变量
}
按存放位置
内存中的静态变量{
静态局部,静态全局,全局
}
动态存储,自动变量和形式参数
Cpu寄存器
变量的作用域和生存期,一个是空间,一个是时间
如果某个变量在某个范围有效,即是作用域,又称可见,又称为变量的可见性
如果变量在某一时刻存在,称为变量的生存期。
静态变量的作用域都在本文件
函数
函数本质全局,一个函数要被另外一个函数调用,也可以指定不能调用,能否被调用,分为内部函数与外部函数。
内部函数
一个函数只能被本文件函数调用就是内部函数,加上static
Static 类型标识符(函数参数)
Static int func(int a,int b)
外部函数
定义函数,如果在首部最左端加上extern,就是外部函数。能被外部文件调用。
Extern int func(int a,int b)
- 默认为外部函数,
- 需要调用此文件的函数,使用extern进行声明
指针
概念
指针作用:
1 有效表示复杂数据结构
2 动态分配内存
3 方便使用字符串
4 方便使用数组
5 获得一个以上结果
6 直接操作内存地址
编译时根据类型分配内存空间
每个字节都有一个编号,相当于地址 相当于旅馆中的房间号
地址所标志的内存单元存放数据 相当于房间中居住旅客
存取实质
通过变量名对内存单元进行存取
实质变量名经过编译转化为变量地址,变量存取通过地址进行。
存取方式
1 直接访问
按照变量地址存取变量值方式
K=i+j;
2 间接访问
将变量名地址存放到另一变量
特殊变量,专门存放地址。
比如 a=&b;
指针实质
通过地址找到所需变量单元,地址指向该变量单元(房间号指向房间)
将地址形象为指针。意思是通过它能找到以它为地址的内存单元
一个变量的地址称为该变量的指针。
如果有一个变量专门存放另一个变量的地址(指针),也就是指针变量。
指针是一个地址,指针变量是存放地址的变量
变量的指针和指向变量的指针变量
指针是一个地址
指针变量存放另一个变量地址,指向另一个变量
表示指针变量和指向变量的联系,用*表示指向
如果i_point为指针变量,则*i_ipont为指向的变量
I=3与*i_point=3作用相同
定义指针变量
C语言规定所有变量使用前必须定义,指定类型,依此分配内存空间。
指针变量专门用来存放地址,必须指定为指针变量
Int *point_1
左端int是定义指针类型必须指定的基类型
指针变量的基类型是该指针变量可以指向的变量的类型
定义指针变量一般形式
基类型 *指针变量名
赋值语句使指针变量获取另一变量的地址
Int *a;
a=&b;
指针变量的一般形式
基类型 *指针变量名
指针变量获取另一个变量地址
Int *b;
b=&a;
注意:
1 指针变量的*表示该变量的类型为指针型变量,指针变量名是i_point1,而不是*i_point1
2 定义指针必须指定基类型。
指定基类型原因:指针移动与运算时,移动一个位置,不同类型,移动字节不同
只有整形变量的地址才能放到整形变量的指针变量中
指针变量的引用:
牢记,指针变量只能存放地址,不要将一个整数(或其它任何非地址类型类型数据)赋给一个指针变量
*point=199; //不合法
运算符
1 & 取地址运算符
2 * 指针运算符 (间接运算符,) 取其指向的内容
说明
1 &*point 的含义,&*的优先级相同,但按照自右向左开始结合,因此先*point,就是a,
在&*point,j就是&a;
2 *&a,先&a得到a的地址,再*运算,即是&a指向的变量,a. *&a和a等价。
3 (*point)++ 相当于a++; 不加括号,因为自右向左结合,相当于*(point++),就是对原值++再进行*
数组
数组元素的指针就是数组的地址
Int *a; a=&arr[0]
数组名代表数组首元素地址
通过指针引用数组元素
如果把指针变量赋值给元素地址,指向元素
*p=1;
表示把1 赋值给 p指向的元素
C语言规定指针变量p指向数组元素,则p+1 是指向数组下一个元素,而不是地址+1
说明:
1 p+i和a+i就是a[i]的地址
2 *(p+i)与*(a+i)就是各自指向的元素
3 指向数组的指针元素可以带下标 p[i]与*(p+i)相同
引用数组元素两种方法:
1 下标法 a[i]
2 指针法 *(a+i)
使用数组注意点
1 通过改变指针变量的值指向不同的元素,
数组名a代表数组首元素地址,是指针常量,运行期间不改变
2 要注意指针变量的当前值
数组名作函数参数
Void foo(int a[]){}
数组名代表数组首元素的地址
实参数组名代表首元素的地址,而形参用来接受传递过来的数组首元素的地址
实质,把形参数组名按照指针变量处理。
f(int arr[]){}
编译后
f(int *arr){}
*(arr+i) 与 arr[i] 无条件等价
调用函数的方式虚实结合,但都是指传递
变量,传递的是变量的值
数组 传递的是数组首元素的地址。
用数组名作为函数实参,实际采用的形参是指针变量
因为用下标法和指针法都可以访问。
如果用指针变量作实参,必须使指针变量有实际值,指向一个已定义的单元
多维数组元素的地址
多维数组的定义:
int a[2][3]={{1,2,3,4},{1,2,3,4},{1,2,3,4}}
可以认为二维数组是数组的数组,二维数组是由3个一维数组组成的
a 代表二位数组的首元素的地址
首元素也是一个数组,即4个元素
所以二位数组的数组名代表首行首地址
假如是2000,第一行就是2000+2*4;
a+1的含义是a[1]的地址(因为这是在二维数组的范围内)
a[0],a[1],a[2]都是数组,C语言规定数组名代表首元素的地址
即a[0]=&a[0][0];a[1]=&a[1][0];a[2]=a[2][0];
一维数组序号为1的元素a[0]+1; 2000+2; (一维数组的范围内)
a[0]+1=a[0][1]的地址=&a[0][1]
a[0]+2=a[0][2]的地址=&a[0][2]
a0]和*(a+0)等价
a[i]和*(a +i)等价
a[0]+1与*(a+0)+1都是&a[0][1];
*(*(a+0)+1)=a[0][1];
a是二维数组名,*a就是第一个数组首元素地址
*(*a+1)=a[0][1];
*(a[i]+j)=(*(*(a+i)+j))=a[i][j]
务记*(a+i)和a[i]是等价
a[i]性质说明:
如果是一维数组,a[i]是数组序号为i的元素所占的内存单元内容。是有物理地址,占有内存单元
如果是二维数组,则a[i]是数组名,它只是一个地址。
对于二维数组
a, a+i,a[i],*(a+i),*(a+i)+J,a[i]+j都是地址
*(*(a+i)+j) 与*(a[i]+j)是值
a+1=*(a+1)=2008
原因:
a+1是数组序号为1的行的首地址,*(a+1)=a[1],a[1]是数组名,也是地址,指向a[1][0];
a和a[0]值相同都是2000,但含义不同
a指向一维数组
a[0]指向指向a[0][0]元素
a+1指向序号为1的行首地址
*(a+1),a[1],a[1]+0指向第一行0个元素
数组名a指向行,a+1,1代表1行中的所有元素。
指向行的指针前面加*转化为指向列指针
列如a和a+1指向行的指针加上*
*a 和*(a+1)指向列的指针
反之就是行的指针,在指向列的指针前面加&,就是指向行的指针
&a[0];
因为a[0]与*(a+0)等价,&a[0]就与&*a等价,
不能简单理解&a[i]为a[i]的物理地址,因为并不存在这样的一个实际变量,只是地址的计算方式,能够得到第i行的首地址。
&a[i]或a+i指向行
a[i]或*(a+i)指向列
&a[i]和a[i]值相等,但指向对象不同,即指针类型不同
*(a+i)是a[i]另一种表现形式,
在一维数组 a+i指向数组的一个存储单元
在二维数组 a+i指向数组的行
a是指向一堆数组(行)的指针,*a是指向列元素的指针,**a就是0行0列元素的值。
计算a[i][j] a[0][0]+i*m+j;
a[2][3]=a[0][0]+2*4+3;
指向多维数组元素的指针变量:
- 指向数组元素的指针变量
Int *p; p=a[0]; 指向数组名a[0],针对列,
- 指向一堆数组的指针变量:
P=a&[0] 指向m个元素的数组
Int (*p)[4] 指向包含4个整形元素的一堆数组
不能写成 int *p[4];这是指针变量数组
字符串与指针
两种方法访问字符串
1用字符数组存放字符串,然后输出
2 用字符地址指向字符串
Char *string=”i lov china”;
等价于:
Char *string;
String=”i love china”;
String 被定义为字符指针变量,只能指向一个字符类型变量。
实质是把 ”i love china”的第一个字符地址赋值给它
赋值实质,是先指向第一个字符,依此加1,直到遇到’\0’;
字符数组名或字符指针变量可以输出字符串。
而整形数组是不行的
通过%s对字符串进行输出
字符数组和字符指针存储说明:
1
- 字符数组由若干元素组成,每个元素放一个字符。
- 字符指针变量存放的是地址(字符串第一个变量地址),不是将字符串放到指针变量。
2 字符数组只能只能给各个元素赋值,
指针变量可以统一赋值
Char str[14];
Str=”i love china”
这种不行
改成Char str[14]=”i love china”;可以
计算a[i][j] a[0][0]+i*m+j;
a[2][3]=a[0][0]+2*4+3;
这样可以,但不是赋值字符串,而是字符串第一个字符地址。
3 对字符指针赋初值
计算a[i][j] a[0][0]+i*m+j;
a[2][3]=a[0][0]+2*4+3;
- 定义字符数组,在编译时分配内存单元,有确定地址。
定义字符指针变量,给字符指针变量分配内存单元,可以存放字符变量地址,指向字符数据。
指针变量未赋值,并未确定指向字符数据
如果输入时写指针变量,很危险,可能指向空白区域,或已存放指令和数据的内存。
4 结构体
在组合项中包含若干个类型不同的数据项,叫结构体。
Struct Student{
String name;
String sno;
}
声明结构体:
Struct 结构体名{成员列表}
结构体名用作结构体的标记,又称结构体标记
成员列表称为域表,每个成员称为结构体的域。
说明
1 声明结构体类型,并不分配内存,为了使用,要定义,并存储。
1 声明结构体类型再定义变量名
Struct Student sudent1,sudent2
2 声明结构体并定义变量
Struct Student{
String name;
String sno;
} sudent1
3 声明结构体不定义变量
Struct {
String name;
String sno;
} sudent1
结构体变量的引用:
1 不能整体进行输入输出,引用成员方式为变量名.成员名
2如果 本身属于结构体,则要若干运算符
3 结构体变量能进行运算
4 可以引用结构体变量地址
结构体变量可以再定义时初始化
Struct sudent{
String name;
}a={“10”};
结构体变量可以存放数组
结构体变量的指针
一个结构体变量的指针指向该结构体变量所占据内存的起始地址
Struct sudent{
String name;
}
Student s1;
Student *p1l
P1=&s1l
S1.name等价于(*p1).name;
为了方便直观,可以将(*p1),name改为p1->name,表示p1指向结构体变量的name成员。
同样 (*p1).name;等价于p1->name
结构体三种形式
结构体.变量名
(*p).name
p1->name
其中->称为指向运算符, (*p1),name
p1->n 得到p指向结构体变量的成员n 的值
p1->n++ 使用完加1
n++ p1-> 加1再使用
结构体变量作为参数
1 结构体变量成员作参数,值传递
2 结构体变量本身作参数,值传递,全部内容传递给形参,形参也占内存单元,开销大
3 结构体变量指针传递给形参,将结构体变量地址传递给形参
指针处理链表
链表是动态进行存储分配的一种结构
链表有一个头指针变量:
每个元素是结点
最后一个元素不指向任何元素,地址是null
链表再内存中可以不是连续存放的
简单链表
Struct Student a,b,c,*head;
Head=&a;
- next=&b
输出 head->next
总结,所有元素再程序中定义,不是临时开辟,也不能用完释放,叫静态链表
处理链表所需函数
Void 无类型 void * 无类型指针 就是可以指向任何类型的指针
C语言默认返回整形,C++默认无返回
链表是动态分配存储,需要时临时开辟一个结点的存储单元
1 malloc函数
其函数原型void *malloc(unsized int size)
作用是在内存的动态存储区中分配一个size的连续空间,此函数的值(返回值)即是一个分配域起始地址的指针(类型为void),如果函数未能成功执行(内存不足),则返回值为空
2 calloc函数
其函数原型void *calloc (unsized n,unsized size)
作用是在内存的动态存储区中分配N个size的连续空间,此函数的值(返回值)即是一个分配域起始地址的指针(类型为void),如果函数未能成功执行(内存不足),则返回值为空
3 free函数
Void free(void *p)
释放p指向内存区
建立动态链表
建立动态链表是指程序执行过程从无到有建立一个链表,即一个个开辟结点和输入各节点数据,并建立前后相链的关系。
函数首部括号内写void,代表没有参数。
Malloc带回的不指向任何数据类型,所以要强制转换(*void);
创建链表
将头节点指向第一个元素即可,
输出
依次输出,不为空即可
删除
依次轮询,找寻之后删除
插入
怎样找到插入位置,实现插入
共用体的概念
几种不同变量存放同一内存单元
不同变量共占同一内存单元叫共用体结构
Union 共用体名
{
成员表列
}变量表列
如:
Uniont data{
Int I;
Char a;
}a,bc
将声明与定义分开
Union data{
Int I;
Char a;
}
Union data u1,u2;
共用体与结构体含义不同。
结构体所占内存单元是各成员所占内存单元之和,每个成员有自己的内存单元。
共用体变量变量所占内存长度等于最长成员的长度。
共用体变量的引用方式
只有定义共用体变量,才能引用它,而且只能引用它的成员
共用体数据的特点:
1 同一个内存段存放不同类型数据,但同一瞬时只能存放一种
2 共用体变量起作用的是最后一次存放成员
3 共用体变量的地址和它各个成员地址一样
4 不能对共用体变量赋值,也不能用它获取值
5 不能用共用体变量作为参数或返回值,但能作为指针(与结构体类型)
6 可以在结构体定义共用体,或使用共用体数组
枚举类型
一个变量只有几种可能的值,则可以定义为枚举类型
所谓枚举就是变量的值一一列举出来,变量的值只限于列举出来的值范围。
声明枚举:
enum weekday(sun,mon,tue);
定义变量
enum weekday workday
weekday workday定义为枚举常量,值只能是sun,mon之间
weekday=sun 正确
sun,mon称为枚举元素或枚举常量,是用户定义的标识符
说明:
1 C语言中,枚举元素按照常量处理,不能赋值
2 枚举元素是有值的,C语言编译按定义的顺序使它们的值为0,1,2….
3 枚举值可以用来判断
4 整数不能直接赋值给枚举变量,可以强制转换后赋值
用typeof定义类型
除了直接使用C提供标准类名,还可以使用typeof声明新的类型代替已有类型
Typeof int INTEGER
下面等价
Int a;
INTEGER a
声明结构体类型
typeof struct
{
Int month;
Int day;
Int year;
}DATE;
这时可以用DATE定义
DATE birthday
进一步:
1 typeof int NUM[10]; 声明整形数组类型
NUM n
2 typeof char *STRING; 声明字符指针类型
NUM n
3 typeof int (*POINTER)() 指向函数的指针类型,该函数返回整形值
POINTER n1,n2
声明新类型的方法:
1 定义变量的方式写出定义体
2 将变量名换成新类型名
3 最前面加tpyof
4 新类型名定义变量
说明:
1 用typeof可以声明各种类型名,但不能定义变量
2 是已存在的类型增加类型名,不是新增类型
3 typef与#define相似,但不同,#define是预编译简单字符串替换
Typeof 是编译将变量名换成类型名
4 不同源文件用到同一类型数据,常用typeof声明,放到统一文件,需要时#incule引进
5 使用typeof利于通用与移植;
以上是阅读C程序设计做的学习笔记