Показать полную графическую версию : Строки в С\С++
При программировании строк в Visual C++ и под Linux я заметил такую штуку:
Если обявить строку как
char* pch = "Some_string" то операция
*pch = 'A' приводит к ошибкам выполнения. В Linux пишет Segmentation Fail т.е. ошибка обращения к памяти. И в тоже время объявление
char pch[] = "Some_string"
и операция
*pch = 'A' отрабатывает нормально без всяких глюков. Может кто знает где здесь собака зарыта. Только если можно поподробнее. Я думаю это как-то связано с защитой памяти в ОС или что-то в этом духе
char pch[] = "Some_string"
мне кажется понятно, почему работает, по той же причине, что и char pch[10] = "Some_string" , только цифры компилятор сам подставляет, т.е. память он выделяет все-таки под переменную,
а
char* pch = "Some_string" память не выделяет, и хранит значение в другом месте. И хочу заметить что не будет доступно и pch[3]='A' вызовет ту же ошибку.
Или вопрос был где именно хранится значение переменной?
ruslandh
06-04-2003, 09:37
для строки *char - надо так:
char *point,*psh="Some_string";
....
point=psh;
point='A'
pch='B'
...
point=pch+1
point='A'
А что по вашему должна делать команда
*pch='A' ? - записать по адресу, указанному в pch значение 'A' ? Т.е по адресу 0x00129 записать 0x100 ?
Вообще-то в C++ строковые литералы имеют тип const char * и char * s = "string literal" оставили только для совместимости со старым кодом. Правильнее будет const char * s = "string literal".
Добавлено:
Соответственно объявление char s[] = "string" это объявление обычного массива.
shurikan
07-04-2003, 01:54
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, в первом случае была попытка через указатель изменить значение константы.
Исправлено: shurikan, 1:56 7-04-2003
ребята, вас память выделять не учили?
shurikan
07-04-2003, 07:09
Vaulter
А к данному случаю это отношения не имеет. Под литерал память выделяет компилятор и под указатель тоже. А потом в ячейку указателя кладёт адрес литерала... Извини...
vasketsov
07-04-2003, 08:23
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
vasketsov
Верно для локальных переменных, а если они глобальные?
Для них память выделяется в сегменте данных.
А результат выражения "x2" - есть адрес, так что это адресное выражение.
vasketsov
07-04-2003, 21:41
shurikan
Для них память выделяется в сегменте данных.
В данном случае вероятнее всего, но теоретически - может и в куче. Например, если нетривиальный конструктор.
А результат выражения "x2" - есть адрес
Опять же, если я в функции напишу
WCHAR x[] = L"sss";
то L"sss" - никакое не "адресное выражение", а параметр инициализации x[]. То есть, у L"sss" может вовсе не быть адреса.
Раз вы так все хорошо со строками ориентируетесь, то объясните такую вещь:
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 использует не адрес а число, адрес содержащее, и в чем загвоздка не пойму
Исправлено: Crew, 23:14 7-04-2003
Crew
> Или вопрос был где именно хранится значение переменной?
Да вопрос как раз в этом то и заключался, то что будут ошибки так это я сам знаю. Почему?
ruslandh
А что по вашему должна делать команда
*pch='A' ? - записать по адресу, указанному в pch значение 'A' ? Т.е по адресу 0x00129 записать 0x100 ?
Фишка в том что *pch - разименование указателя. Т.е. 'A' будет записано в ячейку, адрес которой содержится в pch.
Vaulter
Так если объявление char* pch = "Some_String"; не выделяет память то куда же она будет записана. Ведь вывести я ее могу?
VBMUSTDIE
освободить то ее надо потом )
хотя это будут Memory leaks
Знаете в чем еще фишка. Если сделать Active Confoguration не Debug а Release все работает. Вообще какая-то фигня
:(
VBMUSTDIE
в Debug режиме, VC запускает прогу в своей куче.
vasketsov
09-04-2003, 02:40
Crew
Меня очень ломает разбираться в MFC, потому давайте вместе.
Какие прототипы есть у вызова AddString? То есть, что можно передать этой функции (без учета приведения типа непосредственно внутри скобочек)?
Есть мысль, что AddString просто берет указатель на строку, если у тебя строчка в сегменте данных, то работать будет, если через CString - то ты отдаешь в лист указатель на CString-овую строку - при прибитии его тебе программа вполне обосновано дает по рукам, так как адрес, сохраненный в твоем листе, уже толком никуда не указывает. Аналогично и с массивом, пока не убьешь его, все будет работать.
Vaulter
в Debug режиме, VC запускает прогу в своей куче
Не понял нифига.
Я собрал проект как Debug.
Закрыл к чертям MSVC.
Запустил собранный проект.
Откуда он возьмет кучу MSVC?
shurikan
09-04-2003, 12:51
Vaulter
Когда я выше упоминал, что к выделению памяти этот случай не имеет отношения, я имел ввиду динамическое выделение памяти. Слово "динамическое" я опустил, за что справедливо получил по шапке от vasketsov-а. Так вот, если память выделяется динамически с помощью всяких там new, malloc и им подобных, то память нужно освобождать, а если (как написано) char *pch = "Some string";, то за это отвечает компилятор и об освобождении беспокоиться не нужно.
VBMUSTDIE
скажу всетаки секрет один:
char* a="something"; "а" вообще то будет распознана компилятором как строковая константа:
const char a[9]; поэтому и нельзя a[4]='a';
а вот
char a[]="something"; можно. потому как будет "а" будет как
char a[9];
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[];
Одно из последних "извращений" приводящих к краху
[b]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.
Из всего вышесказанного становится непонятно почему не работает код, ставшей причиной этого топика :)
Исправлено: [b]Crew, 0:04 10-04-2003
vasketsov
10-04-2003, 09:02
Из всего вышесказанного становится непонятно почему не работает код, ставшей причиной этого топика
Потому что это лажа.
Пример 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]
если вылетит - придется либо глобальные строчки использовать, либо руками память выделять/освобождать (освобождать можно при наступлении события удаления элемента, вроде там такое есть, по крайней мере винда такое сообщение шлет).
© OSzone.net 2001-2012
vBulletin v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.