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

一、字符串的定义

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. 几种字符串定义方式之间的区别:
    (1) 方式一的本质是定义了一个char型指针str1, 指向的是字符串常量Hello world!,因此str1所指向地址中的内容是不可更改的,即不能使用类似str1[0] = 'h';的语句对其进行赋值操作。但是指针str1仍然可以指向其他地址,例如可利用str1 = str2;语句将str1指向str2所指向的地址。 此外,字符串的结尾会被编译器自动加上结束符'\0'
    (2) 方式二定义了以一个char型数组str2str2指向数组第一个元素所处内存的地址。此时内存空间是由栈分配的,地址一经分配就不能更改,因此str2不能再指向其他内存空间,但其所指向的内存空间中的内容是可以更改的,即可以使用类似str2[0] = 'h';的语句对其进行赋值操作。字符串的结尾也会被编译器自动加上结束符'\0'
    (3)方式三中如果没有指定大小的话,编译器只会会根据字符串大小分配空间,但不会在字符串结尾添加'\0'。为避免其他异常情况的出现,务必在字符串结尾处手动加上'\0'。以该方式定义字符串时不允许有空的单字符'',即' '中的空格不能省略;
  2. 获取字符串的长度
    常用运算符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
}
  • 43
    点赞
  • 188
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羽墨志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值