一、字符串的定义
char *str1 = {"Hello world!"}; // 方式一 (可省略{})
char str2[] = {"Hello world!"}; // 方式二 (可省略{})
char str3[] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0'}; // 方式三
char str4[16] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0'}; // 方式四
- 几种字符串定义方式之间的区别:
(1) 方式一的本质是定义了一个char
型指针str1
, 指向的是字符串常量Hello world!
,因此str1
所指向地址中的内容是不可更改的,即不能使用类似str1[0] = 'h';
的语句对其进行赋值操作。但是指针str1
仍然可以指向其他地址,例如可利用str1 = str2;
语句将str1
指向str2
所指向的地址。 此外,字符串的结尾会被编译器自动加上结束符'\0'
。
(2) 方式二定义了以一个char
型数组str2
,str2
指向数组第一个元素所处内存的地址。此时内存空间是由栈分配的,地址一经分配就不能更改,因此str2
不能再指向其他内存空间,但其所指向的内存空间中的内容是可以更改的,即可以使用类似str2[0] = 'h';
的语句对其进行赋值操作。字符串的结尾也会被编译器自动加上结束符'\0'
。
(3)方式三中如果没有指定大小的话,编译器只会会根据字符串大小分配空间,但不会在字符串结尾添加'\0'
。为避免其他异常情况的出现,务必在字符串结尾处手动加上'\0'
。以该方式定义字符串时不允许有空的单字符''
,即' '
中的空格不能省略; - 获取字符串的长度
常用运算符sizeof()
和strlen()
函数这两种方式来计算字符串的长度。
sizeof()
的值是在编译时计算得到的,因此不能用于计算动态分配的内存空间大小。sizeof()
可用于基本类型、结构体以及数组等静态分配的对象所占空间大小的计算,其返回值与内存中存储的内容无关。
例如,在32位系统中,char
类型变量占用的空间为一个字节 ,即sizeof(char)
的值为1
。而字符型指针char *
的本质是一个int
型变量,所以其占用的空间大小为四个字节,即sizeof(char *)
的值为4。
函数strlen()
的函数原型为size_t __cdecl strlen(const char *);
,其声明位于头文件string.h
中。strlen()
是在运行时计算的,其返回值为从给定的地址开始到遇见的第一个NULL
之间的长度。 返回的长度并不包含NULL所占用的空间。
【小结】运算符sizeof()
与函数strlen()
的区别。
sizeof() | strlen() |
---|---|
编译时计算。 | 运行时计算。 |
数组、结构体等静态变量。 | char * 类型的变量,必须以'\0' 结尾。 |
数组名传给sizeof() 不会退化。 | 数组名传给strlen() 会退化为指针。 |
利用sizeof()
和strlen()
分别计算上述三种定义方式定义的字符串的长度:
printf("sizeof(str1)=%d\n", sizeof(str1));
printf("sizeof(str2)=%d\n", sizeof(str2));
printf("sizeof(str3)=%d\n", sizeof(str3));
printf("sizeof(str4)=%d\n", sizeof(str4));
printf("strlen(str1)=%d\n", strlen(str1));
printf("strlen(str2)=%d\n", strlen(str2));
printf("strlen(str3)=%d\n", strlen(str3));
printf("strlen(str4)=%d\n", strlen(str4));
计算结果为
sizeof(str1)=4 // 即sizeof(char *),返回的是字符型指针的大小,故sizeof无法计算方式一定义的字符串的长度。
sizeof(str2)=13 // 包含'\0'。
sizeof(str3)=13 // 包含'\0'。
sizeof(str4)=16 // 返回的是实际分配的内存大小,而不是字符串的长度。
strlen(str1)=12 // 不包含'\0'。
strlen(str2)=12 // 不包含'\0',故比sizeof(str2)的值小1。
strlen(str3)=12 // 不包含'\0',故比sizeof(str3)的值小1。
strlen(str4)=12 // 返回的是字符串的实际长度(不包含'\0'),而不是实际分配的内存大小。
二、字符串的遍历
// 逐个访问字符串中的字符并逐行打印
// 思路一:根据数组长度逐个遍历
void travel_str(void)
{
int i = 0;
char str[] = {"Hello World!"};
int len = strlen(str); // 计算字符串大小
// 逐个遍历
for(i=0;i<len;i++)
{
printf("%c\n", str[i]);
}
}
// 思路二:利用指针进行遍历
void travel_str(void)
{
char str[] = {"Hello World!"};
char *ch = str;
// 不能直接采用原指针str遍历,因为此处的str不能改变其指向的地址。
// 即使可以也会因为str指向了别处导致str原来指向的内存无法被释放,造成内存泄露。
while(*ch != '\0') // 以'\0'作为字符串结束标志
{
printf("%c\n", *ch++);
}
}
三、字符串数组的定义
// 方式一:必须指定第二维的大小,且应大于等于数组最长字符串的长度
char str_arr1[][10] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
// 方式二
char *str_arr2[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
四、字符串数组的遍历
// 遍历数组中的字符串
// 思路一
void travel_str_array(void)
{
unsigned char i = 0, size = 0;
// char str_arr[][10] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
char *str_arr[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
size = sizeof(str_arr)/sizeof(str_arr[0]); // 获取数组大小
for(i=0; i<size; i++)
{
// printf("%s\n", str_arr[i]);
printf("%s\n", *(str_arr+i));
}
}
// 思路二
void travel_str_array(void)
{
char *str_arr[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", NULL};
char **str = str_arr; // 采用临时指针指向原数组,避免因原数组指针移动导致内存泄露。
// 采用该方法遍历时建议采用方法二定义数组,并在数组最后手动添加NULL。
while(*str != NULL)
{
printf("%s\n", *str++);
}
// 另一种循环方式
#if 0
char **ptr = NULL;
for(ptr=str_arr; *ptr!=NULL; ptr++)
{
printf("%s\n", *ptr);
}
#endif
}
【小结】
若采用指针遍历字符串数组时,务必在数组最后手动添加NULL
,以确保能够准确找到字符串数组的结尾。否则,指针会指向其他非目标位置,甚至导致程序崩溃。
若通过计算数组大小来遍历字符串数组时,尾部无需添加NULL
。如果手动添加了NULL
,则在遍历数组时应将数组长度减去1
,因为编译器多分配了一个指向NULL
的指针。访问NULL指针会导致程序崩溃。具体分析见第五节。
五、遍历字符串数组中的字符
void travel_str_array_by_char(void)
{
unsigned char i,j = 0;
char *str_arr[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", NULL};
int len = sizeof(str_arr)/sizeof(str_arr[0]);
char **str = str_arr;
// 利用指针遍历字符串数据中的字符
for(str=str_arr; *str!=NULL; str++)
{
for(j=0; j<strlen(*str);j++)
{
printf("%c ", *((*str)+j));
}
printf("\n");
}
// 利用字符串数组大小和字符串长度来遍历字符串数组中的字符
for(i=0; i<len-1; i++)
{
for(j=0;j<strlen(str_arr[i]);j++)
{
// printf("%c ",*(*(str_arr+i)+j));
printf("%c ",str_arr[i][j]);
}
printf("\n");
}
// 错误示例
#if 0
for(i=0; i<len; i++)
{
// 当i=len-1时,str_arr[i] = NULL,此时strlen(NULL)访问NULL指针,程序崩溃。
for(j=0;j<strlen(str_arr[i]);j++)
{
// printf("%c ",*(*(str_arr+i)+j));
printf("%c ",str_arr[i][j]);
}
printf("\n");
}
#endif
}