字符串与字符数组、sizeof与strlen
程序员文章站
2022-07-13 17:31:02
...
一、C语言的字符串类型
C语言没有原生的字符串类型,不像C#
等高级语言中具有string
类型来表示字符串,C
语言中的字符串是通过字符指针来间接实现的,如:
char *p = "WHUT2018";
此时p
就叫做字符串,但是实际上p
只是一个字符指针(本质上就是一个指针变量,只是p
指向了一个字符串的起始地址而已)
1.1 C语言中字符串的本质
- 字符串就是指针指向的一段连续的内存空间,在内存中就是多个字节连续分布构成的
- 字符串就是一串字符,字符在编程语言中就是字符类型的变量。C语言用
ASCII
编码对字符进行编程,编码后可以用char
型变量来表示一个字符。字符串就是多个字符打包在一起构成的。
1.2 C语言中字符串的三个核心要点
- 用一个指针指向字符串头
- 固定尾部:以’\0’结尾
- 组成字符串的各字符彼此地址相连
1.3 关于’\0’
0
和'\0'
是等价的,48
和'0'
是等价的'\0'
是字符串中的一个魔数(一些具有特殊含义的内容,在这里起结尾的标识作用),因此C
语言中无法定义出中间内容含有'\0'
的字符串
1.4 指向字符串的指针和字符串本身
指向字符串的指针和字符串本身是分开的两个东西:在下述代码段中
char *p = "WHUT2018";
p
是一个字符指针,分配在栈上,占4
字节,用来指向字符串,是字符串的引子,但其本身并不是字符串的内容;"WHUT2018"
分配在代码段,占9
个字节,其中:
8
字节的空间用来存放"WHUT2018"
这8
个字符组成的字符串1
个字节的内存空间存放'\0'
这个字符串结束标志(本质上也不是字符串的内容)
1.5 存储多个连续字符的两种方式:
- 字符串
- 字符数组
二、字符串和字符数组
2.1 sizeof
sizeof
是C
语言中的一个关键字,也是一个运算符,不是函数。sizeof
运算符用来返回一个类型或者变量所占用的内存直接数。- sizeof存在的意义:
- ADT占几个字节和平台有关
- UDT无法一眼看出占几个字节
2.2 strlen
-
strlen
是一个C
语言库函数,其原型是:size_t strlen(const char *s);
- 这个函数接收一个字符串的指针,返回这个字符串的长度(以字节为单位)
-
strlen
返回的字符串长度不包含字符串结尾的'\0'
2.3 字符数组的初始化
字符数组有以下三种初始化的方式,其中方式1
是最为方便常用
1. char str1[] = "WHUT2018";
2. char str2[] = {"WHUT2018"};
3. char str3[] = {'W','H','U','T','2','0','1','8','\0'};
测试代码
#include <stdio.h>
#include <string.h>
int main(void)
{
char str0[9];
char str1[] = "WHUT2018";
char str2[8] = "WHUT2018";
char str3[4] = "WHUT2018";
char str4[15] = "WHUT2018";
printf("sizeof(str0) = %ld\n", sizeof(str0));
printf("strlen(str0) = %ld\n", strlen(str0));
printf("sizeof(str1) = %ld\n", sizeof(str1));
printf("strlen(str1) = %ld\n", strlen(str1));
printf("sizeof(str2) = %ld\n", sizeof(str2));
printf("strlen(str2) = %ld\n", strlen(str2));
printf("sizeof(str3) = %ld\n", sizeof(str3));
printf("strlen(str3) = %ld\n", strlen(str3));
printf("sizeof(str4) = %ld\n", sizeof(str4));
printf("strlen(str4) = %ld\n", strlen(str4));
return 0;
}
结果分析
- 我们在编译的过程中会看到这样一个警告:
warning: initializer-string for array of chars is too long
char str3[4] = "WHUT2018";
也就是说字符数组太短,后面的字符太长了。遇到这种情况,编译器会自动截断后面的部分。sizeof(str3) = 4
是好理解的,那为什么strlen(str3) = 12
呢?其实这个值是随机的,strlen
计算长度的时候是从首地址的字符开始,一直计数到'\0'
的位置,所以这个值具体为多少得看栈的情况(栈内存是脏的),同理strlen(0)和strlen(str2)的值也是随机的。
-
sizeof(arr)
得到的永远是字符数组中字符元素的个数(也就是数组的大小),和数组是否初始化、初始化多少是没有关系的。
2.4 字符串初始化
就只有一种方法,直接看测试代码:
测试代码
#include <stdio.h>
#include <string.h>
int main(void)
{
char *p = "WHUT2018";
printf("sizeof(p) = %ld\n", sizeof(p));
printf("strlen(p) = %ld\n", strlen(p));
return 0;
}
测试结果
- 在
64位处理器上64位操作系统上的64位编译器
的环境下,sizeof(p)
得到的永远是8
(其他环境永远是4
),因为sizeof(p)
测的是字符指针p
的字节数; - 而
strlen
才是真正测的字符串的长度(不包括'\0'
)
2.5 字符串与字符数组的比较
字符数组与字符串的本质差异
- 对字符数组
char a[] = "WHUT2018";
来说,就相当于定义了一个数组a
,a
占9
个字节,分配在栈上。右值"WHUT2018"
本身只存在于编译器中,编译器将他用来初始化字符数组a
后丢弃掉(也就是说内存中并没有"WHUT2018"
这个字符串的)。就相当于是:
char a[] = {'W', 'H', 'U', 'T', '2', '0', '1', '8', '\0'};
- 字符串
char *p = "WHUT2018";
定义了一个分配在栈上占4(或8)
个字节的字符指针p
和一个分配在代码段的占9
个字节的字符串常量"WHUT2018
",然后把代码段中的字符串的首地址赋值给p
字符串与字符数组的选用
通过上述对比我们可以看出字符串和字符数组有本质区别:
- 字符数组本身自带内存空间,可以用来存储字符。
- 字符串本身是指针,占
4
个字节的内存空间,这4
个字节用来存储有效数据的首地址。
在工程领域上广泛使用的是字符串,因为字符串的存储地方非常灵活,如下所示:
#include <stdio.h>
#include <stdlib.h>
char str0[10];
int main(void)
{
/*储存在bss段*/
char *p1 = str0;
/*存储在.text段*/
char *str1 = "WHUT2018";
/*存储在stack上*/
char str2[10];
char *p2 = str2;
/*储存在heap上*/
char *str3 = (char *)malloc(16);
if(!str3)
{
printf("MALLOC ERROR!\n");
return -1;
}
free(str3);
str3 = NULL;
return 0;
}
测试结果从略
上一篇: 寻找数组的中心索引