1. 数组的基本用法
1.1 概念
数组(Array)是有序元素序列
若将有限个类型相同的变量的集合命名,那么这个名称为数组名。
重点必须是类型相同和连续的变量集合
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};
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}; //1 2 0 0 0
printf("%d\n", a[0]); //1
printf("%d\n", a[1]); //2
printf("%d\n", a[2]); //0
printf("%d\n", a[3]); //0
printf("%d\n", a[4]); //0
(3) 未初始化:只定义不赋值,需要单个元素赋值。(不然局部变量为随机值)
int a[5];
a[0] = 1;
a[1] = 2;
a[2] = 3;
a[3] = 4;
a[4] = 5;
for(int i=0; i<5;i++) //i的值循环为: 0 1 2 3 4 5(到5不满足循环条件出循环)
printf("%d\n",a[i]); //循环打印:a[0] a[1] a[2] a[3] a[4] a[5]
注意数组越界问题:
int a[3] = {4,3,2,1}; //错误,数组越界
a[3] =10; //错误,数组越界了
2.3 定义空数组
● int a[5] = {0,0,0,0,0};
● int a[5] = {0};
● int a[5] = {};
2.4 数组引用
(1) 先定义后引用
(2) 每次只能引用一个数组元素a[i], 如果想引用所有元素可以循环遍历
(3) 打印数组元素地址用%p格式
练习:输入5个数组元素,然后依次打印出来。
int a[5], i;
for (i = 0; i < 5; i++)
scanf("%d", &a[i]);
for (i = 0; i < 5; i++)
printf("%d\n", a[i]);
2.5 数组的大小
int a[5]; //4*5 = 20
double b[3]; //8*3 = 24
char c[32]; //32
计算方式:
(1) 手算:数元素个数*数据类型大小
(2) 用sizeof: sizeof(数组名);
数组元素的个数 = sizeof(数组名) / 元素大小;
printf("%ld\n", sizeof(a));
printf("%ld\n", sizeof(a[0]));
练习:计算斐波那契数列前15项并逆序输出
1 1 2 3 5 8 13 21 .....
#include <stdio.h>
int main()
{
int a[15] = {};
a[0] = a[1] = 1;
for (int i = 2; i < 15; i++)
a[i] = a[i - 1] + a[i - 2];
for (int i = 14; i >= 0; i--)
printf("%d ", a[i]);
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 <string.h>
int main()
{
int a[15] = {1, 2, 3};
bzero(a, sizeof(a)); //从地址a开始,sizeof(a)个字节全部清空
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
6.2.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 a[15] = {1, 2, 3};
//bzero(a, sizeof(a)); //从地址a开始,sizeof(a)个字节全部清空
memset(a, 0, sizeof(a));
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
2.7 数组的内存分配
int a[5];
a是数组名,也是数组的首地址也就是第一个元素的地址,即&a[0]。
a+1 是数组第二个元素的地址,即&a[1]。
数组再内存中分配如下图:
#include <stdio.h>
#include <string.h>
int main()
{
int a[5] = {1, 2, 3};
printf("%p %p\n", &a[0], a);
printf("%p %p\n", &a[1], a + 1);
printf("%p %p\n", &a[2], a + 2);
return 0;
}
通过地址间接取地址表示空间的内容可以通过*:
3. 字符数组
因为C语言没有字符串类型,所以可以用字符数组的形式表示字符串。
概念:类型为字符的数组,也就是数组中每个元素是字符型。可以组成字符串。
3.1 表示形式
(1) 单个字符表示:
char s[5] = { 'h', 'e', 'l', 'l', 'o'}; //sizeof(5) = 5;
char s1[] = { '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[6] ="hello"; //sizeof(s5) = 6 //常用
char s6[] ={}; //错误,如果定义时不赋值则不能省略元素个数
初始化和其他类型数组相同
3.2 输入和输出
3.2.1 输入
(1) scanf:
char s[32] = {};
scanf("%s", s);
printf("%s\n", s);
不能输入空格,如果有空格,空格后面的内容不会存入数组。
例如:以下程序输入hello world只能存入hello
如果需要输入空格则:
char s[32] = {};
scanf("%[^\n]", s); //除了\n以外所有字符都可以输入到s地址下空间内保存,遇到\n就结束
printf("%s\n", s);
输入字符串会在后面加上'\0'
(2) 用for遍历输入
char s[6] = "";
for (int i = 0; i < 6; i++)
scanf("%c", &s[i]);
(3) gets()
#include <stdio.h>
char *gets(char *s);
功能:从终端获取字符串输入
参数:s:目标字符串首地址
返回值:目标字符串首地址
注意:没有数组越界检查,使用时会报警告。
3.2.2 输出
(1) 用printf()
char s[32]="hello"
printf("%s\n",s);
(2) for循环
char s[32] = "hello";
for (int i = 0; i < 32; i++)
printf("%c", s[i]);
(3) puts()函数
int puts(const char *s)
功能:向终端打印字符串
参数:打印的字符串首地址
返回值:字符串长度
char s[32] = "";
gets(s); //在结尾加个\0,不会接收到回车
puts(s); //在结尾自动加个回车
例题:看下列程序由哪些错误
char s[10]={};
s[10]="hello"; //错误,数组越界,给字符数组元素赋值不能用字符串
s="hello"; //错误,s地址常量,不能为左值
strcpy(s,"string"); //正确
例: 若准备将字符串“This is a string.”用char s[17]={};记录下 来,错误的输入语句为:
(A)scanf(“%20s”,s); //%[^\n]
(B)for(k=0;k<17;k++)
s[k]=getchar();
(C)while((c=getchar())!=‘\n’)
s[k++]=c;
3.3. 计算字符串长度
char s[32]="hello"; //sizeof(s)=23
3.3.1 用循环统计
char s[32] = "hello";
int i;
// int len = 0;
// for (i = 0; s[i] != '\0'; i++)
// len++;
// printf("%d\n", len); //5
i = 0;
while (s[i] != '\0')
{
i++;
}
printf("%d\n", i); //5
3.3.2 用strlen()
#include <string.h>
size_t strlen(const char *s);
功能:计算字符串长度
参数:字符串的首地址
返回值:返回字符串实际长度,不包括'\0'在内。
char s[32] = "hello";
int num = strlen(s);
printf("%d\n", num); //5
3.3.3 sizeof和strlen的区别
1. sizeof是关键字,strlen是函数。
2. sizeof是计算数据所占空间大小,strlen计算字符串的实际长度。
3. sizeof计算包括\0,strlen计算不包括\0;计算字符串长度时(元素个数省略情况下),sizeof比strlen大1。
练习:
(1)实现字符串大小写的转换
#include<stdio.h>
#include<string.h>
int main()
{
char s[32]="";
int len;
gets(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);
}
(2) 将一串字符串进行倒置char buf[32]=”hello”;//olleh
思路:循环长度/2次,每一次前后元素做交换。
#include <stdio.h>
#include <string.h>
int main()
{
char s[32] = "", t;
int len;
gets(s);
len = strlen(s); //5
for (int i = 0; i < len / 2; i++) //i的值: 0 1
{
t = s[i]; //t=s[0] //t=s[1]
s[i] = s[len - 1 - i]; //s[0] = s[4] //s[1]=s[3]
s[len - 1 - i] = t; //s[4]=t; //s[3]=t
//s[i]和s[len-1-i] 进行交换
}
printf("%s\n", s);
}
(3) 在终端输入大写字母、小写字母、空格、数字,分别在终端输出他们的个数
思路: 遍历每个元素然后判断
#include <stdio.h>
#include <string.h>
int main()
{
char s[32] = "";
int len, len1 = 0, len2 = 0, len3 = 0, len4 = 0;
gets(s);
len = strlen(s);
for (int i = 0; i < len; i++)
{
if (s[i] == ' ')
len1++;
else if (s[i] >= 'a' && s[i] <= 'z')
len2++;
else if (s[i] >= 'A' && s[i] <= 'Z')
len3++;
else if (s[i] >= '0' && s[i] <= '9')
len4++;
}
printf("%d %d %d %d\n", len1, len2, len3, len4);
}