[一篇读懂]C语言四讲:一维数组与字符数组


1. 一维数组

1 数组的定义

  • 人难以同时记住多个元素,可以借助数组,通过一个符号来访问多个元素。

如存放鞋子,将衣柜的最下面一层分为10个连续的格子,拿取最下面一层第三个格子的鞋子就很方便。

  • 数据特点:(1)具有相同的数据类型。(2)使用过程中需要保留原始数据

如学生的成绩,用于排名时需要原始数据。

  • 一维数组的定义格式为:
//类型说明符 - 数组名 - [常量表达式]
int a[10];
  • 声明数组时遵循以下规则:
  1. 遵循标识符命名规则,数组名命名规则与变量名相同——由数字、字母和下划线组成,且只能由数字和下划线开头。
  2. 定义数组时需要制定数组中的元素个数,由方括号中的常量表达式表示,即数组长度。
  3. 常量表达式中可以包含常量符号常量,在旧的C标准中不支持变量,即不允许对数组的大小做动态定义,也就是数组的大小不依赖于程序运行过程中变量的值

错误声明示例(最新的C标准支持,但是最好不要这么写):

int n;
scanf("%d",&n); //在程序中临时输入数组的大小
int a[n];

数组声明的其他常见错误如下:

(1) float a[0]; //数组大小为0没有意义
(2) int b(2)(3); //不能使用圆括号
(3) int k = 3, a[k]; //不能用变量说明数组大小

2 一维数组在内存中的存储

(1)定义一个大小为a[10]的数组:

int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
  • 数组元素的引用方式是“数组名[下标]”,其可以访问的值为a[0]到a[9]。

注意,其中没有元素a[10],因为数组元素是从0开始编号的

  • 每个元素都是整型元素,占用4字节。
  • 从低地址向高地址存放
  • 初始化不能写成:
int a[10];
a[10]={ 0,1,2,3,4,5,6,7,8,9 };//Error

(2)可以只给一部分元素赋值。例如,

int a[10] = { 0,1,2,3,4 };

定义a数组有10个元素,但花括号内只提供5个初值,这表示只给前5个元素赋初值,后5个元素的值为0。

(3)如果要使一个数组中全部元素的值为0,那么可以写为

int a[10] = { 0 };//后面没有赋值的默认都是0

最好不要写成

int a[10] = { 0,0,0,0,0,0,0,0,0,0 };//浪费时间,显得学艺不精

(4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组的长度。
例如:

int a[] = { 1,2,3,4,5 };//初试中不建议使用,不便于阅读

这种写法不便于 人类 阅读数组长度。


2. 数组访问越界与数组的传递

1 数组的访问越界

数组越界

#include <stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 }; //定义数组时,数组长度须固定
	int j = 20;
	int i = 10;
	a[5] = 6; //访问越界
	a[6] = 7; //访问越界会造成数据异常
	printf("i = %d\n", i); //i发生改变 - i=7
	return 0;
}

i i i在程序中没有变动,但是 i i i的值发生了改变。

这是因为操作a[6]时,将会把变量 i i i放置的内存位置覆盖。
所以此处 i i i并没有赋值,但是值却变化了。

通常在for循环中没有控制好边界,导致访问越界。
编译器并不检查程序对数组下标的引用是否在数组的合法范围内。

2 数组的传递

重要
下列代码:

#include <stdio.h>
void print(int a[])//[]内最好不写长度 - 没有意义
{
	int i;
	for (i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d\n", a[i]);
	}
}

int main()
{
	int a[6] = { 1,2,3,4,5,6 };
	print(a);
	return 0;
}

运行结果:
运行结果
发现结果只输出了1、2。

这是因为一维数组在传递时,数组传递到子函数后,子函数的形参接收到的是数组的起始地址。
因此不能够把数组的长度传递给子函数

注意,在子函数a[]的方括号中填写任何数字都是没有意义的,最好不写

所以我们通过额外定义一个变量length来传递数组中的元素个数。

#include <stdio.h>
void print(int a[], int length)
{
	int i;
	for (i = 0; i < length; i++)
	{
		printf("%d\n", a[i]);
	}
}

int main()
{
	int a[6] = { 1,2,3,4,5,6 };
	print(a, 6);
	return 0;
}

可以正常输出数组中的所有元素了。

另外,在子函数中修改数组元素,当函数执行结束时,数组中的元素也会得到修改。

#include <stdio.h>
void print(int a[], int length)
{
	int i;
	for (i = 0; i < length; i++)
	{
		printf("%3d\n", a[i]);
	}
	a[3] = 20;//在子函数中修改a[3]
	printf("\n");
}

int main()
{
	int a[6] = { 1,2,3,4,5,6 };
	print(a, 6);
	printf("a[3] = %d\n", a[3]);//此处a[3]已经得到修改
	return 0;
}

运行结果:
运行结果


3. 字符数组与scanf读取字符串

对字符数组进行额外的讲解,其与整形数组和浮点型数组不一样的地方。

1 字符数组初始化及传递

  • 字符数组的定义方法与前面介绍的数组类似:
char c[10];
  • 字符数组初始化的方式:
char c[10] = "Iamhappy";
  • 字符串结束标志为’/0’,所以字符数组存储的字符串长度必须比字符数组少1字节。

例如,char c[10]最长存储9个字符,剩余的1个字符用来存储’/0’

  • 使用%s来输出一个字符串
printf("%s\n",c);//直接把数组名放过来
  • 输出字符串乱码时,要去查看字符数组中是否存储了结束符

注意,没有结束符会输出乱码,直到找到’\0’结束。

char c[5] = { 'h','e','l','l','o' };
printf("%s\n",c);

输出乱码:输出

  • 通过子函数模拟printf(“%s”,c)操作
#include <stdio.h>
void print(char d[])//不需要输入数组长度
{
	int i = 0;
	while (d[i])//当循环到结束符时,循环结束
	{
		printf("%c", d[i]);
		i++;
	}
	printf("\n");
}

int main()
{
	char c[5] = "how";
	printf(c);
	return 0;
}

当c[i]为’\0’时,其值是0,循环结束,也可以写为 c[i] != ‘\0’.

2 scanf读取字符串

  • scanf读取字符串时使用%s
scanf("%s", c);//不需要取地址

注意,字符数组名c中存储了数组的起始地址,因此不需要取地址。
scanf读取字符串操作,会自动往字符数组中放结束符。

  • scanf在使用%s读取字符串时,会忽略空格和回车(这一点与%d和%f类似)。
    输入多个字符串,对c和d分别输入“how”和“are”(中间加一个空格):
#include <stdio.h>

int main()
{
	char c[10];
	char d[10];
	scanf("%s%s", c, d);
	printf("c = %s, d = %s\n", c,d);
	return 0;
}

        运行结果:
结果
        结果中没有输出空格,这是因为空格被忽略了。

  • scanf怎么一次读取一行?

用gets函数实现。


4. gets与puts讲解,strlen_strcmp_strcpy讲解

1 gets函数与puts函数

  • gets中放入我们字符数组的数组名即可
gets(c);

gets函数可以一次读一行,其会读取换行’\n’之前的所有内容,并将’\n’翻译为空字符’\0’。

  • puts等价于printf(“%s\n”, c);
puts(c);//等价于printf("%s\n", c);

puts内放的是字符数组名

2 str系列字符串操作函数

  • strlen用于统计字符串长度
#include <stdio.h>
#include <string.h>
int main()
{
	int len;
	char c[20];
	gets(c);
	puts(c);
	len = strlen(c);
	printf("len = %d\n", len);
	return 0;
}

运行结果:
结果

注意,'\0’不统计在字符串长度之内

  • 自己写一个mystrlen看看
int mystrlen(char c[])
{
	int i = 0;
	while(c[i])//找到结束符后,循环结束,从而得出字符串长度
	{
		i++;
	}
	return i;//返回字符串的长度
}

运行结果:
结果

  • strcat用于把d中的字符串拼接到c中
int main()
{
	char c[20];
	char d[100] = "world";
	gets(c);
	strcat(c, d);//把d中的字符串拼接到c中
	puts(c);
	return 0;
}

运行结果:
结果

  • strcpy用于把c中的字符串复制到e中
	strcpy(e, c);//把c中的字符串复制到e中
	puts(e);

运行结果:
结果

  • strcmp用于比较两个字符串的大小
	gets(c);
	printf("c?d %d\n", strcmp(c, "how"));

当输入hello时,使用strcmp比较字符串hello和字符串how的大小。
注意,strcmp比较的是两个字符串的ASCII码值。
e比o的ASCII码值小。
如果前一个字符串小于后一个字符串,返回值为负值;
如果前一个字符串大于后一个字符串,返回值为正值;
两个字符串相等时,返回值为0。

  • strcat(c,d)和strcpy(e,f)中c和e的位置必须是字符组名字(空间),不可以放字符串常量。

总结

1.1

  • 数组名遵循标识符命名规则。
  • 数组长度由方括号中的常量表达式表示。

1.2

  • 数组元素的引用方式是“数组名[下标]”。
  • 初始化不能写成:
int a[10];
a[10]={ 0,1,2,3,4,5,6,7,8,9 };//Error
  • 可以只给一部分元素赋值。
  • 如果要使一个数组中全部元素的值为0,那么可以写为:
int a[10] = { 0 };//后面没有赋值的默认都是0

2.1

  • 数组下标取值越界会覆盖其他变量。

2.2

  • 数组长度无法传递到子函数中。
  • 子函数中可以改变数组中的元素。

3.1

  • 字符串结束标志为’/0’,所以字符数组存储的字符串长度必须比字符数组少1字节。
  • 使用%s来输出一个字符串
  • 输出字符串乱码时,要去查看字符数组中是否存储了结束符

3.2

  • scanf在使用%s读取字符串时,会忽略空格和回车(这一点与%d和%f类似)。

4.1

  • gets中放入我们字符数组的数组名即可
  • puts等价于printf(“%s\n”, c);

4.2

  • strlen用于统计字符串长度
  • strcat用于把d中的字符串拼接到c中
  • strcpy用于把c中的字符串复制到e中
  • strcmp用于比较两个字符串的大小
  • strcat(c,d)和strcpy(e,f)中c和e的位置必须是字符组名字(空间),不可以放字符串常量。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H3T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值