1. 数组的基本用法
1.1概念
数组是有序的元素序列。
将若干个类型相同的变量的集合命名,那么这个名称就是数组名。
重点是:数据类型相同并且连续的变量的集合。
1.2 定义格式
存储类型 数据类型 数组名[元素个数];
例如:(auto) int a[5];
数组名:代表数组的首地址,a是地址常量,不能为左值,不能被赋值。
数组定义方法:
(1)数组名定义规则跟变量名相同,遵循标识符命名规则。
(2)数组名后使用方括号括起来的表达式常量,表示元素的个数。
(3)常量表达式中可以包含常量和符号常量,不能包含变量。
1.3 访问元素
数组名[下标];
注意:下标从0开始
例如:
访问第一个元素: a[0];
访问第n个元素: a[n-1];
特点:
-
数据类型相同
-
在内存中连续
注意🚨:
-
(1)数组的数据类型就是其元素的数据类型
-
(2)数组名要符合标识符命名规则
-
(3)在同一个函数中,数组名不要与变量名相同。
例如:int a[5];
int a;//错误
(4)数组下标从0开始,到n-1结束。
2. 一维数组
2.1 一维数组的概念
是有一个下标的数组
-
格式:存储类型 数据类型 数组名[元素个数];
-
访问元素: 数组名[下标];下标从0开始
-
数组名:数组的首地址
2.2 初始化
【1】定义时全部初始化
int a[5]={1,2,3,4,5};
【2】定义时部分初始化:
未初始化元素值为0
int a[5]={1,2};
【3】先定义,后初始化。需要单独对每个元素赋值。
int a[5];
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=5;
注意定义时不能数组越界:
int a[3]={6,2,3,5,1}; //错误,数组越界
作业:
-
用思维导图的方式整理笔记
-
输入任意两个数,输出两数之间(包括这两个数)偶数之和。
思路:将输入的两个数a,b中小的数a,依次加1,加到b的值,每次循环判断这个数a是否为偶数,是则累加到sum中。
#include <stdio.h>
int a,b,c,sum;
int main(int argc, char const *argv[])
{
scanf("%d %d",&a, &b);
if(a>=b) //用于两数交换,把小的数放前面。
{
c=a;
a=b;
b=c;
}
while(a<=b) //循环包含两个数在内的所有数
{
if(a%2==0) // 进行判断,如果是偶数就累加。
sum+=a;
a++;
}
printf("%d\n",sum);
}
2.3 定义空数组
【1】int a[5]={0,0,0,0,0};
【2】int a[5]={0};
【3】int a[5]={};
2.4 数组引用
(1)先定义后引用
(2)每次只能引用一个数组元素a[i], 如果想引用所有元素可以通过循环遍历数组。
(3)引用时防止数组越界
(4)打印数组元素地址用%p的格式
例如:
int a[10];
printf("%p",a);
printf("%d",a); //错误,数组名是数组的首地址。
如果想遍历数组可以通过循环:
#include<stdio.h>
int main(int argc, char const *argv[])
{
int a[5]={1,2,3,4,5};
for(int i=0;i<5;i++)
printf("%d ",a[i]);
return 0;
}
练习:输入5个数组元素,然后依次打印出来。
int a[5]={};
for(int i=0;i<5;i++)
scanf("%d",&a[i]);
for(int i=0;i<5;i++)
printf("%d ",a[i]);
2.5 数组的大小
例如:
int a[5];//20
double b[2];//16
char c[32];//32
printf("%d %d %d\n", sizeof(a), sizeof(b), sizeof(c));
计算方式:
-
(1)数组元素的个数 * 数据类型的大小
-
(2)sizeof(数组名)
数组元素的个数=sizeof(数组名)/sizeof(数组名[下标])
例如: printf(“%d\n”,sizeof(a)/sizeof(a[0]));
练习:计算斐波那契数列前15项并逆序输出
1 1 2 3 5 8 13 21 …
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[15] = {1, 1};
for (int i = 2; i < 15; i++)
{
a[i] = a[i - 1] + a[i - 2];
}
for (int j = 14; j >= 0; j--)
printf("%d ", a[j]);
printf("\n");
return 0;
}
2.6 清零函数
2.6.1 bzero()函数
#include <strings.h>
void bzero(void *s, size_t n);
-
功能:将内存空间设置为0
-
参数:
- s:要清空的空间的首地址
- n:字节大小
-
返回值:无
案例:
#include <stdio.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");
bzero(a, sizeof(a));
for (int i = 0; i < 10; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
打印结果:
1 2 3 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
2.6.2 memset( ) 函数
#include <string.h>
void *memset(void *s, int c, size_t n);
-
功能:将指定内存空间设为0
-
参数:
- s:指定空间的首地址
- c:要设置的数,一般为0
- n:要设置的内存大小
-
返回值:无
案例:
#include <stdio.h>
#include <string.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;
}
3. 字符数组
因为语言中没有字符串类型,所以可以用字符数组的形式去表示字符串。
概念:类型为字符的数组,也就是说数组中每个元素是字符类型。可以组成字符串。
3.1 表示形式
(1)单个字符表示:
char s[]={‘h’,’e’,’l’,’l’,’o’}; //sizeof(s)=5
char s1[5]={‘h’,’e’,’l’,’l’,’o’}; //sizeof(s1)=5
(2)用字符串表示,结尾会自动加上 ‘\0’
char s2[]=”hello”; //sizeof(s2)=6
char s3[]={“hello”}; //sizeof(s3)=6
char s4[6]={“hello“} //sizeof(s4)=6
char s5[]={};//0错误
验证字符组大小
printf(“%d %d\n”,sizeof(s),sizeof(s1));
printf(“%d %d %d\n”,sizeof(s2),sizeof(s3),sizeof(s4));
初始化和数组规则相同
3.2 输入和输出
3.2.1 输入
(1)用scanf:
char s[32]={};
scanf("%s",s);
注意📢:不能输入空格,如果输入空格,空格后面内容不会存入数组🚨。例如输入hello word, 只能存入hello。
如需要保存带空格的字符串如下:
char s[32]={};
scanf("%[^\n]",s); //可以输入除了\n以外的字符,遇到\n结束。
printf("%s\n",s);
注意:输入字符串时会在结尾自动加“\0”。
(2) 用循环遍历输入
例如:
char s[6]={};
for(int i=0;i<6;i++)
scanf("%c",&s[i]);
printf("%s",s)
(3) gets()
#include <stdio.h>
char *gets(char *s);
-
功能:从终端获取字符串输入
-
参数:s:目标字符串的首地址
-
返回值:目标字符串的首地址
注意🚨:gets没有数组越界检查,使用时会报警告。
char s[32]={};
gets(s);
printf("%s\n",s);
练习:输入一个字符串,计算其空格个数。
#include <stdio.h>
int main(int argc, char const *argv[])
{
char s[32] = {};
int sum = 0;
gets(s);
for (int i = 0; i < 32; i++)
{
if (s[i] == ' ')
sum++;
}
printf("%d\n", sum);
return 0;
}
#include <stdio.h>
int main(int argc, char const *argv[])
{
char s[32] = {};
int sum = 0;
gets(s);
int i=0;
while (s[i]!='\0')
{
if(s[i] == ' ')
sum++;
i++;
}
printf("%d\n", sum);
return 0;
}
3.2.2 输出
(1)用 printf():
char s[32]="hello";
printf("%s",s);
(2)for 循环
for(int i=0;i<32;i++)
printf("%c",s[i]);
(3)puts
#include <stdio.h>
int puts(const char *s);
-
功能:向终端输出字符串
-
参数:要输出字符串的首地址
-
返回值:字符串长度(包括 ‘\0’)
例题:看下列程序由哪些错误
char s[10]={};
s[10]="hello";//错误,不能把字符串赋值给字符数组的元素
s="hello";//错误,s是地址常量,不能为左值
例 : 若准备将字符串“This is a string.”用char s[17]={};记录下 来,错误的输入语句为:
(A)scanf(“%20s”,s); ❌
(B)for(k=0;k<17;k++)
s[k]=getchar();
(C)while((c=getchar())!=‘\n’)
s[k++]=c;
3.3 计算字符串长度
3.3.1 用循环统计
#include <stdio.h>
int main(int argc, char const *argv[])
{
char string[15]="hello world";
int i=0;
while(string[i]!='\0')
i++;
printf("%d\n",i);
return 0;
}
3.3.2 用strlen( )
#include <string.h>
size_t strlen(const char *s);
-
功能:计算字符串长度
-
参数:字符串的首地址
-
返回值:返回字符串实际长度,不包括’\0’在内。
例如:
char string[]="hello world";
char string2[15]="hello world";
int l=strlen(string);
printf("%d\n",l); //11
printf("%d\n",sizeof(string)/sizeof(char));//12
printf("%d\n",sizeof(string2)/sizeof(char));//15
3.3.3 sizeof 和 strlen 的区别
-
sizeof是关键字,strlen是函数。
-
sizeof是计算数据所占空间大小,strlen是计算字符串的实际长度的。
-
sizeof计算时包含‘\0’, strlen计算时不包含‘\0’。
练习:输入一个字符串,实现字符串大小写的转换。
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char s[32]={};
int len=0;
scanf("%[^\n]",s);
len=strlen(s);
for(int i=0;i<len;i++)
{
if(s[i]>='a' && s[i]<='z')
s[i]-=32;
else if(s[i]>='A' && s[i]<='Z')
s[i]+=32;
}
printf("%s\n",s);
return 0;
}
4. 排序
4.1 冒泡排序
4.1.1 排序过程
(1)比较第一个数和第二个数,如果前面的数比后面的大,则交换。然后比较第二个和第三个数,也是如果前面的数比后面的大则交换。以此类推,直到比较完第n-1个和第n个数为止,第一趟冒泡排序结束。排序结果是最大的数被放在最后一个元素的位置上。
(2)把前n-1个数进行第二趟冒泡排序,结果是次大的数排在第n-1个的元素的位置上。
(3)重复上述过程,一共经过n-1趟冒泡排序,排序结束。
4.1.2 原理
第一遍排序每一个数都会被比较到,最大的数肯定会被排在最后一个位置因为只要比前面的数大就会往后排后面的排序原理一样。
例如: int a[5]={5,4,3,2,1};
第一轮i=1 | 第二轮i=2 | 第三轮i=3 | 第四轮i=4 |
---|---|---|---|
45321 | 34215 | 23145 | 12345 |
43521 | 32415 | 21345 | |
43251 | 32145 | ||
43215 |
第一轮:4=5-1 次排序
第二轮:3=5-2 次排序
第三轮:2=5-3 次排序
第四轮:1=5-4 次排序
设元素个数为N:
总轮数=元素个数-1=N-1
每轮排序次数=元素个数-轮数=N-i
练习:一组数进行冒泡排序:100,34,56,78,99,2
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[5]={5,4,3,2,1},temp=0;
for(int i=1;i<5;i++)
{
for(int j=0;j<5-i;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
for(int i=0;i<5;i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
4.2 选择排序
4.2.1 排序过程
(1)首先通过n-1次比较,从n个数中找到最小的,将它与第一个数进行交换,这是第一趟选择排序。结果是最小的数被安置到第一个元素的位置上。
(2)再通过n-2次比较,从剩余的n-1个数中找到次小的数,将它与第二个数进行交换,这是第二趟选择排序。结果是次小的数排在第二个元素的位置上。
(3)重复上述过程,总共经历n-1次排序,排序结束。
4.2.2 原理
第一次排序存取最小值,然后与第一个数进行交换,以此类推。
4.2.3 编程
把5,4,3,2,1,56,7,8,9,34进行选择排序
#include<stdio.h>
#define N 10
int main(int argc, char const *argv[])
{
int arr[N]={5,4,3,2,1,56,7,8,9,34},min=0,temp=0;
for (int i = 0; i < N-1; i++) //循环轮数
{
min=i; //设最小值下标初值为还未排序的第一个元素下标
for(int j=i+1; j<N;j++) //把要比较的元素和其后面所有元素通过循环进行比较
{
if(arr[min]>arr[j]) //通过每次的比较,找出最小元素的下标。
min=j;
}
if(arr[min]!=arr[i]) ///如果记录的最小值元素和用来比较的元素不一样则交换
{
temp=arr[min];
arr[min]=arr[i];
arr[i]=temp;
}
}
for(int k=0;k<N;k++)
{
printf("%d ",arr[k]);
}
printf("\n");
return 0;
}
练习:
- 将一串字符串进行倒置char buf[32]=”hello”; //olleh
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char str[32] = "hello",c=0;
int l=strlen(str); //l=5
for(int i=0;i<l/2;i++)
{
c=str[i];
str[i]=str[l-i-1];
str[l-i-1]=c;
}
printf("%s\n",str);
return 0;
}
- 在终端输入一个字符串,计算大写字母、小写字母、空格、数字个数,分别在终端输出他们的个数。
#include<stdio.h>
int main(int argc, char const *argv[])
{
char s[32]={},space=0,str=0,Str=0,num=0;
scanf("%[^\n]",s);
for(int i=0;i<32;i++)
{
if(s[i]==' ')
space++;
if(s[i]>='a'&&s[i]<='z')
str++;
if(s[i]>='A'&&s[i]<='Z')
Str++;
if(s[i]>='0'&&s[i]<='9')
num++;
}
printf("space=%d str=%d Str=%d num=%d\n",space,str,Str,num);
return 0;
}
5. 二维数组
下标为两个的数组就是二维数组,下标分别是行下标和列下标。
5.1 定义格式
存储类型 数据类型 数组名[行数][列数];
例如:int a[3][4];
5.2 访问元素
数组名[行下标][列下标]; (下标都是从0开始的)
int a[2][3];
a[0][0]:第一行第一列的元素
a[1][2]:最后一行最后一列的元素。
设n行m列:a[n-1][m-1]就是最后一行最后一列的元素。
注意:
-
(1)行下标和列下标都不能越界。
-
(2)行数可以省略,列数不能省略。
-
(3)数组名代表的是第一行的首地址
例如:一维数组和二维数组对比
- 一维数组:
- 二维数组:
5.3 二维数组元素的个数和二维数组的大小
元素个数:行数*列数
二维数组大小:
(1)数据类型大小 × 行数 × 列数
(2)sizeof(数组名)
5.4 二维数组数组名
二维数组数组名代表第一行首地址
例如:int a[2][3]={1,2,3,4,5,6};
-
a: 第一行的首地址
-
a+1:第二行的首地址
设n行,最后一行首地址是:a+n-1
5.5 初始化
- 【1】全部初始化
int a[2][3]={1,2,3,4,5,6}; //顺序赋值
int b[2][3]={{1,2,3},{4,5,6}}//按行赋值
- 【2】部分初始化
未初始化的元素值为0
int a[2][3]={1,2,3,4,}; //顺序赋值 1 2 3 4 0 0
int b[2][3]={{1,2},{4,5}; //按行赋值 1 2 0 4 5 0
- 【3】未初始化
随机值,需要单独赋值。
5.6 遍历二维数组
for循环嵌套,外层行数,内层列数
int a[n][m]={};
for(int i=0;i<n;i++)//行下标
{
for(int j=0;j<m;j++)
{
//scanf()/printf()
}
}
练习:设一个二维数组,每个数等于行和列数的乘积。
#include <stdio.h>
int a[3][3]={0};
int main(int argc, char const *argv[])
{
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
a[i][j]=i*j;
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
a[0][0]=0; a[0][1]=0;a[0][2]=0
a[1][0]=0; a[1][1]=1;a[1][2]=2
a[2][0]=0; a[2][1]=2;a[2][2]=4
5.7 内存分配
int a[2][3];
(1) 行地址
-
在二维数组中,数组名a的值,表示的实际含义是第一行首地址。这个数值还等于整个数组的首地址,还等于第一个元素的地址(&a[0][0])。
-
a+1代表第二的首地址。这个数值还等于第二行第一个元素的地址(&a[1][0])。
(2) 列地址
-
二维数组中,a[0]的值是第一行第一列的地址,也就是该数组中首个元素的地址(&a[0][0])
-
a[0]+1的值,是数组元素第一行第二列的地址(&a[0][1])
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]);
打印结果:
0xbf858184 0xbf858190
0xbf858184 0xbf858190
0xbf858184 0xbf858190
0xbf858188 0xbf858194
0xbf858188 0xbf858194