ข้อผิดพลาดนี้หมายความว่าอย่างไร ฉันไม่สามารถแก้ปัญหาในทางใดทางหนึ่ง
คำเตือน: การแปลงที่ไม่สนับสนุนจากค่าคงที่สตริงเป็น 'char *' [-Wwrite-strings]
ข้อผิดพลาดนี้หมายความว่าอย่างไร ฉันไม่สามารถแก้ปัญหาในทางใดทางหนึ่ง
คำเตือน: การแปลงที่ไม่สนับสนุนจากค่าคงที่สตริงเป็น 'char *' [-Wwrite-strings]
คำตอบ:
เช่นเดียวกับที่ฉันเคยชินฉันจะให้ข้อมูลทางเทคนิคเล็กน้อยใน whys และที่ใดก็ตามที่ข้อผิดพลาดนี้
ฉันจะตรวจสอบสี่วิธีที่แตกต่างกันของการเริ่มต้นสตริง C และดูความแตกต่างระหว่างพวกเขา คำถามเหล่านี้มีสี่วิธี:
char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";
ตอนนี้ฉันจะต้องการเปลี่ยนอักษรตัวที่สาม "i" เป็น "o" เพื่อให้เป็น "Thos is some text" ในทุกกรณี (คุณอาจคิดว่า) สามารถทำได้โดย:
text[2] = 'o';
ทีนี้ลองมาดูกันว่าแต่ละวิธีในการประกาศสตริงทำอะไรและtext[2] = 'o';
คำสั่งนั้นจะมีผลต่อสิ่งต่าง ๆ อย่างไร
วิธีแรกที่เห็นบ่อยที่สุด: char *text = "This is some text";
. นี่แปลว่าอะไร? ใน C หมายถึง "สร้างตัวแปรที่เรียกว่าtext
ซึ่งเป็นตัวชี้อ่าน - เขียนสำหรับตัวอักษรสตริงนี้ซึ่งจัดขึ้นในพื้นที่อ่านอย่างเดียว (รหัส)" หากคุณ-Wwrite-strings
เปิดใช้งานตัวเลือกคุณจะได้รับคำเตือนตามที่เห็นในคำถามข้างต้น
โดยทั่วไปหมายความว่า "คำเตือน: คุณพยายามสร้างตัวแปรที่เป็นจุดอ่าน - เขียนไปยังพื้นที่ที่คุณไม่สามารถเขียนได้" หากคุณลองแล้วตั้งตัวละครที่สามเป็น "o" คุณจะพยายามเขียนไปยังพื้นที่แบบอ่านอย่างเดียวและสิ่งต่าง ๆ จะไม่ดี บนพีซีแบบดั้งเดิมที่มี Linux ซึ่งให้ผลลัพธ์:
การแบ่งส่วนความผิดพลาด
ตอนนี้อันที่สอง: char text[] = "This is some text";
. แท้จริงใน C หมายถึง "สร้างอาร์เรย์ประเภท" char "และเริ่มต้นด้วยข้อมูล" นี่คือข้อความ \ 0 "ขนาดของอาร์เรย์จะมีขนาดใหญ่พอที่จะเก็บข้อมูล" เพื่อให้จัดสรร RAM จริงและคัดลอกค่า "นี่คือข้อความ \ 0" ลงไปในตอนรันไทม์ ไม่มีคำเตือนไม่มีข้อผิดพลาดใช้ได้อย่างสมบูรณ์แบบ และวิธีการที่เหมาะสมที่จะทำมันถ้าคุณต้องการเพื่อให้สามารถแก้ไขข้อมูล ลองเรียกใช้คำสั่งtext[2] = 'o'
:
ข้อความนี้เป็นข้อความบางส่วน
มันทำงานได้อย่างสมบูรณ์แบบ ดี.
ตอนนี้วิธีที่สาม: const char *text = "This is some text";
. ความหมายตามตัวอักษรอีกครั้ง: "สร้างตัวแปรที่เรียกว่า" text "ซึ่งเป็นตัวชี้แบบอ่านอย่างเดียวสำหรับข้อมูลนี้ในหน่วยความจำแบบอ่านอย่างเดียว" โปรดทราบว่าขณะนี้ทั้งตัวชี้และข้อมูลเป็นแบบอ่านอย่างเดียว ไม่มีข้อผิดพลาดไม่มีคำเตือน จะเกิดอะไรขึ้นถ้าเราลองเรียกใช้คำสั่งทดสอบของเรา เราทำไม่ได้ ผู้เรียบเรียงตอนนี้ฉลาดและรู้ว่าเรากำลังพยายามทำสิ่งที่ไม่ดี:
ข้อผิดพลาด: การกำหนดตำแหน่งอ่านอย่างเดียว '* (ข้อความ + 2u)'
มันจะไม่ได้รวบรวม การพยายามเขียนลงในหน่วยความจำแบบอ่านอย่างเดียวได้รับการป้องกันแล้วเนื่องจากเราได้บอกคอมไพเลอร์ว่าตัวชี้ของเราคือหน่วยความจำแบบอ่านอย่างเดียว แน่นอนว่ามันไม่จำเป็นต้องชี้ไปยังหน่วยความจำแบบอ่านอย่างเดียว แต่ถ้าคุณชี้ไปที่หน่วยความจำแบบอ่าน - เขียน (RAM) หน่วยความจำนั้นจะยังคงได้รับการปกป้องจากการเขียนโดยคอมไพเลอร์
ในที่สุดรูปแบบสุดท้าย: const char text[] = "This is some text";
. อีกครั้งเช่นเดียวกับก่อนที่[]
จะจัดสรรอาร์เรย์ใน RAM และคัดลอกข้อมูลลงไป อย่างไรก็ตามตอนนี้นี่เป็นอาร์เรย์แบบอ่านอย่างเดียว const
คุณไม่สามารถเขียนไปเพราะตัวชี้ไปมันจะติดแท็กเป็น ความพยายามที่จะเขียนถึงผลลัพธ์ใน:
ข้อผิดพลาด: การกำหนดตำแหน่งอ่านอย่างเดียว '* (ข้อความ + 2u)'
ดังนั้นข้อมูลสรุปโดยย่อเกี่ยวกับตำแหน่งที่เราอยู่:
แบบฟอร์มนี้ไม่ถูกต้องสมบูรณ์และควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมด มันเปิดประตูสู่สิ่งเลวร้ายทุกประเภทที่เกิดขึ้น:
char *text = "This is some text";
แบบฟอร์มนี้เป็นแบบฟอร์มที่ถูกต้องหากคุณต้องการให้ข้อมูลแก้ไขได้:
char text[] = "This is some text";
แบบฟอร์มนี้เป็นแบบฟอร์มที่ถูกต้องหากคุณต้องการสตริงที่จะไม่ถูกแก้ไข:
const char *text = "This is some text";
ดูเหมือนว่ารูปแบบนี้จะสิ้นเปลืองแรม แต่มันก็มีประโยชน์ ที่ดีที่สุดลืมมันไปตอนนี้
const char text[] = "This is some text";
PROGMEM
, หรือPSTR()
F()
ดังนั้นจึงconst char text[]
ไม่ได้ใช้ RAM const char *text
มากกว่า
(const char *)(...)
คัดเลือกนักแสดงที่เรียบง่าย ไม่มีผลจริงหากบอร์ดไม่ต้องการ แต่เป็นการประหยัดมากถ้าคุณจากนั้นย้ายรหัสของคุณไปยังบอร์ดที่ทำ
ในการอธิบายอย่างละเอียดเกี่ยวกับคำตอบที่ยอดเยี่ยมของ Makenko มีเหตุผลที่ดีที่ผู้แปลเตือนคุณเกี่ยวกับเรื่องนี้ มาทำสเก็ตช์ทดสอบกัน:
char *foo = "This is some text";
char *bar = "This is some text";
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo [2] = 'o'; // change foo only
Serial.println (foo);
Serial.println (bar);
} // end of setup
void loop ()
{
} // end of loop
เรามีตัวแปรสองตัวที่นี่ foo และ bar ฉันแก้ไขหนึ่งในการตั้งค่า () แต่ดูว่าผลลัพธ์คืออะไร:
Thos is some text
Thos is some text
พวกเขาทั้งคู่เปลี่ยนไป!
อันที่จริงถ้าเราดูคำเตือนที่เราเห็น:
sketch_jul14b.ino:1: warning: deprecated conversion from string constant to ‘char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to ‘char*’
คอมไพเลอร์รู้ว่ามันหลบและมันถูกต้อง! เหตุผลสำหรับสิ่งนี้คือคอมไพเลอร์ (สมเหตุสมผล) คาดว่าค่าคงที่สตริงจะไม่เปลี่ยนแปลง (เนื่องจากเป็นค่าคงที่) ดังนั้นหากคุณอ้างถึงค่าคงที่สตริง"This is some text"
หลายครั้งในรหัสของคุณจะได้รับอนุญาตให้จัดสรรหน่วยความจำเดียวกันให้กับพวกเขาทั้งหมด ตอนนี้ถ้าคุณปรับเปลี่ยนหนึ่งคุณแก้ไขทั้งหมด!
*foo
และการ*bar
ใช้สตริง"ค่าคงที่" ที่แตกต่างกันจะป้องกันไม่ให้เกิดขึ้นได้หรือไม่ นอกจากนี้วิธีนี้แตกต่างจากการไม่ใส่สายใด ๆ เช่น: ? char *foo;
new
, strcpy
และdelete
)
หยุดการพยายามส่งผ่านค่าคงที่สตริงที่ฟังก์ชันใช้char*
หรือเปลี่ยนฟังก์ชันเพื่อใช้const char*
แทน
สตริงเช่น "สตริงสุ่ม" เป็นค่าคงที่
ตัวอย่าง:
void foo (char * s)
{
Serial.println (s);
}
void setup ()
{
Serial.begin (115200);
Serial.println ();
foo ("bar");
} // end of setup
void loop ()
{
} // end of loop
คำเตือน:
sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’
ฟังก์ชันfoo
คาดว่าอักขระ char * (ซึ่งสามารถแก้ไขได้) แต่คุณกำลังส่งผ่านตัวอักษรสตริงซึ่งไม่ควรแก้ไข
คอมไพเลอร์เตือนคุณว่าอย่าทำเช่นนี้ เมื่อเลิกใช้แล้วอาจเปลี่ยนจากคำเตือนเป็นข้อผิดพลาดในเวอร์ชันคอมไพเลอร์ในอนาคต
วิธีแก้ปัญหา: ทำให้ foo ใช้const char *:
void foo (const char * s)
{
Serial.println (s);
}
ฉันไม่เข้าใจ คุณหมายถึงไม่สามารถแก้ไขได้?
เวอร์ชันเก่ากว่าของ C (และ C ++) ให้คุณเขียนโค้ดเหมือนตัวอย่างด้านบน คุณสามารถสร้างฟังก์ชั่น (เช่นfoo
) ที่พิมพ์สิ่งที่คุณส่งลงไปจากนั้นส่งผ่านสตริงตัวอักษร (เช่นfoo ("Hi there!");
)
อย่างไรก็ตามฟังก์ชันที่ใช้char *
เป็นอาร์กิวเมนต์อนุญาตให้แก้ไขอาร์กิวเมนต์ (เช่นแก้ไขHi there!
ในกรณีนี้)
คุณอาจจะเขียนเช่น:
void foo (char * s)
{
Serial.println (s);
strcpy (s, "Goodbye");
}
น่าเสียดายที่ตอนนี้คุณได้แก้ไขตัวอักษรเพื่อที่จะ "สวัสดี!" ตอนนี้ "Goodbye" ซึ่งไม่ดี ในความเป็นจริงหากคุณคัดลอกในสายยาวคุณอาจเขียนทับตัวแปรอื่น ๆ หรือในการใช้งานบางอย่างคุณจะได้รับการละเมิดการเข้าถึงเพราะ "สวัสดี!" อาจถูกวางใน RAM แบบอ่านอย่างเดียว (ป้องกัน)
ดังนั้นคอมไพเลอร์นักเขียนจะค่อยๆเลิกconst
ใช้งานนี้เพื่อให้ฟังก์ชั่นที่คุณผ่านการลงแท้จริงต้องประกาศโต้แย้งว่า
can not
ถูกปรับเปลี่ยน?
ฉันมีข้อผิดพลาดในการรวบรวม:
TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
if(Serial.find(TIME_HEADER)) {
^
โปรดแทนที่บรรทัดนี้:
#define TIME_HEADER "T" // Header tag for serial time sync message
ด้วยสายนี้:
#define TIME_HEADER 'T' // Header tag for serial time sync message
และการรวบรวมเป็นไปด้วยดี