一.字符串与数组的定义
在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。
下面的声明和初始化创建了一个 HelloWorld 字符串。由于在数组的末尾存储了空字符 \0,所以字符数组的大小比单词 HelloWorld 的字符数多一个。
- 字符串的定义:
//写法一:(字符数组定义字符串)
char site[7] = {'H', 'e', 'l', 'l', 'o', 'W','o', 'r', 'l','d', '\0'};
//写法二:(字面量定义字符串)
char site[] = "HelloWorld";
- 数组的定义:
char siteArray[7] = {'H', 'e', 'l', 'l', 'o', 'W','o', 'r', 'l','d'};
由于C语言中没有类似string关键字,所以不能定义字符串,只能用字符数组形式来表示字符串;所以从代码书写层面来看,定义一个字符串 和 定义一个字符类型的数组 两者就很相似,容易让人混淆。下面就主要讲述一下二者的区别。
二.字符串与数组的差别
下面通过Dome代码的测试与解读,对字符串与数组的差别进行描述;
#include <stdio.h>
#include <string.h>
int main()
{
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
char siteArray[7] = {'R', 'U', 'N', 'O', 'O', 'B'};
printf("输出行1:————字符串(字符数组): %s\n", site); // 输出字符串
printf("输出行2:————字符类型数组: %s\n", siteArray); // 输出字符类型数组
char hello[] = "hello world";
char hello2[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};
char hello3[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char hello4[] = {'h', 'e', 'l', 'l', 'o'};
char hello5[] = "hello";
int len = strlen(hello);
int len2 = strlen(hello2);
printf("输出行3:————hello: %s, len: %d\n", hello, len); // 输出字符串及其长度
printf("输出行4:————hello2: %s, len2: %d\n", hello2, len2); // 输出字符数组形式字符串及其长度
printf("输出行5:————hello3: %s, len3: %d\n", hello3, strlen(hello3)); // 输出字符数组形式字符串及其长度
printf("输出行6:————hello3: %s, len3的字符个数: %d\n", hello3, sizeof(hello3)); // 输出字符数组形式字符串及其字符个数
printf("输出行7:————hello4: %s, len4用访问字符长度: %d\n", hello4, strlen(hello4)); // 输出字符类型数组,并用字符串方法获取其长度
printf("输出行8:————hello4: %s, len4的数组元素个数(数组长度): %d\n", hello4, sizeof(hello4)); // 输出字符类型数组,并用数组方法获取其长度
printf("输出行9:————hello4: %s, len4用访问数组长度: %d\n", hello4, sizeof(hello4) / sizeof(hello4[0])); // 输出字符类型数组,并用数组方法获取其长度
printf("输出行10:————hello5: %s, len5: %d\n", hello5, strlen(hello5));
printf("输出行11:————hello5: %s, len5: %d\n", hello5, sizeof(hello5)); // 输出字符串及其长度
return 0;
}
运行结果:
输出行1:————字符串(字符数组): RUNOOB
输出行2:————字符类型数组: RUNOOB
输出行3:————hello: hello world, len: 11
输出行4:————hello2: hello world, len2: 11
输出行5:————hello3: hello, len3: 5
输出行6:————hello3: hello, len3的字符个数: 6
输出行7:————hello4: hellohello, len4用访问字符长度: 10
输出行8:————hello4: hellohello, len4的数组元素个数(数组长度): 5
输出行9:————hello4: hellohello, len4用访问数组长度: 5
输出行10:————hello5: hello, len5: 5
输出行11:————hello5: hello, len5: 6
2.1 补充知识点:
为了解读上面Dome代码我们需要知道以下几点:
sizeof(val)
是一个用于获取变量或数据类型所占内存字节数的操作符。 在C语言中,sizeof可以用于数据类型、变量、表达式等,返回的是这些对象所占的内存字节数;strlen(val)
函数是求当前字符串的长度,遇到’\0’才停住会停止计算,返回值不包括’\0’,遇到空格符不会停机计算,并且strlen可以读取空格并计算在内;
3.printf(val)
函数为输出打印函数,遇到’\0’才停住,遇到空格符不会停机输出。
2.2 代码解读:
- 对比 输出行1 和 输出行2;从代码的编写上看,使用字符数组定义字符串和定义字符类型的数组,区别在于,字符串的定义会比 数组定义 结尾 多一个 ‘\0’ 的 空字符,这也是定义字符串的语法规则。
- 对比 输出行3 和 输出行4;二者都是定义了一个hello world 字符串,但是定义字符串的方法不同,输出行3 使用的是 字面量方法 定义,输出行4 使用的是 字符数组 定义;二者本质上是没有区别的,通过输出结果上看也都一样。
- 对比 输出行5 和 输出行6 ;二者都是输出的
hello3
字符串,但是 “输出行5” 使用strlen()方法
输出 hello3字符串的长度 为 5;“输出行6” 使用sizeof()
方法输出hello3字符串的内存字节数 为 6,也可以说是求该字符串的有多少字符,由于‘\0’
也是一个字符,会将空字符’\0’计算在内,所以比字符串实际长度加一。 - 对比 输出行7 和 输出行 8 ;二者都是对定义的
hello4
数组 进行打印输出。首先看代码,仔细观察其实是有点问题的,因为hello4
最后一个数组元素并没有‘\0’,所以hello4
就是一个普通数组;在printf打印的时候,因为是数组,那么打印就不能使用%s
,%s
用于输出字符串格式数据,所以,从终端输出结果来看,虽然也输出了内容,但是按照代码理解本应该输出一个hello
,然而却输出了两个hello-hellohello
,很明显输出结果是有问题的,原因其实就是不能这样去打印输出数组,定义的char hello4[] = {‘h’, ‘e’, ‘l’, ‘l’, ‘o’};没有以空字符’\0’结尾,当作为字符串传递给printf函数中的%s格式说明符以及传递给strlen函数时,它会一直读取内存直到遇到一个空字符,这会导致未定义行为,可能输出不可预期的结果或者程序崩溃。所以,输出行7 和 输出行8 的 打印结果都是有误的。其次,输出行7 还使用访问字符串长度的方法去取一个数组的长度,因为数组没有‘\0’结束符,所以最终的结果长度结果也是错误的;输出行8 使用sizeof()
方法输出数组的内存字节数 是 正确的。
对比 输出行 7 和8 ;得到结论就是:普通数组 不能使用
strlen()
函数,普通数组中没有‘\0’结束符,strlen()
函数会一直读取内存直到遇到’\0’才停住会停止计算,这会导致未定义行为,可能输出不可预期的结果或者程序崩溃。
- 输出行 9 也是对
hello4
数组的长度 计算,最终长度与sizeof(hello4)
方法读取的长度一致;sizeof(hello4) / sizeof(hello4[0])
其中sizeof(hello4)
表示:数组的内存总字节数,sizeof(hello4[0])
表示:数组中第一个元素的内存总字节数,前者除后者结果就是当前数组有多少个元素,即数组的长度。对比 输出行 8 直接 用sizeof(hello4[0])
方法取得数组的长度(内存总字节数),是因为hello4
数组是字符类型数组,其中每个元素都是一个字符,一个字符 = 一字节 的原因,所以其实sizeof(hello4[0])
的值 就是 1 字节,所以:sizeof(hello4)
==sizeof(hello4) / sizeof(hello4[0])
,数值一样,但是细微差别就是sizeof(hello4) 单位是字节,sizeof(hello4) / sizeof(hello4[0])
单位是个数。 - 输出行 10 与 输出行 6对比;其实和输出行5 和 输出行6 对比 是一样的; 输出行 10与输出行5都是定义的hello 字符串,输出行 10中
hello5
是字面量定义,输出行5中hello3
是字符数组定义,对比 输出行 10 与 输出行 6 就是 加深理解strlen(val)
函数是求当前字符串的长度,遇到’\0’才停住会停止计算,返回值不包括’\0’;sizeof(val)
是一个用于获取变量或数据类型所占内存字节数的操作符,‘\0’
也是一个字符,会将空字符’\0’计算在内,所以比字符串实际长度加一。 - 输出行 10 与 输出行 11对比;二者都是对字面量定义的字符串
hello5
进行打印输出,可以看出字面量定义的情况下,并没有手动给字面量添加'\0'结束符
,但是在 输出行 11中sizeof(val)
的值是比真实长度多一个的,就说明是自动添加了'\0'结束符
,其实也就是:编译器在处理字符串字面量时,会自动在末尾添加’\0’字符。
总结:
在C语言中,字符串的定义与数组的定义有显著的区别。 字符串实际上是一个字符数组,但它以一个特殊的空字符(‘\0’)结尾,而普通的数组则没有这样的限制。
数组是一种数据结构,用于存储相同类型的数据元素的集合。在C语言中,数组是一种线性数据结构,可以在内存中连续地存储多个相同类型的数据元素,这些元素可以通过索引(下标)来访问,索引从0开始。数组的大小在声明时是固定的,不能动态改变,且元素在内存中连续存储。
字符串在C语言中并没有专门的类型,而是通过字符数组来表示。 字符串以空字符’\0’结尾,表示字符串的结束。编译器在处理字符串字面量时,会自动在末尾添加’\0’字符。例如,声明一个字符串char arr[] = “hello”;实际上是在声明一个字符数组,并在末尾添加’\0’作为结束标志(编译器自动添加)。
数组和字符串的主要区别在于结尾字符。普通数组可以存储任意类型的数据,没有特定的结束符号;而字符串必须以空字符’\0’结尾,表示字符串的结束。此外,字符串的声明和初始化方式也有所不同,字符串字面量在编译时会自动添加’\0’字符。