数组的定义是把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。数组简单点来说就是相同数据类型的数据集合。数组分为一元数组、二元数组、多元数组、字符数组(字符串)、结构体数组和指针数组等数组类型。
一元数组
一元数组就像上一条线,每一个元素都是连续存储在内存中。一般数组的大小都是固定的(字符数组除外),因此数组中的每一个值都有一个对应的下标,可以通过下标获取数组的某一个值。
1 一元数组的定义:
格式:
数组类型 数组名称[数组元素个数];
数组类型代表的是数组中元素的数据类型,数据类型包括基本的数据类型和一些复杂的数据类型。(在其他语言中可以是一些自定义的数据类型);
数组名称代表的是我们定义的数组的名称,这个名称可以自由定义,但是不能定义为C语言中的关键词和已经定义过的数据名称。
数组中的元素个数简单点理解就是数组中可以存入的元素个数。
例如:
int aa[100]; //这个定义方式就是定义一个int型的数组,数组名为aa ,数组中可以存放100个int型的数据。
float bb[10];//这个是定义一个float类型的数组,数组名称为bb,数组中可以存放10个float类型的数据。
2 一元数组的初始化
数组的初始化有一下几种方式
- 在定义的时候初始化
- 定义完成之后初始化
- 数组元素一个一个初始化
例如:
1、在定义的时候初始化
int aa[10] ={0,1,2,3,4,5,6,7,8,9};//这种方式初始化数组在元素个数少的情况下比较方便,但是如果元素个数比较多,这种方式就不适用了。如果每一个元素要初始化为0,则可以采用以下方式初始化
int aa[10] ={0}; //如果在数组初始化的时候,部分元素没有初始化,则默认为0,因此就可以采取这种方式将数组中的元素初始化为0.
2、定义完成之后初始化
int aa[10];
aa[10] ={0,1,2,3,4,5,6,7,8,9}; //这种初始化和第一种初始化区别不大,唯一的 优点是在试用前可以对数组初始化,保证数组中的值是我们需要的。
3、单独一个一个对数组元素赋值
int aa[10];
aa[0] = 0; aa[1] = 1; aa[2] = 2; aa[3] = 3;……
这种方式初始化数组比较麻烦,但是可以修改某一个元素的值,这种方式一般和for循环嵌套试用,通过循环对数组赋值。
for(int i = 0;i<10;i++){
a[i] = i;
}
3 一元数组的使用
一元数组的使用很简单,使用数组一般有两种方式
第一种,使用for循环控制,通过下标对元素进行赋值或者取值
例如:
main()
{
int arr[10];、
int ch;
for(int i = 0;i<10 ;i++ )
{
arr[i] = 100;//通过for循环将字符数组中的值赋值为100
}
for(int i = 0;i<10 ;i++ )
{
ch = arr[i];//通过for循环将字符数组中的值赋值给ch
}
}
第二种直接通过数组下标对数组中的值进行存储和使用。
例如:
int a = aa[1];//此语句就是将数组aa中的第二个元素的值取出来赋值给a。
aa[2] = 0;//此语句是将整数0赋值给数组aa的第三个元素。
aa[3] = a;//此语句是将a变量的值赋值给数组的第四个元素。
注意:数组中数组的下标是以0开始的,因此第一个元素是aa[0]。数组在赋值和取值给变量时,数组的类型和变量的类型应保持一致。
二元数组
介绍完一元数组我们可以发现一元数组是通过下标对数组进行赋值的取值操作的。那么有两个下标的数组就称为二元数组。如果说一元数组可以看成一条线的话,那二元数组就可以看成一个面。一元数组有一个维度(一组下标),二元数组就是有两个维度(两组下标)。
1 二元数组的定义
格式:
数组类型 数组名称[横向数组元素个数][纵向数组元素格式];
数组类型代表的是数组中元素的数据类型,数据类型包括基本的数据类型和一些复杂的数据类型。(在其他语言中可以是一些自定义的数据类型);
数组名称代表的是我们定义的数组的名称,这个名称可以自由定义,但是不能定义为C语言中的关键词和已经定义过的数据名称。
横向数组元素个数表示一个二维数组有多少行。
纵向数组元素个数表示一个二维数组有多少列。
例如:
int aa[100][100]; //这个定义方式就是定义一个int型的二维数组,数组名为aa ,数组中可以存放100*100个int型的数据。这个二维数组有100行100列数据。
int bb[3][2];//这个数组就代表定义了一个int型的二维数组,这个数组中有三行两列。总共能存储3*2个数据。
2 二元数组的初始化
二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可按行分段赋值,也可按行连续赋值。
例如对数组a[5][3]:
1)按行分段赋值可写为:
int a[5][3]={ {80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85} };
2)按行连续赋值可写为:
int a[5][3]={ 80,75,92,61,65,71,59,63,70,85,87,90,76,77,85};
3) 根据二元数组的下标对二元数组赋值
int a[5][3];
a[0][0] = 1;a[0][1] = 2;a[1][0] =3;a[1][2] =4;a[2][0] = 5;……
二元数组在初始化的时候和一元数组初始化的时候有一些特征是一样的。在使用按行分段赋值和按行连续赋值的时候,如果只初始化了部分数据,那么其他没有初始化的数据可以自动补全。
例如:
int a[5][3]={ {80},{61,65}};
则数组a中对应元素的值如下所示
第一列 | 第二列 | 第三列 | |
第一行 | a[0][0] 80 | a[0][1] 0 | a[0][3] 0 |
第二行 | a[1][0] 61 | a[1[1] 65 | a[1][1] 0 |
第三行 | a[2][0] 0 | a[2][1] 0 | a[2][2] 0 |
第四行 | a[3][0] 0 | a[3[1] 0 | a[3[2] 0 |
第五行 | a[4][0] 0 | a[4][1] 0 | a[4][2] 0 |
int a[5][3]={ 80,75,92,61,65,71,59,63};
第一列 | 第二列 | 第三列 | |
第一行 | a[0][0] 80 | a[0][1] 75 | a[0][3] 92 |
第二行 | a[1][0] 61 | a[1[1] 65 | a[1][1] 71 |
第三行 | a[2][0] 59 | a[2][1] 63 | a[2][2] 0 |
第四行 | a[3][0] 0 | a[3[1] 0 | a[3[2] 0 |
第五行 | a[4][0] 0 | a[4][1] 0 | a[4][2] 0 |
3 二元数组的使用
二元数组的使用和一元数组的使用方法相同,只不过在使用或者存储数据的时候需要通过两个下标来确定要使用的数组元素。
例如:定义一个二元数组arr[2][2]。这是一个两行两列的而元数组。如果要对二元数组的第一行,第一列的值赋值,那么就需要使用下标。如arr[0][0] = 100。通过下标行下标0和列下标0就可以对arr[0][0]进行赋值。取值和赋值一样,也可以通过下标进行取值,如a = arr[0][0],这是将第一行第一列的值赋值给a。同理其他位置的元素都可以以下标的形式进行赋值和取值。
二元数组的使用方式大致也有两种:
第一种是使用的时候使用一个for循环就可以了,而二元数组在使用的是由一般要嵌套两个for循环。外部循环控制行数,内部循环控制列。
例如:使用一个a[5][3]数组,这数组有5行3列。我们可以通过如下代码适应
……
for(int i = 0;i<5;i++){
for(int j=0;j<3;j++)
{
a[i][j] = 0;//向a数组中赋值
int c = a[i][j];//将数组a中的值赋值给c变量
}
}
一般二元数组的使用不一定使用for循环,根据需求可以在不同的位置对单个数组的元素赋值,具体的使用可根据情况而定。有些数组存储的是数值不在同一个地方写入或者取出,那么就可以在不同的地方根虎数组的下标对数组赋值或者取出数组中的数值。
第二种是根据数组下标单独对二元数组找那个的内容赋值
例如
a[0][0] = 100;//将二元数组a的第一行第一列元素(a[0][0])赋值为100
int c = a[1][1];//将二元数组a的第二行第二列元素(a[1][1])的传递个c变量
4 二元数组的存储结构
二元数组在内存中的存储方式和一元数组一样都是连续的存储在内存空间中,但是二元数组在存储的过程中分为行和列,二元数组在存储的时候是根据行进行存储的,一行一行的将数据存入的内存中。
例如:
int a[5][3]={ {80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85} };
数组a的表示结构:
第一列 | 第二列 | 第三列 | |
第一行 | a[0][0] 80 | a[0][1] 75 | a[0][3] 92 |
第二行 | a[1][0] 61 | a[1[1] 65 | a[1][1] 71 |
第三行 | a[2][0] 59 | a[2][1] 63 | a[2][2] 70 |
第四行 | a[3][0] 85 | a[3[1] 87 | a[3[2] 90 |
第五行 | a[4][0] 76 | a[4][1] 77 | a[4][2] 85 |
数组a在内存中的存储如下所示
…… | 80 第一行首地址,整个数组首地址。数组第一个元素地址 | 75 | 92 | 61 第二行首地地址。 第四个元素数组四肢 | 65 | 71 | 59 第三行首地地址。 第七个元素数组四肢 | 63 | 70 | 85 第四行行首地地址。 第十个元素数组四肢 | 87 | 90 | 76 第五行首地地址。 第十三个元素数组四肢 | 77 | 85
| …… |
80所在的位置为a数组内存的首地址,a[0][0]所在的地址,80是a[0][0]元素的值,之后连续一行一行的让数组中的数组存入到数组中。加入二元数组的首地址是0xff00,那么第二个元素的地址就是第一个元素的地址加上int类型的数据大小,也就是第二是数据所在的地址位置0xff04,以此类推第二行的首地址为0xff0c。
多元数组
多元数组顾名思义就是有多组下标的数组,一元数组有一个维度,二元数组有两个维度,那么多元数组就有多个维度。多元数组的定义和初始化跟二元数组的类似,只不过是多了一组下标。这里就不多介绍。
字符数组
字符数组从名字中就可以知道是专门存储字符的数组。字符数组和上面介绍的一元数组和二元数组一样,只不过数组中存储的是char型的字符。
char型字符包括常用的字符 @、#、& 、大小写字母、数字、空格等ACSII中有的字符。
1 字符数组的定义
格式:
char 字符数组名[字符数组中字符的个数];
char 代表的是字符数组的类型
字符数组名为定义的数组变量的名称
字符数组中的字符个数是一个字符数组中可以存储字符的个数。
例如:
char aa[10]; //这个定义字符数组名为aa的字符数组,这个数组中可以存储10个字符
2 字符数组的初始化
1)字符数组也允许在定义时作初始化赋值。
例如:
char c[10]={‘c’, ‘ ’, ‘p’, ‘r’, ‘o’, ‘g’, ‘r’, ‘a’,’m’};
赋值后各元素的值为:
数组C c[0]的值为‘c’
c[1]的值为‘ ’
c[2]的值为‘p’
c[3]的值为‘r’
c[4]的值为‘0’
c[5]的值为‘g’
c[6]的值为‘r’
c[7]的值为‘a’
c[8]的值为‘m’
其中c[9]未赋值,由的值为‘p’系统自动赋予0值。
当对全体元素赋初值时也可以省去长度说明。
例如:
char c[]={`c`,` `,`p`,`r`,`o`,`g`,`r`,`a`,`m`};
这时C数组的长度自动定为9。
2)字符数组可以对单个字符数组元素进行赋值
例如:c[0]=‘c’;c[1]=‘ ’;c[2]=‘p’ ;c[3]=‘r’;c[4]=‘0’……
3 字符数组的使用
1)字符数组的使用和一元数组的使用方式一样。可以通过for循环对数组中的字符进行输入输出。
例如:
main()
{
char arr[10];、
char ch;
for(int i = 0;i<10 ;i++ )
{
arr[i] = 'a';//通过for循环将字符数组中的值赋值为字符a
}
for(int i = 0;i<10 ;i++ )
{
ch = arr[i];//通过for循环将字符数组中的值赋值给字符ch
}
}
2)也可以根据数组下标对数组中的元素进行使用。
chara = aa[1];//此语句就是将数组aa中的第二个元素的值取出来赋值给a。
aa[2] = ‘0’;//此语句是将字符0赋值给数组aa的第三个元素。
aa[3] = ‘a’;//此语句是将字符a值赋值给数组的第四个元素
4字符串和字符串结束标志
字符串在C语言中就是字符数组,只不过是在字符数组初始化的时候是以字符串的形式初始化,字符数组的大小是由字符串的结束标志控制。
在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。前面介绍字符串常量时,已说明字符串总是以'\0'作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符'\0'存入数组,并以此作为该字符串是否结束的标志。有了'\0'标志后,就不必再用字符数组的长度来判断字符串的长度了。
C语言允许用字符串的方式对数组作初始化赋值。
例如:
char c[]={'c', ' ','p','r','o','g','r','a','m'};
可写为:
char c[]={"C program"};
或去掉{}写为:
char c[]="C program";
用字符串方式赋值比用字符逐个赋值要多占一个字节, 用于存放字符串结束标志'\0'。上面的数组c在内存中的实际存放情况为:
C | 空格 | p | r | o | g | r | a | m | \0 |
‘\0'是由C编译系统自动加上的。由于采用了‘\0'标志,所以在用字符串赋初值时一般无须指定数组的长度, 而由系统自行处理。
注意:
- 使用字符串初始化的字符数组的大小可以使用sizeof(字符数组名称)来回去字符串的大小。
- 使用字符串初始化的字符数组中的字符也可以通过数组下标单个字符输出和赋值。
字符串的输入输出函数
1)gets函数:
函数原型:char * gets(const *s);
函数功能:从标准输入中读取一个字符串,并保存到 s 指定内存地址中,直达出现换行或者 字符串结束标志为止。相比于scanf输入功能,gets()函数可以输入空格,而scanf函数不能输入空格。
返回值:读取成功测返回输入的字符,失败则为NULL
2)puts函数
函数原型:int puts(const char *s)
函数功能:标准设备输出 s 字符串,在输出完成之后增加一个 \n 字符
返回值:成功返回非负数,失败返回0
3)fgets函数:
函数原型:char *fget( char *s, int size , FILE *stream);
函数功能:从stream指定的文件内存中读入字符,保存到 s 所指定的内存空间中,直到出现换行字符、读到文件结尾或者已读了size -1 个字符为止。最后会自动增加一个 \0 作为字符的结束
返回值:成功读取到的字符串,失败为NULL
4)fputs函数
函数原型:int fputs(const char *str,FILE * stream)
函数功能:将str所指的字符串写入到stream指定的文件中,字符串结束 \0不会写入到文件中
如果把字符串输出到屏幕,则stream 固定为stdout
fputs函数是puts函数的文件操作版本,但是fputs函数不会自动输出 \n
返回值:成功 0,失败 -1
5)sacnf标准输入
1、接收换行字符串结束,可添加空格
scanf("%[^\n]",arr)
2、在scanf中获取数据时,建议不要添加除空格之外的任何字符
3、有限定字符宽度的格式
scanf("%1d%2d%3d",&a,&b,&c);//输出a有一位数字,b有两位数字,c有三位数字
scanf("%1s%2s",arr1,arr2);//输出arr1有一位字符,arr2有两位字符
5、屏蔽一个区间的字符
scanf("%*[a-z]",&c);//*号表示屏蔽之后的字符
6)printf标准输出
7)sprintf
格式化输出字符串
int sprintf(char *_CRT_SECURE_NO_WARNINGS,const char *format,...)
参数:str 字符串首地址
format 字符串格式,用法和printf()一样
返回值:成功:实际格式化的字符个数
失败:-1
8)ssconf
格式化输入字符串
int sscanf(const char *str,const char *format,...)
从str指定的字符串读取数据,并根据参数format字符串转换并格式化数据
参数:str:指定的字符串首地址
format字符串格式,用法scanf函数一样
返回值:成功:参数数目,成功站换的值的个数
失败:-1
9)atoi、atof、atol
ini atoi(const char *nptr)
atoi函数会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或者正负号才开始转换,而遇到非字符或者字符串结束符才转换结束,并将结果返回
六、字符串处理函数
1)strlen
获取字符串的有效长度,不包括\0,返回一个int类型的字符串的长度。
此函数不能处理字符数组
2)strcpy
char *strcpy(char *dest,const char *src)
将src指向的字符串复制到dest所指的空间中,\0 也会拷贝
返回值:成功返回dest字符串的首地址,失败NULL
如果参数dest所指的内存空间不够大,会造成缓冲区溢出的错误
3)strncpy
char *strncpy(cahr *dest,const char *src,size_t n)
把src指向的字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束由指定的长度是否包含 \0
4)strcat
char *strcat(char *dest,const char *src)
将src字符串连接到dest的尾部 \0 也会追加过去
返回值:成功:返回dest字符串的首地址
5)strncat
char *strncat(char *dest,const char *src,size_t n)
将src字符串前n个字符串追加到dest的尾部 \0也会追加过去
6)strcmp
int strcmp(const char *s1,const *s2)
比较s1和s2的大小,(比较的是ASCII码的值)
返回值:相等:0,大于:>0,于 :<0
一般系统不一样,返回值有可能不一样
7)strncmp
int strcmp(const char *s1,const *s2,size_t n)
比较s1和s2前n个字符串的大小,(比较的是ASCII码的值)
8)strchr
char *strchr(const char *s,int c)
在字符串s中查找字母c出现的位置
返回值:成功:返回第一次出现字符成的地址,失败NULL
9)strstr
char *strstr(const char *haystack,const char *needle)
在字符串haystack中查找needle字符串
返回值:成功:返回第一次出现的needle的地址,失败:NULL
10)strtok
char *strtok(char *str,const char *delim)
将字符串分割成一个个的片段、当strtok()在参数str的字符串中发现delim中包含的分割字符时,则会将该字符改为\0字符,当连续出现多个时,只替换为 \0
返回值:成功:返回分割后的字符串的首地址,失败:NULL
注意:在第一次调用时,strtok函数必须给予参数s字符串
之后调用则将参数s设置成NULL,每次调用成功则会返回指向被分割出片段的指针