Jump to content

    

Си строки. Мир рухнул...

Вчера столкнулся с удивительным на мой взгляд явлением.

После переноса части исходников из рабочего проекта для bare-metal embedded в проект Qt под linux неожиданно стал появляться SEGMENTATION FAULT.

После недолгой отладки выяснил что падает при попытке записи байта в строку (прошу заметить не константную строку!!!).

void foo(char* s)
{
	s[4] = '\0';
}

foo("prog"); // segmentation fault inside!

Погуглил, оказалось вот в чём причина. Я выпал в осадок...

https://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha

Share this post


Link to post
Share on other sites

Классический пример UB, все, наверное, рано или поздно наступают.

 

Quote

char s[] = "abc", t[3] = "abc";

defines ‘‘plain’’ char array objects s and t whose elements are initialized with character string literals.
This declaration is identical to

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

The contents of the arrays are modifiable. On the other hand, the declaration

char *p = "abc";

defines p with type ‘‘pointer to char’’ and initializes it to point to an object with type ‘‘array of char’’
with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to
modify the contents of the array, the behavior is undefined.

 

Share this post


Link to post
Share on other sites

Не понимаю зачем это сделано.

Ведь когда мне будет явно нужно сделать строку константной то я сам это и сделаю

const char* str1 = "abc";

А когда не надо, то без const.

Логичнее некуда. Не согласны?

Share this post


Link to post
Share on other sites
7 minutes ago, demiurg_spb said:

Не понимаю зачем это сделано.

Ведь когда мне будет явно нужно сделать строку константной то я сам это и сделаю

А когда не надо, то без const.

Логичнее некуда. Не согласны?

 

Логика в современном С++ ?

Да у вас даже функция не кошерно описана - теперь модно писать void foo(char s[10]) {....}, ну и референсы с амперсантами не забывать...

Share this post


Link to post
Share on other sites

Это не С++, а Си модуль подключенный к плюсатому проекту. Так что Ваше замечание не в тему. ИМХО. 

Share this post


Link to post
Share on other sites
16 минут назад, demiurg_spb сказал:

Не понимаю зачем это сделано.

Ведь когда мне будет явно нужно сделать строку константной то я сам это и сделаю

Логичнее некуда. Не согласны?

Не согласны. Логично, что строковые литералы должны быть константами. Чтобы например printf("строка") занимало только 7 байт в RO-памяти, а не 7 байт в RO + 7 байт в ОЗУ.

А когда нужна явно не просто строка, а массив с начальной инициализацией строкой, то сами его и сделаете без проблем:

char string[] = "строка";

Так что - сделано правильно.

 

PS: И в Вашем примере:

const char* str1

str1 - это не сама строка, а указатель. Который сейчас указывает на строку, а через секунду - на что-то другое. Строка это: char const str[] = "...";

Share this post


Link to post
Share on other sites

Ну да. Палка о двух концах. Теперь исходники не так красиво будут выглядеть.

char str[] = "prog";
foo(str);

вместо

foo("prog");

Но я всё равно против. Для решения указанных Вами задач я бы использовал две отдельные функции.

cprintf(const char*, ...) 

printf(char*, ...) 

Share this post


Link to post
Share on other sites
8 minutes ago, demiurg_spb said:

Но я всё равно против. Для решения указанных Вами задач я бы использовал две отдельные функции.

cprintf(const char*, ...) 

printf(char*, ...) 

То есть количество функций мы умножим на два, чтобы не запутаться, а тип объекта будем выбирать исходя из того, к чему где-то привели указатель на него? Адъ.

Share this post


Link to post
Share on other sites
15 минут назад, demiurg_spb сказал:

Но я всё равно против. Для решения указанных Вами задач я бы использовал две отдельные функции.

cprintf(const char*, ...) 

printf(char*, ...) 

Ну да. И потом сами же первый и наступите на эти широко разложенные грабли. Если даже в свою же функцию, объявленную как принимающую char *:

void foo(char* s)

передаёте char const * и не замечаете ошибки, то с такими правилами вообще бардак будет.

 

PS: Вам "Qt под linux" нашёл ошибку в вашем "рабочем проекте". Поблагодарите его и исправьте её. Я не свои правила изобретайте :king:

PPS: Плохо что си позволяет передачу литеральных строк в функции, принимающие аргумент char *. Лучше бы он ошибку выдавал. Но это уже как то обсуждали не так давно здесь.

 

Share this post


Link to post
Share on other sites
11 минут назад, demiurg_spb сказал:

я бы использовал две отдельные функции.

cprintf(const char*, ...)

их и так уже вагон fprintf sprintf wprintf kprintf qdb_vmprintf vprintf vsprintf vfwprintf    swprintf fwprintf vsnprintf

Share this post


Link to post
Share on other sites

to: jcxz

Ничего страшного не будет.

Компилятор выдаст ошибку, что неявно const char* приводится к char* - это стандартное поведение.

Мне не ясно почему он мне не дал этого предупреждения (из примера в первом посте).

Он же знал что строковый литерал положит в RO и смолчал. 

 

А функций действительно будет больше в 2 раза. Печаль.

 

Я понял что так принято и ничего с этим уже не сделать. Понять и простить...

 

 

Share this post


Link to post
Share on other sites
11 минут назад, demiurg_spb сказал:

Ничего страшного не будет.

Компилятор выдаст ошибку, что неявно const char* приводится к char* - это стандартное поведение.

Где он такое выдаст??? Си как раз не выдаст ничего и молча приведёт char const * -> char *. Вот это как раз - плохо.

Но это поведение уже обсуждалось тут как-то несколько месяцев назад. Поищите тему.

 

Цитата

Мне не ясно почему он мне не дал этого предупреждения (из примера в первом посте).

Вот это то и плохо.... :sad:

вот, нашёл:  https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=141853&page=1

Share this post


Link to post
Share on other sites
7 minutes ago, jcxz said:

Где он такое выдаст??? Си как раз не выдаст ничего и молча приведёт char const * -> char *. 

void foo(char* str)        {puts(str);}
void cfoo(const char* str) {puts(str);}

int main(void)
{
	const char str[] = "123";
//	const char* str = "123"; // так тоже самое

	foo(str);
	cfoo(str);

	return 0;    
}

test_const.c: In function ‘main’:
test_const.c:20:6: warning: passing argument 1 of ‘foo’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  foo(str);
      ^~~
test_const.c:5:16: note: expected ‘char *’ but argument is of type ‘const char *’
 void foo(char* str)
          ~~~~~~^~~
 

Share this post


Link to post
Share on other sites
3 минуты назад, demiurg_spb сказал:

test_const.c: In function ‘main’:
test_const.c:20:6: warning: passing argument 1 of ‘foo’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
 

Пример некорректный. Разговор шёл о foo("..."), а Вы передаёте явно объявленный массив явно указанного типа.

Share this post


Link to post
Share on other sites

Это правильный пример в контексте необходимости дублирования функций (const и не const) для достижения целей оптимизации.

Дайте ссылку что-ли на стандарт где хоть как-то упоминается о том что Си приводит  char const * -> char *.

Это нонсенс. ИМХО.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now