1.32k likes | 1.54k Views
C 语言程序设计. 王正杰. 第四章 数组、指针与字符串. [ 本章重点 ] 数组的定义、初始化和使用;指针的定义、访问和运算;指针与数组和函数的联系;字符串与数组和指针的关系;结构体的应用;几种常用的算法和数据结构。 [ 本章难点 ] 指针的定义、赋值和运算;数组、字符串、结构体的操作以及动态存储分配。.
E N D
C语言程序设计 王正杰
[本章重点]数组的定义、初始化和使用;指针的定义、访问和运算;指针与数组和函数的联系;字符串与数组和指针的关系;结构体的应用;几种常用的算法和数据结构。 [本章难点]指针的定义、赋值和运算;数组、字符串、结构体的操作以及动态存储分配。
在C中,除了前面介绍的基本类型外,还有一种用基本类型数据按一定的规则组成的构造类型,构造类型的每一个分量可以是一个简单的类型变量,也可以又是一个构造类型的变量,它们可以像简单变量一样使用。C语言的构造类型有数组、结构体、共同体等。指针是C语言区别于其他程序设计语言的主要特征,指针可用于有效的表示数据之间复杂的逻辑关系,也可用于动态分配内存,还可以简单有效地处理数组。用来存放字符串的数组是字符数组,除了用访问数组的方式来在C中,除了前面介绍的基本类型外,还有一种用基本类型数据按一定的规则组成的构造类型,构造类型的每一个分量可以是一个简单的类型变量,也可以又是一个构造类型的变量,它们可以像简单变量一样使用。C语言的构造类型有数组、结构体、共同体等。指针是C语言区别于其他程序设计语言的主要特征,指针可用于有效的表示数据之间复杂的逻辑关系,也可用于动态分配内存,还可以简单有效地处理数组。用来存放字符串的数组是字符数组,除了用访问数组的方式来
操纵字符串外,我们还可以利用指针来使用字符串。本章将介绍典型的构造类型——数组和结构体,以及与数组和结构体密切相关的指针和字符串。操纵字符串外,我们还可以利用指针来使用字符串。本章将介绍典型的构造类型——数组和结构体,以及与数组和结构体密切相关的指针和字符串。
数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量称为该数组的元素。数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量称为该数组的元素。 • 一维数组通常用于表示由固定多个同类型的具有线性次序关系的数据所构成的复合数据,如向量、某个学生的各门课成绩、学生的姓名表等。在C语言中使用数组必须先进行定义,一维数组的定义形式为: • 存储类型说明符 数据类型标识符 数组名 [常量表达式]; • int a[5];
数组名是用户定义的数组标识符,数组名的命名规则要遵循标识符命名规则。方括号中的常量表达式表示数组元素的个数,也称为数组的长度。数组名是用户定义的数组标识符,数组名的命名规则要遵循标识符命名规则。方括号中的常量表达式表示数组元素的个数,也称为数组的长度。 • 对于数组定义应注意以下几点:1)数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。2)数组名不能与其它变量名相同。3)不能在方括号中用变量来表示元素的个数,可以用符号常数或常量表达式,如arrry[10]表示数组arrry有 10个元素,其下标从0开始。
一维数组的初始化 数组的初始化就是在定义的同时,给部分或全部元素赋值,一维数组的初始化的格式是: • 数据类型标识符 数组名[常量表达式]={初值表}; • 其中,初值表用一对花括号{}括起,每个初始值之间用逗号隔开。例如: • int array[5]={ 1,2,3,4,5}; • 该语句定义了一个包含5个整型元素的数组array并对其进行了初始化,初始化后array [0]=1、array[1]=2、array[2]=3、array[3]=4、array[4]=5。
初值表元素的数量可以少于数组长度,也就是可以只对数组的部分元素赋初值。例如:初值表元素的数量可以少于数组长度,也就是可以只对数组的部分元素赋初值。例如: • int array[5]={1,2,3}; • 初始化后前三个元素的值为array [0]=1、array[1]=2、array[2]=3,其余没赋初值的元素的值都为0,即array[3]=0、array[4]=0。
也可以省略数组长度,例如: • int array[]={ 1,2,3,4,5}; • 初始化后数组的长度为5。
一维数组的访问 像普通变量一样,数组定义之后,就可以在程序中访问数组中的元素。我们只能逐个访问其中的元素,对于数组元素的访问可以通过以下形式表示:数组名[下标] • int array[5]={ 1,2,3,4,5}; • a=array[3]; • array[3]=6;
voidmain() { intarray[5],i ; //使用循环语句对数组的5个元素分别赋值 for (i=0; i<5; i++) array[i]=i*i; //分别输出数组元素的值 for (i=0; i<5; i++) cout<<"下标为"<<i<<"的元素的值是:"<<array[i]<<endl; }
用数组来处理求Fibonacci数列问题 voidmain() { inti; intf[20]={1,1}; //初始化第0、1个数 for(i=2;i<20;i++) f[i]=f[i-2]+f[i-1]; //求第2~19个数 for(i=0;i<20;i++) //输出,每行5个数 { if(i%5==0) cout<<endl; cout<<f[i]<<" "; } }
向函数传递一维数组 当需要把一个一维数组传给一个函数时,调用者需要把该一维数组变量的名以及数组元素的个数传给被调函数,而被调函数的形参应为不带数组大小的一维数组定义以及数组元素的个数,下面我们通过一个例子来了解向函数传递一维数组的过程。
简单选择排序 假设排序过程中,待排记录序列的状态为: 有序序列R[1..i-1] 无序序列 R[i..n] 从中选出 关键字最小的记录 第 i 趟 简单选择排序 有序序列R[1..i] 无序序列R[i+1..n]
intmin ( inta[], intlength) { inti,m=0; //m用于记录最小元的下标 for (i=1; i<length; i++) if(a[i]<a[m]) //逐个比较数组的元素 m=i; //找出最小的元素 returnm; //函数 min将返回最小 //元素的下标: }
voidmain() { intarray[10]={18,26,23,13,15,25,27, 14,29,31},tag; tag=min(array,10); //调用函数min,并将数组 //名作为参数传给函数 cout<<"数组中最小的元素是:"<<array[tag]<<endl; }
对主调函数传递的数组进行排序 voidsort(inta[],intlength) { //定义变量tag以标记当前最小元素之下标,//定义交换变量buffer inti,j,tag,buffer; for(i=0;i<length;i++) { … } }
tag=i; //每次外循环,都将tag的值置为 //内循环处理的第一个元素的下标 //内循环的任务是找出数组中下标 //从i到length的元素的最小者 for(j=i;j<length;j++) if(a[tag]>a[j]) tag=j; //标记小元素的下标 //将当前未排好序数组的最小元素a[tag] //和元素a[i]交换 buffer=a[tag]; a[tag]=a[i]; a[i]=buffer;
void main() { intarray[10]={6,2,10,3,7,5,9,4,8,1},i; cout<<endl<<"排序前数组array:"; for(i=0;i<10;i++)cout<<array[i]<<" "; sort(array,10);//调用函数sort,并将数组array //和数组长度作为参数传递给函数 cout<<endl<<"排序后数组array为:"; for(i=0;i<10;i++)cout<<array[i]<<“ ”; //输出排序 //后的数组array cout<<endl; }
该程序使用的是选择排序,其思想是,第一次从所有元素中选取最小值,与a [0]交换;第二次从剩余元素中选取最小值,与a [1]交换;……;直至完成整个数组的排序。函数sort中,每轮内循环结束后tag中存放的就是剩余元素中最小者的下标;第i次外循环结束后,数组前i个元素就都是已经排好序的元素。当排序完成返回主调函数main后,主调函数的数组array就是已经完成排序的数组,这是因为main函数中的数组array和sort函数中的数组a代表同一块存储空间,从本质上讲它们是同一个数组。
用起泡法对10个数排序(由小到大)。 • 起泡法的思路是:将相邻两个数比较,将小的调到前头。
起泡排序 假设在排序过程中,记录序列R[1..n]的状态为: n-i+1 无序序列R[1..n-i+1] 有序序列 R[n-i+2..n] 比较相邻记录,将关键字最大的记录交换到n-i+1 的位置上 第 i 趟起泡排序 无序序列R[1..n-i] 有序序列 R[n-i+1..n]
voidmain() { inta[10]={6,2,10,3,7,5,9,4,8,1}; inti,j,t; for(j=1;j<10;j++) for(i=0;i<10-j;i++) if (a[i]>a[i+1]) {t=a[i];a[i]=a[i+1];a[i+1]=t;} cout<<"the sorted numberS :"<<endl; for(i=0;i<10;i++) cout<<a[i]<<" "; }
二维数组 • 前面介绍的数组只有一个下标,称为一维数组,而在实际问题中经常要处理多维的量,需要构造二维数组或更多维的数组。简单地说,二维数组就是具有两个下标的数组,其元素有两个下标,以标识它在数组中的位置。数组维数的概念与几何中维数的概念是相似的,如果说一维数组的元素是线性排列的,只要给出一个下标就可以确定某一特定元素。那么,二维数组就类似于几何中的X-Y坐标系,坐标系中只要指出X坐标和Y坐标就能确定一个点,而要确定二维数组中的某一个元素也只需指出元素所在的行和列。
二维数组定义 • 存储类型说明符 数据类型标识符 数组名[常量表达式1][常量表达式2]; • 其中存储类型说明和数据类型说明与一维数组相同,常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度,两个常量表达式中不能包含变量。例如: • int array[2][3]; • 定义了一个二维数组array,该数组有两行,每行有三个元素。
二维数组的行下标和列下标都是从0开始的,对于n行m列的二维数组,行下标的取值范围为0—n-1,列下标取值范围为0—m-1。二维数组的行下标和列下标都是从0开始的,对于n行m列的二维数组,行下标的取值范围为0—n-1,列下标取值范围为0—m-1。 • 二维数组的所有元素占有的内存空间是连续的,因此只要知道数组在内存中的起始地址就可以很容易的算出其它元素的存储单元地址。
二维数组的初始化 • 二维数组赋初值的方式有两种,一种是不分行给二维数组所有元素赋初值,比如int array[2][3]={1,2,3,4,5,6};,另一种更直观的方法是分行给二维数组所有元素赋初值,如int array[2][3]={{1,2,3},{4,5,6}};。
同一维数组一样,二维数组也可以只给部分元素赋初值,例如:同一维数组一样,二维数组也可以只给部分元素赋初值,例如: • int array[2][3]={1,2,3}; • 对数组前三个元素array[0][0]、array[0][1]、array[0][2]分别赋以1、2、3,其他剩余元素都为0。 • 此外二维数组还可以用分行的方法给部分元素赋值,例如: • int array[2][3]={{0,2},{4}}; • 初始化后各元素的值是array[0][0]=0、array[0][1]=2、array[0][2]=0、array[1][0]=4、array[1][1]=0、array[1][2]=0,
对二维数组赋初值可以省略说明第一维的长度,但是无论何种情况,都不能省略说明第二维的长度。在给全部元素赋值时,例如:对二维数组赋初值可以省略说明第一维的长度,但是无论何种情况,都不能省略说明第二维的长度。在给全部元素赋值时,例如: • int array[2][3]={1,2,3,4,5,6}; • 编译系统会根据初值个数分配相应的存储单元。在分行给元素赋初值时,如: • int array[][3]={{2},{4,5,6}}; • 编译系统也会根据所分的行数确定第一维的长度。
二维数组的使用 • 对二维数组的访问同一维数组类似,也是通过下标运算符“[]”和下标值来进行的。访问的形式如下: 数组名 [行下标][列下标] • 例如: • int array[2][3]={ 1,2,3,4,5,6}; • a=array[1][0]; • array[1][0]=6; • 这几条语句的功能是访问数组array中,第二行第一列的那个元素,将其值4赋予变量a后,赋予其新值6。
求4*4二维数组各行和各列元素之 void main() { intc[4],r[4], i,j; //定义c[4]存放列元素的和, // r[4]存放行元素的和 intarray[4][4]= {{1,2,3,4},{5,6,7,8}, {9,10,11,12},{13,14,15,16}}; for(i=0;i<4;i++) r[i]=c[i]=0; //初始化一维数组 …. for(i=0;i<4;i++) //输出行元素的和 cout<<“第”<<i+1<<“行元素的和为: "<<r[i]<<endl; for(i=0;i<4;i++) //输出列元素的和 cout<<“第”<<i+1<<“列元素的和为: "<<c[i]<<endl; }
for(i=0;i<4;i++) for(j=0;j<4;j++) { //将第i行第j列的元素的值加入r[i] r[i]=r[i]+array[i][j]; //将第i行第j列的元素的值加入c[j] c[i]=c[i]+array[j][i]; }
二维数组名作函数的参数 • 主函数中初始化一个矩阵并将每个元素都输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。
voidRowSum(intA[][4], intnrow) { intsum; for (inti = 0; i < nrow; i++) { sum = 0; for(intj = 0; j < 4; j++) sum += A[i][j]; cout << "Sum of row " << i<< " is " << sum << endl; A[i][0]=sum; } }
voidmain(void) { inti; intTable[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}}; for (i = 0; i < 3; i++) { for (intj = 0; j < 4; j++) cout << Table[i][j] << " "; cout << endl; } RowSum(Table,3); for (i = 0; i < 3; i++) cout << Table[i][0]; }
指 针 • 指针是C++语言最主要的风格之一,也是C++语言最重要的内容之一,利用指针变量不仅可以有效地表示各种数据结构,也可以用于参数传递和动态分配存储空间,并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针的应用使得C++语言具有灵活、实用、高效的特点。
在计算机中,所有的数据都是存放在存储器中的。计算机的存储器就像一个巨大的一维数组,每个数组元素就是一个存储单元。一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型数据占4个单元,字符型数据占1个单元,为了正确地访问这些内存单元,必须为每个内存单元编上号,根据一个内存单元的编号即可准确的找到该内存单元,内存单元的编号也叫做地址。在计算机中,所有的数据都是存放在存储器中的。计算机的存储器就像一个巨大的一维数组,每个数组元素就是一个存储单元。一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型数据占4个单元,字符型数据占1个单元,为了正确地访问这些内存单元,必须为每个内存单元编上号,根据一个内存单元的编号即可准确的找到该内存单元,内存单元的编号也叫做地址。
关于内存地址 • 内存空间的访问方式 • 通过变量名访问 • 通过地址访问 • 地址运算符:& 例: int var; 则&var 表示变量var在内存中的起始地址
指针就是指向其它变量的变量,指针的值就是其所指对象的内存地址。严格地讲,指针是C语言中的一种数据类型,但它是一种特殊的数据类型,指针并不是某个具体的数据值,而是指向数据存储单元的计算机存储器的地址。内存单元的地址(指针)和内存单元的内容是两个不同的概念,对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。指针就是指向其它变量的变量,指针的值就是其所指对象的内存地址。严格地讲,指针是C语言中的一种数据类型,但它是一种特殊的数据类型,指针并不是某个具体的数据值,而是指向数据存储单元的计算机存储器的地址。内存单元的地址(指针)和内存单元的内容是两个不同的概念,对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。
指针的定义 • 与普通变量一样,指针变量在使用前也需要定义。其一般形式为: • 数据类型标识符 *变量名; • int *p; //说明p是一个指针变量,它的值是某个整型变量的地址 • float *q; //说明q是指向浮点变量的指针变量 • char f ,*r; //说明f是字符变量,r是指向字符变量的指针变量
指针的赋值 • 在定义一个指针的同时也可以对它进行初始化赋值,通常是用一个与说明的指针具有相同类型的变量的地址来进行赋值的。初始化赋值的形式如下: • 数据类型说明符 *指针变量名=地址; • 其中地址可以是同类型变量的地址、数组的地址或数组元素的地址。地址可以用取地址运算符&(形式为 &变量名)获得。例如: • int a=5,*p=&a; • 语句执行完后,变量a的地址将赋给指针变量p,或称将指针p指向变量a
指针变量也可以在定义后赋值,如上述语句就可以改为 int a=5, *p; p=&a; • 需要注意的是,没有赋值的指针变量中存放的是一个随机值,使用没有赋过值的指针是相当危险的。如果要使指针变量中的指针不指向任何存储单元,那么可以将空指针赋给指针变量,空指针的值为0,也可以用在stdio.h中定义的符号常量NULL表示。
指针的访问 • 前面介绍的是如何对指针变量进行赋值,对指针变量赋值后就可以对其进行访问了。 • 对指针值的访问有两种形式, • 一种是将一个指针的值赋给另一个指针 • 另一种是用指针运算符*将指针所指向的变量的值取出用以访问。
voidmain() { inta=10,b,*p=&a,*q; q=p; // 将一个指针的值赋给另一个指针 b=*q; //用指针运算符*将指针q所指向 //的变量的值取出并赋给b cout<<"b的值是:"<<b<<endl; }
以指针作为函数参数 • 以地址方式传递数据,可以用来返回函数处理结果。 • 实参是数组名时形参可以是指针。
读入三个浮点数,将整数部分和小数部分分别输出读入三个浮点数,将整数部分和小数部分分别输出 void splitfloat(floatx, int *intpart,float *fracpart) { //形参intpart、 fracpart是指针 *intpart = int(x); // 取x的整数部分 *fracpart = x - *intpart; //取x的小数部分 }