Здравствуйте, INsideR, Вы писали:
INR>Есть следующий код:
INR>char* s = "Test"; INR>s[2] = 'b';
INR>У Страуструпа сказано, что это не верно, потому что char * указывает на строку с константными символами, надо писать так: INR>char a[] = "Test";
INR>У меня в программе есть пару мест с char*, надо ли их переделывать?
Да надо переделать так как советует Страуструп
Линкер может поместить эту строку в секцию с аттрибутом Readonly и запись в эту строку s[2] = 'b'; вызовет Access violation
типа память не может быть written
Здравствуйте, INsideR, Вы писали:
INR>Есть следующий код:
INR>char* s = "Test"; INR>s[2] = 'b';
INR>У Страуструпа сказано, что это не верно, потому что char * указывает на строку с константными символами, надо писать так: INR>char a[] = "Test";
Неправильно и так и этак. По-любому нужно писать const.
const char * s = "Test";
const char s[] = "Test";
А разница в том, что во втором случае НЕ выделяется память под переменную-указатель.
K>А разница в том, что во втором случае НЕ выделяется память под переменную-указатель.
Не вкурил про второй случай... Во втором случае вроде как таки резервируется кусок памяти и в него таки копируется содержимое строки... Это на случай если без const. Если с const — то более грамотные товарищи меня поправят...
Здравствуйте, INsideR, Вы писали:
INR>char* s = "Test"; INR>s[2] = 'b';
INR>У Страуструпа сказано, что это не верно, потому что char * указывает на строку с константными символами, надо писать так: INR>char a[] = "Test";
INR>У меня в программе есть пару мест с char*, надо ли их переделывать?
Создание указателя на Си-строку — некоректное объявление.
Лучше задать массив символв произвольной размерности: char a[]='Test';
Поменяй, малоли что!
С чего бы во втором случае const? Это же простое объявление массива. Просто для массивов с типо элемента char введен специфический синтаксис инициализации.
A>С чего бы во втором случае const? Это же простое объявление массива. Просто для массивов с типо элемента char введен специфический синтаксис инициализации.
Здравствуйте, INsideR, Вы писали:
INR>Есть следующий код:
INR>char* s = "Test"; INR>s[2] = 'b';
INR>У Страуструпа сказано, что это не верно, потому что char * указывает на строку с константными символами, надо писать так: INR>char a[] = "Test";
INR>У меня в программе есть пару мест с char*, надо ли их переделывать?
Ты гонишь =)))
char a[] = "Test";
значит тоже самое что
char a[5] = "Test";
Наша работа -- во тьме,
Мы делаем, что умеем,
Мы отдаем, что имеем,
Наша работа во тьме.
Сомнения стали страстью,
А страсть стала судьбой,
Все остальное -- искуство,
В безумии быть собой
Если нужна "постоянная" строка, то как раз-таки лучше писать const char str[] = "abcdef";
На счет массивов и указателей:
В случае const char *a = "abc"; под саму строку будет выделено четыре байта плюс на стеке (в случае локальной переменой) компилятор зарезервирует четыре байта для указателя и поместит туда адрес этой строки. В случае с глобальной — аналогично (четыре байта для самой строки и четыре байта на глобальный указатель). Если же у тебя есть const char b[] = "abc"; то здесь будет зарезервировано только четыре байта под саму строку без какого-либо указателя. При обращении к "а" (например printf(a) будет использовано содержимое указателя "а" в то время как при обращении к "b" (printf(b)) будет использоваться какое-то постоянно значение (непосредственный адрес строки в памяти).
И массив можно помечать как const!
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Аноним, Вы писали:
А>>Ничего не прогнал. Это два товарища выше не совсем понимают разницу между массивами и указателями.
A>И в чём принципиальная разница?
Здравствуйте, Аноним, Вы писали:
А>Если нужна "постоянная" строка, то как раз-таки лучше писать const char str[] = "abcdef"; А>На счет массивов и указателей: А>В случае const char *a = "abc"; под саму строку будет выделено четыре байта плюс на стеке (в случае локальной переменой) компилятор зарезервирует четыре байта для указателя и поместит туда адрес этой строки. В случае с глобальной — аналогично (четыре байта для самой строки и четыре байта на глобальный указатель). Если же у тебя есть const char b[] = "abc"; то здесь будет зарезервировано только четыре байта под саму строку без какого-либо указателя. При обращении к "а" (например printf(a) будет использовано содержимое указателя "а" в то время как при обращении к "b" (printf(b)) будет использоваться какое-то постоянно значение (непосредственный адрес строки в памяти).
Мое понимание строк было нечетким. Теперь вроде все стало на свои места.
const char * sz = "TEXT";
выделяется память под указатель на строку находящуюся в отсеке RO (память на строку выделена на этапе компиляции, а под переменная в рантайме на стеке)
const char sz[] = "TEXT";
везде используется непосредственный адрес строки находящейся в RO (память выделена на этапе компиляции)
char * sz = "TEXT";
подозреваю. что зависит от компилятора.. Помнится 6.0 такую строку размещал в отсеке WR, а 7.1 — RO. Соответственно 7.1 падает при попытке скопировать что-нить в такой буфер (хотя компилит без предупреждений).
char sz[] = "TEXT";
непосредственный адрес строки выделенной в рантайме на стеке и скопированный из отсека RO. Самый медленный, но в такой буфер можно писать.
Т.е. сомнительным является 3 случай. Следует избегать, но думаю переписывать куски уже рабочего кода не нужно (конечно если строка не используется как буфер).
Здравствуйте, Аноним, Вы писали:
А>Если нужна "постоянная" строка, то как раз-таки лучше писать const char str[] = "abcdef";
Если вообще нужно что-то постоянное, то лучше писать const вне зависимости от того что именно.
А>На счет массивов и указателей:
Угу.
А>В случае const char *a = "abc"; под саму строку будет выделено четыре байта плюс на стеке (в случае локальной переменой) компилятор зарезервирует четыре байта для указателя и поместит туда адрес этой строки.
Ну как-то так. Согласен.
А>Если же у тебя есть const char b[] = "abc"; то здесь будет зарезервировано только четыре байта под саму строку без какого-либо указателя.
Тоже верно.
А>При обращении к "а" (например printf(a) будет использовано содержимое указателя "а" в то время как при обращении к "b" (printf(b)) будет использоваться какое-то постоянно значение (непосредственный адрес строки в памяти).
И это праавильно. Но! В первоначальном вопросе была необходимость записи. Так что причём здесь const?
Да,
char * a = "text";
компилируется, ну а почему не должно? Ведь значение a может менятся во времени и если сейчас она указывает на "test" то потом на что-то другое.
K>const char sz[] = "TEXT"; K>везде используется непосредственный адрес строки находящейся в RO (память выделена на этапе компиляции)
Мысль выражена не вполне четко . Уточню. Массив константных символов инициализируется строковым литералом TEST. Однако, насчет памяти (RO или RW) я бы не был так категоричен. В конце-концов, есть const_cast. Попытка изменить содержимое этого массива вызовет ошибку на этапе компиляции, вот единственное, что можно утверждать наверняка.
K>char * sz = "TEXT"; K>...Соответственно 7.1 падает при попытке скопировать что-нить в такой буфер (хотя компилит без предупреждений).
Наследство от С, в котором не было const. Я тоже не очень понимаю, почему не выдать хотя бы предупреждение.
K>char sz[] = "TEXT"; K>непосредственный адрес строки выделенной в рантайме на стеке и скопированный из отсека RO. Самый медленный, но в такой буфер можно писать.
По скорости не зависит от самого первого варианта.
A>компилируется, ну а почему не должно? Ведь значение a может менятся во времени и если сейчас она указывает на "test" то потом на что-то другое.
Я считаю, уместно было бы предупреждение. Делов том, что тип "hello" — const char*. Тип a — char*. Попробуй провернуть такой фокус:
int i;
const int* cpi=&i;
int* ncpi=&i;
ncpi=cpi; //error: a value of type "const int *" cannot be assigned to
//an entity of type "int *"
А со строковыми литералами именно это и получается.
Здравствуйте, Vamp, Вы писали:
V>Я считаю, уместно было бы предупреждение. Делов том, что тип "hello" — const char*. Тип a — char*. Попробуй провернуть такой фокус: V>
V>int i;
V>const int* cpi=&i;
V>int* ncpi=&i;
V>ncpi=cpi; //error: a value of type "const int *" cannot be assigned to
V> //an entity of type "int *"
V>
V>А со строковыми литералами именно это и получается.
А кто сказал что у "text" есть тип? Это у переменной есть тип. Это у "text" d контексте использования есть тип, а без контекста и типа нет.
const char * sz = "TEXT"; K>выделяется память под указатель на строку находящуюся в отсеке RO (память на строку выделена на этапе компиляции, а под переменная в рантайме на стеке)
Около того. Литерал "TEXT" может быть размещен в read-only памяти, но не обязательно. Существенным является то, что отдельно существует литерал "TEXT" (массив пяти const символов), и отдельно — указатель sz на первый элемент этого массива.
const char sz[] = "TEXT"; K>везде используется непосредственный адрес строки находящейся в RO (память выделена на этапе компиляции)
Формально существует литерал "TEXT", под который выделена память, плюс память под содержимое массива sz, в который скопированы символы литерала "TEXT". На практике некоторые компиляторы, естественно, могут использовать одну и ту же память как для литерала, так и для массива sz.
char * sz = "TEXT"; K>подозреваю. что зависит от компилятора.. Помнится 6.0 такую строку размещал в отсеке WR, а 7.1 — RO. Соответственно 7.1 падает при попытке скопировать что-нить в такой буфер (хотя компилит без предупреждений).
Размещение строковых литералов в read-only или read-write памяти в любом случае зависит от компилятора и его настроек и не зависит от того, какие указатели инициализируются данными литералами.
char sz[] = "TEXT"; K>непосредственный адрес строки выделенной в рантайме на стеке и скопированный из отсека RO. Самый медленный, но в такой буфер можно писать.
Формально это эквивалентно const char sz[] = "TEXT" за исключением того, что содержимое массива можно модифицировать. Однако, действительно, практически отсутствие const означает то, что компилятор вынужден копировать содержимое литерала в массив и не будет использовать одну и ту же память для литерала и массива sz.
K>Т.е. сомнительным является 3 случай. Следует избегать, но думаю переписывать куски уже рабочего кода не нужно (конечно если строка не используется как буфер).
Вывод верен.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, adontz, Вы писали:
A>причём здесь const? A>Да, A>
A>char * a = "text";
A>
A>компилируется, ну а почему не должно?
Если бы не было специального правила, введенного для совместимости со старым кодом, и явно разрешающего преобразование строковых литералов к char*, исходя из общих правил языка это не должно было бы компилироваться, т.к. строковые литералы имеют тип const char [...].
A> Ведь значение a может менятся во времени и если сейчас она указывает на "test" то потом на что-то другое.
Может ли меняться a к делу отношения не имеет, так как const перед char относился бы к данным, на которые указывает a, а не к самой переменной a. Т.е. означал бы, что, пользуясь переменной a нельзя изменять данные, на которые она указывает, но изменить a, так чтобы она указывала на другие данные вполне можно:
const char* a = "text";
a = "another text"; // ok
a[2] = 'b'; // error
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, adontz, Вы писали:
A>А кто сказал что у "text" есть тип? Это у переменной есть тип. Это у "text" d контексте использования есть тип, а без контекста и типа нет.
Тип есть у любого выражения. Например:
1; // int
1l; // long
1u; // unsigned int
0.5; // double
0.5f; // float'a'; // char
'ab'; // char
L'a'; // wchar_t
L'ab'; // wchar_t
1 + 2; // int
1 + 'a'; // int'a' + 'b'; // int
Строковые литералы без префикса L имеют тип const char [...], с префиксом L — const wchar_t [...]. В некоторых контекстах производится неявное преобразование к указателю: