題:
從字符串常量到'char *'的棄用轉換
Federico Corazza
2015-07-14 07:20:16 UTC
view on stackexchange narkive permalink

此錯誤是什麼意思?我什麼都無法解決。

警告:不建議將字符串常量轉換為'char *'[-Wwrite-strings]

這個問題應該在StackOverflow上,而不是Arduino :)
五 答案:
Majenko
2015-07-14 20:10:36 UTC
view on stackexchange narkive permalink

盡我所能,我將提供一些背景技術信息來說明此錯誤的原因和原因。

我將檢查初始化C字符串的四種不同方法看看它們之間有什麼區別。這些是有問題的四種方法:

  char * text =“這是一些文本”; char text [] =“這是一些文本”; const char * text =“這是一些文本” text“; const char text [] =” This is some text“;  

現在,我要將第三個字母“ i”更改為“ o”以使它“這是一些文字”。在所有情況下(您會認為),都可以通過以下方式實現:

  text [2] ='o';  

現在讓我們來看一下每種聲明字符串的方式都有什麼作用,以及 text [2] ='o'; 語句將如何影響事物。

首先最常見的方式是: char * text =“這是一些文本”; 。這從字面上意味著什麼?嗯,在C語言中,它的字面意思是“創建一個名為 text 的變量,該變量是對此字符串文字的讀寫指針,該字符串文字存儲在只讀(代碼)空間中”。如果打開了 -Wwrite-strings 選項,則會收到警告,如上面的問題所示。

基本上,這表示“警告:您試圖創建一個變量那是您無法寫入的區域的讀寫點”。如果您嘗試然後將第三個字符設置為“ o”,則實際上您將嘗試寫入只讀區域,並且情況會變糟。在裝有Linux的傳統PC上,會導致:

分段錯誤

現在第二個: char text [] =“這是一些文本”; 。從字面上看,在C語言中,這意味著“創建一個類型為“ char”的數組,並使用數據“ This is some text \ 0”對其進行初始化。該數組的大小將足以存儲數據。這樣實際上就分配了RAM,並在運行時將值“ This is some text \ 0”複製到其中。沒有警告,沒有錯誤,完全有效。正確的操作方法如果您希望能夠編輯數據。讓我們嘗試運行命令 text [2] ='o'

這是一些文本

它的工作原理非常完美。好的。

現在第三種方式: const char * text =“這是一些文本”; 。再次是字面意思:“創建一個名為“文本”的變量,該變量是指向只讀存儲器中該數據的只讀指針。注意,指針和數據現在都是只讀的。沒有錯誤,沒有警告。如果嘗試運行測試命令會怎樣?好吧,我們不能。編譯器現在很聰明,並且知道我們正在嘗試做不好的事情:

錯誤:分配只讀位置'*(text + 2u)'

它甚至不會編譯。嘗試寫入只讀存儲器現在受到保護,因為我們已經告訴編譯器我們的指針是只讀存儲器。當然,不必指向只讀存儲器,但是如果將其指向讀寫存儲器(RAM),則仍將保護該存儲器不被編譯器寫入。 。

最後一種形式: const char text [] =“這是一些文本”; 。再次,與之前使用 [] 一樣,它在RAM中分配一個數組並將數據複製到其中。但是,現在這是一個只讀數組。您無法寫入它,因為指向它的指針被標記為 const 。嘗試對其進行寫操作將導致:

錯誤:分配只讀位置'*(text + 2u)'

因此,簡要總結一下我們的位置:

此表格完全無效,應不惜一切代價避免使用。它打開了各種不良事件發生的大門:

  char * text =“這是一些文本”;  

此形式是正確的形式如果您想使數據可編輯:

  char text [] =“這是一些文本”;  

如果您想要不被編輯的字符串:

  const char * text =“這是一些文本”;  

這種形式似乎浪費了RAM,但是它確實有其用途。最好還是暫時忘記它。

  const char text [] =“這是一些文本”;  
值得注意的是,在Arduino上(至少是基於AVR的),字符串文字常存在於RAM中,除非您使用諸如PROGMEM,PSTR()或F()之類的宏對其進行聲明。因此,`const char text []`不會比`const char * text`使用更多的RAM。
Teensyduino和許多其他較新的arduino-compatibles自動將字符串文字放置在代碼空間中,因此值得檢查板上是否需要F()。
@Craig.Feied通常,無論使用F(),都應使用。那些“不需要”它的人傾向於將其定義為簡單的`(const char *)(...)`強制轉換。如果板子不需要它,則沒有真正的效果,但是如果您將代碼移植到需要的板上,則可以節省很多。
Nick Gammon
2015-07-15 03:03:26 UTC
view on stackexchange narkive permalink

要詳細說明Makenko的出色答案,編譯器會警告您有關此的充分理由。讓我們做一個測試草圖:

  char * foo =“這是一些文本”; char * bar =“這是一些文本”;無效設置(){Serial.begin(115200); Serial.println(); foo [2] ='o'; //僅更改foo Serial.println(foo); Serial.println(bar); } // setupvoid循環結束(){} //循環結束 

在這裡,我們有兩個變量foo和bar。我在setup()中修改了其中的一個,但看到的結果是:

  Thos是一些文本Thos是一些文本 

它們被更改了!

實際上,如果我們查看警告,就會看到:

  sketch_jul14b.ino:1:警告:不建議將字符串常量轉換為'char *'sketch_jul14b.ino:2:警告:不建議將字符串常量轉換為'char *' 代碼> 

編譯器知道這很狡猾,這是正確的!這樣做的原因是,編譯器(合理地)期望字符串常量不變(因為它們是常量)。因此,如果您在代碼中多次引用字符串常量“這是一些文本” ,則可以為所有常量分配相同內存。現在,如果您修改其中的一個,就可以全部修改!

天吶!誰會知道...對於最新的ArduinoIDE編譯器來說是否仍然如此?我只是在ESP32上嘗試過,它會導致重複的* GuruMeditation *錯誤。
我剛剛在Arduino 1.8.9上測試了@not2qubit,事實確實如此。
出現警告是有原因的。這次我得到了:*警告:ISO C ++禁止將字符串常量轉換為'char *'[-Wwrite-strings] char * bar =“ This is some text”; *-FORBIDS是一個強詞。由於禁止這樣做,因此編譯器可以隨意修改並在兩個變量之間共享相同的字符串。不要做*禁止*的事情! (此外,請閱讀並消除警告)。 :)
因此,如果您遇到像這樣糟糕的代碼,並希望度過這一天。是否可以使用** different **字符串*“ constants” *來初始化`* foo`和`* bar`來防止這種情況發生?另外,這與根本不放置任何字符串(例如:char * foo;`)有何不同?
不同的常量可能會有所幫助,但我個人不會在此放置*任何東西,以後再以通常的方式在其中放置數據(例如,使用`new`,`strcpy`和`delete`)。
Ignacio Vazquez-Abrams
2015-07-14 07:22:22 UTC
view on stackexchange narkive permalink

要么停止嘗試在函數採用 char * 的地方傳遞字符串常量,要么更改函數以使其採用 const char * 代替。

類似於“隨機字符串”的字符串是常量。

諸如“隨機字符”之類的文本是否為常數char?
字符串文字是字符串常量。
Nick Gammon
2015-07-14 07:50:04 UTC
view on stackexchange narkive permalink

示例:

  void foo(char * s){Serial.println(s); } void setup(){Serial.begin(115200); Serial.println(); foo(“ bar”); } // setupvoid循環結束(){} //循環結束 

警告:

  sketch_jul14b .ino:在函數'void setup()':sketch_jul14b.ino:10中:警告:不建議將字符串常量轉換為'char *' 

函數 foo 期望使用char *(因此可以修改),但是您正在傳遞字符串文字,不應對其進行修改。不建議使用,它可能在將來的編譯器版本中從警告變為錯誤。


解決方案:使foo帶有 const char *:

  void foo(const char * s){Serial.println(s); }  

我不明白。您是說不能被修改嗎?

較舊版本的C(和C ++)使您可以像上面的示例一樣編寫代碼。您可以創建一個函數(例如 foo ),該函數先打印傳遞給它的內容,然後傳遞一個文字字符串(例如, foo(“嗨!”!));

但是,允許將以 char * 作為參數的函數修改其參數(例如,在這種情況下,修改 Hi here!) 。

例如,您可能已經寫過:

  void foo(char * s){Serial.println(s) ; strcpy(s,“再見”); }  

不幸的是,通過傳遞文字,您現在可能已經修改了該文字,以便“嗨!”現在是“再見”,這不好。實際上,如果您複製了更長的字符串,則可能會覆蓋其他變量。或者,在某些實現中,您會遇到訪問衝突,因為“嗨!”可能已放入只讀(受保護的)RAM中。

因此,編譯器-編寫器正在逐漸棄用此用法,以便您傳遞文字的函數必須將該參數聲明為 const

如果不使用指針,是否有問題?
什麼樣的問題?該特定警告是關於將字符串常量轉換為char *指針。你能詳細說明嗎?
@Nick:您的意思是“(..)您正在傳遞字符串文字,不應對其進行修改”。我不明白您是說“不能”被修改嗎?
我修改了答案。馬延科在回答中涵蓋了大多數觀點。
gin
2016-01-03 17:00:30 UTC
view on stackexchange narkive permalink

我遇到此編譯錯誤:

  TimeSerial.ino:68:29:警告:不建議將字符串常量轉換為'char *'[-Wwrite-strings] if(Serial.find (TIME_HEADER)){^  

請替換此行:
#define TIME_HEADER“ T” //串行時間同步消息的標頭標記

具有以下行:
#define TIME_HEADER'T'//串行時間同步消息的標題標籤

並且編譯順利。

此更改將定義從一個字符串“ T”更改為具有字母大寫T的ASCII碼值的單個字符。


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 3.0許可。
Loading...