《小菜狗 C 语言入门 + 进阶笔记》目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介
1、字符数组
1.1、引入
- 在 C 语言中,字符串本质是
使用空字符
\0
结尾的一维字符数组
。 - 在 C 语言中,没有专门的字符串变量,没有 string 类型,通常就用
一个字符数组来存放一个字符串
。
用来存放字符的数组
称为字符数组
,可以将字符串直接赋值给字符数组。
1.2、字符数组的创建和初始化
- 创建一维字符数组
char a[10];
- 创建字符常量并初始化字符数组
char c[20] = {'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a','m'}; //给部分数组元素赋值
char c[] = {'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' }; //对全体元素赋值时可以省去长度下标长度
- 创建字符串常量并初始化字符数组
(1)指定数组大小
char str[20] = {"http://baidu.com"};
(2)不指定数组长度(不定长数组)。通过初始化内容来确定数组元素个数
给字符数组赋值时,我们通常使用这种写法,将字符串一次性地赋值(可以指明数组长度,也可以不指明),而不是一个字符一个字符地赋值,那样做太麻烦了。
char str[] = "http://baidu.com"; //这种形式更加简洁,实际开发中常用
数组第 0 个元素为'h'
,第 1 个元素为't'
,后面的元素以此类推。
1.3、字符常量与字符串常量
强调
‘a’ 表示是一个字符,“a” 表示一个字符串相当于 ‘a’+‘\0’;
‘’ 里面只能放一个字符;
“” 里面表示是字符串系统自动会在串末尾补一个 0。
#include <stdio.h>
int main()
{
char arr1[] = {'a', 'b', 'c'};
char arr2[] = "abc";
return 0;
}
调试后,我们在监视窗口中观察是这样的:
在使用常量字符串
初始化的时候,数组中多了⼀个 '\0' 字符
;这个 '\0' 就是字符串常量中隐藏的
。
1.4、不定长数组与定长数组
#include <stdio.h>
int main()
{
char arr1[] = {'a', 'b', 'c'};
char arr2[5] = {'a', 'b', 'c'};
return 0;
}
注意:arr1 就是 "abc",arr2 就是 "abc0"!
1.5、'\0' 字符
作用解刨
先看一个代码例子。
1.5.1、初始化赋值
#include <stdio.h>
int main()
{
char arr1[] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
结果分析:
abc烫烫烫?侵7(?╔?╚╔╔
abc
我们可以看到:
- arr1 字符数组在打印的时候,打印了 a、b、c 后还打印了⼀些随机值,这就是
因为 arr1 在末尾的地方没有 \0 字符作为结束标志,在打印的时候没有停止
。 - 但是 arr2 的打印就是完全正常的,就是因为 arr2 数组是使⽤字符串常量初始化的,
数组中有 '\0' 作为标志,打印可以正常停止
。
如果我们在 arr1 数组中单独放⼀个 ‘\0’ 字符会怎么样呢??
#include <stdio.h>
int main()
{
char arr1[] = {'a', 'b', 'c', '\0'};
char arr2[] = "abc";
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
结果分析:
abc
abc
看到两次打印的结果是⼀样的了,那从上述的例子我们确实能够观察到 ‘\0’ 的作⽤和重要性的。
那当不在初始化的时候赋值,在使用的时候赋值会发生什么情况呢?
1.5.2、使用时赋值
有些时候,程序的逻辑要求我们必须逐个字符地为数组赋值,这个时候就很容易遗忘字符串结束标志'\0'
。
下面的代码中,我们将 26 个大写英文字符存入字符数组,并以字符串的形式输出:
#include <stdio.h>
int main()
{
char str[30];
char c;
int i;
for (c=65, i=0; c<=90; c++, i++) {
str[i] = c;
}
printf("%s\n", str);
return 0;
}
在 VScode 下的运行结果:
ABCDEFGHIJKLMNOPQRSTUVWXYZ口口口口i口口0 ?
详细解释:
printf() 输出字符串时,会从第 0 个元素开始往后检索,直到遇见'\0'
才停止,然后把'\0'
前面的字符全部输出,这就是 printf() 输出字符串的原理
。
本例中我们使用 printf() 输出 str,按理说到了第 26 个元素就能检索到'\0'
,就到达了字符串的末尾,然而事实却不是这样,由于我们并未对最后 4 个元素赋值,所以第 26 个元素不是'\0'
,第 27 个也不是,第 28 个也不是……可能到了第 50 个元素才遇到'\0'
,printf() 把这 50 个字符全部输出出来,就是上面的样子,多出来的字符毫无意义,甚至不能显示。
数组总共才 30 个元素,到了第 50 个元素不早就超出数组范围了吗?然而,数组后面依然有其它的数据,printf() 也会将这些数据作为字符串输出。
你看,不注意 ‘\0’ 的后果有多严重,不但不能正确处理字符串,甚至还会毁坏其它数据。
要想避免这些问题也很容易,在字符串的最后手动添加'\0'即可
。类似之前的做法,在循环结束后添加'\0'
,修改上面的代码:
#include <stdio.h>
int main()
{
char str[30];
char c;
int i;
for (c=65,i=0; c<=90; c++,i++) {
str[i] = c;
}
str[i] = 0; //此处为添加的代码,也可以写作 str[i] = '\0';
printf("%s\n", str);
return 0;
}
第 9 行为新添加的代码,它让字符串能够正常结束
。根据 ASCII 码表,字符'\0'
的编码值就是 0。 但是,这样的写法貌似有点业余,或者说不够简洁,更加专业的做法是将数组的所有元素都初始化为“零”值
,这样才能够从根本上避免问题
。
再次修改上面的代码:
#include <stdio.h>
int main()
{
char str[30] = {0}; //将所有元素都初始化为 0,或者说 '\0'
char c;
int i;
for (c=65,i=0; c<=90; c++,i++) {
str[i] = c;
}
printf("%s\n", str);
return 0;
}
1.6、注意
这里还需要留意一个坑
,字符数组只有在定义时才能将整个字符串一次性地赋值给它
,一旦定义完了,就只能一个字符一个字符地赋值
。
请看下面的例子:
char str[7];
str = "abc123"; //错误
//正确
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
str[3] = '1'; str[4] = '2'; str[5] = '3';
2、判断数组中是否包含某个元素
在实际开发中,经常需要查询数组中的元素。
不幸的是,C 语言标准库没有提供与数组查询相关的函数
,所以我们只能自己编写代码。
2.1、对无序数组的查询
所谓无序数组,就是数组元素的排列没有规律
。无序数组元素查询的思路也很简单,就是用循环遍历数组中的每个元素,把要查询的值挨个比较一遍。
请看下面的代码:
#include <stdio.h>
int main()
{
int nums[10] = {1, 10, 6, 296, 177, 23, 0, 100, 34, 999};
int i, num, thisindex = -1;
printf("Input an integer: ");
scanf("%d", &num);
for (i=0; i<10; i++) {
if (nums[i] == num) {
thisindex = i;
break;
}
}
if (thisindex < 0) {
printf("%d isn't in the array.\n", num);
} else {
printf("%d is in the array, it's index is %d.\n", num, thisindex);
}
return 0;
}
运行结果:
Input an integer: 100↙
100 is in the array, it's index is 7.
或者
Input an integer: 28↙
28 isn't in the array.
详细解释:
这段代码的作用是让用户输入一个数字,判断该数字是否在数组中,如果在,就打印出下标。
第 10~15 行代码是关键,它会遍历数组中的每个元素,和用户输入的数字进行比较,如果相等就获取它的下标并跳出循环。
注意:
数组下标的取值范围是非负数,当 thisindex >= 0 时,该数字在数组中,当 thisindex < 0 时,该数字不在数组中,所以在定义 thisindex 变量时,必须将其初始化为一个负数。
2.2、对有序数组的查询
查询无序数组需要遍历数组中的所有元素,而查询有序数组只需要遍历其中一部分元素。例如有一个长度为 10 的整型数组,它所包含的元素按照从小到大的顺序(升序)排列,假设比较到第 4 个元素时发现它的值大于输入的数字,那么剩下的 5 个元素就没必要再比较了,肯定也大于输入的数字,这样就减少了循环的次数,提高了执行效率。
请看下面的代码:
#include <stdio.h>
int main()
{
int nums[10] = {0, 1, 6, 10, 23, 34, 100, 177, 296, 999};
int i, num, thisindex = -1;
printf("Input an integer: ");
scanf("%d", &num);
for (i=0; i<10; i++) {
if (nums[i] == num) {
thisindex = i;
break;
} else if (nums[i] > num) {
break;
}
}
if (thisindex < 0) {
printf("%d isn't in the array.\n", num);
} else {
printf("%d is in the array, it's index is %d.\n", num, thisindex);
}
return 0;
}
详细解释:
与前面的代码相比,这段代码的改动很小,只增加了一个判断语句,也就是 12~14 行。因为数组元素是升序排列的,所以当 nums[i] > num 时,i 后边的元素也都大于 num 了,num 肯定不在数组中了,就没有必要再继续比较了,终止循环即可。
《小菜狗 C 语言入门 + 进阶笔记》目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介
每日一更!
公众号、优快云等博客:小菜狗编程笔记
谢谢点赞关注哈!目前在飞书持续优化更新~
日更较慢有需要完整笔记请私我,C/C++/数据结构-算法/单片机51-STM32-GD32-ESP32/嵌入式/Linux操作系统/uboot/Linux内核-驱动-应用/硬件入门-PCB-layout/Python/后期小程序和机器学习!