LINUX.ORG.RU

[Си][newbie] про строки

 ,


0

1

Валиден ли такой синтаксис для присваивания строк?

char *str;
str = "first";
str = "second";

Или рано или поздно это обернется ошибкой сегментирования?

Вспоминается, что в книжках писали, что для строк нужно выделять память с помощью malloc. С malloc всё понятно. Самостоятельно выделил, самостоятельно освободил. А что происходит здесь? Компилятор сам выделяет память? А когда str присваивается «second» память занятая «first» освобождается? Откуда выделяется память, из стека или кучи?

Валиден. Память распределяется на этапе сборки. Когда str присваивается «second» память, занятая «first» не освобождается. Не из стека и не из кучи, «first» и «second» находятся в статической памяти.

backbone ★★★★★
()

K&R в помощь

anonymous
()
Ответ на: комментарий от backbone

Большое спасибо, теперь примерно представил как это происходит.

Olegymous ★★★
() автор топика

Это будет работать в Си. (в си++ будет предупреждение). Здесь в специальную область твоего бинарника (секция .data) будут зашиты строки «first» и «second». При запуске эта область целиком закатается в память (ещё до main и вообще до всего). При выполнении указанного кода указатель str будет указывать на места в памяти, где лежат эти строки. 6 байт в первом случае и 7 во втором доступны для изменения, но увеличить пространство невозможно, при попытке записать мимо (типа str = «second»;str[10]='X';) ты можешь попасть куда угодно, хоть в «first», хоть в код, хоть в null. Короче, это нормально работает, когда тебе надо, например, текст ошибки выбрать и вернуть, а для ввода и формирования вывода такой способ не подходит.

legolegs ★★★★★
()

Валиден ли такой синтаксис для присваивания строк?

Ты тут не строки присваиваешь, а указатели.

anonymous
()

Может, лучше сначала сдать зачёт по букварю?

Led ★★★☆☆
()

Чтобы было еще понятнее, переменной str, которая есть указатель на char, присваиваются адреса двух различных строк, которые на этапе компиляции помещены в статическую память.

westtrd
()

Ну и плюс const char *str, ибо литералы по стандарту немутабельны. Из-за этого какие-то компиляторы могут класть строковые литералы в .code, а оно бывает read-only.

arturpub ★★
()
Ответ на: комментарий от jeuta

Спасибо за признание, если не шутите, конечно :)

Насчет раздела по С не уверен что он так уж необходим

westtrd
()
Ответ на: комментарий от legolegs

> Здесь в специальную область твоего бинарника (секция .data) будут зашиты строки «first» и «second».

Нормальные тулы помещают литеральные строки в секцию .rodata

6 байт в первом случае и 7 во втором доступны для изменения

В твоем говнокомпиляторе может быть и можно изменять, а в нормальных нельзя

anonymous
()
Ответ на: комментарий от anonymous

Чем какашками кидаться, лучше узнай, что есть разные языки и разные стандарты языков. В си код топикстартера валиден. Мне лично это не нравится, поэтому я предпочитаю си++.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

> Чем какашками кидаться, лучше узнай, что есть разные языки и разные стандарты языков.

ТС явно указал язык программирования - Си

В си код топикстартера валиден. Мне лично это не нравится, поэтому я предпочитаю си++.

Код топикстартера валиден в Си ровно настолько насколько и в С++ (запись по такому указателю является ошибкой). Более того, ты наверное сейчас разочоруешься, если узнаешь что в C++ абсолютно легально присваивание строкового литерала переменной типа char*.

anonymous
()
Ответ на: комментарий от anonymous

«stroka» в C++ он имеет тип const char [7]; -> const char * естественно то что он автоматом присваивается char * это просто хак и писать по такому указателю нельзя но вот в C все намного интереснее в C char [7]; -> char * можно или нет писать по такому указателю зависит от реализации это и не запрещено и не разрешено просто implementation defined как видим ситуация сильно отличается между C и C++ неверно гоаорит что в си это запрещено это просто неправильно так говорить запрещено только в C++ как и многое другое в этом драконовском по сравнению с C языке

anonymous
()
Ответ на: комментарий от anonymous
vadim@host3:~/tmp$ cat tmp.c
int main(void) {
  char* str = "qwerty";
}
vadim@host3:~/tmp$ gcc -S -x c tmp.c
vadim@host3:~/tmp$ gcc -S -x c++ tmp.c
tmp.c: In function ‘int main()’:
tmp.c:2: предупреждение: deprecated conversion from string constant to ‘char*’
vadim@host3:~/tmp$ 
geekless ★★
()
Ответ на: комментарий от geekless

ну вот мы и видим implementation defined а вот например MS C/C++ в режиме C

int main(){
	char* str = "qwerty";
	*str = 'k';
	puts(str);
}
 
	*str = 'k';
0040100B 8B 45 FC         mov         eax,dword ptr [str] 
0040100E C6 00 6B         mov         byte ptr [eax],6Bh  <-- тут все гладко
Deleting intermediate and output files for project 'xxx', configuration 'Release|Win32'
Compiling...
main.c
Linking...
Generating code
Finished generating code
Build Time 0:00
xxx - 0 error(s), 0 warning(s)
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
но этот же компилятор в режиме C++
int main(){
	char* str = "qwerty";
	*str = 'k';
	puts(str);
}
 0040100B 8B 45 FC         mov         eax,dword ptr [str] 
0040100E C6 00 6B         mov         byte ptr [eax],6Bh  <-- ошибка
Unhandled exception at 0x0040100e in xxx.exe: 0xC0000005: Access violation writing location 0x0040202c.
что происходит для C++ он тупо ставит защиту на память где лежит строка только для чтения и ты падаеш если пишеш туда хотя разрешает неявно приводить к char * а вот для C он записывает в память с атрибутами RW и разрешает писать читать в/из нее другой вопрос что нормальный прогер из чувства чистоплотности этого делать не станет

anonymous
()
Ответ на: комментарий от anonymous

> можно или нет писать по такому указателю зависит от реализации это и не запрещено и не разрешено просто implementation defined

Из стандарта:

6.4.5 String literals

(6) ... If the program attempts to modify such an array, the behavior is undefined.

А теперь покажите где здесь говорится об «implementation defined»?

anonymous
()
Ответ на: комментарий от anonymous

а вот для C он записывает в память с атрибутами RW и разрешает писать читать в/из нее

Тебя мама не учила, что обманывать не хорошо:

CONST	SEGMENT
??_C@_06DEKFEHO@qwerty?$AA@ DB 'qwerty', 00H		; `string'
CONST	ENDS

...

; 3    :    *str = 'k';
	mov	eax, DWORD PTR _str$[ebp]
	mov	BYTE PTR [eax], 107			; 0000006bH

anonymous
()
Ответ на: комментарий от anonymous

сейчас посмотрел стандарт C99 да в C99 сделали undefined behavior -> стало запрещено в C89 было implementation defined -> в C89 зависело от реализации ну а MS C/C++ просто когда в режиме C работает по стандарту C89 он не поддерживает C99 извиняюсь если кого то ввел в заблуждение каюсь

anonymous
()
Ответ на: комментарий от anonymous

вы смотрите при записи нет исключения глупо с вашей стороны утверждать обратное основываясь на записи в тестовом файле против прогона под отладчиком на уровне ассемблера а когда прогоняеш C++ падает на записи

anonymous
()
Ответ на: комментарий от anonymous

вы смотрите при записи нет исключения глупо с вашей стороны утверждать обратное основываясь на записи в тестовом файле против прогона под отладчиком на уровне ассемблера

Проверил на VS2008:

a) Debug версия падает с исключением б) Release версия не падает, но результат выглядит «странным» - на экран выводится qwerty вместо kwerty. Ассемблерный выход объясняет такое поведение (студия просто выкинула присваивание *str = 'k'):

CONST	SEGMENT
??_C@_06DEKFEHO@qwerty?$AA@ DB 'qwerty', 00H		; `string'
CONST	ENDS

_TEXT	SEGMENT
_main	PROC						; COMDAT

; 2    :    char* str = "qwerty";
; 3    :    *str = 'k';
; 4    :    puts(str);

	push	OFFSET ??_C@_06DEKFEHO@qwerty?$AA@
	call	_puts
	add	esp, 4

; 5    : }

	xor	eax, eax
	ret	0
_main	ENDP
_TEXT	ENDS

anonymous
()
Ответ на: комментарий от anonymous

> Теперь проверил на VS2005 - результат аналогичен VS2008.

До кучи проверил на gcc:

a) при отключенной оптимизации (-O0) получаем «Segmentation fault»

б) при включенной оптимизации (-O1, -O2, -O3) на экране получаем «qwerty»

anonymous
()
Ответ на: комментарий от anonymous

> а вот для C он записывает в память с атрибутами RW и разрешает писать читать в/из нее другой

Как видим ни в VS2005, ни в VS2008 такого поведения не наблюдается. В какой же версии VS вы это наблюдаете?

anonymous
()
Ответ на: комментарий от anonymous

> в C89 было implementation defined

Приведите пожалуйста здесь соответствующую цитату из стандарта C89, а то я вам не очень верю.

anonymous
()
Ответ на: комментарий от anonymous

в C89 было implementation defined

Приведите пожалуйста здесь соответствующую цитату из стандарта C89, а то я вам не очень верю.

с чего я помнил что зависит от реализации бред какой то .. в C89 это тоже было undefined behavior вот из K&R

Существует важное различие между следующими определениями:

char amessage[] = «now is the time»; /* массив */ char *pmessage = «now is the time»; /* указатель */

amessage - это массив, имеющий такой объем, что в нем как раз помещается указанная последовательность символов и '\0'. Отдельные символы внутри массива могут изменяться, но amessage всегда указывает на одно и то же место памяти. В противоположность ему pmessage есть указатель, инициализированный так, чтобы указывать на строковую константу. А значение указателя можно изменить, и тогда последний будет указывать на что-либо другое. Кроме того, результат будет неопределен, если вы попытаетесь изменить содержимое константы.

а вот для C он записывает в память с атрибутами RW и разрешает писать читать в/из нее другой

Как видим ни в VS2005, ни в VS2008 такого поведения не наблюдается. В какой же версии VS вы это наблюдаете?

MS C/C++ 2008

в release /O1 выводит kqwert как и должно быть
в release /O2 выводит qwerty как и у вас выкинул приваивание
в debug просто вываливается в отладчик
вообщем ведет себя крайне непредсказуемо я проверял только на release /O1 из чего и сделал ложные выводы

anonymous
()
Ответ на: комментарий от anonymous

> в release /O1 выводит kqwert как и должно быть

Во-первых, если бы литеральную строку можно было изменить, то результат был бы kwerty, а не как у вас kqwert.

Во-вторых, /O1, так же как и /O2, выводит qwerty.

anonymous
()
Ответ на: комментарий от anonymous

Проверил в VC6. В Debug литералы кладутся в сегмент CONST:

CONST	SEGMENT
??_C@_06JPJA@qwerty?$AA@ DB 'qwerty', 00H		; `string'
CONST	ENDS

а вот в Release литералы кладутся в сегмент _DATA:

_DATA	SEGMENT
??_C@_06JPJA@qwerty?$AA@ DB 'qwerty', 00H		; `string'
_DATA	ENDS

При этом справедливости ради надо заметить, что такое же поведение наблюдается и для C++.

anonymous
()
Ответ на: комментарий от anonymous

в release /O1 выводит kqwert как и должно быть

Во-первых, если бы литеральную строку можно было изменить, то результат был бы kwerty, а не как у вас kqwert.

Во-вторых, /O1, так же как и /O2, выводит qwerty.

нет не так kqwert это опечатка неужели не очевидно ? еще раз для непонятливых MS C 2008 release

/O1 выводит kwerty
/O2 выводит qwerty

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.