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 не додає його.
Патріс Бернассола

2
Безпечніше все-таки говорити "strncpy (str, func, sizeof (str)); str [sizeof (str) - 1] = '\ 0';"
Воррен Янг

3
Я не думаю, що зазначене вище дає попередження, яке ви цитували, хоча я впевнений, що подібний код був би. Щоб отримати змістовні відповіді, слід опублікувати мінімальний, складений приклад, який створює попередження.
sbi

3
@Patrice, Warren: не використовуйте strncpy, це не безпечніша версія strcpy. Використовуйте (або повторно реалізуйте) strcpy_s.
Стів Джессоп

У мене з’явилася проблема, вона відображає лише ці проблеми для складання -X86, а не для звичайних побудови solaris або ARM (target), тому я ігнорую це. Не вдалося знайти виправлення, оскільки воно не показує попередження, як правило, і для мого зразкового коду. Дякую вам всім!
mkamthan

Відповіді:


144

Це повідомлення про помилку, яке ви бачите щоразу, коли виникає така ситуація:

char* pointer_to_nonconst = "string literal";

Чому? Ну, C і C ++ відрізняються за типом рядкового літералу. У C тип - це масив char, а в C ++ - це постійний масив char. У будь-якому випадку вам заборонено змінювати символи буквеного рядка, тому const в C ++ насправді не є обмеженням, а більше предметом безпеки типу. Перехід від const char*до, char*як правило, неможливо без чіткого виступу з міркувань безпеки. Але для зворотної сумісності з C мова C ++ все ще дозволяє призначити рядковий літерал a char*і надає попередження про припинення цієї конверсії.

Отже, десь вам не вистачає одного або декількох constс у вашій програмі для правильності const. Але код, який ви нам показали, не є проблемою, оскільки він не робить такого роду застарілого перетворення. Попередження, мабуть, надійшло з іншого місця.


17
Прикро, враховуючи думку та голоси щодо цього питання, що ОП ніколи не надала код, який насправді демонструє проблему.
Шафік Ягмур

1
Ви можете відтворити проблему з кодом OP, видаливши constз MyClassконструктора ... тоді ви можете виправити це, додавши constзворотну сторону.
Теодор Мердок

145

Попередження:

застаріле перетворення з константи рядка в "char *"

надається тому, що ви робите десь (а не в опублікованому вами коді) щось на кшталт:

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

Проблема полягає в тому, що ви намагаєтеся перетворити рядковий літерал (з типом const char[]) в char*.

Ви можете перетворити const char[]на, const char*тому що масив розпадається на вказівник, але те, що ви робите, робить мутантною константою.

Ця конверсія, ймовірно, дозволена для сумісності з C і просто дає вам попередження.


96

Як відповідь ні. 2 від fnieto - Фернандо Нієто чітко і правильно описує, що це попередження подано тому, що десь у коді ви робите (а не в коді, який ви розмістили) щось на кшталт:

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)як є? Я думав, що ми не можемо модифікувати strв fooбудь-якому випадку, навіть параметр записаний як non-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
Для рішення 3 існує strdup. На відміну від вашого коду, він виділить простір для закінчуючого символу NUL, а не перевищить розподіл.
Бен Войгт

2
Рішення 2 слід уникати.
Гонки легкості по орбіті

Насправді рішенням 2 може бути: char * x = static_cast <char *> ("foo bar") в C ++.
Kehe CAI

3
Аніл, ти коли-небудь будеш інтегрувати коментарі у свою відповідь? Рішення 3 все ще небезпечно неправильне.
Гонки легкості по орбіті

@LightnessRacesinOrbit Чи можете ви надати відповідь? Я не розумію, чому ви кажете, що рішення 2 і 3 слід уникати і небезпечно помиляються.
Гладклеф

4

Насправді константа постійного літералу не є ні const char *, ні char *, але char []. Це досить дивно, але записано в специфікаціях c ++; Якщо ви модифікуєте його, поведінка не визначена, оскільки компілятор може зберігати її в сегменті коду.


5
Я б сказав, що це const char [], оскільки, як рецензія, ви не можете її змінити.
fnieto - Фернандо Нієто

3

Можливо, ви можете спробувати це:

void foo(const char* str) 
{
    // Do something
}

foo("Hello")

Це працює для мене


2

Я вирішую цю проблему, додавши цей макрос на початку коду, десь. Або додайте його <iostream>, хе-хе.

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

8
"Або додати його в <iostream>" Що ?!
Гонки легкості на орбіті

Було ", хе-хе", яке з будь-якої причини було відредаговано (мається на увазі, що це жарт)
Someguynamedpie

C_TEXTдобре для виклику функції ( foo(C_TEXT("foo"));), але кричить за невизначеною поведінкою, якщо значення зберігається в змінній на зразок char *x = C_TEXT("foo");- будь-яке використання x(крім призначення) є невизначеним поведінкою, оскільки пам'ять, на яку він вказує, звільнена.
Мартін Боннер підтримує Моніку

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 gibberish ↔ English для перетворення Cдекларацій у легко зрозумілі англійські твердження та навпаки. Це Cєдиний інструмент, і таким чином не будемо підтримувати речі (наприклад, constexpr), які є ексклюзивними C++.


0

У мене теж була така ж проблема. Я просто зробив додавання const char * замість char *. І проблема вирішена. Як уже згадували інші, це помилка сумісна. C розглядає рядки як масиви char, тоді як C ++ трактує їх як масиви const char.


0

Наскільки це варте, я вважаю цей простий клас обгортки корисним для перетворення рядків 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

Далі ілюструється рішення, призначте рядок змінному покажчику до постійного масиву char (рядок є постійним вказівником на постійний масив char - плюс інформація про довжину):

#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';
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.