C ++ ไม่สนับสนุนการแปลงจากค่าคงที่สตริงเป็น 'char *'


154

ฉันมีชั้นเรียนด้วย private char str[256];

และสำหรับฉันมีตัวสร้างที่ชัดเจน:

explicit myClass(const char *func)
{
    strcpy(str,func);
}

ฉันเรียกมันว่า:

myClass obj("example");

เมื่อฉันรวบรวมสิ่งนี้ฉันจะได้รับคำเตือนต่อไปนี้:

การแปลงที่เลิกใช้แล้วจากค่าคงที่สตริงเป็น 'char *'

ทำไมสิ่งนี้จึงเกิดขึ้น


1
คุณควรใช้strncpy(str, func, 255)แทนstrcpy(str, func)สำเนาที่ปลอดภัยกว่า และอย่าลืมเพิ่ม '\ 0' ที่ท้ายสตริงเนื่องจาก strncpy ไม่ได้เพิ่มเข้าไป
Patrice Bernassola

2
ยังปลอดภัยกว่าที่จะพูดว่า "strncpy (str, func, sizeof (str)); str [sizeof (str) - 1] = '\ 0';"
Warren Young

3
ฉันไม่คิดว่าข้อความด้านบนให้คำเตือนที่คุณยกมาถึงแม้ว่าฉันจะแน่ใจว่ารหัสที่คล้ายกันมากจะ เพื่อให้ได้คำตอบที่มีความหมายคุณควรโพสต์ตัวอย่างการรวบรวมที่ทำให้เกิดคำเตือนน้อยที่สุด
sbi

3
@ Patrice, Warren: อย่าใช้ strncpy ไม่ใช่ strcpy รุ่นที่ปลอดภัยกว่า ใช้ strcpy_s (หรือนำไปใช้ใหม่)
464 Steve Jessop

ฉันได้รับปัญหามันแสดงเฉพาะปัญหาเหล่านี้สำหรับ -X86 build และไม่ใช่สำหรับ Solaris ปกติหรือ ARM (เป้าหมาย) สร้างดังนั้นฉันจึงไม่สนใจสิ่งนี้ ไม่พบการแก้ไขเนื่องจากไม่แสดงคำเตือนตามปกติสำหรับโค้ดตัวอย่างของฉันเช่นกัน ขอบคุณทุกคน!
mkamthan

คำตอบ:


144

นี่คือข้อความแสดงข้อผิดพลาดที่คุณเห็นเมื่อใดก็ตามที่คุณมีสถานการณ์เช่นนี้:

char* pointer_to_nonconst = "string literal";

ทำไม? ดี C และ C ++ แตกต่างกันในประเภทของตัวอักษรสตริง ใน C ประเภทคืออาร์เรย์ของถ่านและใน C ++ มันเป็นอาร์เรย์คงที่ของถ่าน ไม่ว่าในกรณีใดคุณไม่ได้รับอนุญาตให้เปลี่ยนอักขระของตัวอักษรสตริงดังนั้น const ใน C ++ จึงไม่ใช่ข้อ จำกัด จริงๆ แต่เป็นประเภทความปลอดภัยที่มากกว่า การแปลงจากconst char*ไปchar*โดยทั่วไปไม่ได้โดยไม่ต้องหล่อที่ชัดเจนสำหรับเหตุผลด้านความปลอดภัย แต่สำหรับความเข้ากันได้ย้อนหลังกับ C ภาษา C ++ ยังคงอนุญาตให้กำหนดสตริงตัวอักษรให้กับchar*และให้คำเตือนเกี่ยวกับการแปลงนี้จะถูกคัดค้าน

ดังนั้นบางที่คุณขาดหายไปอย่างน้อยหนึ่งตัวconstในโปรแกรมเพื่อความถูกต้องของ const แต่รหัสที่คุณแสดงให้เรานั้นไม่ใช่ปัญหาเพราะมันไม่ได้ทำการแปลงที่เลิกใช้แล้ว คำเตือนต้องมาจากที่อื่น


17
โชคไม่ดีที่พิจารณามุมมองและการลงคะแนนของคำถามนี้ว่า OP ไม่เคยให้รหัสที่แสดงให้เห็นถึงปัญหา
Shafik Yaghmour

1
คุณสามารถสร้างปัญหาขึ้นมาใหม่ด้วยรหัสของ OP โดยการลบconstจากตัวMyClassสร้าง ... จากนั้นคุณสามารถแก้ไขได้โดยการเพิ่มconstด้านหลัง
Theodore Murdock

145

คำเตือน:

การแปลงที่เลิกใช้แล้วจากค่าคงที่สตริงเป็น 'char *'

ได้รับเนื่องจากคุณกำลังทำบางแห่ง (ไม่ใช่ในรหัสที่คุณโพสต์) บางสิ่งเช่น:

void foo(char* str);
foo("hello");

ปัญหาคือว่าคุณกำลังพยายามที่จะแปลงตัวอักษรสตริง (กับชนิดconst char[]) char*เพื่อ

คุณสามารถแปลงconst char[]ไปconst char*เพราะอาร์เรย์สูญสลายตัวชี้ แต่สิ่งที่คุณกำลังทำคือการทำอย่างต่อเนื่องแน่นอน

การแปลงนี้อาจได้รับอนุญาตสำหรับการทำงานร่วมกันของ C และเพียงแค่แจ้งเตือนให้คุณทราบ


96

ในฐานะที่เป็นคำตอบที่ไม่มี 2 โดย fnieto - Fernando Nietoอธิบายอย่างชัดเจนและถูกต้องว่ามีการแจ้งเตือนนี้เนื่องจากมีบางอย่างในรหัสของคุณที่คุณกำลังทำ (ไม่ใช่ในรหัสที่คุณโพสต์) สิ่งที่ต้องการ:

void foo(char* str);
foo("hello");

อย่างไรก็ตามหากคุณต้องการให้รหัสของคุณไม่มีการเตือนอีกให้ทำการเปลี่ยนแปลงรหัสของคุณตามลำดับ:

void foo(char* str);
foo((char *)"hello");

นั่นคือเพียงแค่โยนอย่างต่อเนื่องเพื่อstring(char *)


17
อีกทางเลือกหนึ่งทำให้ฟังก์ชั่น: void foo (const char * str)
Caprooja

3
@Caprooja ใช่ประกาศพารามิเตอร์เป็น 'ตัวชี้ไปยังค่าคงที่' จะทำงานในกรณีนี้ แต่ด้วยการเปลี่ยนแปลงนี้ผู้ใช้ไม่สามารถเปลี่ยนแปลง / กำหนดค่าที่เก็บไว้ในที่อยู่ได้อีกต่อไปโดยใช้ตัวชี้ 'str' ซึ่งผู้ใช้อาจทำในส่วนการนำไปปฏิบัติ นั่นคือสิ่งที่คุณอาจต้องระวัง
sactiw

1
@sactiw มีเหตุผลอะไรที่จะต้องรักษาไว้void foo(char* str)เหมือนเดิม? ผมคิดว่าเราไม่สามารถ modity strในfooอย่างไรก็ตามแม้ว่าพารามิเตอร์เขียนเป็นไม่ใช่ const
kgf3JfUtW

37

มี 3 วิธีคือ:

โซลูชันที่ 1:

const char *x = "foo bar";

โซลูชันที่ 2:

char *x = (char *)"foo bar";

โซลูชันที่ 3:

char* x = (char*) malloc(strlen("foo bar")+1); // +1 for the terminator
strcpy(x,"foo bar");

อาร์เรย์ยังสามารถใช้แทนตัวชี้ได้เนื่องจากอาร์เรย์เป็นตัวชี้ค่าคงที่แล้ว


7
สำหรับ Solution 3 strdupมี ซึ่งแตกต่างจากรหัสของคุณมันจะจัดสรรพื้นที่สำหรับตัวละคร NUL ยุติและไม่เกินการจัดสรร
Ben Voigt

2
โซลูชันที่ 2 คือการหลีกเลี่ยง
การแข่งขัน Lightness ใน Orbit

วิธีแก้ปัญหาที่แท้จริง 2 สามารถเป็น: char * x = static_cast <char *> ("foo bar") ใน C ++
Kehe CAI

3
ครามคุณจะรวมความคิดเห็นไว้ในคำตอบหรือไม่ โซลูชันที่ 3 ยังคงผิดพลาดอันตราย
การแข่งขัน Lightness ใน Orbit

@LightnessRacesinOrbit คุณกรุณาให้คำตอบได้ไหม? ฉันไม่เข้าใจว่าทำไมคุณพูดว่าโซลูชัน 2 และ 3 ควรหลีกเลี่ยงและผิดอย่างอันตราย
Gladclef

4

ในความเป็นจริงสตริงคงที่ตัวอักษรไม่ใช่ const char * หรือ char * แต่เป็น char [] มันค่อนข้างแปลก แต่เขียนลงในสเปค c ++; หากคุณปรับเปลี่ยนพฤติกรรมจะไม่ได้กำหนดเนื่องจากคอมไพเลอร์อาจเก็บไว้ในส่วนรหัส


5
ฉันจะบอกว่ามันเป็น const char [] เพราะเนื่องจากค่า rvalue คุณไม่สามารถแก้ไขได้
fnieto - Fernando Nieto


2

ฉันแก้ปัญหานี้ด้วยการเพิ่มมาโครนี้ในส่วนเริ่มต้นของรหัสที่ไหนสักแห่ง หรือเพิ่มเข้าไป<iostream>ฮิฮิ

 #define C_TEXT( text ) ((char*)std::string( text ).c_str())

8
"หรือเพิ่มใน <iostream>"อะไรนะ!
การแข่งขัน Lightness ใน Orbit

มี ", hehe" นั่นคือเหตุผลใดก็ตามแก้ไขออก (โดยนัยว่ามันเป็นเรื่องตลก)
Someguynamedpie

C_TEXTใช้ได้สำหรับการเรียกใช้ฟังก์ชัน ( foo(C_TEXT("foo"));) แต่กำลังร้องหาพฤติกรรมที่ไม่ได้กำหนดหากค่าถูกเก็บไว้ในตัวแปรเช่นchar *x = C_TEXT("foo");- การใช้ใด ๆx(นอกเหนือจากการมอบหมาย) เป็นการกระทำที่ไม่ได้กำหนดเนื่องจากหน่วยความจำที่ชี้ไปยังถูกปลดปล่อย
Martin Bonner สนับสนุนโมนิก้า

1

เหตุผลสำหรับปัญหานี้ (ซึ่งก็ยากที่จะตรวจสอบกว่าปัญหาด้วยchar* str = "some string"- ซึ่งคนอื่น ๆ ได้อธิบาย) constexprคือเมื่อคุณกำลังใช้

constexpr char* str = "some string";

ดูเหมือนว่ามันจะมีลักษณะการทำงานคล้ายกับconst char* strและจะไม่ก่อให้เกิดคำเตือนที่มันเกิดขึ้นก่อนแต่แทนที่จะทำงานเป็นchar*char* const str

รายละเอียด

ตัวชี้ค่าคงที่และตัวชี้ไปยังค่าคงที่ ความแตกต่างระหว่างconst char* strและchar* const strสามารถอธิบายได้ดังนี้

  1. const char* str: ประกาศ str เป็นตัวชี้ไปยัง const char ซึ่งหมายความว่าข้อมูลที่ตัวชี้นี้ชี้ไปยังค่าคงที่ ตัวชี้สามารถแก้ไขได้ แต่ความพยายามใด ๆ ในการปรับเปลี่ยนข้อมูลจะทำให้เกิดข้อผิดพลาดในการรวบรวม
    1. str++ ;: ถูกต้อง เรากำลังแก้ไขตัวชี้ไม่ใช่ข้อมูลที่ชี้ไป
    2. *str = 'a';: ไม่ถูกต้อง เราพยายามแก้ไขข้อมูลที่ชี้ไป
  2. char* const str: ประกาศ str เพื่อเป็นตัวชี้ const เพื่อ char ซึ่งหมายความว่าตอนนี้จุดนั้นคงที่ แต่ข้อมูลที่ชี้ไปนั้นไม่ได้เป็นเช่นนั้น ตัวชี้ไม่สามารถแก้ไขได้ แต่เราสามารถแก้ไขข้อมูลโดยใช้ตัวชี้
    1. str++ ;: ไม่ถูกต้องไม่ถูกต้องเราพยายามแก้ไขตัวแปรพอยน์เตอร์ซึ่งเป็นค่าคงที่
    2. *str = 'a';: ถูกต้อง เราพยายามแก้ไขข้อมูลที่ชี้ไป ในกรณีของเราสิ่งนี้จะไม่ทำให้เกิดข้อผิดพลาดในการคอมไพล์ แต่จะทำให้เกิดข้อผิดพลาดรันไทม์เนื่องจากสตริงส่วนใหญ่จะเข้าสู่ส่วนอ่านอย่างเดียวของไบนารีที่คอมไพล์แล้ว คำสั่งนี้จะสมเหตุสมผลถ้าเรามีการจัดสรรหน่วยความจำแบบไดนามิกเช่น char* const str = new char[5];.
  3. const char* const str: ประกาศ str เป็นตัวชี้ const ถึง const char ในกรณีนี้เราไม่สามารถแก้ไขตัวชี้หรือข้อมูลที่ชี้ไป
    1. str++ ;: ไม่ถูกต้อง เราพยายามแก้ไขตัวแปรพอยน์เตอร์ซึ่งเป็นค่าคงที่
    2. *str = 'a';: ไม่ถูกต้อง เรากำลังพยายามแก้ไขข้อมูลที่ชี้โดยตัวชี้นี้ซึ่งเป็นค่าคงที่

ในกรณีของฉันปัญหาคือว่าฉันคาดหวังว่าconstexpr char* strจะทำตัวเป็นconst char* strและไม่ได้char* const strเพราะตั้งแต่มองเห็นมันดูเหมือนจะใกล้ชิดกับอดีต

นอกจากนี้ยังมีคำเตือนที่สร้างขึ้นสำหรับเป็นที่แตกต่างกันเล็กน้อยจากconstexpr char* str = "some string"char* str = "some string"

  1. คอมไพเลอร์คำเตือนสำหรับconstexpr char* str = "some string":ISO C++11 does not allow conversion from string literal to 'char *const'
  2. คอมไพเลอร์คำเตือนสำหรับchar* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.

ปลาย

คุณสามารถใช้C ซึ่งพูดพล่อยๆ↔ตัวแปลงภาษาอังกฤษเพื่อแปลงCการประกาศเป็นภาษาอังกฤษที่เข้าใจได้ง่ายและในทางกลับกัน นี้เป็นCเครื่องมือเท่านั้นและสิ่งสนับสนุนเคยชินจึง (เช่น constexpr) C++ซึ่งเป็นเอกสิทธิ์เฉพาะบุคคล


0

ฉันยังได้รับปัญหาเดียวกัน และสิ่งที่ฉันทำง่ายๆคือแค่เพิ่ม const char * แทนที่จะเป็น char * และปัญหาได้รับการแก้ไข ตามที่คนอื่น ๆ ได้กล่าวไว้ข้างต้นมันเป็นข้อผิดพลาดที่เข้ากันได้ C ถือว่าสตริงเป็นถ่านอาร์เรย์ในขณะที่ C ++ ถือว่าเป็นถ่านอาร์เรย์


0

สำหรับสิ่งที่คุ้มค่าฉันพบชั้น wrapper ง่าย ๆ นี้จะเป็นประโยชน์สำหรับการแปลงสตริง C ++ เป็นchar *:

class StringWrapper {
    std::vector<char> vec;
public:
    StringWrapper(const std::string &str) : vec(str.begin(), str.end()) {
    }

    char *getChars() {
        return &vec[0];
    }
};

-1

ต่อไปนี้แสดงวิธีแก้ปัญหากำหนดสตริงของคุณให้กับตัวชี้ตัวแปรไปยังอาร์เรย์คงที่ของอักขระถ่าน (สตริงเป็นตัวชี้คงที่ไปยังอาร์เรย์คงที่ของอักขระถ่าน - บวกข้อมูลความยาว):

#include <iostream>

void Swap(const char * & left, const char * & right) {
    const char *const temp = left;
    left = right;
    right = temp;
}

int main() {
    const char * x = "Hello"; // These works because you are making a variable
    const char * y = "World"; // pointer to a constant string
    std::cout << "x = " << x << ", y = " << y << '\n';
    Swap(x, y);
    std::cout << "x = " << x << ", y = " << y << '\n';
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.