无偿分享学习资料,需要的小伙伴评论区或私信dd。。。
无偿分享学习资料,需要的小伙伴评论区或私信dd。。。
无偿分享学习资料,需要的小伙伴评论区或私信dd。。。
完整资料如下:纯干货、纯干货、纯干货!!!
关注专栏:<后续持续更新>
目录
数组
分类:一维数组、二维数组 ... n维数组
一维数组
定义:使用数组名加一个下标就能唯一确定数组中的元素的数组就是一维数组。
- 一维数组所占空间:其数据类型 * 长度,例如int a[10]长度为 4(int) ✖️ 10(长度)=40个字节
一维数组的定义:
格式:
数据类型 数组名[常量表达式]; 例如 int arr[10];
-
数组也属于变量,只是在内存中占用一片连续的存储单元,所以都必须先定义后使用 ,数组名的命名规则也遵循标识符命名规则。
-
定义数组时(即未初始化)必须指定数组的长度 。数组的长度只能是整型(包括字符型)的常量或表达式,不能包含变量。
int arr[10],arr2['a'],arr3[10+'a'];//他们都是正确的数组定义 int a=12,arr4[a];//理论上错误的数组定义,但目前绝大多数编程软件已实现。
⚠️注意:c语言明确规定变量不允许做数组长度,即数组不允许动态定义,但是目前绝大多数编程软件都允许使用变量作数组长度。
-
c语言不允许对变量的长度动态定义,所以不能使用变量定义长度。(如上)
-
数组下标从0开始,即最大下标等于n-1。
int arr[3];//分别为a[0]、a[1]、a[2]
一维数组的引用:
格式:
数组名 [下标]
-
数组的引用是引用数组中的元素,而不能引用整个数组。
printf("%d",arr[0]);//只能是单个引用 printf("%d",arr);//不允许对整个数组进行引用,没有语法错误,但是结果是一个该数组的首地址
-
数组的下标是从0开始的,即a[10]里面的数据为a[0]~a[9],引用时不能超过最大长度。
-
引用时下标可以是整型与字符型 的常量、变量、表达式。(即数值)
arr[10]、arr[n]、arr[10+n];//都是正确的引用
-
数组的引用有两方面作用:
- 单独赋值。
- 出现在表达式中进行运算。
定义数组的长度与引用数组的下标的区别:
两者形式上都是一样的,都是 数组名[表达式],区别在于:
-
首先定义时里面的表达式只能是常量表达式,而引用时还可以是变量表达式。
-
其次两者含义不同,定义时是为了指定数组的长度,而引用时是为了指定数组中的元素。
int a[10];//数组a包含10个元素。
a[6];//引用数组a中第七个元素。
一维数组的初始化:
定义:在数组定义的同时给各数组元素赋值,这称为数组的初始化。(使用"初始化列表"的方法实现数组的初始化)
格式:
数据类型 数组名[常量表达式]={初始化列表};
-
对全部数组元素初始化时,可以省略数组的长度。
int arr[3]={1,2,3}; 等价于 int arr[]={1,2,3};
-
只对部分数组元素初始化,其余未初始化元素根据数据类型分别赋0、0.0、'\0'、NULL,注意此时数组长度不能省略。
-
使数组元素全部为0的初始化方法。
int a[3]={0,0,0}; int a[3]={0}; int a[3]={};//这里注意不能省略长度,否则系统无法确定数组长度
-
未初始化的数组与变量相同各元素都是不确定的值,部分初始化其余元素根据数据类型分别赋值0、0.0、'\0'、NULL。
int arr[3];//该数组未初始化,各数组元素为不确定的值 int arr[3]={0};//该数组部分初始化,其余自动赋0,所以全部为0 int arr[3]={}://该数组全部初始化为0,注意这种初始化不能省略数组长度。因为省略长度数组根据初始化列表指定数组大小,此时初始化 //列表为空,形成矛盾,导致数组没有正确定义。
-
数组的初始化列表个数不能大于数组长度,否则会编译错误。
-
数组初始化列表不能是变量,哪怕是已初始化的变量。【此内容摘自 张玉生-c语言程序设计 课本】
int a=10; int arr[3]={a};//理论上不允许。
⚠️注意:目前绝大多数编译软件都允许变量作为数组的初始化列表。
二维数组
定义:使用数组名加两个下标就能唯一确定数组中的元素(二维数组又称为矩阵)。
- 二维数组和多维数组的定义、引用、初始化与上面的一维数组要求大体通用。(请学者参考一维数组学习二维数组)
二维数组的定义:
格式:
数据类型 数组名[行(常量表达式)] [列(常量表带)]; 例如 int arr[2][3];
-
二维数组在内存中是按行存放的,也是按行赋值的。(本质上也是一维数组,在内存中是一片连续的存储单元)
⚠️注意:用矩阵形式表示二维数组是逻辑上的概念,实际上二维数组在内存中同样占用一片连续的存储单元,属于线性结构。
-
二维数组也必须先定义后使用,数组名遵循标识符命名规则。
-
二维数组(未初始化时)定义时,也必须指定行与列 。长度只能是整型(包括字符型)的常量或表达式。
⚠️注意:如果二维数组进行初始化,可以省略行的长度。反之不进行初始化单纯定义就必须指定行和列。
二维数组的引用:
格式:数组名 [行下标] [列下标];
-
二维数组的下标都是从0开始的,引用时不能超过最大长度。下标可以是整型与字符型 的常量、变量、表达式。(即数值)
-
二维数组的引用可以单独赋值也可以出现在表达式中进行运算。
二维数组的初始化:
数据类型 变量名 [行] [列]={ {初始化列表1},{初始化列表2}...{初始化列表n} }
-
二维数组是按行存放的,所以也是按行赋初值的。
-
二维数组不管是全部初始化还是部分初始化,可以省略行但是不允许省略列,此时系统会根据第二维的长度推算出第一维的长度。还可以把数据全部放在一对{}中。
int arr[][2]={{1,2},{3,4}};//全部初始化,不允许省略列,可以省略行,此时等价于:int arr[][2]={1,2,3,4} int arr[][2]={{1,2},{3}};//部分初始化,也不允许省略列,可以省略行,此时等价于:int arr[][2]={1,2,3}
部分初始化其余未初始化元素系统自动根据数据类型赋0、0.0、'\0'、NULL。
字符数组
定义:存放字符数据的数组
字符数组的定义:
格式:
一维字符数组:char 数组名[长度];
二维字符数组:char 数组名[行][列];
- 可以使用整型数组来存放字符数据(合法但浪费空间),反之也可以使用字符数组来存放整型数据(注意不要超过字符数据的限值)。
字符数组的初始化:
格式:
一维字符数组:char 数组名[长度]={初始化列表};
二维字符数组:char 数组名[行][列]={{初始化列表1},{初始化列表2}...,{初始化列表n}};
二维数组的引用:
格式:
一维字符数组:数组名 [下标];
二维字符数组:数组名 [行下标] [列下标];
字符串
-
c语言没有字符串类型,也没有字符串变量(有字符串常量),字符串是存放在字符数组中的。
-
字符串的结束标志为 '\0'(若字符串没有'\0'则系统会自动给字符串加上)。'\0'表示ASCII码为0的字符(空)
-
使用sizeof运算符计算字符串占用的内存空间,strlen字符处理函数计算字符串的长度。
char str[]="hello\0mom"; char str2[]="hello\0"; printf("str长度:%d\nstr2长度:%d\n",strlen(str),strlen(str2)); printf("str占用空间:%d\nstr2占用空间:%d\n",sizeof(str),sizeof(str2)); printf("%s\n%s\n",str,str2); 输出结果: str长度:5 str2长度:5 str占用空间:10 str2占用空间:7 hello hello
-
c系统在字符数组存储字符串常量时,会自动在结尾加上'\0'作为字符串的结束标识。
char str[]="hello"; printf("%d\n",sizeof str);//6 ⚠️注意:哪怕结尾手动加上'\0',c也会将它作为一个字符串常量的一部分,即仍然加上'\0'。
-
如果一个字符数组遇到一个及以上\0,则以第一个\0为准。
char str[]="hello\0mom\0"; printf("%s\n",str);//hello
-
字符\0不占用列宽。
char str[]="hello\0mom"; int i=0; while(i!=9){printf("%c",*(str+i));i++;}//hellomom printf("\n");
字符串常量初始化字符数组
格式:
char str[]={"字符串"};
char str[]="字符串";
字符串初始化与字符列表初始化的不同:
主要体现在使用字符串初始化字符数组系统会自动在字符串末尾加上'\0',而使用字符列表初始化则不会。
char str[]="hello!";
等价于:
char str[]={'h','e','l','l','o','!','\0'};
而不等价于:
char str[]={'h','e','l','l','o','!'};
但是如果给上面的数组加上长度,那么就等价了
char str[8]={'h','e','l','l','o','!',};//因为部分初始化,未初始化的部分系统自动赋\0,虽然后面两个\0,但是以第一个为准,但是占用空间不等价
同理:
char str[5]="hello";//str虽然是一个字符串,但是最大保存五个,所以最后一个\0没有保存,他与下面的表达式不等价
char str2[]="hello";
// scanf("%s",str);//给str输入一个字符串,虽然最后一个\0没有保存,但是他与上面的表达式是等价的(数组是动态的,可以扩展)
if(strcmp(str,str2)==0){
printf("ok");
}else{
printf("no");
}
⚠️注意:字符数组并不要求最后一个字符是'\0',是否需要加'\0'根据需求决定,但是由于字符串作为初值时系统会自动加上一个'\0',所以使用字符初值表初始化时一般也手动加上一个'\0',这样做便于引用字符数组中的字符串。
char str[]="hello";
char str2={'h','e','l','l','o','\0'};
字符串常量末尾自动加上'\0'的好处:
char c[]="C program.";
//他在系统中的存储方式为:C program.\0
scanf("%s",c);//输入hello
//如果不自动加\0,则数组中最终数据为:hellogram.\0
//若自动加上\0,则最终数据为:hello\0
printf("%s\n",c);//只输出hello,而不输入后面的内容。
结论:符合人们一般的尝试,这样新输入的字符串可完全代替旧字符串,方便字符串的引用。
字符数组的输入与输出
分类:"%c"逐个输入输出、"%s"连续输入输出
-
字符串在使用%s输入时,地址列表不加&。输出时,变量也无需加*取值。
-
多个字符串的连续输入默认使用空格隔开,遇到空格则认为字符串输入结束。
-
%s与%c一起出现在scanf输入时,两个之间必须加上一个空格作为分隔符。
char c; char b[10]; scanf("%s %c",b,&c);//%s默认采用空格结束,%c又必须与前面连续输入,所以必须以空格作为分隔符。但是如果%c在%s前面就不 printf("b=%s\nc=%c\n",b,c);//用,系统会把第一个字符给字符变量,后面整个作为字符串给字符数组
-
字符变量也可采用%s输入,但是输出时只能使用%c。
-
字符数组与字符串都可以使用%s输入输出,使用%c输入输出字符数组与字符串时一般配合循环。
-
字符串输出的字符中不包括'\0'。
-
scanf函数使用%s输入字符串时无法处理带空格的字符串。
字符串处理函数
使用字符串处理函数必须在程序顶部加上string.h头文件。
-
puts(参数)---输出一个字符串(输出后会自动换行)
参数:字符数组或字符串(只能是单个)
puts(str); puts("hello!")
注意:
char c[]="hello"; puts(c+2);//去掉前两个输出
-
Gets(参数)---输入一个字符串(可输入带空格的字符串,完成的句子,弥补了scanf的不足)
参数:字符数组(只能是单个,函数的返回值是数组的首地址)
gets(str);//将输入的字符串保存在字符数组str中,例如输入 how are you?将该字符串保存给str
注意:gets里面输入的\0与我们直接赋值的\0是有区别的,电脑会当作两个字符输入,其在字符串里面也是占两个字节空间(scanf也是)
-
strcat(参数1、参数2)---连接两个字符串
参数:参数均为字符数组,参数2可为字符常量(把数组2接到数组1的后面,结果保存在数组1中,函数的返回值是参数1的首地址 )注意若参数1+x,x最大为原数组原本个数。超出则无法覆盖第一个字符串的\0。
char str[20]="boy"; printf("%s\n",strcat(str,"girl"));//boygirl
重要:strcat两个参数都是地址,但是第一个参数不能是常量地址。且参数1的长度要大于参数2
-
strcpy(参数1,参数2)---将参数2中的字符串复制给参数1中的字符数组(函数的返回值是参数1的首地址)
参数1:字符数组
参数2:字符串或字符数组
char str[]="hello"; strcpy(str,"ok"); printf("%s\n",str);//ok
注意:strcpy可以充当连接的作用,弥补strcat的不足
char str[20]={"hello!"}; strcpy(str+6,"TOM");//从str的第6个字符后面开始连接 printf("%s",str);//hello!TOM
strcpy会覆盖目标字符数组中的全部内容
重要:strcpy两个参数都是地址,但是第一个参数不能是常量地址。且参数1的长度要大于参数2
-
strncpy(参数1,参数2,参数3)---将参数2中的前n个字符赋值到参数1中的前n个字符(函数的返回值是参数1的首地址)
参数1:字符数组
参数2:字符串或字符数组
参数3:数值
char str[20]="boy"; strncpy(str,"B",1); printf("%s\n",str);//Boy
重要:strcnpy两个参数都是地址,但是第一个参数不能是常量地址。且参数1的长度要大于参数2
-
strcmp(参数1,参数2)---比较两个字符串
参数:参数都可以是字符数组或字符串常量
strcmp(str1,str2); strcmp("hello","hello!");
规则:从左到右依次比较ASCLL码 结果:如果相等函数返回0,参数1>参数2返回一个正整数,参数2>参数1返回一个负整数
注意:对两个字符串的比较不允许使用 if(str1>str2)这种格式
-
strlen(参数)---检测字符串的长度
参数:字符数组或字符串(函数的返回值是字符串的长度)
strlen(str); strlen("hello");
-
Strlwr(参数)---将字符串中的字符全部转换为小写字母
参数:字符串或字符数组
strlwr(str1); strlwr("HELLO!");
-
strupr(参数)---将字符串中的字符全部转换为大写字母
参数:字符串或字符数组
本章知识补充:
-
c语言中,主要是根据维度讨论数组。以数据类型分类,数组还分为整型数组、实型数组、字符数组、指针数组、自定义类型数组等
-
数组是一组有序数据的集合,在内存中是一片连续的空间。数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。
-
数组使用数组名和下标来唯一的确定数组中的元素。如 s[0]就表示s数组中第1个元素。
-
数组中的每个数据的数据类型相同。(不能把不同数据类型的数据放在同一数组中)
-
数组与变量相同都必须先定义后使用。
-
定义时不允许省略长度,初始化时一般省略长度(如果数组会进行替换,定义的长度一般大于实际用到的长度)
-
数组的大小在定义或初始化时就是数组的唯一大小,无法修改。
-
数组在使用字符串处理函数重新赋值时,只要没有覆盖后面数据,那么只会对指定的数据元素覆盖,其他元素还是之前的值。但是此时属于无效字符(特别是字符串)
-
不允许使用赋值运算直接对数组名进行赋值(数组名代表数组首地址)
char str[20]; str={....}或str=str2都是错误的
-
初值个数大于定义长度会出现编译错误(不会语法错误)
-
数组的初始化列表一般为常量,还可以是变量、符号常量与表达式(只要数据类型一致一般都可以)
注意:张玉生c表明初值表不允许是变量。
int a[2]={N,n,1};//N为符号常量,n是变量
-
数组的引用不会检测数组的下标 ,下标越界会出现编译错误 ,但是仍然会输出一个不确定的值。(不会语法错误)
-
数组中元素的地址使用&获得(也可以使用&获取变量的地址,但是c不允许把地址进行加运算。例如&a+&b)
printf("%d",&a[0]);//输出该数组元素的地址
-
二维数组可以采用一维数组的形式访问
int b[3][3]={{1,2,3},{4,5,6},{7,8,9}}; printf("%d\n",b[0][3]);//4
-
数组如果定义了长度,sizeof按照定义的长度输出,若没有再去根据数组内容判断。
char str[5]="hel"; printf("%d\n",sizeof(str));//5 int arr[10]; printf("%d\n",sizeof(arr));//40 int arr2[]={1,2}; printf("%d\n",sizeof(arr2));//8
-
c语言不允许函数直接返回一个数组
-
字符串常量可以为空,表示空串,但是字符常量不可以为空。
-
对于一维数组(形参),可以省略数组大小。对于多维数组(形参),不能省略最低维度的长度
float fn(int a[][3]){//数组a不能省略最低维度列的长度,否则会报错 return 0; }
-
一维数组做函数参数形参可以省略长度,二维数组必须有最低维度长度(声明时也一样)
void fn(int arr[][]){}//报错,最低维度没有长度 void fn(int arr[]){}//不报错
-
字符数组与字符串的区别:字符串是用字符数组存储的,但字符数组不一定就是字符串,字符串一定要有'\0',所以字符数组的长度必须是字符长度+1.