Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Строки в С\С++ (http://forum.oszone.net/showthread.php?t=30189)

netmax 05-04-2003 23:13 207695

При программировании строк в Visual C++ и под Linux я заметил такую штуку:
Если обявить строку как
char* pch = "Some_string" то операция
*pch = 'A' приводит к ошибкам выполнения. В Linux пишет Segmentation Fail т.е. ошибка обращения к памяти. И в тоже время объявление
char pch[] = "Some_string"
и операция
*pch = 'A' отрабатывает нормально без всяких глюков. Может кто знает где здесь собака зарыта. Только если можно поподробнее. Я думаю это как-то связано с защитой памяти в ОС или что-то в этом духе

Crew 06-04-2003 01:43 207696

char pch[] = "Some_string"
мне кажется понятно, почему работает, по той же причине, что и char pch[10] = "Some_string" , только цифры компилятор сам подставляет, т.е. память он выделяет все-таки под переменную,
а
char* pch = "Some_string" память не выделяет, и хранит значение в другом месте. И хочу заметить что не будет доступно и pch[3]='A' вызовет ту же ошибку.
Или вопрос был где именно хранится значение переменной?

ruslandh 06-04-2003 09:37 207697

для строки *char - надо так:

char *point,*psh="Some_string";

....
point=psh;
point='A'
pch='B'
...
point=pch+1
point='A'


А что по вашему должна делать команда
*pch='A' ? - записать по адресу, указанному в pch значение 'A' ? Т.е по адресу 0x00129 записать 0x100 ?




ivank 06-04-2003 13:45 207698

Вообще-то в C++ строковые литералы имеют тип const char * и char * s = "string literal" оставили только для совместимости со старым кодом. Правильнее будет const char * s = "string literal".

Добавлено:

Соответственно объявление char s[] = "string" это объявление обычного массива.

shurikan 07-04-2003 01:54 207699

ruslandh
Вынужден разочаровать, но ты не прав.
Цитата:

для строки *char - надо так:

char *point,*psh="Some_string";

....
point=psh;
point='A'
pch='B'
...
point=pch+1
point='A'


А что по вашему должна делать команда
*pch='A' ? - записать по адресу, указанному в pch значение 'A' ? Т.е по адресу 0x00129 записать 0x100 ?
point = pch; //Правомерно, оба указателя ссылаются на один и тот же адрес.
point = pch+1; //Правомерно, point указывает на второй символ строки.

point = 'A'; //
pch='B'; * * // А вот так нельзя. Указателю нельзя присваивать неадресные значения.

Надо именно:
*point = 'A';
*pch = 'B'; //Т.е. по адресам, куда они указывают записываются значения того типа, указателями на которые они являются.

Это эквивалентно записи:
point[0] = 'A';
pch[0] = 'B';

Другое дело, что как сказал ivank, в первом случае была попытка через указатель изменить значение константы.


[s]Исправлено: shurikan, 1:56 7-04-2003[/s]

Vaulter 07-04-2003 02:38 207700

ребята, вас память выделять не учили?

shurikan 07-04-2003 07:09 207701

Vaulter
А к данному случаю это отношения не имеет. Под литерал память выделяет компилятор и под указатель тоже. А потом в ячейку указателя кладёт адрес литерала... Извини...

vasketsov 07-04-2003 08:23 207702

shurikan
Ты не совсем прав, что это не имеет отношения к выделению памяти.

char* pch = "Some_string"

В этом случае в стеке создается pch, в сегменте данных - КОНСТАНТА "Some_string", и pch инициализируется адресом константы. Менять значение указателя можно без проблем, а вот в "Some_string" писать нельзя именно из-за того, что под нее так выделена память.

char pch[] = "Some_string"

На стеке создается массив pch[] и его элементы инициализируются значениями из строковой константы (в принципе, компилятор мог бы, как и в прошлый раз, выделить место с сегменте данных, а после создания pch[] написать какой-нибудь memcpy, если эта константа много где встречается и так будет компактнее). То есть, "Some_string" как таковой может и вовсе не существовать. Естественно, в pch[] можно писать.


Цитата:

Указателю нельзя присваивать неадресные значения.
pchar x = "x1";
x = "x2";
x = "x3";
и т.д.

shurikan 07-04-2003 08:39 207703

vasketsov
Верно для локальных переменных, а если они глобальные?
Для них память выделяется в сегменте данных.
А результат выражения "x2" - есть адрес, так что это адресное выражение.

vasketsov 07-04-2003 21:41 207704

shurikan
Цитата:

Для них память выделяется в сегменте данных.
В данном случае вероятнее всего, но теоретически - может и в куче. Например, если нетривиальный конструктор.

Цитата:

А результат выражения "x2" - есть адрес
Опять же, если я в функции напишу

WCHAR x[] = L"sss";

то L"sss" - никакое не "адресное выражение", а параметр инициализации x[]. То есть, у L"sss" может вовсе не быть адреса.

Crew 07-04-2003 23:12 207705

Раз вы так все хорошо со строками ориентируетесь, то объясните такую вещь:
Visual C++
С помощью CListCtrl создаю свой собственный класс списка (использую пример, естественно), где каждый элемент списка представляет собой кнопку. Для отрисовки строк используется процедура
Код:

DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
...
dc.DrawTedxt((LPCTSTR)lpDrawItemStruct->itemData,rect,
 * *DT_LEFT | DT_WORKBREAK);
dc.Detach();
}
//Строки добавляем сюда

m_List.AddString(_T("Some String"));

но если строки добавлять не константы, а так
CString str="Some String";
m_List.AddString(str); *
на экране галиматья,
т.е. любое использование локальных для данной процедуры переменных приводит к тому, что данные не отображаются.
если использовать
char str=new char[20];
...
то все отображается, но при попытке
delete str[] все накрывается, потому что список использует эти значения
Итог такой работает только если самостоятельно плодить утечку памяти, где ж это все хранится?

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


[s]Исправлено: Crew, 23:14 7-04-2003[/s]

netmax 08-04-2003 14:32 207706

Crew
> Или вопрос был где именно хранится значение переменной?
Да вопрос как раз в этом то и заключался, то что будут ошибки так это я сам знаю. Почему?

ruslandh
А что по вашему должна делать команда
*pch='A' ? - записать по адресу, указанному в pch значение 'A' ? Т.е по адресу 0x00129 записать 0x100 ?
Фишка в том что *pch - разименование указателя. Т.е. 'A' будет записано в ячейку, адрес которой содержится в pch.
Vaulter
Так если объявление char* pch = "Some_String"; не выделяет память то куда же она будет записана. Ведь вывести я ее могу?


Vaulter 08-04-2003 22:23 207707

VBMUSTDIE
освободить то ее надо потом )
хотя это будут Memory leaks

netmax 08-04-2003 23:09 207708

Знаете в чем еще фишка. Если сделать Active Confoguration не Debug а Release все работает. Вообще какая-то фигня
:(

Vaulter 09-04-2003 02:01 207709

VBMUSTDIE
в Debug режиме, VC запускает прогу в своей куче.

vasketsov 09-04-2003 02:40 207710

Crew
Меня очень ломает разбираться в MFC, потому давайте вместе.

Какие прототипы есть у вызова AddString? То есть, что можно передать этой функции (без учета приведения типа непосредственно внутри скобочек)?

Есть мысль, что AddString просто берет указатель на строку, если у тебя строчка в сегменте данных, то работать будет, если через CString - то ты отдаешь в лист указатель на CString-овую строку - при прибитии его тебе программа вполне обосновано дает по рукам, так как адрес, сохраненный в твоем листе, уже толком никуда не указывает. Аналогично и с массивом, пока не убьешь его, все будет работать.

Vaulter
Цитата:

в Debug режиме, VC запускает прогу в своей куче
Не понял нифига.

Я собрал проект как Debug.
Закрыл к чертям MSVC.
Запустил собранный проект.
Откуда он возьмет кучу MSVC?

shurikan 09-04-2003 12:51 207711

Vaulter
Когда я выше упоминал, что к выделению памяти этот случай не имеет отношения, я имел ввиду динамическое выделение памяти. Слово "динамическое" я опустил, за что справедливо получил по шапке от vasketsov-а. Так вот, если память выделяется динамически с помощью всяких там new, malloc и им подобных, то память нужно освобождать, а если (как написано)
Код:

char *pch = "Some string";
, то за это отвечает компилятор и об освобождении беспокоиться не нужно.

Vaulter 09-04-2003 14:13 207712

VBMUSTDIE

скажу всетаки секрет один:
char* a="something"; "а" вообще то будет распознана компилятором как строковая константа:
const char a[9]; поэтому и нельзя a[4]='a';

а вот
char a[]="something"; можно. потому как будет "а" будет как
char a[9];

Crew 09-04-2003 23:51 207713

vasketsov
AddString может принимать как CString, так и char *
функция прорисовки в любом случае получает только DWORD, вытаскивать что там такое моя задача (несложная) Я трейсером смотрел все она передает, но только почему-то (трейсер не улавливает) перерисовывает эти данные не один раз а три.
Но ладно бы так как я делаю, но я удалить не могу эти строки (которые создаю), иначе на экран выводится
"ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ"
причем удалить не могу ни до, ни после отрисовки, потому-что она еще раз вызывается. и если я удаляю после AddString, оно понятно почему накрывается.
А если в DrawItem(...)

Код:

* *const char*s=_T(LPCTSTR(lpDrawItemStruct->itemData));
 * *dc.DrawText(s), rect,
 * *DT_LEFT | DT_WORDBREAK);
 * *dc.Detach();
 * *delete s[];

Одно из последних "извращений" приводящих к краху
Vaulter
Цитирую из книги "Программирование на VC++ 6.0" серия для профессионалов
Чтобы константы гарантированно хранились вместе с программой, придется немного поработать. Во превых, обратите внимание на строковые константы, которыми часто изобилуют программы. Может, Вы решили, что они будут данными "только для чтения"? Тогда попробуйте угадать еще раз. Действительно, поскольку Вы имеете право написать что-нибудь вроде
Код:

char *pch ="test";
*pch='x';

"test" не может быть константой. Чтобы строка стала константой, ее надо соответствующим образом *объявить и инициализировать, например, так:

Код:

const char g_pch[]="test";
Теперь g_pch хранится вместе с кодом. Но где именно?Чтобы ответить на этот вопрос, надо знать о секциях данных (data session), генерируемых компоновщиком Visual С++.
...
Имя * * *Тип * * * * * Доступ * * * * * * * * * * Содержимое
.text * * код * * * * * *только чтение * * * * *код программы
.rdata * данные * * только чтение * * * * *инициализированные константы
.data * *данные * * *чтение и запись * * *инициализированные данные (не кон)
.bss * * данные * * * чтение и запись * * неинициализ данные (не кон)

Секция .rdata - часть EXE файла, именно сюда компоновщик поместит переменную g_pch.

Из всего вышесказанного становится непонятно почему не работает код, ставшей причиной этого топика :)


[s]Исправлено: Crew, 0:04 10-04-2003[/s]

vasketsov 10-04-2003 09:02 207714

Цитата:

Из всего вышесказанного становится непонятно почему не работает код, ставшей причиной этого топика
Потому что это лажа.

Пример 1.
Цитата:

char *pch ="test"; *pch='x';
"test" не может быть константой.
В C/C++ константность - свойство не данных, а переменных. Две переменные, одна константная, другая нет, могут ссылаться на одно и то же "test", это никак взаимно не ограничивает операции с ними. Потому речь о константности "test" вести абсолютно бессмысленно.

Пример 2.
Цитата:

const char g_pch[]="test";
Секция .rdata - часть EXE файла, именно сюда компоновщик поместит переменную g_pch
Опаньки!
Код:

void x(void)
{
  const xx[]="test";
}

Про то, что xx[] на стеке будет, можно даже не говорить. Если нет - стековый параметр суется в секцию данных - это просто ужас, такие компиляторы если и есть в природе, они абсолютно не жизнеспособны.

А вот "test" может быть как в стеке (прямо здесь же, зачем ее иметь в других местах?), а может быть и в секции данных (если "test" много и компилятор решил заняться оптимизацией).

Crew
Попробуй сделать так

void addx(идентификация листа, напр., указатель на него)
{
 char x[8] = "йцукен";
 x[0]='q'; //чтоб не оптимизировалось
 твой Add(x)
}
[/code]

если вылетит - придется либо глобальные строчки использовать, либо руками память выделять/освобождать (освобождать можно при наступлении события удаления элемента, вроде там такое есть, по крайней мере винда такое сообщение шлет).

Crew 10-04-2003 22:52 207715

Цитата:

Потому что это лажа.
Вот и читай после этого умные книжки
vasketsov
Преложенный код вывел
%МB_-ьd

при добавлении слова static, заработало, но в цикле использовать переменную не могу,
потому что
Код:

static char x[8]="йцукен";
m_List.AddString(x);
strcpy(x,"2йцукен");
m_List.AddString(x);

выведет на экран два раза
2йцукен
а заранее предсказать количество строк в списке я не могу, не делать же массив глобальных переменных?

Я правильно понял идею с глобальными пременными?
Цитата:

если вылетит - придется либо глобальные строчки использовать, либо руками память выделять/освобождать
я пытаюсь понять где это сделать руками (с радостью бы сделал),но пока не знаю где именно он меня не пошлют с моими попытками высвободить память:(

glassMonk 11-04-2003 03:15 207716

Так что я могу положить сколь угодно большое число в
char *point="asdfasdfsad....n"
это както небезопасно получается.
Меня почемуто это больше всего тревожит.
А не обрашение к ниму.

[s]Исправлено: glassMonk, 3:25 11-04-2003[/s]

vasketsov 11-04-2003 07:43 207717

Crew
Если AddString действительно может принимать pchar, можешь сделать так, вроде противопоказаний нет:

Цитата:

//глобально
#define TXT1 "Text1"
#define TXT2 "Text2"
#define TXT3 "Text3"


//локально
pchar p = TXT1;
m_List.AddString(p);

pchar p = TXT2;
m_List.AddString(p);

pchar p = TXT3;
m_List.AddString(p);

netmax 11-04-2003 12:44 207718

vasketsov
Цитата:

если вылетит - придется либо глобальные строчки использовать, либо руками память выделять/освобождать (освобождать можно при наступлении события удаления элемента, вроде там такое есть, по крайней мере винда такое сообщение шлет).
Так получается при объявлении
char* pch = "Some_String"
память не выделяется?
Но если попробовать вести строку в ЛЮБОМ месте программы то она выведится
printf("%s\n", pch);
Как так память не выделяется а функция выводит?

Crew 11-04-2003 22:15 207719

vasketsov
Зачем мне это?. Мне строки днамически создавать надо.
а так и AddString("Some string"); прекрасно работает.
Мне это вообще зачем нужно - это достаточно большая база с вопросами, где выбрав нужный ответ, нажав на кнопку с текстом ответа, уменьшаем время, затрачиваемое на прохождение теста. Вопросы берутся из базы, в виде переменных -естественно.
VBMUSTDIE
Я так для себя упростил, что записалась эта строка куда-то в ЕХЕ, компилятор делает вид что это обычный адрес, однако разрешает только читать из самого себя и только столько, сколько при компиляции отведено, и любые попытки поменять значения воспринимает как попытки поменять сам ЕХЕ.

ivank 11-04-2003 22:40 207720

Crew
Сделай глобальный CList (или как там аналог std::list называется?) строк и заполняй его динамически по мере надобности.

Цитата:

Вот и читай после этого умные книжки
Умные читай, лажу не надо.

Цитата:

и любые попытки поменять значения воспринимает как попытки поменять сам ЕХЕ.
Не совсем так. компилятор воспринемает попытку изменить часть такой "строки" как просто замену чего-то по какому-то адресу. Другое дело, что он имеет полное право поместить строковой литерал в конcтантную область памяти, где ты по понятным причинам ничего не сможешь поменять.

vasketsov 11-04-2003 23:10 207721

VBMUSTDIE
Цитата:

Так получается при объявлении
char* pch = "Some_String"
память не выделяется?
Наиболее вероятно, что строка будет помещена в сегменте данных, а переменная - в стеке (если локальная). Считать это выделением памяти или нет - решать вам. Я же имел в виду что-нибудь типа new.

Цитата:

Но если попробовать вести строку в ЛЮБОМ месте программы то она выведится
printf("%s\n", pch);
Как так память не выделяется а функция выводит?
1) В printf передается адрес строки "%s\n", тут ситуация с памятью такая же, как и выше.
2) Если только %s, то нетрудно видеть, чтоб можно вывести этот текст без дополнительного выделения памяти даже на стеке. Выводим текст до %s, потом аргумент, потом снова текст до %s, снова аргумент, и т.д.
3) Если есть %d и т.п., то сложнее, но память может выделиться внутри printf (особенно если ей на вход отдается свой нетривиальный объект) и освободиться там же.

netmax 11-04-2003 23:31 207722

glassMonk
Цитата:

Так что я могу положить сколь угодно большое число в
char *point="asdfasdfsad....n"
это както небезопасно получается.
А почему это может быть не безопастным?


glassMonk 13-04-2003 07:23 207723

Ответ оказался в цитате, я догадался сам.

Guest 03-11-2003 18:09 207724

int len=strlen(name);
char* buffer = new char[2*sizeof(int)+len];
memcpy(buffer,&len,sizeof(int));
memcpy(buffer+sizeof(int),name,len);
memcpy(buffer+sizeof(int)+len,&age,sizeof(int));
return buffer;


почему после этого в буфере пусто?

shurikan 03-11-2003 18:57 207725

Guest
Это что, попытка превратить C-строку в Pascal-строку?
Pascal-строка (короткая) содержит длину в одном (первом) байте. А размер данных типа int зависит от ОС и компилятора - м.б. 2 байта, а м.б *- 4. В любом случае старшие байты переменной len, скорее всего нулевые. А что такое age?


Время: 07:29.

Время: 07:29.
© OSzone.net 2001-