C语言笔记(谭版 第六章~末)
第六章 利用数组批量处理数据
数组是一组有序数据的集合,数组中的每一个元素都属于同一数据类型。
算是一种数据的组织结构。初现结构端倪。
一维数组
定义:
类型符 数组名[常量表达式] 数组名命名遵行标示符命名规则
C语言中不允许对数组的大小作动态定义。
说明:如果在被调用函数中定义数组,其长度可以是变量或非常量表达式。实参。此情况称为可变长数组。若数组指定为静态存储方式,此方式不合法。
引用: 数组名[下标] 下标是常量或常量表达式 序号从0开始计起。
初始化:初始化列表 int a[5]={1,2,3,4,5}; 循环语句
未被初始化列表初始化的元素,自动为0(字符’\0’,指针为NULL)
二维数组
二维数组常称为矩阵。可以看成是矩阵,亦可看成是元素为数组的一维数组。
定义:类型符 数组名[常量表达式][常量表达式] 行 列 按行存放。
引用:数组名[整型表达式][整型表达式] 行列序号均从0算起
初始化:利用初始化列表 a[2][3]={{},{}}; a[][3]={};a[][3]={{},{}}; 自动计算,列数必写
多维数组情况类推。
字符型数组
C语言没有字符串类型,字符串是通过数组来实现的。
定义:char a[10]; 若用int a[10];合法但浪费空间。
初始化:初始化列表 未赋为’\0’ 空 ‘ ‘为空格 可手动加’\0’,便于处理
字符串常量初始法 char a[]={“I am a student!”};
此时字符串后自动加上字符串结束标志 ’\0’
字符数组输入输出:
scanf(“%s”,c); 输入时遇到空格或回车时结束并补上’\0’ 系统将空格视为字符串的分隔符。
printf(“%s”,c); 输出第一个’\0’前的字符串
字符串处理函数
puts(字符数组名) 输出完字符串后换行。
gets(字符数组名) 输入字符串给字符数组,并返回数组首地址
strcat(字符数组1,字符数组2); 数组1必须足够大,以便容纳 返回数组1地址
strcpy(字符数组1,字符数组2(字符串));复制,1长度不小于2,包括’\0’
strncpy(str1,str2,2); 部分复制,指定覆盖个数,不包含’\0’
strcmp(字符串1,字符串2); 相等返回0,1>2返回正数,反之负数
从左至右ascii码比较
strlen(字符数组);返回字符串长度
第二章 用函数实现规模化程序设计
每个函数实现一个特定功能,函数名字应反映其代表的功能。
一个c程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。便于分别编写和编译,提高调试效率。一个源文件可以为多个程序共用。
一个源程序文件由一个或多个函数及相关内容(指令,数据声明,定义等)组成。一个源文件为一个编译单位。
C程序执行从main函数开始。所有函数都是平行的,不能嵌套,只有调用,main函数只能被操作系统调用。
定义函数
无参 类型名 函数名(void(或置空))
{
函数体(声明部分,语句部分)
}
有参 类型名 函数名(形参列表)
{
函数体
}
空函数 类型名 函数名() {}
调用函数
函数名(实参列表);
调用形式:函数调用语句,函数表达式,函数参数
调用时的数据传递:实参可以是变量,常量,表达式,调用时实参赋给形参,实参和形参类型应相同或赋值兼容。(自动转换)
形参存储单元是临时分配的,调用结束后单元释放。值传递,结束后实参不变。
return (变量常量表达式);
函数声明和函数原型
库函数:用#include<>将有关库函数的信息包含到文件中来。(库函数声明)
自定义函数:函数声明
函数原型即函数定义的首行,C中用函数原型来进行函数声明。写函数声明时可以省略形参。写出形参便于理解函数。
如果声明写在文件开头(所有函数之前)则在其函数中不必再声明。在整个文件范围有效。
函数的嵌套调用
函数不可嵌套定义,但可以嵌套调用。即在函数定义中用调用的方式来使用其他函数。
函数的递归调用
直接或间接调用该函数本身。
数组作为函数参数
数组元素可以用作函数实参,其用法和变量相同,向形参传递数组元素的值。
数组元素不可以用作形参。
数组名也可以做实参和形参,传递的是数组第一个元素的地址。
用数组名作函数实参时,向形参(数组名或指针变量)传递的是数组首元素地址。
如定义函数: float average(float array[10]) (c编译系统并不会检查形参数组的大小)
{ } 可以不加10
调用时 float score[10]; ave=average(score);
地址传递,加下标,依次读存储单元,此时对形参的操作就是对实参的操作
可以用多维数组名做函数的实参和形参,形参数组定义时可以省略第一维大小说明。
C编译系统不检查第一维大小,可从数组存储形式领会。
局部变量和全局变量 变量作用域问题
局部变量:函数内部定义的(整函数范围内),复合语句定义的(复合语句内) 形式参数
两个作用域不同的局部变量可重名。包括复合语句{}
全局变量:写在函数之外,全局变量可以为本文件中其它函数共用,它的有效范围为从定义变量的位置开始到本源文件结束。
利用全局变量来增加函数间的联系渠道。一般全局变量名首字母为大写(非规定)
不在必要时不要使用全局变量:
独占内存,通用性,可靠性低,降低程序的清晰性(迷糊)
局部全局变量冲突(重名,作用域重叠)局部有效全局屏蔽。
变量的存储方式和生存期
静态存储方式(固定),动态存储方式(根据需要)
内存供用户使用存储空间:程序区,静态存储区,动态存储区
全局变量存放在静态存储区。
动态存储区:形参,自动变量,函数调用时的现场保护和返回地址等
在C语言中,每一个变量和函数都有两个属性:数据类型和数据的存储类别
局部变量的存储类别:
自动变量(auto)函数中局部变量,不专门声明为static的存储类别,动态存储区。函数调用时分配,结束时释放。
静态局部变量,用关键字static声明,结束后不消失,存储单元保留。下一次调用函数原值保留。静态存储区,在程序运行期间不释放。
!!对静态局部变量是在编译时赋初值的,只赋一次,程序运行时已有初值,每次调用时不再赋初值而是保留上次的值。自动变量运行时赋值,故会反复赋新值。
自动变量不赋初值:无法预知,因为自动分配单元
静态变量不赋初值:0或’\0’
用静态存储多占内存,降低可读性。
寄存器变量(register)
针对于使用频繁的变量,直接存储在CPU寄存器中,提高执行效率。
register int f; 有些编译系统能自动识别优化转换。
全局变量的存储类别
全局变量存放在静态存储区中。生存期整个运行过程。主要研究其作用域范围。
扩展本文件:extern声明 (如函数声明)
扩展到不同文件:在不同文件作extern声明。编译系统在编译和连接时会处理。
extern声明:外部变量声明,让外部变量从此处开始有效。
extern 变量名1,变量名2,….
扩展全局变量作用域应十分慎重,因为占固定存储空间,可被任意修改。
限制作用域 static声明 static int a; 静态外部变量 只在本文件中生效
可在不同文件中使用相同外部变量名而不互相干扰 勉误用
声明局部变量存储类型是为了确定其存储区及生存期,而对全局变量的存储类型声明是作用域扩展问题。
内部函数和外部函数
内部函数:只能被本文件中其他函数所调用 static int fun(int a,int b) {}
不同文件若有同名,亦不相干扰
外部函数 可供其他文件调用 可省略extern 在需要调用此函数的其他文件中,需要对此函数作声明,要加关键字extern 表示该函数是在其他文件中定义的外部函数,但一般都省略 .h文件包含所在库所有函数的声明
第三章 善于利用指针
需正确而灵活运用
直接按变量名进行访问,称为直接访问,间接访问为通过存有地址信息的变量来访问。
若有一个变量专门存放另一变量的地址,则它称为“指针变量”
指针是一个地址。区分指针与指针变量之分。
指针变量 (能够指向变量,数组,函数等)
定义 类型名 *指针变量名 如 int *pointer_1; 只能指向规定的基类型
一个变量的指针含义包括两个方面,一是以存储单元编号表示的地址,二是它指向的存储单元的数据类型
指针变量中只能存放地址,不要将一个整数赋给一个指针变量。
引用 赋值 p=&a; *p=1; printf(“%o”,p); &取地址运算符 *指针运算符
指针变量作为函数参数(传地址)
通过指针变量,可以实现通过调用函数使变量的值发生变化,在主调函数中可以使用这些改变了的值。函数的调用只可以得到一个返回值,而使用指针变量做参数,可以得到多个变化了的值。
通过指针引用数组
引用数组可以用下标法(a[i]),亦可以用指针法(*(a+i))。 指针法能使程序质量高。
在C语言中,数组名代表数组中首元素地址。 故 p=&a[0]; 与p=a;等价
p+1指向下一个元素,p-1指向上一个元素。并不是简单加1,与基类型有关。
p++,++p,p--,--p 亦同原理
[]实际是变址运算符,按指针机理寻找元素。超出数组,C编译系统并不知情。
指针变量可带下标 p[i]处理成*(p+i) 容易出错,少用
p2-p1 相对位置
数组名是一个指针常量,不是变量。不可自加,自减。数组名可作为函数参数,为地址传递。
常用这种方法通过调用一个函数来改变实参数组的值。
如果指针变量做实参,必须先使其有确定值。
通过指针引用多维数组 *(a+i) a[i] 等价 a[i][j] *(*(a+i)+j)等价 这里必须注意理解
二维数组a a 与*a 地址值是一样的 但如果进行 a+1 (*a)+1 就不一样了 步进不一样
&a[0]即为a,不要把&a[i]简单理解为a[i]元素的物理地址,因为并不存在a[i]这样一个实际的数据存储单元。仅是一种地址的计算方法。
指向多维数组元素的指针变量 可以是指向元素的指针变量,亦可是指向m个元素组成的一维数组的指针变量。
int * p; 指向的是一个元素 若 int (*p)[4]; p为指向包含4个整型元素的一维数组,指向数组的指针 p的基类型是一维数组
用指向数组的指针作函数参数
实参与形参如果是指针类型,应当要求他们的类型一致,int * 与 int (*)[4]就是不同类型
数组与指针常紧密联系,使用熟练,可以提高程序质量,更方便灵活。
通过指针引用字符串
Char * string=”I am a student”; 与char string[]=”I am a student”;一致
可以利用数组或指针的方法引用字符串,对于字符元素,可用下标法或指针法。
字符指针作函数参数 字符串处理 字符指针与字符数组名是有区别的 变量 常量
使用字符指针变量和字符数组的比较
编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元(VC++为指针变量分4个字节) 应当在定义指针变量后,及时指定其指向。
字符数组各元素是可以改变的,但字符指针变量指向的字符串常量中的内容是不可以被取代的。
字符串在内存中是按数组的形式存储的。
用指针变量指向一个格式字符串,可用它来代替printf中的格式字符串。
Char * format; format=”a=%d,b=%f\n”; printf(format,a,b);
只要改变format所指向的字符串,就可以改变输入输出格式。可变格式输出函数。
也可以用字符数组实现 char format[]=”a=%d,b=%f\n”; printf(format,a,b);
使用字符数组时,只能采用在定义数组时初始化或逐个对元素赋值的方法,而不能用赋值语句对数组整体赋值(字符指针变量就可以)
指向函数的指针
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配了一段存储空间,这段存储空间的起始地址称为这个函数的指针。
int (*p)(int,int); p指向一个整型函数,该函数有两个整型参数
int *p(int,int); 则为定义一个返回指针的函数
调用一个函数,除了用函数名调用外,还可以通过指向函数的指针变量调用。
p=max; (指向符合定义格式的函数,max为函数名) c=(*p)(a,b);(调用函数)
和数组名代表数组首元素类似,函数名代表该函数的入口地址。
返回值类型名 (*指针变量名)(参数列表)
用函数名调用函数,只能调用所指定的一个函数,而通过指针变量调用函数比较灵活,可以根据不同情况先后调用不同函数。
用指向函数的指针作函数参数
指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到其他函数。
实现可变化的调用。即只需改变指针,内部调用的函数就可以改变。增进灵活性。
如此可编写一些较复杂的程序。
返回指针值的函数
类型名 * 函数名(参数表列) 与用指针调用函数少一个()应区分
指针数组和多重指针
一个数组,若其元素均为指针类型数据,称为指针数组。
类型名 * 数组名[数组长度]
指向指针数据的指针
类型名 ** 变量名; 可分两部分理解 指向某类型指针的指针变量
指针数组作main函数的形参
以前一般写作int main() 在某些情况下main函数可以有参数 int main(int argc,char *argv[]);
Main函数是操作系统调用的,实现只能由操作系统给出,在操作命令下,实参是和执行文件的命令一起给出的。 故像DOS,UNIX,linux,命令行的一般形式为 命令名 参数1 参数2….
利用指针数组作main函数形参,可以向程序传送命令行参数(这些参数是字符串)。
动态内存分配与指向它的指针变量
C语言允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,亦不必等到函数结束时才释放,需要即开辟,不用即释放。
由于未经声明,因此不能用变量名或数组名去引用,只能通过指针引用。
建立内存动态分配
使用malloc函数 void *malloc(unsigned int size); 开辟长度为size的连续空间,返回的是无类型规定的指针,指向第一个地址。分配不成功返回NULL。
使用calloc函数
Void *calloc(unsigned n,unsigned size); 在内存的动态存储区分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。函数返回指向所分配域的起始位置的指针,分配不成功返回NULL。
free函数
void free(void *p); 释放P所指向的动态空间 p是最近一次调用calloc或malloc函数时得到的返回值。
realloc函数 void *realloc(void *p,unsigned int size); 改变已分配的动态空间的大小
以上四函数声明在stdlib.h中。
C99标准把以上函数的基类型定为void类型,这种指针称为无类型指针,即不指向哪一种具体的类型数据,仅提供一个纯地址,不能指向任何具体对象。
void指针类型 (C99)
void * 不指向任何确定类型的指针变量 即纯地址
当把void指针赋值给不同基类型的指针变量,编译系统会自动进行转换。不必强制转换。
(int *) malloc(); 强制
内存动态分配主要应用于建立程序中动态数据结构。
在stdio.h文件中对Null进行了定义 #define NULL 0; 引用指针变量之前应对它赋值。
使用指针优点:提高程序效率,可从函数调用得到多个可改变的值,实现动态存储。
第四章 用户自己建立数据类型
C语言允许用户根据需要自己建立一些数据类型,用它来定义变量。
定义和使用结构体变量
C语言允许用户自己建立由不同类型数据组成的组合型数据结构,它称为结构体。
声明结构体类型 struct 结构体名 {成员列表};
成员可以是另一个结构体类型。
定义结构体变量 先声明,再定义 声明同时定义
不指定类型名而直接定义(用得少)
结构体类型中的成员名与程序中变量名可以相同,不冲突
变量的初始化和引用
初始化列表是用花括号括起来的一些常量。
C99允许对某一成员初始化 struct Student b={.name=”zhang heng”};
系统初始化都为0(\0,NULL)
引用成员值 结构以变量名.成员名 student.num=10010; 对成员赋值
不能企图全部输出结构体,只能对各个成员分别输入输出。
若成员本身又为一结构体,只能层层深入,只能对最低级的成分进行赋值和存取以及运算。
同类的结构体变量可以相互赋值
可以引用结构体成员地址,也可以引用结构体变量地址。
结构体变量的地址主要用作函数参数,传递结构体变量的地址。
使用结构体数组
结构类型名 数组名[数组长度];
如 struct person leader[3]; struct 结构体名 {成员列表} 数组名[数组长度];
Struct person leader[3]={“li”,0,”zhang”,0,”sun”,0};
结构体指针
指向结构体对象的指针,可指向结构体变量,也可指向结构体数组中的元素。
Struct student *pt; 基类型必须一致
为了方便和直观,C语言允许把(*p).num用p->num来代替。->称为指向运算符。
如果要将成员地址赋给p,可以用起那个强制类型转换。
p=(struct student *)stu[0].name; p的指向类型并不改变 所以p++则指向stu[1].name的首地址
用结构体变量和结构体变量的指针作函数参数
成员作实参,值传递。结构体变量作实参,值传递。用指向结构体变量的指针作实参,地址传递。
用指针处理链表
链表是种常见的重要的数据结构。动态地进行存储分配的一种结构。根据需要开辟内存单元。
用结构体变量来建立链表是最合适的。
struct student {int num; float score; struct student *next;}; 链表节点的基本形式
静态链表 所有结点都是在程序中定义的,不是临时开辟的,亦不能用完后释放。程序运行完后才消除。
建立动态链表 用完后能释放 即采用动态内存分配方法来建立。
sizeof();返回的是编译系统规定的占有字节数而不是理论字节数。切记。
结构体和指针的应用领域很宽广,除了单向链表外,还有环形链表和双向链表。此外还有队列,树,栈,图等数据结构。
共用体类型
使几个不同的变量共享同一段内存的结构,称为“共用体”类型的结构。
union 共用体名 {成员列表} 变量表列;使用覆盖技术
可以把声明与定义分开。与结构体类似。
内存长度等于最长成员的长度。
不能引用共用体变量本身,而只能引用其成员。a.i a.ch a.f
共用体变量的存储区,可以按不同类型存放数据。
在使用共用体变量时应注意当前存放在共用体存储区的是哪个成员类型。
共用体变量地址与各成员地址都为同一地址。&a.i; &a.ch; &a.f;
不能对共用体变量名赋值,C99允许同类型的共用体变量互相赋值。
现在的C99允许用共用体变量作为函数参数。
共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。结构体也可以出席那在共用体类型定义中,数组也可以作为共用体的成员。
数据处理中,有时需要对同一段空间安排不同的用途。
使用枚举类型
如果一个变量只有几种可能的值,可以定义为枚举类型。
声明枚举类型:enum [枚举名]{枚举元素列表}; 默认第一个元素为0,依次加1。亦可人为指定。
枚举元素按常量处理,是个代表常量的标识符。每一个枚举元素都代表一个整数。由于枚举型变量的值是整数,因此C99把枚举类型作为整型类型中的一种。
使用枚举,是使程序直观,方便。
用typedef声明新类型名
可以用typedef指定新的类型名来代替已有的类型名。
简单地用一个新的类型名代替原有类型名
typedef int Integer; 增加程序可读性。
命名一个简单的类型名代替复杂的类型
复杂的诸如结构体,共用体,枚举,指针,数组等,名字冗长。
按定义变量的方式,把变量名换上新类型名,并且在最前面加typedef,就声明了新类型名代表原来的类型。
当不同源文件中用到同一类型数据,常用typedef声明一些数据类型。可以把所有的typedef声明单独放在一个头文件中,然后在需要它们的文件中调用头文件即可。这样就不必在每个文件都自己定义typedef名称了。
使编程方便,便于移植,程序简洁,易读。程序移植只需改动定义体而已。
第五章 对文件的输入输出
C文件的有关基本知识
程序文件 c obj exe等 内容是程序代码
数据文件 内容不是程序而是供程序运行时读写的数据
前面各章所处理的数据输入输出,都是依靠终端的。
所谓文件一般指存储在外部介质上数据的集合。
为了简化用户对输入输出设备的操作,使用户不必区分各设备之区别,操作系统把各种设备都统一作为文件来处理。终端键盘是输入文件,显示屏和打印机都是输出文件。
操作系统以文件为单位对数据进行管理的。
作为输入输出的各种文件或设备都是统一以逻辑数据流的方式出现的。
文件标识 包括 文件路径 文件名主干 文件后缀 3个都写出来才能唯一标识
文件的分类
数据文件可分为ASCII文件和二进制文件(映像文件)
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储(输入输出都要转换)亦可用二进制形式存储。
文件缓冲区
缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区。 分为输入文件缓冲区和输出文件缓冲区
文件类型指针
每个被使用的文件都在内存中开辟一个相应的文件信息去,用来存放文件信息。这些信息保存在一个结构体变量中。 定义为FILE类型 常定义一个文件指针 用来指向该文件信息区
FILE *pt; 指向内存中文件信息区开头 通过文件信息区中的信息就能够访问该文件
打开与关闭文件
打开文件即为文件建立相应的信息区和缓存区(暂时存放输入输出数据)指针变量指定
关闭文件就是撤销这一切。
fopen函数打开数据文件 fopen(文件名,使用文件方式); 返回值是指向文件信息区的指针。
FILE *fp; fp=fopen(“a1”,”r”);
文本文件:r 只读(输入数据) w (输出数据)只写 a 追加 文本文件尾添加数据
r+ w+ a+
二进制文件:rb只读 输入数据 wb (输出数据) ab 追加
rb+ wb+ ab+ 不同编译系统规定可能不同
程序中可以使用3个标准的流文件,标准输入流,标准输出流,标准出错输出流。
程序开始运行时系统自动打开这3个标准流文件。
fclose函数关闭数据文件 fclose(文件指针);
如果不关闭文件将会丢失数据,但有的编译系统在程序结束前会自动将缓存区的内容写到文件,避免了这个问题。成功执行后fclose返回0,不成功返回EOF(-1)
顺序读写数据文件
读写一个字符
fgetc(文件指针);返回所读字符或EOF(文件结束标志)
fputc(字符变量,文件指针); exit是标准C的库函数,程序终止。
读写一个字符串 利用fgets和fputs函数
用格式化的方法读写文件 fprintf和fscanf函数
用二进制方式向文件读写一组数据 fread和fwrite函数
随机读写数据文件
相对于顺序读写来说的,可以对文件中任何位置的数据进行访问。
文件读写的出错检测
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。