为什么将字符串文字传递给char *参数有时只是编译器错误?

我正在使用C和C ++程序。 我们曾经在没有make-strings-writable选项的情况下进行编译。 但那是一堆警告,所以我把它关掉了。

然后我得到了一堆forms的错误“无法将const char *转换为函数foo的argmuent 3中的char *”。 所以,我经历了很多修改来解决这些问题。

然而,今天,程序崩溃,因为文字“”被传递到一个期待char *的函数,并将第0个字符设置为0.它没有做任何坏事,只是试图编辑一个常量,并且崩溃。

我的问题是,为什么不是编译器错误?

如果它很重要,这是在使用gcc-4.0编译的mac上。

编辑:添加代码:

char * host = FindArgDefault("EMailLinkHost", ""); stripCRLF(linkHost, '\n'); 

哪里:

 char *FindArgDefault(char *argName, char *defVal) {// simplified char * val = defVal; return(val); } 

 void stripCRLF(char *str, char delim) { char *p, *q; for (p = q = str; *p; ++p) { if (*p == 0xd || *p == 0xa) { if (p[1] == (*p ^ 7)) ++p; if (delim == -1) *p = delim; } *q++ = *p; } *q = 0; // DIES HERE } 

这编译并运行,直到它试图将* q设置为0 …

编辑2:

大多数人似乎都忽略了我的问题。 我知道为什么char foo [] =“bar”有效。 我知道为什么char * foo =“bar”; 不起作用。

我的问题主要是关于传递参数。 有一件事发生在我身上:“这可能是C与C ++的问题吗?” 因为我有一些.c文件和一些.cpp文件,C很可能允许它,但C ++没有……反之亦然……

该标准规定了一个特殊的规则,允许文字到char*转换悄然降低const限定。 (4.2 / 2):

不是宽字符串文字的字符串文字(2.13.4)可以转换为“指向字符的指针”类型的右值; 可以将宽字符串文字转换为“指向wchar_t的指针”类型的右值。 在任何一种情况下,结果都是指向数组第一个元素的指针。 仅当存在明确的适当指针目标类型时才考虑此转换,而不是在通常需要从左值转换为右值时。 [注意:此转换已弃用。 见附件D.]

C ++ 0x标准进一步采用了这种弃用……这个无意义的规则完全从即将推出的标准中删除。

const char* to char*错误必须是首先将文字转换为const char*

使用字符串文字初始化C ++中的char *指针是一个不推荐使用的function,但它是合法的。 这不是错误。 您有责任确保不通过此类指针进行任何修改尝试。

换句话说,你必须误解你之前得到的编译错误。 我不认为你这样的初始化/分配有任何错误。 您在问题中提到的“无法将const char *转换为char *”错误必须由其他内容生成。

请注意,您可以使用字符串文字初始化char *指针并不意味着您可以使用任意const char *值来初始化char *指针。 这段代码

 const char *pc = "A"; char *p = pc; 

会产生错误,而这个

 char *p = "A"; 

将不会。 上述不推荐使用的function仅适用于字符串文字,不适用于所有const char *指针。

我完全赞同其他答案,我只想补充一点,g ++(至少4.4版本)实际上将这些已弃用的转换作为警告在任何警告级别捕获(如果以前的版本默认不这样做,可能你必须提出警告水平):

 #include  using namespace std; void WithConst(const char * Str) { cout< 

 matteo@teoubuntu:~/cpp/test$ g++ --version g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. matteo@teoubuntu:~/cpp/test$ g++ -O3 lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' 

此外,引擎盖下还有一些有趣的事情:如果我在没有优化的情况下编译它,它“就像代码所说的那样”,因此它会崩溃,因为它会尝试写入只读内存位置:

 matteo@teoubuntu:~/cpp/test$ g++ -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu:~/cpp/test$ ./lit_const_corr.x Test Test Segmentation fault 

但是 ,如果你打开优化器,就没有崩溃:

 matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu:~/cpp/test$ ./lit_const_corr.x Test Test Test 

我想这是由于一些魔术优化技巧,但我不明白它为什么适用; 任何的想法?


附录

当我宣布一个char * foo =“bar”时,它实际上会抱怨。 但是,当我宣布char foo [] =“bar”时,它没有

嘿,小心不要混淆这两件事:

 char * foo = "bar"; 

您正在声明一个指向char的指针 ,并为其分配文字“bar”的地址,该地址实际存储在某个只读内存位置(通常它是映射到内存中的可执行文件的一部分)。 相反,用

 char foo[]="bar"; 

你正在声明和分配RW内存(在堆栈或其他地方,取决于上下文)为一个字符数组,用“bar”值初始化,但它根本不与字符串表相关,它是完全合法地改变那个字符串。

这真的取决于你如何“经历并做出了很多修改来解决这些问题。”

如果你只是将字符串文字向下转换为char*那么你告诉编译器不要捕获该错误。 如果要修改它,则需要制作副本。 否则声明你的函数接口采用const以便编译器可以为你检查这些。

回答这个转换为何合法的问题(虽然已弃用)。 好吧有一段时间,当C语言中没有const关键字时,人们在那段时间内设法生成了一些代码。 C ++的设计者必须想通过打破他们的代码来打扰这么多人并不是一个好主意。

由于stripCRLF函数修改了一个字符串,但没有对它做任何事情或返回任何值,因此将字符串文字传递给它本质上是一个无操作,应该被认为是一个错误。 您可以通过修改函数并返回字符串的副本来解决此问题,也可以通过设置更严格的警告标志来帮助检测何时发生这种情况。

如果你想让gcc提醒你这样的事情,请打开-Wwrite-strings编译器选项。 这将强制编译器在字符串常量转换为非常量char*发出警告。 同样可能有用的是-Wcast-qual选项; 每当指针以一种删除类型限定符的方式进行转换时(这种情况下, const被删除),这应该发出警告。 如果您希望更强烈地使用这些消息,请使用-Werror将所有警告变为错误。

另一个争用点是FindArgDefault函数。 如前所述,函数签名应该更准确地使用const char*而不是char*作为返回和参数类型。 这应该导致编译器在将返回值分配给char*时进行投诉(如果使用了-Wcast-qual选项)。 由于您没有发布完整的function,因此这可能不是一个有效的更改。 如果在函数内部修改了任一字符串,则相应的参数必须保持为char* ,但在该事件中,传递字符串文字作为参数应生成编译器警告(使用-Wwrite-strings )。

顺便说一下,当传入NULL指针时,你的stripCRLF函数很容易出现问题。另外,你的意思是说if (delim == -1) ,还是应该是!=

编辑:在看到有关OP正在获取的错误消息的更多信息后,我删除了原始post的部分偏离主题并添加了一些其他注释。

Edit2:我测试了你的程序的以下简化版本:

 char *FindArgDefault(char *argName, char *defVal) { char * val = defVal; return(val); } int main (void) { char * host = FindArgDefault("EMailLinkHost", ""); return (int)(host); } 

当我用gcc -Wall test.c -o test.o编译时,我得到零编译器警告或错误。

当我用gcc -Wwrite-strings -Wall test.c -o test.o编译时,我得到了

test.c:在函数’main’中:

test.c:10:警告:传递’FindArgDefault’的arg 1会丢弃指针目标类型的限定符

test.c:10:警告:传递’FindArgDefault’的arg 2会丢弃指针目标类型的限定符

我绝对认为-Wwrite-strings编译器选项是您要启用的选项,用于警告您此类问题。