Инициализация массива строк
От: winogr  
Дата: 10.03.09 06:03
Оценка: :)
Есть код:

#include "stdio.h"

void main(void)
{
  char *msgs[]={"123"};
  char *pc;
  
  pc=msgs[0];  
  pc[0]='0';

  printf("%c\n",pc[0]);
}


вылетает в строке:
pc[0]='0';

кто может объяснить в чем некорректность?
Re: Инициализация массива строк
От: Чили Россия  
Дата: 10.03.09 06:17
Оценка:
Здравствуйте, winogr, Вы писали:

W>Есть код:


W>
W>#include "stdio.h"

W>void main(void)
W>{
W>  char *msgs[]={"123"}; // указатель на массив КОНСТАНТНЫХ строк
W>  char *pc;
  
W>  pc=msgs[0];  
W>  pc[0]='0';

W>  printf("%c\n",pc[0]);
W>}
W>


W>вылетает в строке:

W>pc[0]='0'; // константу меннять нельзя !!!

W>кто может объяснить в чем некорректность?
Re[2]: Инициализация массива строк
От: winogr  
Дата: 10.03.09 06:54
Оценка:
Здравствуйте, Чили
Спасибо за ответ.

Но почему:
char *msgs[]={"123"}; // указатель на массив КОНСТАНТНЫХ строк

являются КОНСТАНТНЫМИ стоками
ведь компиляторы не ругаются
и даже в некоторых случаях все работает:
BC31 — выдает рабочую прогу
VC6 — release-работает, debug-нет
gcc — нет
Re: Инициализация массива строк
От: Bell Россия  
Дата: 10.03.09 07:06
Оценка:
Здравствуйте, winogr, Вы писали:

W>Есть код:


W>
W>#include "stdio.h"

W>void main(void)
W>{
W>  char *msgs[]={"123"};
W>  char *pc;
  
W>  pc=msgs[0];  
W>  pc[0]='0';

W>  printf("%c\n",pc[0]);
W>}
W>


W>вылетает в строке:

W>pc[0]='0';

W>кто может объяснить в чем некорректность?


Некореректность в том, что за счет стандартного преобразования (4.2/2) в msgs[0] лежит неконстантный указатель на литерал "123". Последующая попытка модификации этого литерала есть не что иное, как неопределенное поведение. В твоем случае получился вылет, а могло быть и хуже
Любите книгу — источник знаний (с) М.Горький
Re[2]: Инициализация массива строк
От: winogr  
Дата: 10.03.09 07:20
Оценка:
Здравствуйте, Bell, Вы писали:

B>Некореректность в том, что за счет стандартного преобразования (4.2/2)

Не понял, что такое: "преобразование (4.2/2)"
можно по подробнее
Re[3]: Инициализация массива строк
От: Bell Россия  
Дата: 10.03.09 07:34
Оценка:
Здравствуйте, winogr, Вы писали:

W>Не понял, что такое: "преобразование (4.2/2)"

Преобразование, описаное в стандарте языка C++, в пункте 4.2, раздел 2.

W>можно по подробнее

Конечно:

4.2 Array-to-pointer conversion
...

2 A string literal (2.13.4) that is not a wide string literal can be converted to an rvalue of type “pointer to char”;
...

Любите книгу — источник знаний (с) М.Горький
Re[3]: Инициализация массива строк
От: Vlad_SP  
Дата: 10.03.09 07:36
Оценка:
Здравствуйте, winogr,

Это означает ссылку на Стандарт языка С++ ANSI/ISO 14882-2003, пункт 4.2, clause 2, который гласит:

[conv.array] 4.2 Array-to-pointer conversion
........
2 A string literal (2.13.4) that is not a wide string literal can be converted to an rvalue of type “pointer to
char”; ........
Re[4]: Инициализация массива строк
От: winogr  
Дата: 10.03.09 08:15
Оценка:
Здравствуйте, Bell, Вы писали:

B>Некореректность в том, что за счет стандартного преобразования (4.2/2)

B>в msgs[0] лежит неконстантный указатель на литерал "123".
правильнее наверное было бы получить константный указатель
но и не константный указатель это нормально.

B>Последующая попытка модификации этого литерала есть не что иное, как неопределенное поведение.

Но почему вдруг нельзя модифицировать литерал
я всегда считал что литерал это не константа
Re[5]: Инициализация массива строк
От: Bell Россия  
Дата: 10.03.09 08:33
Оценка:
Здравствуйте, winogr, Вы писали:

B>>Некореректность в том, что за счет стандартного преобразования (4.2/2)

B>>в msgs[0] лежит неконстантный указатель на литерал "123".
W>правильнее наверное было бы получить константный указатель
Да, наверное.
W>но и не константный указатель это нормально.
Насколько это нормально — видно из исходного вопроса. ИМХО конечно.

B>>Последующая попытка модификации этого литерала есть не что иное, как неопределенное поведение.

W>Но почему вдруг нельзя модифицировать литерал
W>я всегда считал что литерал это не константа
Это неправильно.

2.13.4/1
A string literal is a sequence of characters (as defined in 2.13.2) surrounded by double quotes, optionally
beginning with the letter L, as in "..." or L"...". A string literal that does not begin with L is an ordinary
string literal, also referred to as a narrow string literal. An ordinary string literal has type “array of n
const char”
and static storage duration (3.7), where n is the size of the string as defined below, and is
initialized with the given characters.
...

Любите книгу — источник знаний (с) М.Горький
Re[6]: Инициализация массива строк
От: winogr  
Дата: 10.03.09 09:27
Оценка:
Здравствуйте, Bell, Вы писали:

B>>>Последующая попытка модификации этого литерала есть не что иное, как неопределенное поведение.

W>>Но почему вдруг нельзя модифицировать литерал
W>>я всегда считал что литерал это не константа
B>Это неправильно.

B>

B>2.13.4/1
B>A string literal is a sequence of characters (as defined in 2.13.2) surrounded by double quotes, optionally
B>beginning with the letter L, as in "..." or L"...". A string literal that does not begin with L is an ordinary
B>string literal, also referred to as a narrow string literal. An ordinary string literal has type “array of n
B>const char”
and static storage duration (3.7), where n is the size of the string as defined below, and is
B>initialized with the given characters.
B>...


Спасибо.
Но к сожалению этот стандарт описывает не совсем тот язык "C" к которому я привык.
Придется переучиваться.
Re[7]: Инициализация массива строк
От: Аноним  
Дата: 10.03.09 11:38
Оценка:
Здравствуйте, winogr, Вы писали:

W>Спасибо.

W>Но к сожалению этот стандарт описывает не совсем тот язык "C" к которому я привык.

скорее похоже, что вы привыкли к не совсем тому языку С++
или, что более похоже, к не совсем тем компиляторам, которые не совсем соответствуют стандарту
Re[4]: А почкму С++? (-)
От: Erop Россия  
Дата: 11.03.09 08:33
Оценка:
Здравствуйте, Vlad_SP, Вы писали:

V_S>Это означает ссылку на Стандарт языка С++ ANSI/ISO 14882-2003, пункт 4.2, clause 2, который гласит:
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Это С или С++?
От: Erop Россия  
Дата: 11.03.09 08:34
Оценка:
Здравствуйте, winogr, Вы писали:

W>кто может объяснить в чем некорректность?


А тебе какой из двух языков? Может ты С, как С++ компилируешь?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Инициализация массива строк
От: ariets  
Дата: 11.03.09 12:22
Оценка: 2 (2)
Здравствуйте, winogr, Вы писали:

W>Есть код:


W>кто может объяснить в чем некорректность?


Засыпали бедного парня цитатами из стандарта. Объяснить все надо проще, имхо.
Итак, что написано в строке:

char * p = "ABC";


Там написано, что компилятор в области статических данных( read only memory ) помещает строку "ABC". Указатель p на char хранит в себе значение адреса ячейки памяти, где расположен символ "A"
Теперь попробуем

p[0] = 'D';


Мы пытаемся записать по этому адресу другое значение. Но доступа на запись у нас туда нет — Visual Studio говорит на это четко: 0xC0000005: Access violation writing location 0x0040313c.!
( с массивами строк — аналогично ).

Работающий код выглядить должен как-то так:

char * p = new char[4];
strcpy( p, "ABC\0" );
p[0]='D';


или так:

char m[] = "ABC";
char * p = m;
p[0] = 'D';


В обоих примерах p теперь указывает на область памяти, которую можно модифицировать!

Так что различие-то не не в константах и литералах, а в памяти и указателях.
Re[2]: Инициализация массива строк
От: Erop Россия  
Дата: 11.03.09 13:03
Оценка:
Здравствуйте, ariets, Вы писали:

A>
A>char * p = "ABC";
A>


A>Там написано, что компилятор в области статических данных( read only memory ) помещает строку "ABC". Указатель p на char хранит в себе значение адреса ячейки памяти, где расположен символ "A"

A>Теперь попробуем

А почему область статических данных обязательно read only memory?

Скажем такой вот код:
int a1 = 5;
int a2 = a1 *= 2;


Вполне так себе порождает модифицируемые статические данные...

Возможно меня глючит, но мне кажется, что в языке С строковые литералы модифицируемые, а в С++ нет. Я так подозреваю, что у топикстартера язык С, а компилятор почему-то берётся С++, от того все и проблемы...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Инициализация массива строк
От: Кодёнок  
Дата: 11.03.09 13:07
Оценка:
Здравствуйте, ariets, Вы писали:

A>Засыпали бедного парня цитатами из стандарта. Объяснить все надо проще, имхо.


Действительно. Проще всего так: вообще-то implicit конвертирование const в не-const не должно быть позволено, мы имеем дело с очередной нелогичностью языка C++, которых там еще много-много-много, так что придется привыкать.

A>Так что различие-то не не в константах и литералах, а в памяти и указателях.


В специфике операционной системы, если быть точнее. Если предварительно сменить защиту памяти на секции константных данных, то можно будет спокойно писать туда без access violation.
Re[3]: Инициализация массива строк
От: ariets  
Дата: 11.03.09 13:18
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, ariets, Вы писали:


A>>
A>>char * p = "ABC";
A>>


A>>Там написано, что компилятор в области статических данных( read only memory ) помещает строку "ABC". Указатель p на char хранит в себе значение адреса ячейки памяти, где расположен символ "A"

A>>Теперь попробуем

E>А почему область статических данных обязательно read only memory?


E>Скажем такой вот код:
E>int a1 = 5;
E>int a2 = a1 *= 2;


Вообще-то в скобках я уточнил, какие именно статические данные для строковой константы — read only. Я не имел ввиду, что все статические данные — read only.

Перефразирую: "Там написано, что компилятор в секции константных данных( read only memory ) помещает строку "ABC"
Re[4]: Инициализация массива строк
От: Erop Россия  
Дата: 11.03.09 14:30
Оценка:
Здравствуйте, ariets, Вы писали:

A>Перефразирую: "Там написано, что компилятор в секции константных данных( read only memory ) помещает строку "ABC"


А разве там это написано? Вроде как с С литералы не константны...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Инициализация массива строк
От: Андрей Тарасевич Беларусь  
Дата: 11.03.09 14:40
Оценка:
Здравствуйте, Erop, Вы писали:

A>>Перефразирую: "Там написано, что компилятор в секции константных данных( read only memory ) помещает строку "ABC"


E>А разве там это написано? Вроде как с С литералы не константны...


В С строковые литералы не константны по типу (т.е. в C это 'array of char' а не 'array of const char'). Тем не менее и в С модификация строкового литерала запрещена, т.е. четко сказано, что попытка модификации строкового литерала приводит к неопределенному поведению.
Best regards,
Андрей Тарасевич
Re[5]: Инициализация массива строк
От: ariets  
Дата: 11.03.09 14:47
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, ariets, Вы писали:


A>>Перефразирую: "Там написано, что компилятор в секции константных данных( read only memory ) помещает строку "ABC"


E>А разве там это написано? Вроде как с С литералы не константны...

Поподробнее про память, чтобы точно объяснить почему все так происходит.

Есть строка. Она хранится в памяти. Область памяти называется "Константные данные"(Const Data). То, что туда нельзя писать, говорит сам компилятор VS( говорят, правда, что можно настроить, чтобы запись туда была разрешена, но вопрос форума — объяснить, почему вылетает прога )

По поводу области памяти: их вообще 5 — константные данные, стек, свободная динамическая память, куча, глобальная/статическая.

Константные данные: в этой области хранятся строки и другие данные, чьи значения известны во время компиляции. В этой области не могут находиться объекты классов.Все данные из этой области доступны в течение всего времени жизни программы. Кроме того, все данные в этой области памяти доступны только для чтения, и результат попытки их изменения не определен. В частности, это связано с тем, что формат хранения этих данных может быть оптимизирован конкретной реализацией языка. Например, компилятор может оптимизировать хранение строк и разместить их в перекрывающихся объектах. Это объясняет, почему разные компиляторы дают разные результаты.
Re[6]: Инициализация массива строк
От: Erop Россия  
Дата: 11.03.09 19:46
Оценка:
Здравствуйте, ariets, Вы писали:

A>По поводу области памяти: их вообще 5 — константные данные, стек, свободная динамическая память, куча, глобальная/статическая.

Это всё конечно очень интересно, но вопрос-то не про то, вроде...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Инициализация массива строк
От: Erop Россия  
Дата: 11.03.09 19:47
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>В С строковые литералы не константны по типу (т.е. в C это 'array of char' а не 'array of const char'). Тем не менее и в С модификация строкового литерала запрещена, т.е. четко сказано, что попытка модификации строкового литерала приводит к неопределенному поведению.


В ANSI стандарте? Точно написано? Я понмю, что у всех компиляторов была опция "можно совмещать совпадающие строковые литералы", и припоминаю проги, которые использовали литералы, как буферы в памяти...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Инициализация массива строк
От: Аноним  
Дата: 11.03.09 19:52
Оценка:
W>Но почему:
W>
W>char *msgs[]={"123"}; // указатель на массив КОНСТАНТНЫХ строк
W>

W>являются КОНСТАНТНЫМИ стоками
W>ведь компиляторы не ругаются
W>и даже в некоторых случаях все работает:
W>BC31 — выдает рабочую прогу
W>VC6 — release-работает, debug-нет
W>gcc — нет

потому что модификация константы — UB
Re[3]: Инициализация массива строк
От: Аноним  
Дата: 11.03.09 20:04
Оценка:
Кё>В специфике операционной системы, если быть точнее. Если предварительно сменить защиту памяти на секции константных данных, то можно будет спокойно писать туда без access violation.
и потом нарваться на спецэффекты:
char *a = "aaa";
a[2] = b;
printf("aaa");

А на экран выведется 'aab'.
А если эти строки раскиданы по проекту то заколебаетесь баг фиксить.
Re[4]: Инициализация массива строк
От: MShura  
Дата: 11.03.09 21:03
Оценка:
А>и потом нарваться на спецэффекты:
А>
А>char *a = "aaa";
А>a[2] = b;
А>printf("aaa");
А>

А>А на экран выведется 'aab'.

Вовсе не факт, что компилятор совместит две одинаковые строковые литеры.
Зависит от его возможностей и ключей.
Re[5]: Инициализация массива строк
От: Аноним  
Дата: 11.03.09 21:44
Оценка:
Здравствуйте, MShura, Вы писали:

А>>и потом нарваться на спецэффекты:

А>>
А>>char *a = "aaa";
А>>a[2] = b;
А>>printf("aaa");
А>>

А>>А на экран выведется 'aab'.

MS>Вовсе не факт, что компилятор совместит две одинаковые строковые литеры.

MS>Зависит от его возможностей и ключей.
это и называется UB
Re[5]: Инициализация массива строк
От: Аноним  
Дата: 11.03.09 21:51
Оценка:
MS>Вовсе не факт, что компилятор совместит две одинаковые строковые литеры.
MS>Зависит от его возможностей и ключей.
даже если не совместит (если насильно выключить strings pooling) можно будет нарваться на другую совсем неочевидную ситуевину:
void foo()
{
char *s = "aaa";
printf("s=%s\n", s)
s[2] = b;
printf("s=%s\n", s)
}
...
foo();
foo();


Догадайтесь что получим в stdout выхлопе, даже если компилятор не сделает strings pooling и не разместит их в RO памяти.
Re[6]: Инициализация массива строк
От: Erop Россия  
Дата: 12.03.09 03:48
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Догадайтесь что получим в stdout выхлопе, даже если компилятор не сделает strings pooling и не разместит их в RO памяти.


Дык это же фича!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Инициализация массива строк
От: winogr  
Дата: 12.03.09 07:33
Оценка:
Да я узнал много интересного
ведь я считал что код:
char *s="123";

эквивалентен:
char bs[]="123";
char *s=bs;

во втором случае
происходит копирование литерала в локальный массив
и его уже можно модифицировать
Re[8]: Инициализация массива строк
От: Аноним  
Дата: 12.03.09 08:11
Оценка:
так короче:
char s[]="123";
Re[9]: Инициализация массива строк
От: winogr  
Дата: 12.03.09 09:01
Оценка:
Здравствуйте, Аноним, Вы писали:

А>так короче:

А>
А>char s[]="123";
А>

да,
но при этом получается
константный указатель
Re[10]: Инициализация массива строк
От: Аноним  
Дата: 12.03.09 09:14
Оценка:
А>>
А>>char s[]="123";
А>>

W>да,
W>но при этом получается
W>константный указатель
нет он получается если написать char *s="123";
Re[11]: Инициализация массива строк
От: winogr  
Дата: 12.03.09 09:33
Оценка:
Здравствуйте, Аноним, Вы писали:

А>>>
А>>>char s[]="123";
А>>>

W>>да,
W>>но при этом получается
W>>константный указатель
А>нет он получается если написать char *s="123";
почему нет
попробуй скомпилить
char s[]="123";
s++;
Re[12]: Инициализация массива строк
От: Bell Россия  
Дата: 12.03.09 09:40
Оценка:
Здравствуйте, winogr, Вы писали:

W>попробуй скомпилить

W>
W>char s[]="123";
W>s++;
W>


В данном случае s — это массив, а не указатель (ни константный, ни неконстантный). Со всеми вытекающими.
Просто за счет стандартного преобразования array-to-pointer conversion массив в ряде случаев ведет себя как указатель.
Любите книгу — источник знаний (с) М.Горький
Re[13]: Инициализация массива строк
От: winogr  
Дата: 12.03.09 10:21
Оценка:
Здравствуйте, Bell, Вы писали:

B>Здравствуйте, winogr, Вы писали:


W>>попробуй скомпилить

W>>
W>>char s[]="123";
W>>s++;
W>>


B>В данном случае s — это массив, а не указатель (ни константный, ни неконстантный). Со всеми вытекающими.

B>Просто за счет стандартного преобразования array-to-pointer conversion массив в ряде случаев ведет себя как указатель.

есть три варианта:
char *s="123"; // указатель (но можно только читать)


char bs[]="123"; 
char *s=bs;      // указатель


char s[]="123";  // массив (т.к. s нельзя менять я назвал его константым указателем)


первый и второй дают указатели
третий не является их эквивалентом так значение s нельзя изменить
Re[7]: Инициализация массива строк
От: Андрей Тарасевич Беларусь  
Дата: 12.03.09 16:27
Оценка: 10 (1)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Андрей Тарасевич, Вы писали:


АТ>>В С строковые литералы не константны по типу (т.е. в C это 'array of char' а не 'array of const char'). Тем не менее и в С модификация строкового литерала запрещена, т.е. четко сказано, что попытка модификации строкового литерала приводит к неопределенному поведению.


E>В ANSI стандарте?


Да, именно там.

E>Точно написано?


Абсолютно точно. Даже в замшелом С89/90: "6.1.4 String Literals [...] If the program attempts to modify a string literal of either form, the behavior is undefined".

E>Я понмю, что у всех компиляторов была опция "можно совмещать совпадающие строковые литералы",


Эта опция влияет не только на результаты модификации литералов, но и на адресные свойства литералов, т.е. ее "ценность" не сводится только к вопросам модификации.

У компиляторов в дополнение к этому есть еще бывает опция, явно делающая строковые литералы модифицируемыми. Тем не менее к стандартному все С это отношения не имеет.

E>и припоминаю проги, которые использовали литералы, как буферы в памяти...


В "прогах" еще и не такое можно увидеть.
Best regards,
Андрей Тарасевич
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.