簡體   English   中英

為什么有時將字符串文字傳遞給char *參數只是編譯器錯誤?

[英]Why is passing a string literal into a char* argument only sometimes a compiler error?

我正在使用C和C ++程序。 我們曾經在沒有make-strings-writable選項的情況下進行編譯。 但是這得到了一堆警告,所以我把它關掉了。

然后我得到了一堆形式的錯誤“無法將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 *指針是一個不推薦使用的功能,但它是合法的。 這不是錯誤。 您有責任確保不通過此類指針進行任何修改嘗試。

換句話說,你必須誤解你之前得到的編譯錯誤。 對於這樣的初始化/分配,我認為你沒有任何錯誤。 您在問題中提到的“無法將const char *轉換為char *”錯誤必須由其他內容生成。

請注意,您可以使用字符串文字初始化char *指針並不意味着您可以使用任意const char *值來初始化char *指針。 這段代碼

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

會產生錯誤,而這個

char *p = "A";

將不會。 上述不推薦使用的功能僅適用於字符串文字,不適用於所有const char *指針。

我完全同意其他答案,我只想補充一點,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";

你正在聲明和分配RW內存(在堆棧或其他地方,取決於上下文)為一個字符數組,用“bar”值初始化,但它根本不與字符串表相關,它是完全合法地改變那個字符串。

這真的取決於你如何“經歷並做出了很多修改來解決這些問題。”

如果你只是將字符串文字向下轉換為char*那么你告訴編譯器不要捕獲該錯誤。 如果要修改它,則需要制作副本。 否則聲明你的函數接口采用const以便編譯器可以為你檢查這些。

回答這個轉換為何合法的問題(盡管已棄用)。 好吧有一段時間,當C語言中沒有const關鍵字時,人們在那段時間內設法生成了一些代碼。 C ++的設計者必須想通過打破他們的代碼來打擾這么多人並不是一個好主意。

由於stripCRLF函數修改了一個字符串,但沒有對它執行任何操作或返回任何值,因此將字符串文字傳遞給它本質上是一個無操作,應該被視為一個錯誤。 您可以通過修改函數並返回字符串的副本來解決此問題,也可以通過設置更嚴格的警告標志來幫助檢測何時發生這種情況。

如果你想讓gcc提醒你這樣的事情,請打開-Wwrite-strings編譯器選項。 這將強制編譯器在字符串常量轉換為非常量char*發出警告。 同樣可能有用的是-Wcast-qual選項; 每當指針以一種刪除類型限定符的方式進行轉換時,這應該發出警告(在你的情況下, const被刪除)。 如果您希望更強烈地使用這些消息,請使用-Werror將所有警告變為錯誤。

另一個爭論點是FindArgDefault函數。 如前所述,函數簽名應該更准確地使用const char*而不是char*作為返回和參數類型。 這會導致編譯器在將返回值分配給char*時進行投訴(如果使用了-Wcast-qual選項)。 由於您沒有發布完整的功能,因此這可能不是一個有效的更改。 如果在函數內部修改了任一字符串,則相應的參數必須保持為char* ,但在該事件中,將字符串文字作為參數傳遞應生成編譯器警告(使用-Wwrite-strings )。

順便說一下,當傳入NULL指針時,你的stripCRLF函數很容易出現問題。另外,你的意思是說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編譯器選項是你想要啟用以警告你這類問題的選項。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM