Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.73/15: Рейтинг темы: голосов - 15, средняя оценка - 4.73
2 / 2 / 0
Регистрация: 12.12.2014
Сообщений: 87
1

Не получается присвоить значение элементу массива указателей типа char *a[3]

10.02.2017, 11:22. Просмотров 3091. Ответов 5
Метки нет (Все метки)

В попытках разобраться с массивами, указателями, массивами указателей и т.п. написал вот такой код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
    char *a[3] = { "abcde", "xyz", "QWERTY" };
    char b[3][20] = { "abcde", "xyz", "QWERTY" };
 
    cout << "*b[0] before changing is " << *b[0] << endl;
    *b[0] = '8';
    cout << "*b[0] after changing is " << *b[0] << endl;
 
    cout << "*a[0] before changing is " << *a[0] << endl;
    *a[0] = '8';
    //a[0][0] = '8'; // тоже не работает
    cout << "*a[0] after changing is " << *a[0] << endl;
 
    system("pause");
    return 0;
}
Он компилируется без ошибок, но при запуске вылетает. Это происходит при попытке выполнить операцию
C++
1
*a[0] = '8';
Аналогично программа ведёт себя и при выполнении операции
C++
1
a[0][0] = '8';
При этом такие операции для переменной b работают как положено.

Почему так происходит?

Компилятор MS Visual Studio.
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
10.02.2017, 11:22
Ответы с готовыми решениями:

Как присвоить значение одного из элементов массива типа char переменной типа int?
С++ начал буквально только что так что буду благодарен за терпение и понимание ) Сразу оговорюсь...

Значение типа const char * нельзя присвоить сущности типа char *
Добрый день. Я новичок в c++ и столкнулся с проблемой при написании маленькой программы. Собственно...

Как полю класса типа char* присвоить значение типа *char
Проблема в строчке 46 (не пинайте сильно за формат кода и за говнокод) #include &quot;stdafx.h&quot; ...

В чем отличие двумерного массива типа char от массива указателей на char?
В чем отличие двумерного массива типа char и массива указателей на char ?

5
(80 / 20 || 50 / 50) = x
1647 / 1120 / 432
Регистрация: 16.08.2014
Сообщений: 4,685
Записей в блоге: 1
10.02.2017, 11:39 2
Thor,
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(int argc, char* argv[] )
{
    char *a[3] = { "abcde", "xyz", "QWERTY" };
    char b[3][20] = { "abcde", "xyz", "QWERTY" };
 
    std::cout << "*b[0] before changing is " << *b[0] << std::endl;
    b[0][0] = '8';
    std::cout << "*b[0] after changing is " << *b[0] << std::endl;
 
    std::cout << "*a[0] before changing is " << *a[0] << std::endl;
    a[0] = "8";
    //a[0][0] = '8'; // тоже не работает
    std::cout << "*a[0] after changing is " << *a[0] << std::endl;
 
    system("pause");
 
    return 0;
}
так работает.

Добавлено через 5 минут
C++
1
char *a[3] = { "abcde", "xyz", "QWERTY" };
тут ты создаешь одномерный массив из указателей на char.
C++
1
char b[3][20] = { "abcde", "xyz", "QWERTY" };
тут ты создаешь двумерный массив из самих char.
0
2 / 2 / 0
Регистрация: 12.12.2014
Сообщений: 87
10.02.2017, 11:39  [ТС] 3
_stanislav, да, так работает, спасибо. Но я пытаюсь разобраться.

a - массив указателей на char.
a[0] - первый указатель на char (на 'a' из "abcde").
*a[0] - разыменование указателя, получаем char.
Так почему же нельзя *a[0] = '8', если *a[0] - это char и '8' - это char?
0
(80 / 20 || 50 / 50) = x
1647 / 1120 / 432
Регистрация: 16.08.2014
Сообщений: 4,685
Записей в блоге: 1
10.02.2017, 11:57 4
это не одно и то же, даже если ты применил к ним одинаковый список инициализации
C++
1
{ "abcde", "xyz", "QWERTY" }
Добавлено через 5 минут
Thor, тебе нужно копать в сторону строковых литералов.

Добавлено через 3 минуты
Thor,
C++
1
2
char* str = "123";
str[0] = '0';
вот пример он тоже не работает.

Добавлено через 2 минуты
как я понимаю, тут
C++
1
char *a[3]
у тебя массив указателей на начало строковых литералов, и как мы уже с тобой видели строковый литерал нельзя изменять.

Добавлено через 3 минуты
Thor, Строковые литералы — это константы, поэтому любая попытка изменить их — как, например в строке str[2] = "A" — приведет к ошибке компилятора.


Чтобы прочитать статью на английском языке, установите флажок Английский. Вы также можете просматривать текст на английском языке во всплывающем окне, наводя указатель мыши на текст.
Перевод Английский
Строковые литералы в C++

Visual Studio 2013 Другие версии
Строковый литерал представляет последовательность символов, которые вместе образуют строку с завершающим нулем. Символы должны быть заключены в двойные кавычки. Существуют следующие типы строковых литералов.
Узкие строковые литералы, представленные как "xxx".
Расширенные строковые литералы, представленные как L"xxx".
Необработанные строковые литералы, представленные как R"ddd(xxx) ddd", где ddd — разделитель. Необработанные строковые литералы могут быть либо узкими (представленными с помощью R) или расширенными (представленными с помощью LR).
Узкий строковый литерал — это завершающийся нулем массив констант char, который может содержать любые графические символы, за исключением двойных кавычек ("), обратной косой черты (\) или символа новой строки. Узкий строковый литерал может содержать escape-последовательности, перечисленные в разделе Знаковые литералы C++.
C++
const char *narrow = "abcd";

// represents the string: yes\no
const char *escaped = "yes\\no";
Расширенный строковый литерал — это завершающийся нулем массив констант wchar_t, который может содержать любые графические символы, за исключением двойных кавычек ("), обратной косой черты (\) или символа новой строки. Расширенный строковый литерал может содержать escape-последовательности, перечисленные в разделе Знаковые литералы C++.
C++
const wchar_t* wide = L"zyxw";
const wchar_t* newline = L"hello\ngoodbye";
Необработанный строковый литерал — это завершающийся нулевым значением массив констант типа char или wchar_t, который может содержать любые графические символы, включая двойные кавычки ("), обратную косую черту (\) и символ новой строки. Необработанные строковые литералы часто применяются в регулярных выражениях, которые используют классы символов, а также в строках HTML и XML. Примеры см. в следующей статье: Bjarne Stroustrup's FAQ on C++11 (Вопросы и ответы о C++11 от Бьярна Страуструпа).
C++
// represents the string: An unescaped \ character
const char* raw_narrow = R"(An unescaped \ character)";

// represents the string: An unescaped " character
const wchar_t* raw_wide = LR"(An unescaped " character)";
Разделитель — это содержащая до 16 символов пользовательская последовательность, которая стоит непосредственно перед открывающей круглой скобкой и сразу после закрывающей круглой скобки необработанного строкового литерала. Разделители можно использовать для различения строк, содержащих двойные кавычки и круглые скобки. Следующая строка приводит к ошибке компилятора:
C++
// meant to represent the string: )”
const char* bad_parens = R"()")";
Однако ошибку можно устранить с помощью разделителя:
C++
const char* good_parens = R"xyz()")xyz";
Можно создать необработанный строковый литерал, содержащий символ новой строки (не escape-символ) в исходном коде:
C++
// represents the string: hello
//goodbye
const wchar_t* newline = LR"(hello
goodbye)";
Размер строковых литералов
Размер узкого строкового литерала (в байтах) равен количеству символов плюс 1 (для завершающего нуль-символа); размер расширенного строкового литерала (в байтах) равен удвоенному числу символов плюс 2 (для завершающего нуль-символа). Ниже представлен размер расширенного строкового литерала.
C++
const wchar_t* str = L"Hello!";
const size_t byteSize = (wcslen(str) + 1) * sizeof(wchar_t);
Обратите внимание, что значения strlen() и wcslen() не включают размер завершающего нуль-символа.
Максимальная длина строкового литерала — 65535 байт. Это ограничение применимо как к узким, так и к расширенным строковым литералам.
Изменение строковых литералов
Строковые литералы — это константы, поэтому любая попытка изменить их — как, например в строке str[2] = "A" — приведет к ошибке компилятора.
Блок, относящийся только к системам Microsoft
В Visual C++ строковый литерал можно использовать для инициализации указателя не являющимся константным значением типа char или wchar_t. Это разрешено в коде C, но не рекомендуется в С++98 и удалено из С++11. Попытка изменить строку вызовет нарушение прав доступа, как показано в следующем примере:
C++
wchar_t* str = L"hello";
str[2] = L'a'; // run-time error: access violation
Чтобы указать компилятору выдавать ошибку при преобразовании строкового литерала в символ non_const, можно задать компилятора /Zc:strictStrings (отключение преобразования типов строковых литералов). Рекомендуется объявлять инициализируемые строковым литералом указатели с помощью ключевого слова auto. Таким образом происходит преобразование в правильный тип (const). В следующем примере перехватывается попытка записать строковый литерал во время компиляции:
C++
auto str = L"hello";
str[2] = L'a'; // Compiler error: you cannot assign to a variable that is const
В некоторых случаях идентичные строковые литералы могут быть объединены в пул для экономии места в исполняемом файле. При объединении строковых литералов в пулы компилятор делает так, что все ссылки на определенный строковый литерал указывают на одну и ту же область в памяти, вместо того чтобы каждая ссылка указывала на отдельный экземпляр строкового литерала. Для включения объединения строковых литералов в пулы используется параметр компилятора /GF.
Завершение блока, относящегося только к системам Майкрософт
2
25 / 25 / 5
Регистрация: 04.01.2017
Сообщений: 52
10.02.2017, 11:58 5
Цитата Сообщение от Thor Посмотреть сообщение
Почему так происходит?
Вам сказали правильно, надо читать про поведение строковых литералов.

Смог нагуглить такую отсылку к стандарту(2.13.5.16p):
Код
The effect of attempting to modify a string literal is undefined.
Это значит что эффект от попытки модифицировать строковый литерал неопределен.
0
1118 / 931 / 429
Регистрация: 25.12.2016
Сообщений: 3,030
10.02.2017, 12:01 6
Лучший ответ Сообщение было отмечено Thor как решение

Решение

Более простой пример:
C++
1
2
3
4
    char s1[] = "abcd";
    char *s2 = "abcd";
    s1[0] = 'A'; // нормально
    s2[0] = 'A'; // ошибка времени выполнения
Смысл в том, что строка "abcd" хранится в исполняемой части программы, доступной только в режиме чтения.
Указатель s2 указывает непосредственно на эту строку, поэтому попытки записи приводят к вылету программы.
Массив же s1 хранится в сегменте данных или в стеке, поэтому его можно безболезненно модифицировать.

P.S. Именно поэтому указатели на строки принято записывать со словом const: const char *s = "abcd".
P.P.S. Вот здесь ешё неплохо написано.
2
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
10.02.2017, 12:01

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

Xcode: не получается присвоить переменной типа char русский символ
Здравствуйте, недавно перешел в среду Xcode (перешел с VisualStudio) и столкнулся с такой...

Присвоить 8-му элементу массива значение 10-го элемента, увеличенное на 5, а 10-му присвоить значение 13-го
Одномерный массив из 15 элементов заполнить случайным образом числами от 7 до 12. Присвоить 8-му...

Как исправить ошибку: Значение типа "const char *" нельзя присвоить сущности типа "char *"?
#include &lt;conio.h&gt; #include &lt;iostream&gt; using namespace std; struct Car_Specifications {...

Не получается присвоить значение двумерному массиву char
объявляю двумерный массив (массив массивов в char для хранения имен файлов) в cpp файле char...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2020, vBulletin Solutions, Inc.