盡我所能,我將提供一些背景技術信息來說明此錯誤的原因和原因。
我將檢查初始化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 [] =“這是一些文本”;
要詳細說明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 *' 代碼>
編譯器知道這很狡猾,這是正確的!這樣做的原因是,編譯器(合理地)期望字符串常量不變(因為它們是常量)。因此,如果您在代碼中多次引用字符串常量“這是一些文本”
,則可以為所有常量分配相同內存。現在,如果您修改其中的一個,就可以全部修改!
要么停止嘗試在函數採用 char *
的地方傳遞字符串常量,要么更改函數以使其採用 const char *
代替。
類似於“隨機字符串”的字符串是常量。
示例:
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
。
我遇到此編譯錯誤:
TimeSerial.ino:68:29:警告:不建議將字符串常量轉換為'char *'[-Wwrite-strings] if(Serial.find (TIME_HEADER)){^
請替換此行:
#define TIME_HEADER“ T” //串行時間同步消息的標頭標記
具有以下行:
#define TIME_HEADER'T'//串行時間同步消息的標題標籤
並且編譯順利。