在编写一个简单的函数来从字符串中删除特定字符时,我遇到了这个奇怪的问题:
void str_remove_chars( char *str, char to_remove)
{
if(str && to_remove)
{
char *ptr = str;
char *cur = str;
while(*ptr != '\0')
{
if(*ptr != to_remove)
{
if(ptr != cur)
{
cur[0] = ptr[0];
}
cur++;
}
ptr++;
}
cur[0] = '\0';
}
}
int main()
{
setbuf(stdout, NULL);
{
char test[] = "string test"; // stack allocation?
printf("Test: %s\n", test);
str_remove_chars(test, ' '); // works
printf("After: %s\n",test);
}
{
char *test = "string test"; // non-writable?
printf("Test: %s\n", test);
str_remove_chars(test, ' '); // crash!!
printf("After: %s\n",test);
}
return 0;
}
我没有得到的是第二次测试失败的原因?
对我而言,第一个符号char *ptr = "string";
看起来与此相同:char ptr[] = "string";
。
不是这样吗?
答案 0 :(得分:40)
两个声明不一样。
char ptr[] = "string";
声明一个大小为7
的字符数组,并使用字符为s
,t
,r
,{{1}对其进行初始化},i
,n
和g
。您允许修改此数组的内容。
\0
将char *ptr = "string";
声明为char指针,并使用字符串文字 ptr
的地址对其进行初始化,只读 。修改字符串文字是未定义的行为。你所看到的(seg fault)是未定义行为的一种表现形式。
答案 1 :(得分:5)
严格地说,char *ptr
的声明只能保证指向字符类型的指针。字符串构成编译应用程序的代码段的一部分并不罕见,该代码段将由某些操作系统设置为只读。问题在于你正在假设预定义字符串的性质(它是可写的),事实上,你自己从未明确地为该字符串创建内存。编译器和操作系统的某些实现可能允许您执行您尝试执行的操作。
另一方面,根据定义,char test[]
的声明实际上为堆栈中的整个字符数组分配了可读写的内存。
答案 2 :(得分:3)
char *test = "string test";
错了,应该是const char*
。此代码仅因后向兼容性原因而编译。 const char*
指向的内存是只读内存,每当您尝试写入内存时,它都会调用未定义的行为。另一方面,char test[] = "string test"
在堆栈上创建可写字符数组。这与您可以编写的任何其他regualr局部变量一样。
答案 3 :(得分:2)
据我记得
char ptr[] = "string";
在堆栈上创建"string"
的副本,因此这个是可变的。
表格
char *ptr = "string";
只是
的向后兼容性const char *ptr = "string";
并且不允许(在未定义的行为方面)修改其内容。 编译器可以将这些字符串放在内存的只读段中。
答案 4 :(得分:0)
好的答案@codaddict。
此外,sizeof(ptr)
将为不同的声明提供不同的结果。
第一个,即数组声明,将返回数组的长度,包括终止空字符。
第二个,char* ptr = "a long text...";
将返回指针的长度,通常为4或8。
答案 5 :(得分:0)
char *str = strdup("test");
str[0] = 'r';
是正确的代码并创建一个可变字符串。 str在堆中分配了一个内存,其中填充了值'test'。