我们可以用字符数组或者字符指针来保存字符串,那么这两个有什么区别呢?
- 需要从内存角度来分析
#include <stdio.h>
int main()
{
char str[] = "hello world!\n";
// 直接输出字符串
printf("%s", str);
// 每次输出一个字符
for (int i = 0; i < strlen(str); i++)
{
printf("%c", str[i]);
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
char *str = "hello world\n";
// 直接输出字符串
printf("%s", str);
int i;
int len = strlen(str);
// 使用*(str+i)
for (i = 0; i < len; i++)
{
printf("%c", *(str + i));
}
// 使用str[i]
for (i = 0; i < len; i++)
{
printf("%c", str[i]);
}
return 0;
}
- 这一切看起来和字符数组是多么地相似,它们都可以使用%s输出整个字符串,都可以使用 * 或 [ ] 获取单个字符
- 区别:
- 内存中的存储区域不一样,字符数组存储在全局数据区或栈区,字符指针的字符串存储在常量区。全局数据区和栈区的字符串有读取和写入的权限,而常量区的字符串只有读取权限,没有写入权限
#include <stdio.h>
int main()
{
char arr[] = "abc";
printf("%s\n", arr);
arr[0] = 'b';
printf("%s\n", arr);
char *p = "abc";
printf("%s\n", p);
*p = 'b'; // Segmentation fault (core dumped)
printf("%s\n", p);
}
- 比较地址值
#include <stdio.h>
int main()
{
char s1[] = "abc"; // “abc”是来自.rodata的副本,这条语句让“abc” 这个字符串在内存中有两份拷贝
// 一份放在动态分配的栈里,一份放在.rodata里。
char s2[] = "abc"; // “abc”是来自.rodata的副本。
printf("s1==s2? %d\n", s1 == s2); // s1==s2? 0 说明s1不等于s2,s1==s2比较的是首元素的地址
char *p1 = "abc"; // p1 指向的是.rodata中“abc”的地址
char *p2 = "abc"; // p2 指向的是.rodata中“abc”的地址
printf("p1==p2? %d\n", p1 == p2); // p1==p2? 1 p1==p2比较的是p1和p2指针变量存的地址值
return 0;
}
例:
#include <stdio.h>
char *str()
{
// char *p ="hello";
static char p[] = "hello"; // 随着函数str出栈而消亡,如果加上static 则把p放在了.data里,不会随着str函数出栈而消亡
return p;
}
int main()
{
char *q = NULL;
q = str();
printf("%s\n", q); // Segmentation fault (core dumped)
return 0;
}
- 在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量