C语言程序设计(Part Ⅳ)的整理笔记,若有错误,欢迎指正。
C语言程序设计(Part I)
C语言程序设计(Part II
C语言程序设计(Part III)
一维数组
一维数组的定义:类型符 数组名[常量表达式];
常量表达式中可以包括常量和符号常量,不能包括变量。
例:(错误❌)
int n;
scanf("%d,&n"); //企图在程序中临时输入数组的大小n
int a[n];
一维数组的引用:数组名[下标]
- 数组下标是从0开始的。
- 定义数组时用到的"数组名[常量表达式]"和引用数组元素时用的"数组名[下标]"在形式上相似,但在含义和用法上是不同的。
- 简单的判别方法:如果在数组名[常量]前有类型名(char,int,float,double等),则此时是定义数组;如果在其前面没有类型名,则是引用数组元素。
例:
int a[10]; //定义数组长度为10
t=a[6]; //引用a数组中序号为6的元素。此时6不代表数组长度。
- 只能逐个引用数组元素而不能一次引用整个数组中的全部元素。
- 数组的名字代表数组首元素的地址,而不是代表数组中的全部元素的值。
例:(错误❌)
int a[4];
printf("%d,%d,%d,%d",a); //企图用数组名一次输出全部元素
一维数组的初始化:
- 在定义数组时对全部数组元素赋初值
int a[6]={0,1,2,3,4,5};
//将数组元素的初值依次放在一对大括号里,按顺序赋给相应的数组元素。
- 可以只给一部分元素赋值。
int a[6]={0,1,2,3};
//只给前面4个元素赋初值,后2个元素自动设0
- 在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度,系统会根据数据的数量确定数组的长度。
例:
int a[6]={0,1,2,3,4,5};
//等价于int a[]={0,1,2,3,4,5};
!此方法只可用于:所定义的数组的长度和初始化的数据个数相同;如果希望数组的长度与提供初值的个数不相同,则数组长度不能省略。
一维数组冒泡排序
!冒泡排序的思想: 每次比较两个相邻的元素, 如果他们的顺序错误就把他们交换位置。
比如有五个数: 78, 21, 8, 14, 30,要求从大到小排序,首先对相邻的两个进行比较,如果前一个小于后一个,则进行交换。
第一趟:
第一次比较: 78, 21, 8, 14, 30(78>21,不交换)
第二次比较: 78, 21, 8, 14, 30(21>8,不交换)
第三次比较: 78, 21, 14, 8, 30(8<14,交换8和14)
第四次比较: 78, 21, 14, 30, 8(8<30,交换8和30)
经过第一趟比较后, 5个数中最小的数已经在最后面了, 接下来只需比较前4个数, 依次类推
第二趟:
第一次比较: 78, 21, 14, 30, 8(78>21,不交换)
第二次比较: 78, 21, 14, 30, 8(21>14,不交换)
第三次比较: 78, 21, 30, 14, 8(14<30,交换14和30)
经过第二趟比较后, 5个数中最小的2个数已经在最后面了, 接下来只需比较前3个数, 依次类推
第三趟:
第一次比较: 78, 21, 30, 14, 8(78>21,不交换)
第二次比较: 78, 30, 21, 14, 8(21<30,交换21和30)
经过第三趟比较后, 5个数中最小的3个数已经在最后面了, 接下来只需比较前2个数, 依次类推
第四趟:
第一次比较: 78, 30, 21, 14, 8(78>30,不交换)
排序完成
冒泡排序原理:每一趟只能将一个数归位,如果有n个数进行排序,只需将n-1个数归位,也就是说要进行n-1趟操作(已经归位的数不用再比较)。
#include<stdio.h>
int main()
{
int a[10] ;
int i, j, t;
printf("input 10 numbers :\n");
for (i = 0; i < 10; i++)
scanf("%d", &a[i]); //先后输入10个整数
printf("\n");
for (j = 0; j < 9; j++) //进行9次循环,实现9趟比较
for (i = 0; i < 9 - j; i++) //在每一趟中进行9-j次比较
if (a[i] > a[i + 1]) //相邻两个数比较
{
t = a[i]; a[i] = a[i + 1]; a[i + 1] = t;
}
printf("the sorted numbers :\n");
for (i = 0; i < 10; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
!冒泡排序最好的时间复杂度为
O
(
n
)
O(n)
O(n);冒泡排序的最坏时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
因此,冒泡排序总的平均时间复杂度为
O
(
n
2
)
O(n^2)
O(n2) 。
- 最好情况是指:(假如要求最终的输出结果为逆序排序)输入时即为逆序,此时总共需要比较 n − 1 n-1 n−1次(第一趟冒泡排序既可以完成排序工作),总共需要交换的次数是0;
- 最坏情况是指:(假如要求最终的输出结果为逆序排序)输入时全为顺序,此时总共需要比较 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1)次(第 n − 1 n-1 n−1趟冒泡排序结束才可以完成排序工作),总共需要交换的次数是 3 n ( n − 1 ) 2 \frac{3n(n-1)}{2} 23n(n−1)次。
!冒泡排序的临时变量所占空间不随处理数据n的大小改变而改变,则空间复杂度为 O ( 1 ) O(1) O(1)。
- 稳定性:因为每次比较后如果两个相邻元素相等我们并不会将它们交换,所以冒泡不会改变相同元素的下标,所以冒泡排序是一个稳定的排序。
- 优点:比较简单,空间复杂度较低,是稳定的;
- 缺点:时间复杂度太高,效率慢;
二维数组
二维数组的定义:类型符 数组名[常量表达式][常量表达式];
不能写成
float a[3,4],b[5,10];
(错误❌)
C语言中,二维数组中元素排列的顺序是:按行存放,即在内存中先顺序存放第一行的元素,再存放第二行的元素。在内存中第一行元素和第二行元素是连续存放的。
!用矩阵形式表示二维数组,是逻辑上的概念,能形象地表示出行和列关系。而在内存中存放数组是物理上的实现,是线性的、连续存放的,而不是二维的。
二维数组的引用:数组名[下标][下标]
!行序号和列序号均从0起算
二维数组的引用:
-
分行给二维数组赋初值
例:int a[3][4]={ {1,2,3,4} , {5,6,7,8} , {9,10,11,12} };
-
可将所有数据写在一个大括号内,按数组排列的顺序对各元素赋初值。
例:int a[3][4]={ 1,2,3,4,5,6,7,8,9,10,11,12 };
-
可以对部分元素赋初值,其余元素自动为0。
例:
int a[3][4]={ {1},{5,6},{9,10} };
结果为:
1 0 0 0 1\;\;\;0\;\;\;0\;\;\;0 1000
5 6 0 0 5\;\;\;6\;\;\;0\;\;\;0 5600
9 10 0 0 9\;\;10\;\;0\;\;\;0 91000
- 如果对全部元素都赋初值,则定义数组时对=第一维的长度可以不指定,但第二维的长度不能省。
int a[][4]={ 1,2,3,4,5,6,7,8,9,10,11,12 };
//等价于int a[3][4]={ 1,2,3,4,5,6,7,8,9,10,11,12 };
在定义时也可以只对部分元素赋初值而省略第一维的长度,但应分行赋初值。
例:
int a[][4]={ {1,2} , {3} , {4,5,6} }
结果为:
1 2 0 0 1\;\;\;2\;\;\;0\;\;\;0 1200
3 0 0 0 3\;\;\;0\;\;\;0\;\;\;0 3000
4 5 6 0 4\;\;\;5\;\;\;6\;\;\;0 4560
字符数组
用来存放字符数据的数组是字符数组。
一维字符数组的定义及初始化:
char c[9];
c[0]='a';c[1]='m';c[2]='';c[3]='i';c[4]='s';c[5]='';c[6]='a';c[7]='r';c[8]='e';
或者
char c[9]={'a','m',' ','i','s','' ',a','r','e'};
//结果为:am is are
- 如果在定义字符数组时不进行初始化,则数组中各元素的值是不可预料的。
- 如果大括号中提供的初值个数(即字符个数)大于数组长度,则按语法错误处理。
- 如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动赋空字符(即’\0’)。
- 如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值字符个数确定数组长度。
二维字符数组的定义及初始化与一维类似。
字符串与字符串结束标志
-
在C语言中,是将字符串作为字符数组来处理的。
-
在实际工作中,人们关心的往往是字符串的有效长度而不是字符数组的长度。为了测定字符串的实际长度,C语言规定了一个“字符串结束标志”,以字符’\0’作为标志。即遇到字符’\0’时,表示字符串结束,由它前面的字符组成字符串。
-
编译系统在处理字符串常量时会自动加一个’\0’作为结束符。
例:“C Program” 共有9个字符,但把他存储在内存时占10个字节,最后一个字节’\0’是由系统自动加上的。
‘\0’ 代表 ASCII码为0的字符,‘空操作符’,即什么也不做。用它来作为字符串结束标志不会产生附加的操作或增加有效字符,只是一个供辨别的标志。
因此还可以用字符串常量来使字符数组初始化。
例:
char c[]={"am is are"}
//也可以省略大括号
char c[]="am is are";
- 字符串的两端是用双引号,而不是单引号。
- 用字符串常量初始化时,数组的长度不是9了,而是10,因为字符串常量的最后由系统加上了一个’\0’。
例:
char c[]={'a','m',' ','i','s','' ',a','r','e'};//①:长度为9
char c[]={'a','m',' ','i','s','' ',a','r','e','\0'};//②:长度为10
char c[]={"am is are"}//③:长度为10
//因此①与③不等价,①与②等价
字符数组的输入输出
两种方法:
- 逐个字符输入输出,用格式声明“%c”输入或输出一个字符;
- 将整个字符串一次输入或输出,用格式声明“%s”。
- 输出的字符中不包括结束符’\0’。
例:char c[6]={"China"}; printf("%s",c);
(输出:China)- 用"%s"格式符输出字符串时,printf函数中的输出项是字符数组名,而不是数组元素名。写成下面这样是不对的!
例:(错误❌)printf("%s,c[0]");
- 如果数组长度大于字符串的实际长度,也只输出到遇’\0’结束。
例:char c[10]={"China"}; printf("%s",c);
(输出:China)- 如果一个字符数组中包含一个以上’\0’,则遇到第一个’\0’时输出就结束。
例:char c[20]={"China\0Chinese\0"}; printf("%s",c);
(输出:China)- 可以用scanf函数输入一个字符串。例:
scanf("%s",c);
- scanf函数中的输入项如果是字符数组名,不要再加地址符&,因为在C语言中数组名代表数组的起止地址。例:(错误❌)
scanf("%s",&str);
- 如果想知道数组str在内存中的起始地址,可以用以下输出语句:
printf("%d",str);
(由于数组str代表数组起始地址,因此得到用十进制数形式表示的数组str的起始地址)
!如果利用一个scanf函数输入多个字符串,则应在输入时以空格分隔。
例:
char str1[5],str2[5],str3[5];
scanf("%s%s%s",str1,str2,str3);
//输入数据:How are you?
字符串处理函数
C语言没有提供对字符串进行整体操作的运算符,但是C语言的函数库中提供了一些用来处理字符串的函数。在调用这些函数前,需在程序前面的命令行包含标准头文件"string.h"。
- gets函数(读入字符串函数)
gets函数的一般形式为:gets(字符数组)
其作用是从终端输入一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址。
gets()可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值,由于可以无限读取,所以在2011年12月,ANSI采纳了ISO/IEC 9899:2011标准,标准中删除了gets()函数,使用一个新的更安全的函数gets_s()替代
gets_s()函数的一般形式为:gets_s(buffer,size) //推荐用字符数组长度-1作为size(留空'\0')
- puts函数(输出字符串函数)
puts函数的一般形式为:puts(字符数组)
其作用是将一个字符串(以’\0’结束的字符序列)输出到终端。
!gets函数读入与scanf函数读入的比较:
使用gets函数时,输出结果为完整的字符串
使用scanf函数时,遇到第一个空格就结束
由于可以用printf函数输出字符串,因此在实际上puts函数用得不多。
- stract函数(字符串连接函数)
stract函数的一般形式为:stract(字符数组1,字符数组2)
(STRing CATenate的缩写)
其作用是连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值——字符数组1的地址。
char str1[30] = {"Thursday July "};
char str2[15] = {"Tuesday July" };
printf("%s", strcat(str1, str2));
//结果为:Thursday July Tuesday July
- strcpy和strncpy函数(字符串复制函数)
strcpy函数的一般形式为:strcpy(字符串1,字符串2)
(STRing CoPY的缩写)
其作用是将字符串2复制到字符数组1中去。(因此字符数组1的长度不应小于字符串2的长度)
char str1[30] = {"Thursday July "};
char str2[15] = {"Tuesday July" };
printf("%s", strcpy(str1, str2));
//结果为:Tuesday July
!"字符数组1"必须写成数组名形式,"字符串2"可以是字符数组名,也可以是一个字符串常量。
char str1[30] = {"Thursday July "};
printf("%s", strcpy(str1, "China"));
//结果为:China
- 如果在复制前未对str1数组初始化或赋值,则str1各字节中的内容是无法预知的。复制时将字符串2中的字符串和其后的‘\0’一起复制到字符数组1中,取代字符数组1中的前面几个字符,后面的字符不一定是‘\0’,而是str1中原有的内容。
- 不能用赋值语句将一个字符串常量或者字符数组直接给一个字符数组,而只能用strcpy函数将一个字符串复制到另一个字符数组中去。
- 用赋值语句只能将一个字符赋给一个字符型变量或字符数组元素。
例:(错误❌)
str1="China";
str1=str2;
strcpy函数的一般形式为:strncpy(字符串1,字符串2,复制长度n)
其作用是将字符串2中前n个字符复制到字符串1中,取代最前面n个字符。复制个数n不应多于字符串数组1长度(不包括‘\0’)。
char str1[20] = {"Thursday July"};
char str2[15] = {"Tuesday July"};
printf("%s", strncpy(str1, str2,4));
//结果为:Tuessday July
- strcmp函数(字符串比较函数)
strcmp函数的一般形式为: strcmp(字符数组1,字符串2)
(STRing CoMPare的缩写)
其作用是比较字符串1和字符串2。
char str1[20] = {"Thursday July"};
char str2[15] = {"Tuesday July"};
printf("%s", strncpy(str1, str2,4));
//结果为:-1
!字符串比较规则:对两个字符串自左向右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到’\0’为止。
“dog”>“Dog” “computer”>“compare” “34+54”>“!$&#”
比较的结果由函数值带回
- 如果字符串1=字符串2,函数值为0。
- 如果字符串1>字符串2,函数值为一个正整数。
- 如果字符串1<字符串2,函数值为一个负整数。
!注意:对两个字符串比较,不能用以下形式:if(str1>str2)
(错误❌)
而只能用if(strcmp(str1,str2)>0)
- strlen函数(测字符串长度函数)
strlen函数的一般形式为:strlen(字符数组)
(STRing LENgth的缩写)
其作用是测试字符串长度的函数。函数的值为字符串中的实际长度(不包括’\0’在内)
char str1[20] = {"Thursday July" };
printf("%d", strlen(str1));
结果为:13
- strlwr函数(转换为小写字符函数)
strlwr函数的一般形式为:strlwr(字符串)
(STRing LoWeRcase的缩写)
其作用是将字符串中大写字母换成小写字母。
- strupr函数(转换为大写字符函数)
strupr函数的一般形式为:strupr(字符串)
(STRing UPpeRcase的缩写)
其作用是将字符串中小写字母换成大写字母。
char str1[20] = {"Thursday July" };
char str2[15] = {"Tuesday July" };
printf("%s\n", strlwr(str1));
printf("%s\n", strupr(str2));
结果为:
thursday july
TUESDAY JULY