7

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

然后我得到了一大堆“无法在函数 foo 的参数 3 中将 const char* 转换为 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++ 不允许......反之亦然......

4

6 回答 6

8

该标准指定了一个允许字面char*转换的特殊规则,它悄悄地放弃了const资格。(4.2/2):

不是宽字符串文字的字符串文字(2.13.4)可以转换为“pointer to char”类型的右值;宽字符串文字可以转换为“指向 wchar_t 的指针”类型的右值。无论哪种情况,结果都是指向数组第一个元素的指针。仅当存在显式适当的指针目标类型时才考虑这种转换,而不是当一般需要从左值转换为右值时。[注意:此转换已弃用。见附件 D。]

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

const char*tochar*错误必须是将文字转换为第一个的结果const char*

于 2010-05-03T19:12:22.467 回答
6

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

换句话说,你一定是误解了你之前得到的编译错误。我认为您从未对此类初始化/分配有任何错误。您在问题中提到的“无法将 const char* 转换为 char*”错误一定是由其他东西产生的。

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

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

会产生错误,而这

char *p = "A";

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

于 2010-05-03T19:07:01.070 回答
2

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

#include <iostream>

using namespace std;

void WithConst(const char * Str)
{
    cout<<Str<<endl;
}

void WithoutConst_NoEdit(char * Str)
{
    cout<<Str<<endl;
}

void WithoutConst_Edit(char * Str)
{
    *Str='a';
    cout<<Str<<endl;
}

int main()
{
    WithConst("Test");
    WithoutConst_NoEdit("Test");
    WithoutConst_Edit("Test");
    return 0;
}

 

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";

您正在为 chars 数组声明和分配 RW 内存(在堆栈或其他地方,取决于上下文),该数组使用“bar”值初始化,但它与字符串表完全无关,它是完全合法地更改该字符串。

于 2010-05-03T20:35:12.163 回答
1

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

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

于 2010-05-03T19:09:02.057 回答
1

回答为什么这种转换是合法的问题(尽管已弃用)。好吧,曾经有一段时间,C 语言中没有 const 关键字,而在那段时间里,人们设法生成了一些代码。C++ 的设计者一定已经意识到,通过破坏他们的代码来激怒这么多人并不是一个好主意。

于 2010-05-03T20:48:00.177 回答
1

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

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

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

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

编辑:在看到有关 OP 收到的错误消息的更多信息后,我删除了原始帖子中偏离主题的部分,并添加了一些额外的评论。

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编译器选项是您想要启用以警告您此类问题的选项。

于 2010-05-03T21:42:06.807 回答