char * и char[]
Недавно начал изучать С++ и возник такой вопрос: почему
не то же самое что и:
?
В голове появилась такая идея, что
то же самое что и
из-за того что в случае с указателем с данными массива работать нельзя.
Объясните, пожайлуста, новичку чем отличаются друг от друга 2 первые приведенные конструкции. :)
char [] - выделение памяти под массив символов с заранее известным(до компиляции) размером. Когда пишем:
Общее между указателем на единичный символ и массивом то, что и то и другое реализовано через указатели.
ЗЫ правда я говорю о С, но должно быть индентично.
В первом случае
в статичексой памяти выделяется место для строковой переменной и указатель инициализируется адресом этой строки.
Во втором случае
компилятор выделяет место под 6 символов в динамической памяти для указателя.
была уверена что это и компиляцию проходить не должно, сорри за ложную инфу.
ДА ЭТА КОНСТРУКЦИЯ ИСПОЛЬЗУЕТСЯ И КОРРЕКТНО РАБОТАЕТ С НЕЗАПАМЯТНЫХ ВРЕМЁН!!! Я УБИТ НАПОВАЛ.:confused:
Оно то может и работать и не работать.. Это ж явно небезопасный код. А работает такая конструкция только если строка не затрет другие данные, или код.. Поправьте, если ошибаюсь =)
Я бы писал либо второй вариант, либо так:
name = new char [6];
name = "Vitka\0";
.....
/// Working with variable name
...
delete [] name;
Да нет. Чем он не безопасен? Компилятор точно знает размер строки, так что такое определение вполне нормально
Почему нельзя? можно.
Просто у тебя же там константная строка.
Я бы писал либо второй вариант, либо так:
name = new char [6];
name = "Vitka\0";
.....
/// Working with variable name
...
delete [] name;
=))) Вот как раз твой вариант куда более небезопасный, плюс ведет к утечке памяти.
Вот смотри что ты делаешь, сначала выделяешь память под 6 char-ов, и присваиваешь указатель на эту память в name. Потом в name ты суешь другой указатель на строковую константу (кстати накой там '\0' в конце, когда строковые константы итак прекрасно вставляют ноль на конце) при этом указатель на выделенную тобой ранее пямять безвозвратно теряется. После этого ты пытаешь отчистить память где распологается эта строковая константа по новому указателю на него, рискуя схлопотать access violation =)
А вот вариант с константным указателем, который ты критикуешь, вполне нормален. Указатель инициализируется той строкой, и если его так же неккорректно не удалять - все будет нормально =)
Просто у тебя же там константная строка.
Вот в это я не въезжаю. :confused:
name = new char [6];
memcpy(name, "Vitka\0",6);
.....
/// Working with variable name
...
delete [] name;
[QUOTE=Бьерн Страуструп]...
Строковым литералом называется последовательность символов, заключенная в двойные
кавычки :
"Это строка"
В строковом литерале на один символ больше,чем используется при записи; он всегда заканчивается нулевым символом '\0',значение которого равно 0.Например :
sizeof("Бор") == 4
Тип строкового литерала есть "массив с надлежащим количеством константных символов";таким образом "Бор" принадлежит типу const
char[4].Строковый литерал можно присвоить переменной типа char*.Это разрешается потому что в
предыдущих определениях С и С++ типом строкового литерала был char*.Благодаря такому разрешению миллионы строк кода С и С++ остаются синтаксически корректными.Однако изменения строкового литерала через такой указатель является ошибкой :
void f()
{
char* p = "Платон";
p[4] = 'e'; // ошибка : присваивание константе;
// результат не определен
}
Такого рода ошибки,как правило,не могут быть выявлены до выполнения программы.Кроме того,различные реализации по разному относятся к нарушению этого правила.То,что строковые литералы являются константами,не только является очевидным,но и позволяет при реализации произвести значительную оптимизацию методов хранения и доступа к строковым литералам...
[/QUOTE]
Как сказано в цитате, строковый литерал - это константный массив, т.о. запись
char* name = "Vitka";
представляет собой неявное преобразование типов
char* name = const char[];
что само по себе опасно.
Как сказано все в той же цитате, это рудимент оставшийся от языка С.
Я рекомендую всегда писать так:
const char* name = "Vitka";
Что же касается разницы между записью
const char *name = "Vitka";
и
const char name[] = "Vitka";
то для программиста С++ основная разница в том, что первый вариант - это указатель, а во втором - массив. И это РАЗНЫЕ ТИПЫ. Частой ошибкой начинающих является путаницей между массивом и указателем на первый элемент. Это принципиально разные вещи, хотя тип массив неявно приводится к типу указатель.
Ну я так понял с dart Bobr-ом мы уже вовсю обсуждали const char*
Или я не прав?
name = new char [6];
memcpy(name, "Vitka\0",6);
.....
/// Working with variable name
...
delete [] name;
очередная чушь. Опять ненужный ноль в конце строки, и снова дважды выделяется память под одну и ту же строку.
Массивы очень похожи на указатели. Но отличия, все же есть.
Для элементов массива в с++ будут вызываться конструкторы при создании и деструкторы при уничтожении массива.
Потом...
пусть у нас есть одномерный массив b[x], по сути мы можем придумать ему какую-то замену посредством указателя - *(b+i*size).
Попробуем создайть двумерный массив b[x][y].
когда мы используем массив, нам не надо беспокоится, при доступе к корректному элементу, о его размерностях... а при аналогичном доступе посредством указателя - доступ к b[2][3] будет таким = *(b+2*(y+1)*size+3*size). То есть вместе с указателем вам придется таскать за собой еще и размерность массива. Так что так просто добавить квадратные скобки к указателю для организации многомерного массива ИМХО не получиться.
Вроде так.
//++ да, и еще наверно стоит упомянуть, что массив это всегда однородные объекты
та где? =)
Да, где там дважды то память выделяется? Или имеешь ввиду то, что под "Vitka\0" тоже будет выделенна память? ну так от этого никуда не деться в таком подходе. )
А вот ноль в конце и правда ненужен.
Язык С++ очень сильно завязан на типы. Менее высокоуровневые языки, например С, не сильно завязан на типы. Более высокоуровневые, например Python, осуществляют многую работу с типами автоматически и поэтому там тоже не сильно задумываешься о типах.
Язык же С++ требует от программиста аккуратного самостоятельного оперирования типами. Такая типизация языка является мощным инструментом, за что мне и нравится С++. Но как любой мощный инструмент в руках неумелого пользователя... :)
Я уже не раз говорил о типовой безопасности,- наборе элементарных правил такого оперирования. Вот почему мне так не нравится void* (потеря инф. типе).
В чем выражается такая типизация? В перегрузке функций и операторов, в шаблонах, в виртуализации и в интересных механизмах, которые появляются при использовании их совокупности. Тот кто читал Александреску, тот поймет, о чем я.
Так вот тип "указатель" - это указатель, это как стрелка на что-то указывающая. А тип "массив" - это массив, это как хранилище, коробка.
И это разные типы с разной инф. Какую информацию содержит указатель? Лишь информацию о типе элемента на который он указывает и расположение этого элемента в памяти. А какую информацию содержит переменная типа "массив": информацию о типе элементов массива, информацию о количестве элементов в массиве и информацию о расположении этих элементов в памяти.
Из этого следует, что массив имеет как минимум на одну инф. составляющую больше, чем указатель,- информация о количестве элементов, и эта составляющая безвозвратно теряется при неявном преобразовании массива к указателю.
Кроме того, как и для любого др. типа мы можем узнать место, которое занимает данная переменная в памяти. Для указателя это будет обычно одно число для любого указываемого типа, а для массива это будет суммарный размер всех элементов этого массива.
Надеюсь, я понятно объяснил... :)
А то все слишком абстрактно получилось, но могу показать примерами. Просто надо много примеров приводить, так что буду приводить по мере поступления вопросов.
Возможен ли полиморфизм для элементов хранящихся в массиве?
Я так понимаю что возможен? Ведь массив хранит информацию о типе.
Но тогда получатется неоднородный массив? Или нет?
Можно осветить этот вопрос.
А вот ноль в конце и правда ненужен.
Ну деться то можно - заполняя посимвольно массив. Но это, имхо, быцтво. =)
А вместо заполнения можно сразу и инициализировать им, как уже обсуждалось.
Возможен ли полиморфизм для элементов хранящихся в массиве?
Я так понимаю что возможен? Ведь массив хранит информацию о типе.
Но тогда получатется неоднородный массив? Или нет?
Можно осветить этот вопрос.
Прошу обратить внимание я сказал, что массив содержит "информацию о типе элементов массива", а не о типах, т.е. все элементы массива однотипные.
Но! Вспомним, что такое полиморфизм (Wikipedia):
Полиморфи́зм (в языках программирования) — взаимозаменяемость объектов с одинаковым интерфейсом.
Т.о. достаточно хранить в массиве объекты с однотипным интерфейсом.
Это реализуется в С++ с помощью указателей или ссылок.
Т.е. мы должны хранить в массиве указатели или ссылки на базовый класс, ссылающиеся на экземпляры производных классов.
Возможен ли полиморфизм для элементов хранящихся в массиве?
Я так понимаю что возможен? Ведь массив хранит информацию о типе.
Да возможен. А почему неоднородный - в итоге же там хранится указатель на базовый тип. А уже исходя из этого указателя и механизм полиморфизма проявляется.
Да, именно это и имею в виду. А почему никуда не деться? Написать по-человечески const char *szName="Vitka" и всё. Зачем что-то куда-то копировать ещё?
Насколько я понимаю константа "Vitka" зашивается в екзешник, а при виполнении операции = она всеровно копируется в память по адресу szName. По любому двох екземплярах получится. Или я не прав ?
Я, конечно, не специалист в этих вещах (да и вообще в программировании, прямо скажем :-) ), но сколько я понимаю, строковая переменная должна попасть в область инициализированных данных, которые в самом начале выполнения программы будут считаны операционной системой с диска в определённую область оперативной памяти, так что никакого дублирования в этом случае не будет.
Для того, что-бы иметь возможность изменить значение по адресу szName.
Если ты напишешь char *szName="Vitka", то в одном. Компилятор в памяти выделит память под массив символов, а потом присвоит его адрес указателю szName. Но, это будет массив констант. Если писать как я приводил - то в памяти строка будет 2 раза. Один раз по адресу, который грубо говоря выберет компилятор, а второй по адресу в переменной name.
Я чего-то не догоняю? Где автор темы писал, что ему нужно что-то менять в строке с именем?
из-за того что в случае с указателем с данными массива работать нельзя.
{
char* pName = "Suxx1";
const char* pName2 = "Suxx2";
const char* const pName3 = "Suxx3";
char pName4[] = "Suxx4";
const char pName5[] = "Suxx5";
return 0;
}
.text:004113AF mov [ebp+var_18], offset aSuxx2 ; "Suxx2"
.text:004113B6 mov [ebp+var_24], offset aSuxx3 ; "Suxx3"
.text:004113BD mov eax, dword ptr ds:aSuxx_0 ; "Suxx"
.text:004113C2 mov [ebp+var_34], eax
.text:004113C5 mov cx, word ptr ds:a4 ; "4"
.text:004113CC mov [ebp+var_30], cx
.text:004113D0 mov eax, dword ptr ds:aSuxx ; "Suxx"
.text:004113D5 mov [ebp+var_44], eax
.text:004113D8 mov cx, word ptr ds:a5 ; "5"
.text:004113DF mov [ebp+var_40], cx
...
.rdata:0041563C aSuxx db 'Suxx' ; DATA XREF: main+50r
.rdata:00415640 a5 db '5',0 ; DATA XREF: main+58r
.rdata:00415642 align 4
.rdata:00415644 aSuxx_0 db 'Suxx' ; DATA XREF: main+3Dr
.rdata:00415648 a4 db '4',0 ; DATA XREF: main+45r
.rdata:0041564A align 4
.rdata:0041564C aSuxx3 db 'Suxx3',0 ; DATA XREF: main+36o
.rdata:00415652 align 4
.rdata:00415654 aSuxx2 db 'Suxx2',0 ; DATA XREF: main+2Fo
.rdata:0041565A align 4
.rdata:0041565C aSuxx1 db 'Suxx1',0 ; DATA XREF: main+28o
PS: И так поступят практически все компилеры.
и
абсолютно идентичны с точки зрения компилятора, более того
всегда приводиться к
поэтому разница только в удобстве для програмиста. в обоих случаях name - указатель на первый символ строки а запись char name[] всего лишь алтернатива char *name.
и
абсолютно идентичны с точки зрения компилятора, более того
всегда приводиться к
поэтому разница только в удобстве для програмиста.
Ты предыдущий пост смотрел ? Или скажеш что Ramon етот асмовский код руками настучал ? :)
Массив аналогичен указателю во всем, кроме константности.
Запись
char name[] = "Vitka"
приводит к созданию константного указателя, т. е. указателя, который нельзя "перевести" на другой объект (строку, в данном случае).
Запись же
char *name = "Vitka"
приводит к созданию указателя, который изначально указывает на строку "Vitka", но этому указателю можно назначить другой адрес, и он станет указывать на другую строку.
Кстати, сама строка в обоих случаях неконстантна, т. е. можно изменять ее содержимое.
Просьба не путать константность содержимого строки с константностью указателя на нее.
Пример.
name = "Bulat"; // Нормально
char name1[] = "Vitka";
name1 = "Bulat"; // Ошибка компиляции
Массив аналогичен указателю во всем, кроме константности.
абсолютно идентичны с точки зрения компилятора, более того
поэтому разница только в удобстве для програмиста. в обоих случаях name - указатель на первый символ строки а запись char name[] всего лишь алтернатива char *name.
А вы мой пост почитайте и подумайте: во всем ли и аналогичен ли вообще, идентичны записи или нет.
Почитал, в принципе со всем согласен, за одним маленьким исключением. Не массив как тип хранит информацию о своем размере, а компилятор, для контроля границ на этапе компиляции, там где это возможно. Если бы все, что ты говоришь, было бы правдой то массивы в c/c++ контролировали бы границы при исполнении. Приводить ассемблерный код здесь все таки не стоит, он будет слишком разный у разных компиляторов, на разных уровнях оптимизации.
Значит вы ничего не поняли. Посмотрите еще раз и подумайте.
PS: А код, генерируемый практически любым компилером останется примерно таким, как приведенный выше, при любых уровнях оптимизациях, не считая случая выкидывания неиспользуемого кода и данных, с отличиями по используемым регистрам и в 2х случаях из приведенных 5и будут отличия генерируемого кода при иной длине строки. Так что "слишком" это сильно сказано. Не верите - проверьте.
М-да...
Нифига то ты не понял.
Что значит "хранит" информацию о своем размере?
При чем тут размер массива и его границы?
Компилятор контролирует границы? Это как?
При чем тут вообще компилятор? Я говорю о языке программирования.
Опять же, при чем тут контроль границ при исполнении?
При чем тут вообще время исполнения?
Во-во! Первая здравая мысль в теме. :-)
[QUOTE=Green]Нифига то ты не понял.
Что значит "хранит" информацию о своем размере?
При чем тут размер массива и его границы?
Компилятор контролирует границы? Это как?
При чем тут вообще компилятор? Я говорю о языке программирования.
Опять же, при чем тут контроль границ при исполнении?
При чем тут вообще время исполнения?[/QUOTE]
А ведь DaemonDZK абсолютно прав. То, что массив хранит информацию о количестве элементов в нём, в языках, производных от С, имеет очень маленькое значение. Ошибка "out of bounds" выдаётся лишь в случае индекса-константы. Все мы хорошо знаем, что чаще для этого используется переменная, значение которой известно лишь во время исполнения, о чём совершенно справедливо говорит DaemonDZK. Чего ты, Green, брызгаешь слюной, совершенно не понятно.