数组的基本用法
概念
数组是有序元素序列。如果将若干个数据类型相同的变量的集合命名,那么该命名就是数组名。数组元素的重点是类型相同并且连续在内存中存放的数据。
定义格式
存储类型 数据类型 数组名 [ 元素个数 ] ;
例如:
(auto)int arr[5] ;
数组名:代表数组的首地址,是地址常量,不能够作为左值,不能被赋值。
数组定义方法:
- 数组名的定义规则和变量相同,遵循标识符命名规则。
- 数组名后使用方括号括起来的是元素个数,并且必须是常量或常量表达式,不能是变量。
访问数组元素
数组名 [ 下标 ] ;
数组的下标从0开始!!!
例如:
int a[ 5 ] ;
访问第一个元素:a[ 0 ] ;
访问最后一个元素:a[ 4 ] ;
注意数组越界!!!!
注意:
1.数组的数据类型就是数组元素的数据类型。
2.数组名要符合标识符命名规则。
3.同一个函数中,数组名不要和变量名相同。
例如:
int a;
int a[ 5 ];//错误,变量名和数组名重复
4.数组元素下标从0开始,到n-1结束,一定要注意数组越界问题。
一维数组
一维数组和二维数组
一维数组的概念
只有一个下标的数组。
格式:存储类型 数据类型 数组名[ 元素个数 ];
访问元素:数组名[ 下标 ];
下标从0开始到n-1。
数组名:数组的首地址。
初始化
1.定义时全部初始化
int a[5]={1,2,3,4,5};
printf("%d\n",a[0]);
printf("%d\n",a[1]);
printf("%d\n",a[2]);
printf("%d\n",a[3]);
printf("%d\n",a[4]);
2.定义时部分初始化,未初始化的部分自动为0。
int a[5]={1,2};
printf("%d\n",a[0]);
printf("%d\n",a[1]);
printf("%d\n",a[2]);
printf("%d\n",a[3]);
printf("%d\n",a[4]);
3.定义时未初始化,需要对单个元素进行赋值,要不然是随机数(垃圾数)。
int a[5];
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=5;
printf("%d\n",a[0]);
printf("%d\n",a[1]);
printf("%d\n",a[2]);
printf("%d\n",a[3]);
printf("%d\n",a[4]);
注意数组越界:
int a[3]={5,6,7,8};//数组越界
定义空数组
int a[5]={0,0,0,0,0};
int a[5]={0};
int a[5]={};
数组引用
- 先定义后引用。
- 每次只能引用一个数组元素a[i],如果想引用所有元素可以通过循环遍历数组中每一个元素。
- 引用时一定要防止数组越界,虽然有时候不会报错。
- 打印数组元素的地址用%p。
例如:
int a[10];
printf("%d",a);//错误,不能把数组名用来引用数组元素,因为数组名是数组首地址
printf("%p",a);//获取数组首地址
遍历数组可以使用循环
int a[5]={1,2,3,4,5}; for(int i=0;i<5;i++) printf("%d\n",a[i]);
练习:输入5个数组元素,然后依次打印出来
#include <stdio.h> #include <stdlib.h> int main(int argc,char const *argv[]) { int a[5]={}; int i; for(i=0;i<5;i++) scanf("%d",&a[i]); for(i=0;i<5;i++) printf("%d\n",a[i]); return 0; }
数组的大小
例如:
int a[5];//20 char c[32];//32 double b[2];//16 printf("%d %d %d\n",sizeof(a),sizeof(b),sizeof(c));//20 16 32
计算方式
- 元素个数*数据类型的大小
- sizeof(数组名)
数组元素个数=sizeof(数组名)/sizeof(元素数据类型)
练习:计算斐波那契数列前十五项并逆向输出
解析:斐波那契数列1 1 2 3 5 8 13 21 ······
#include <stdio.h> #include <stdlib.h> int main(int argc,char const *argv[]) { int b[15]={1,1}; for(int i=2;i<15;i++) b[i]=b[i-1]+b[1-2]; for(int j=14;j>=0;j--) printf("%d ",b[j]); printf("\n"); return 0; }
清零函数
bzero()函数
#include <strings.h> void bzero(void *s,size_t n);
功 能:将内存空间设置为0
参 数:s:要清空的空间的首地址
n:要清空的内存空间大小(单位是字节)
返回值:无
练习:将数组清空
#include <stdio.h> #include <stdlib.h> int main(int argc,char const *argv[]) { int a[10]={1,2,3}; for(int i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); bzero(a,sizeof(a)); for(int i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); return 0; }
memset()函数
#include <strings.h> void *memset(void *s,int c,size_t n);
功 能:将指定内存空间内容设置为0
参 数:s:要清空内存空间首地址
c:要设置的数,一般为0
n:要清空的内存大小
返回值:无
练习:将数组清空
#include <stdio.h> #include <stdlib.h> #include <strings.h> int main(int argc,char const *argv[]) { int a[10]={1,2,3}; for(int i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); memset(a,0,sizeof(a)); for(int i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); return 0; }
数组内存分配
数组在内存中的分配如下图:
a是数组名,是数组的首地址,也是第一个数组元素的地址,即&a[0];
a+1是数组的第二个元素的地址,即&a[1]。
练习:获取数组元素的地址
int a[10]={1,2,3}; printf("%p %p\n",a,&a[0]); printf("%p %p\n",a+1,&a[1]); printf("%p %p\n",a+2,&a[2]);
打印结果:
0xbfe47dd4 0xbfe47dd4
0xbfe47dd8 0xbfe47dd8
0xbfe47ddc 0xbfe47ddc
字符串数组
因为C语言没有字符串类型,所以可以用字符数组的形式表示字符串。
概念:类型为字符的数组,也就是数组中每个元素的数据类型都是字符型,该数组组成字符串。
表示形式
单个字符表示
char s[]={'h','e','l','l','o'};//sizeof(s)=5
char s1[5]={'h','e','l','l','o'};//sizeof(s1)=5
字符串的形式表示,结尾自动加上' \0 '
char s2[]="hello";//sizeof(s2)=6
char s3[]={"hello"};//sizeof(s3)=6
char s4[6]={"hello"};//sizeof(s4)=6
char s5[]={};//错误
初始化和数组规则相同
输入和输出
输入
用scanf输入
char s[32]={};
scanf("%s",s);
printf("%s\n",s);
如果用%s不能输入空格,如果有空格空格包括后面内容不会输入进入数组 存入;如果需要输入空格,则需要用%[^\n]:
char s[32]={};
scanf("%[^\n]",s);//正则表达式,可以输入除了\n以外的字符,遇到\n结束
printf("%s\n",s);
用for循环遍历输入
例如:
char s[6]={}; for(int i=0;i<6;i++) scanf("%c",&s[i]); printf("%s\n",s);
用gets()输入
#include <stdio.h> char *gets(char *s);
功 能:从终端获取字符串输入
参 数:s:目标字符串的首地址
返回值:目标字符串的首地址
注意:
- 该函数没有数组越界检查,使用该函数时会有警告。
- 该函数可以输入空格,字符串结束的标志是换行符 ('\n') 或文件描述符(EOF)。
例如:
char s[32]={}; gets(s); printf("%s",s);
练习:计算字符串空格数
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { char s[32]={}; int num=0; scanf("%[^\n]",s); for(int i=0;i<32;i++) { if(s[i]==' ') num++; } printf("num:%d\n",num); return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { char s[32]={}; int num=0; scanf("%[^\n]",s); int i=0; while(s[i]!='\0') { if(s[i]==' ') num++; i++; } printf("num:%d\n",num); return 0; }
输出
用printf()输出
char s[32]="hello";
printf("%s\n",s);
用for()循环输出
char s[32]="hello";
for(int i=0;i<32;i++)
printf("%c",s[i]);
printf("\n");
用puts()输出
#include <stdio.h> int puts(const char *s);
功 能:向终端输出字符串
参 数:要输出的目标字符串首地址
返回值:字符串长度
例题:
看一下程序有哪些错误
char s[10]={}; s[10]="hello"; //s[10] 试图访问数组的第11个元素(数组索引从0开始),但数组 s 只有10个元素,所以这是越界访问:s[10] 是一个 char 类型的变量,而 "hello" 是一个字符串字面量(即 char 类型的数组),它们之间不能直接赋值。 s="hello"; //数组名 s 是一个指向数组首元素的指针常量,它不能重新被赋值指向另一个地址(即不能让它指向另一个字符串)也就是s不能作为左值使用,“hello”是一个常量字符数组,其地址是固定的 //如果想将字符串 "hello" 复制到数组 s 中,应该使用 strcpy 函数(需要包含头文件 <string.h>)
例题:
将字符串“This is a string.”用char s[17]={};记录下来,错误的输入语句为(A)
A. scanf("%20s",s);
B.for(k=0;k<17;k++)
s[k]=getchar();
C.while((c=getchar())!='\n')
s[k++]=c;
解析:
A.
scanf
函数中的%20s
指示符意味着scanf
会尝试读取最多 20 个字符(不包括结束的空字符)到一个字符数组中,但是s[17]只分配了17个字符空间大小,如果输入超过 16 个字符(加上一个空字符),就会发生缓冲区溢出,因此,正确的做法应该是使用%16s
,确保不会超过数组s
的边界。
计算字符串长度
用循环
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char const *argv[])
{
char str[15]="hello world";
int i=0,len=0;
while(str[i]!='\0')
{
len++;
i++;
}
printf("%d",len);
return 0;
}
用strlen()
#include <string.h> size_t strlen(const char *s);
功 能:计算字符串实际长度
参 数:字符串首地址
返回值:返回字符串的实际长度,不包括 '\0'
例如:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { char str[32]="hello world"; int len=strlen(str); printf("%d\n",len); return 0; }
sizeof()和strlen()的区别
sizeof()是关键字,strlen()是函数
sizeof()是计算数据所占空间大小,strlen()是计算字符串的实际长度
sizeof()计算包括 '\0' 在内,strlen()计算不包括 '\0' 在内,因此,sizeof计算的值比strlen多1
char str[]="hello world";
char str1[32]="hello world";
int len=strlen(str);
int len1=strlen(str1);
printf("%d\n",len);//11
printf("%d\n",len1);//11
printf("%d\n",sieof(str));//12
printf("%d\n",sizeof(str1));//32
二维数组
下标是两个的数组是二维数组,其中下标分别为行下标和列下标。
定义格式
存储类型 数据类型 数组名 [行数] [列数];
int a[3][2];
访问元素
数组名 [行下标] [列下标];
//下标都是从零开始
int a[2][3];
a[0][0];//第一行第一列的元素
a[1][2];//最后一行最后一列的元素
注意:
- 行下标和列下标都不能越界。
- 定义时行数可以省略但是列数不能省略。
- 数组名代表的是第一行的首地址。
数组元素的个数和二维数组的大小
元素个数=行数*列数
计算二维数组的大小:
- 数据类型的大小*行数*列数
- sizeof(数组名)
二维数组的数组名
二维数组的数组名代表第一行首地址
例如:
int a[][3]={1,2,3,4,5,6};
a:第一行的首地址
a+1:第二行的首地址
初始化
定义时全部初始化
int a[2][3]={1,2,3,4,5,6};//顺序赋值
int a[2][3]={{1,2,3},{4,5,6}};//按行赋值
定义时部分初始化
int a[2][3]={1,2,3,4};//顺序赋值 1 2 3 4 0 0
int a[2][3]={{1,2},{3,4}};//按行赋值 1 2 0 3 4 0
未初始化
随机值,需要对每个元素都进行赋值。
遍历二维数组
for循环嵌套遍历,外层循环行数,内层循环列数。
int a[2][3]={};
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
{
printf()/scanf()
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char const *argv[])
{
int a[3][3]={};
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
scanf("%d",&a[i][j]);
}
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
printf("%d",a[i][j]);
printf("\n");
}
return 0;
}
练习:设一个二维数组,每个数组元素的值都为行下标和列下标的乘积
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { int arr[4][4]={}; for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { arr[i][j]=i*j; printf("arr[%d][%d]=%d ",i,j,arr[i][j]); } printf("\n"); } return 0; }
内存分配
行地址
在二维数组中,数组名的本质是第一行的首地址,其数值还是第一行第一列的地址,即&array[0][0]。
array+1的本质是第二行的首地址,其数值还是第二行第一列的地址,即&array[1][0]。
列地址
在二维数组中array[0]的本质是第一行第一列,即&array[0][0]。
array[0]+1是第一行第二列的地址,即&array[0][1]。
列地址:&array[i][j] a[i]+j
验证:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { int a[2][3]={1,2,3,4,5,6}; printf("%p %p\n",a,a+1); printf("%p %p\n",a[0],a[1]); printf("%p %p\n",&a[0][0],&a[1][0]); printf("%p %p\n",a[0]+1,a[1]+1); printf("%p %p\n",&a[0][1],&a[1][1]); return 0; }
打印结果如下:
练习:
在执行语句: int a[][3]={1,2,3,4,5,6}; 后,a[1][0]的值为(A)
A. 4 B. 1 C. 2 D. 5
在定义 int a[5][6]; 后,数组a中的第十个元素是(C)(设 a[0][0] 为第一个元素)
A. a[2][5] B. a[2][4] C. a[1][3] D.a[1][5]
下列程序执行后的输出结果为(B)
main() { int i,j,a[2][3]; for(i=0;i<3;i++) for(j=0;j<=i;j++) a[i][j]=i*j; printf("%d,%d\n",a[1][2],a[2][1]); }
A. 2,2 B. 不定值,2 C. 2 D. 2,0
有一个3*4的矩阵(元素值不同),要求输出其中最大值以及它的行号和列号。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { int a[3][4]={{12,3,56,2},{7,8,89,9},{100,890,567,1}}; int max=a[0][0],h=0,l=0; for(int i=0;i<3;i++) { for(int j=0;j<4;j++) { if(max<a[i][j]) { max=a[i][j]; h=i; l=j; } } } printf("max:a[%d][%d]=%d\n",h,l,max); return 0; }