Прекращение преобразования C ++ из строковой константы в 'char *'

154

У меня есть класс с private char str[256];

и для этого у меня есть явный конструктор:

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

Я называю это так:

myClass obj("example");

Когда я компилирую это, я получаю следующее предупреждение:

устаревшее преобразование из строковой константы в 'char *'

Почему это происходит?

mkamthan
источник
1
Вы должны использовать strncpy(str, func, 255)вместо strcpy(str, func)более безопасной копии. И затем не забудьте добавить '\ 0' в конце строки, так как strncpy не добавляет его.
Патрис Бернассола
2
Еще безопаснее сказать "strncpy (str, func, sizeof (str)); str [sizeof (str) - 1] = '\ 0';"
Уоррен Янг
3
Я не думаю, что вышесказанное дает предупреждение, которое вы цитировали, хотя я уверен, что довольно похожий код будет. Чтобы получить осмысленные ответы, вы должны опубликовать минимальный компилирующий пример, который выдает предупреждение.
ВОО
3
@Patrice, Уоррен: не используйте strncpy, это не более безопасная версия strcpy. Используйте (или повторно внедрите) strcpy_s.
Стив Джессоп
У меня проблема, она показывает только эти проблемы для сборки -X86, а не для обычных сборок Solaris или ARM (цель), поэтому я игнорирую это. Не удалось найти исправление, поскольку оно также не отображает предупреждение для моего примера кода. Спасибо вам всем!
mkamthan

Ответы:

144

Это сообщение об ошибке вы видите всякий раз, когда у вас возникает такая ситуация:

char* pointer_to_nonconst = "string literal";

Зачем? Ну, C и C ++ различаются по типу строкового литерала. В C тип является массивом char, а в C ++ - постоянным массивом char. В любом случае вам не разрешено изменять символы строкового литерала, поэтому const в C ++ на самом деле не ограничение, а скорее вопрос безопасности типов. Преобразование из const char*в char*обычно невозможно без явного приведения по соображениям безопасности. Но для обратной совместимости с C язык C ++ все еще позволяет присваивать строковый литерал a char*и выдает предупреждение о том, что это преобразование не рекомендуется.

Таким образом, где-то вам не хватает одного или нескольких constсимволов в вашей программе для правильности констант. Но код, который вы показали нам, не является проблемой, так как он не делает такого рода устаревшее преобразование. Предупреждение, должно быть, пришло из другого места.

sellibitze
источник
17
К сожалению, учитывая мнение и голоса по этому вопросу, ФП никогда не предоставляла код, который фактически демонстрирует проблему.
Шафик Ягмур
1
Вы можете воспроизвести проблему с кодом OP, удалив constиз MyClassконструктора ... затем вы можете исправить ее, добавив constобратно.
Теодор Мердок
145

Предупреждение:

устаревшее преобразование из строковой константы в 'char *'

дается, потому что вы делаете где-то (не в коде, который вы опубликовали) что-то вроде:

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

Проблема в том, что вы пытаетесь преобразовать строковый литерал (с типом const char[]) в char*.

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

Это преобразование, вероятно, разрешено для совместимости с C и дает вам упомянутое предупреждение.

Фнието - Фернандо Ньето
источник
96

Как ответа нет. 2 by fnieto - Фернандо Ньето ясно и правильно описывает, что это предупреждение выдается, потому что где-то в вашем коде вы делаете (не в коде, который вы опубликовали) что-то вроде:

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

Однако, если вы хотите, чтобы ваш код также не содержал предупреждений, просто внесите соответствующие изменения в свой код:

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

То есть просто приведите stringконстанту к (char *).

sactiw
источник
17
В качестве альтернативы, сделайте функцию: void foo (const char * str)
Caprooja
3
@Caprooja Да, объявление параметра как «указатель на константу» также будет работать в этом случае. Но с этим изменением пользователь больше не может изменять / переназначать значение, хранящееся по адресу, используя указатель 'str', что пользователь может делать в части реализации. Так что это то, что вы можете посмотреть.
Сакви
1
@sactiw Есть ли какие-то причины, чтобы оставить все void foo(char* str)как есть? Я думал, что мы не можем модифицировать strв fooлюбом случае, даже параметр записан как неконстантный.
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");

Массивы также можно использовать вместо указателей, поскольку массив уже является постоянным указателем.

anilbey
источник
7
Для решения 3 есть strdup. В отличие от вашего кода, он будет выделять место для завершающего символа NUL и не будет превышать выделение.
Бен Фойгт
2
Решение 2 следует избегать.
Гонки легкости на орбите
На самом деле решение 2 может быть таким: char * x = static_cast <char *> ("foo bar") в C ++.
Кехе Цай
3
Анил, ты когда-нибудь включишь комментарии в свой ответ? Решение 3 все еще опасно неправильно.
Гонки легкости на орбите
@LightnessRacesinOrbit Можете ли вы дать ответ? Я не понимаю, почему вы говорите, что Решения 2 и 3 следует избегать и они опасно ошибочны.
Gladclef
4

Фактически строковая константа-литерал не является ни const char *, ни char *, а char []. Это довольно странно, но записано в спецификациях c ++; Если вы измените его, поведение не определено, потому что компилятор может сохранить его в сегменте кода.

Дан Ионеску
источник
5
Я бы сказал, что это const char [], потому что в качестве значения вы не можете его изменить.
Фнието - Фернандо Ньето
3

Может быть, вы можете попробовать это:

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

foo("Hello")

Меня устраивает

Ален Ли
источник
2

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

 #define C_TEXT( text ) ((char*)std::string( text ).c_str())
TWOPIR
источник
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 константным указателем на char. Это означает, что точка теперь постоянна, но данные, на которые указывают, тоже нет. Указатель нельзя изменить, но мы можем изменить данные, используя указатель.
    1. str++ ;: Недействительно . Мы пытаемся изменить указатель переменной, которая является константой.
    2. *str = 'a';: ДЕЙСТВИТЕЛЬНО . Мы пытаемся изменить данные, на которые указывают. В нашем случае это не вызовет ошибки компиляции, но вызовет ошибку времени выполнения , поскольку строка, скорее всего, попадет в секцию только для чтения скомпилированного двоичного файла. Это утверждение имело бы смысл, если бы у нас была динамически распределенная память, например. char* const str = new char[5];,
  3. const char* const str: Объявляем str константным указателем на константный символ. В этом случае мы не можем ни изменить указатель, ни данные, на которые он указывает.
    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 ↔ английский конвертер для преобразования Cобъявлений в легко понятные английские выражения, и наоборот. Это Cединственный инструмент, и поэтому он не будет поддерживать вещи (например, constexpr), которые являются эксклюзивными для C++.

Сахил Сингх
источник
0

У меня тоже такая же проблема. Я просто добавил const char * вместо char *. И проблема решена. Как уже упоминалось выше, это совместимая ошибка. C рассматривает строки как массивы символов, а C ++ обрабатывает их как массивы const.

dilantha111
источник
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];
    }
};
bremen_matt
источник
-1

Следующее иллюстрирует решение, назначьте вашу строку указателю переменной на постоянный массив символов char (строка является постоянным указателем на постоянный массив символов char + plus length):

#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';
}
Говард Ловатт
источник