Я работаю в C и программе C++. Мы раньше компилировали без make-strings-writable опции. Но это получало набор предупреждений, таким образом, я выключил его.
Затем я добрался, целый набор ошибок формы "Не может преобразовать символ константы* для обугливания* в argmuent 3 функционального нечто". Так, я прошел и внес большое изменения для фиксации их.
Однако сегодня программа ОТКАЗАЛА, потому что литерал "" становился переданным в функцию, которая ожидала символ* и устанавливала 0th символ на 0. Это ничего не делало плохо, просто пытаясь отредактировать константу, и катастрофический отказ.
Мой вопрос, почему это не было ошибкой компилятора?
В случае, если это имеет значение, это было на Mac, скомпилированном с gcc-4.0.
Править: добавленный код:
char * host = FindArgDefault("EMailLinkHost", "");
stripCRLF(linkHost, '\n');
где:
char *FindArgDefault(char *argName, char *defVal)
{// simplified
char * val = defVal;
return(val);
}
и
void stripCRLF(char *str, char delim)
{
char *p, *q;
for (p = q = str; *p; ++p) {
if (*p == 0xd || *p == 0xa) {
if (p[1] == (*p ^ 7)) ++p;
if (delim == -1) *p = delim;
}
*q++ = *p;
}
*q = 0; // DIES HERE
}
Это скомпилировало и работало, пока это не пыталось установить *q на 0...
РЕДАКТИРОВАНИЕ 2:
Большинство людей, кажется, упускает суть моего вопроса. Я знаю, почему символьное нечто [] = "панель" работает. Я знаю почему символ * нечто = "панель"; не работает.
Мой вопрос главным образом относительно передающих параметров. Одна вещь, которая происходит со мной, "Действительно ли возможно, что это - C по сравнению с проблемой C++?" потому что у меня есть некоторые.c файлы и некоторые .cpp файлы, и довольно возможно, что C позволяет его, но C++ не делает... или наоборот...
Это действительно зависит от того, как вы «прошли и внесли множество изменений, чтобы исправить это».
Если вы просто приведете строковый литерал к char *
, то вы сообщаете компилятору , а не , чтобы отловить эту ошибку. Вам необходимо сделать копию, если вы собираетесь ее модифицировать. В противном случае объявите интерфейсы функций, которые принимают const
, чтобы компилятор мог проверить их за вас.
Поскольку функция stripCRLF
изменяет строку на месте, но ничего с ней не делает и не возвращает никакого значения, передача ей строкового литерала, по сути, не имеет смысла и должна рассматриваться как ошибка. Это можно решить либо тем, что функция будет модифицировать и возвращать копию строки, либо установив более строгие флаги предупреждения, чтобы помочь обнаружить, когда это происходит.
Если вы хотите, чтобы gcc предупреждал вас о подобных вещах, включите опцию компилятора -Wwrite-strings
. Это заставит компилятор предупреждать вас, если строковая константа преобразуется в неконстантный char*
. Также, возможно, полезна опция -Wcast-qual
; она должна выдавать предупреждение всякий раз, когда указатель приводится таким образом, что удаляется квалификатор типа (в вашем случае удаляется const
). Если вы хотите, чтобы эти сообщения были более строгими, используйте -Werror
, чтобы превратить все предупреждения в ошибки.
Другим спорным моментом является функция FindArgDefault
. В представленном виде сигнатура функции должна более точно использовать const char*
вместо char*
для типов возврата и параметров. Это должно вызвать недовольство компилятора, когда возвращаемое значение присваивается char*
(если используется опция -Wcast-qual
). Поскольку вы не опубликовали полную версию функции, это может быть не совсем корректным изменением. Если внутри функции изменяется любая из строк, то соответствующий параметр должен оставаться char*
, но в этом случае передача строкового литерала в качестве аргумента должна вызвать предупреждение компилятора (используйте -Wwrite-strings
).
Кстати, ваша функция stripCRLF
уязвима к проблемам, когда передается указатель NULL. Также, вы хотели сказать if (delim == -1)
, или это должно быть !=
?
Edit: После получения дополнительной информации об ошибках, которые получал ОП, я удалил части оригинального сообщения, которые были не по теме, и добавил несколько дополнительных комментариев.
Edit2: Я протестировал следующую упрощенную версию вашей программы:
char *FindArgDefault(char *argName, char *defVal) {
char * val = defVal;
return(val);
}
int main (void) {
char * host = FindArgDefault("EMailLinkHost", "");
return (int)(host);
}
Когда я компилировал с помощью gcc -Wall test.c -o test.o
, я получил ноль предупреждений или ошибок компилятора.
Когда я скомпилировал с помощью gcc -Wwrite-strings -Wall test.c -o test.o
, я получил
test.c: In function 'main':
test.c:10: warning: passing arg 1 of 'FindArgDefault' discards qualifiers from pointer target type
test.c:10: warning: passing arg 2 of 'FindArgDefault' discards qualifiers from pointer target type
Я определенно думаю, что опция компилятора -Wwrite-strings
- это та, которую вы хотите включить, чтобы предупредить вас о такого рода проблемах.
Чтобы ответить на вопрос, почему это преобразование является законным (хотя и устаревшим). Было время, когда в языке Си не было ключевого слова const, и за это время люди успели создать немного кода. Разработчики C++, должно быть, поняли, что не стоит расстраивать так много людей, ломая их код.
Использование строкового литерала для инициализации char *
указатель в C ++ является устаревшей функцией, но, тем не менее, является законной. Это не ошибка. Вы несете ответственность за то, чтобы с помощью такого указателя не было предпринято никаких попыток модификации.
Другими словами, вы, должно быть, неправильно понимаете ранее полученные ошибки компиляции. Не думаю, что у вас когда-либо возникали ошибки при такой инициализации / назначении. Ошибки «Невозможно преобразовать const char * в char *», упомянутые в вашем вопросе, должны быть вызваны чем-то другим.
Обратите внимание, что тот факт, что вы можете инициализировать указатель char *
строковым литералом, не означает, что вы можете использовать любое произвольное значение const char *
для инициализации char *
указатель. Этот код
const char *pc = "A";
char *p = pc;
вызовет ошибку, а этот
char *p = "A";
- нет. Вышеупомянутая устаревшая функция применяется только к строковым литералам, а не ко всем указателям const char *
.
Я полностью согласен с другими ответами, я только хочу добавить, что g++ (по крайней мере, версия 4. 4) действительно ловит эти устаревшие преобразования как предупреждения на любом уровне предупреждения (если предыдущие версии не делают этого по умолчанию, вероятно, вам придется поднять уровень предупреждения):
#include <iostream>
using namespace std;
void WithConst(const char * Str)
{
cout<<Str<<endl;
}
void WithoutConst_NoEdit(char * Str)
{
cout<<Str<<endl;
}
void WithoutConst_Edit(char * Str)
{
*Str='a';
cout<<Str<<endl;
}
int main()
{
WithConst("Test");
WithoutConst_NoEdit("Test");
WithoutConst_Edit("Test");
return 0;
}
matteo@teoubuntu:~/cpp/test$ g++ --version
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
matteo@teoubuntu:~/cpp/test$ g++ -O3 lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
Более того, под капотом происходит интересная вещь:
matteo@teoubuntu:~/cpp/test$ g++ -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ ./lit_const_corr.x
Test
Test
Segmentation fault
но, если включить оптимизатор, то сбоя нет:
matteo@teoubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x
lit_const_corr.cpp: In function ‘int main()’:
lit_const_corr.cpp:24: warning: deprecated conversion from string constant to ‘char*’
lit_const_corr.cpp:25: warning: deprecated conversion from string constant to ‘char*’
matteo@teoubuntu:~/cpp/test$ ./lit_const_corr.x
Test
Test
Test
Я предполагаю, что это связано с каким-то магическим трюком оптимизации, но не понимаю, зачем он применяется; есть идеи?
Когда я объявляю char* foo = "bar", он действительно жалуется. Но когда я объявляю char foo[] = "bar", он не
Эй, будьте осторожны, не путайте эти две вещи: с
char * foo = "bar";
вы объявляете указатель на char, и присваиваете ему адрес литерала "bar", который на самом деле хранится в каком-то доступном только для чтения месте памяти (обычно это часть исполняемого файла, который отображается в памяти). Вместо этого, с помощью
char foo[]="bar";
вы объявляете и выделяете память RW (на стеке или где-то еще, в зависимости от контекста) для массива символов, который инициализируется значением "bar", но он вообще не связан с таблицей строк, и совершенно законно может изменить эту строку.
Стандарт определяет специальное правило, разрешающее преобразование литерала в- char *
, которое незаметно отбрасывает квалификацию const
. (4.2 / 2):
Строковый литерал (2.13.4), который не является широким строковым литералом, может быть преобразован в rvalue типа «указатель на char»; широкий строковый литерал может быть преобразован в rvalue типа «указатель на wchar_t». В любом случае результатом является указатель на первый элемент массива. Это преобразование рассматривается только при наличии явного соответствующего целевого типа указателя, а не тогда, когда есть общая потребность в преобразовании из lvalue в rvalue. [Примечание: это преобразование устарело. См. Приложение D.]
Стандарт C ++ 0x продвигает это устаревание дальше… это бессмысленное правило полностью удалено из будущего стандарта.
Ошибка const char *
в char *
должна быть результатом преобразования литерала сначала в const char *
.