IE盒子

搜索
查看: 167|回复: 4

C语言| 字符串数组,看这一篇就够了!

[复制链接]

3

主题

15

帖子

24

积分

新手上路

Rank: 1

积分
24
发表于 2022-9-23 14:57:02 | 显示全部楼层 |阅读模式
一、字符串的定义
char *str1 = {"Hello world!"};  // 方式一 (可省略{})
char str2[] = {"Hello world!"}; // 方式二 (可省略{})
char str3[] = {'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);
        }
}

// 思路二:利用指针进行遍历
void travel_str(void)
{
        char str[] = {"Hello World!"};
        char *ch = str;
       
        // 不能直接采用原指针str遍历,因为此处的str不能改变其指向的地址。
        // 即使可以也会因为str指向了别处导致str原来指向的内存无法被释放,造成内存泄露。
        while(*ch != '\0') // 以'\0'作为字符串结束标志
        {
                printf("%c\n", *ch++);
        }
}


羽墨志 | C语言遍历字符串

三、字符串数组的定义
// 方式一:必须指定第二维的大小,且应大于等于数组最长字符串的长度
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);
                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);j++)
                {
                        // printf("%c ",*(*(str_arr+i)+j));
                        printf("%c ",str_arr[j]);
                }
                printf("\n");
        }

        // 错误示例
#if 0
        for(i=0; i<len; i++)
        {
                // 当i=len-1时,str_arr = NULL,此时strlen(NULL)访问NULL指针,程序崩溃。
                for(j=0;j<strlen(str_arr);j++)
                {
                        // printf("%c ",*(*(str_arr+i)+j));
                        printf("%c ",str_arr[j]);
                }
                printf("\n");
        }
#endif
}
回复

使用道具 举报

2

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-9-23 14:57:27 | 显示全部楼层
清晰明了啊
回复

使用道具 举报

2

主题

7

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2022-9-23 14:58:26 | 显示全部楼层
很多地方让我豁然开朗,谢谢up[爱]
回复

使用道具 举报

1

主题

4

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2022-9-23 14:58:39 | 显示全部楼层
char *str_arr2[] ,字符指针数组,字符指针组成的数组
回复

使用道具 举报

3

主题

14

帖子

26

积分

新手上路

Rank: 1

积分
26
发表于 2025-3-20 07:38:01 | 显示全部楼层
我了个去,顶了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表