Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.67/3: Рейтинг темы: голосов - 3, средняя оценка - 4.67
MJ_PRUTYG
0 / 0 / 0
Регистрация: 28.11.2018
Сообщений: 136
1

Почему работает динамический массив из 0 - элементов

05.02.2019, 22:57. Просмотров 630. Ответов 18
Метки нет (Все метки)

Всем доброго времени суток! Столкнулся нечаянно с такой проблемой: создавая динамический массив из нуля элементов - он создается! Да и при этом, могу записать в нулевой элемент, первый элемент, второй - числа(например), а потом он их еще и сохраняет в себе, как будто там не ноль элементов, а гораздо больше и я в свободные ячейки просто что-то записываю. Это..Как так-то вообще? Что за магия?
Т.к. создавая обычный массив НЕ в динам. памяти с 0 - элементами, выдает ошибку - всё верно и логично! (Ну как бы логично, если я создаю "массив" в комнате из нуля коробок, то я ничего не смогу в них положить, т.к. их попросту нет - ноль!)

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
    //int arr[0]; - ERROR
    int *p = new int[0];//???
    p[0] = 10;
    p[1] = 5;
    p[2] = 17;
 
    cout << p[0] << endl;
    cout << p[1] << endl;
    cout << p[2] << endl;
 
    return 0;
}
0
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.02.2019, 22:57
Ответы с готовыми решениями:

Не работает, если массив динамический ?! Почему?
Простая задача, есть текст , удалить из него набор символов, вот код - все работает -&gt; char c...

Почему двумерный динамический массив не запускается?
#include &lt;iostream&gt; #include &lt;cstdlib&gt; #include &lt;ctime&gt; using namespace std; int main() {...

Почему программа крашается(двумерный динамический массив)
Cам код #include &lt;iostream&gt; #include &lt;string&gt; #include &lt;conio.h&gt; #include &lt;iomanip&gt; #include...

Почему нельзя объявить динамический массив глобально
вот код #include &lt;iostream&gt; #include &lt;string&gt; int w = 0; string *s = new string ; using...

Динамический массив нулевого размера. Почему не падает программа?
char* p = new char; cout &lt;&lt; &amp;p &lt;&lt; endl; Странно, но всё работает. Хотя если зададим...

18
Azazel-San
Mental handicap
1082 / 540 / 154
Регистрация: 24.11.2015
Сообщений: 2,194
Завершенные тесты: 1
05.02.2019, 23:16 2
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
создавая динамический массив из нуля элементов - он создается
А чего бы ему и не создаться? Стандарт разрешает.
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Да и при этом, могу записать в нулевой элемент, первый элемент, второй - числа(например)
Нет не можете. Доступ к такому указателю UB.
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
а потом он их еще и сохраняет в себе, как будто там не ноль элементов, а гораздо больше и я в свободные ячейки просто что-то записываю
А кто дает гарантию что будет выделено 0 элементов? Компилятор выделит например 1 байт в случае с gcc. Потенциально ОС выделит еще больше тк вызов нативных функций довольно затратный, поэтому весьма вероятно ОС себя перестрахует.
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Это..Как так-то вообще?
Ну, вот так.
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Т.к. создавая обычный массив НЕ в динам. памяти с 0 - элементами, выдает ошибку - всё верно и логично! (Ну как бы логично, если я создаю "массив" в комнате из нуля коробок, то я ничего не смогу в них положить, т.к. их попросту нет - ноль!)
С чего вдруг [] тоже самое что и new[]?
0
nd2
3099 / 2593 / 1134
Регистрация: 29.01.2016
Сообщений: 8,690
06.02.2019, 01:50 3
Нюансы синтаксиса: запись double *array - это указатель или что-то иное?
0
MJ_PRUTYG
0 / 0 / 0
Регистрация: 28.11.2018
Сообщений: 136
06.02.2019, 02:26  [ТС] 4
Azazel-San,
Цитата Сообщение от Azazel-San Посмотреть сообщение
Нет не можете. Доступ к такому указателю UB.
Почему не могу, если могу? У меня работает...Код тот же что и в шапке.
И что за указатели UB??? Первый раз о таком слышу. Буду благодарен за ответ
Цитата Сообщение от Azazel-San Посмотреть сообщение
А чего бы ему и не создаться? Стандарт разрешает.
А где почитать про конкретно ЭТОТ стандарт? Интересно.
Цитата Сообщение от Azazel-San Посмотреть сообщение
С чего вдруг [] тоже самое что и new[]?
Искал по форумам, в гугле. Что-то не нашел ничего на эту тему. Где об этом почитать?

Буду очень благодарен за ответ!
0
06.02.2019, 02:26
nd2
3099 / 2593 / 1134
Регистрация: 29.01.2016
Сообщений: 8,690
06.02.2019, 06:08 5
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
И что за указатели UB???
https://ru.wikipedia.org/wiki/Неопределённое_поведение
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
А где почитать про конкретно ЭТОТ стандарт?
Стандарт С++, § 5.3.4 New.
Липпман "Язык программирования С++. Базовый курс", 5-е издание, стр.609
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Что-то не нашел ничего на эту тему. Где об этом почитать?
Как добавить объект в массив объектов?
0
COKPOWEHEU
1695 / 1159 / 266
Регистрация: 09.09.2017
Сообщений: 4,756
06.02.2019, 11:22 6
Цитата Сообщение от Azazel-San Посмотреть сообщение
Нет не можете. Доступ к такому указателю UB.
Записать-то может, но вот к чему это приведет? Если повезет - программа полезет в недоступную память и просто крашнется, если нет... ну, не стоит до этого доводить.
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Да и при этом, могу записать в нулевой элемент, первый элемент, второй - числа(например)
С/С++ не проверяют выход за границы массива, а тем более указателей. Например, есть функция
void bzero(void *s, size_t n)
Она заполняет первые n байтов массива s нулями. Откуда ей знать какой у этого массива размер на самом деле? Собственно, по указателю ей можно передать не только массив, но и переменную, структуру, да что угодно.
0
Azazel-San
Mental handicap
1082 / 540 / 154
Регистрация: 24.11.2015
Сообщений: 2,194
Завершенные тесты: 1
06.02.2019, 11:43 7
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Почему не могу, если могу?
Потому что. Само выделение динамического массива из 0 элементов - implementation defined behavior, а полученное значение будет не nullptr указатель, т.е. такой указатель вполне валидный, хоть у вас там и выделялось 0 элементов, но обращение к такому указателю, тем более запись туда чего либо UB.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Если повезет - программа полезет в недоступную память и просто крашнется, если нет...
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
У меня работает...
Это и есть UB. Программа содержащая UB может вполне себе валидно работать, до какого-то момента.
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
А где почитать про конкретно ЭТОТ стандарт?
http://eel.is/c++draft/expr.new#8
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Где об этом почитать?
Сама конструкция уже намекает что это как бы немного разные вещи, хотя и имеют очень много общего.
0
SomniPhobia
352 / 256 / 101
Регистрация: 22.11.2017
Сообщений: 707
06.02.2019, 11:45 8
MJ_PRUTYG, привет!
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
int *p = new int[0];
Напиши такие строки
C++
1
2
int *a = new int[777u];
cout << a[-4] / sizeof(int) << endl;
Что появится на консоле?

Добавлено через 1 минуту
Вот ещё для эксперимента код.
C++
1
2
3
4
5
int *a = new int[10u];
    for (int u = -10; u < 100; ++u)
    {
        cout << u << " " << a[u] << endl;
    }
0
Azazel-San
Mental handicap
1082 / 540 / 154
Регистрация: 24.11.2015
Сообщений: 2,194
Завершенные тесты: 1
06.02.2019, 11:52 9
Цитата Сообщение от SomniPhobia Посмотреть сообщение
Напиши такие строки
Какое это имеет отношение к теме?
Цитата Сообщение от SomniPhobia Посмотреть сообщение
Вот ещё для эксперимента код.
0
SomniPhobia
352 / 256 / 101
Регистрация: 22.11.2017
Сообщений: 707
06.02.2019, 11:54 10
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
int *p = new int[n];
Это просто создание указателя с резервацией места в памяти на n позиций.
Механизм подыскивает участок в памяти, где можно зарезервировать n ячеек, идущих монотонно друг за другом. Резервирует его путём прописи маркеров до и после занимаемого участка.
Пусть объявлен массив
int *p = new int[5];
У меня в системе индексации массива на позициях -1 и 5 значение -33686019 (мне интересно, у Вас также). И эти числа неизменны у меня, то есть каждую компиляцию и запуск выходят они не зависимо от длины массива, его обрамляют эти числа.
Если смотреть за пределы выделенной памяти, то там тоже есть ячейки со значениями, причём можно у них спокойно поменять значения и что - то повалить.
0
DrOffset
10921 / 5818 / 1434
Регистрация: 30.01.2014
Сообщений: 9,362
06.02.2019, 12:08 11
Цитата Сообщение от SomniPhobia Посмотреть сообщение
значение -33686019
Это 0xFDFDFDFD, известное также как `no mans land` (дословно - ничейная земля).
См таблицу: http://www.nobugs.org/developer/win32/debug_crt_heap.html#table
Значение, используемое CRT Microsoft в целях отладки для обозначения динамической памяти недоступной для использования.
1
SomniPhobia
352 / 256 / 101
Регистрация: 22.11.2017
Сообщений: 707
06.02.2019, 12:18 12
MJ_PRUTYG,
int *p = new int[n];
p - указатель на 0 элемент массива.
Если его разыменуем, то получим значение из 0 ячейки массива p[0].
Указатель можно смещать на k позиций памяти. k может быть не только положительным (вперёд), но и отрицательным (назад).
p + k
Смещённый указатель также можно разыменовать и получить значение из ячейки памяти, на которую показывает этот указатель.
*(p + k) эквивалентно p[k]
Сам указатель p не знает размер массива и может свободно ходить по памяти, даже за пределами массива.
Указателю p безразлично, чему равно n в выражении int *p = new int[n]; хоть нулю. n нужна только для того, чтобы компилятор корректно выделил память под массив из n элементов, так чтобы они все были рядом, шли непрерывно друг за другом. А указатель p осуществляет навигацию по памяти. При объявлении int *p = new int[n]; указатель p инициализируется значением адреса ячейки массива с 0 индексом, а может обслуживать не только массив, но и выходить за его пределы и мусорить в памяти или читать память, не относящуюся к массиву.
1
MJ_PRUTYG
0 / 0 / 0
Регистрация: 28.11.2018
Сообщений: 136
07.02.2019, 03:17  [ТС] 13
SomniPhobia, это почему так получается? Что за 777u - что за "u"???

Добавлено через 1 минуту
SomniPhobia, та это всё я знаю, хорошо понимаю. Просто тот же вопрос, вернее люди выше разъяснили - спасибо огромное - но всё же как-то сложновато. Массив вроде создается, но вроде бы и нет...Нужно еще повтыкать, подумать.
0
Avaddon74
564 / 348 / 132
Регистрация: 15.09.2017
Сообщений: 1,230
07.02.2019, 08:37 14
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Массив вроде создается, но вроде бы и нет
Ты запросил у ОС память, тебе дали, а вот сколько дали, это на усмотрение ОСи, поэтому тебе выше и написали, что поведение будет Неопределенно!

Добавлено через 1 минуту
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
что за "u"???
unsigned - положительное число
1
SomniPhobia
352 / 256 / 101
Регистрация: 22.11.2017
Сообщений: 707
07.02.2019, 10:02 15
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
Массив вроде создается, но вроде бы и нет...Нужно еще повтыкать, подумать.
Массив из 0 элементов создаётся в любом случае. Память выделяется, но она выделяется не под 0 элементов. У меня при создании массива 0 длины создаётся массив на 1 элемент (зарезервировано памяти под 1 элемент), но я мало раз запускал, может разное кол-во быть.
Таким образом, если ты запрашиваешь массив на 0 элементов, то он создаётся как минимум на 1 элемент.

Добавлено через 5 минут
Цитата Сообщение от MJ_PRUTYG Посмотреть сообщение
это почему так получается
При резервации памяти, при создании массива, перед этим участком памяти создаётся метка, в которой на -4 позиции прописывается объём памяти, выделенный под этот массив. Там также всякие разметки стоят до и после выделенного под массив участка памяти. До - много всяких ячеек inf о массиве, после одна метка завершения выделенного участка.
Оказывается, при передачи массива в стороннюю функцию, можно не передавать размер, а передать только указатель. Затем с помощью функции _msize(a) (она берёт значение с позиции -4), где a - указатель на 0 элемент массива, можно узнать сколько памяти занимает массив. С помощью функции sizeof(int) можно узнать сколько занимает в памяти 1 элемент типа int. Чтобы найти кол-во элементов в массиве нужно общий объём выделенной памяти разделить на объём, занимаемый одним элементом.
auto size = _msize(a) / sizeof(int);
2
COKPOWEHEU
1695 / 1159 / 266
Регистрация: 09.09.2017
Сообщений: 4,756
07.02.2019, 13:00 16
SomniPhobia, все это зависит от компилятора. В одном может быть так, в другом - по-другому. Например, при выделении памяти на 0 элементов может выдаваться целая страница памяти, или указатель в запрещенную для доступа область. Метаинформация о выделенной памяти может храниться в специальной структуре не привязанной к собственно данным. Подобные подробности стандартом не регламентируются и полагаться на них нельзя.
1
SomniPhobia
352 / 256 / 101
Регистрация: 22.11.2017
Сообщений: 707
07.02.2019, 13:05 17
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
все это зависит от компилятора
Я заметил, что в ряде случаев ситуации разрешаются одним компилятором так, другим иначе. Почему так в C++?
0
rat0r
256 / 149 / 19
Регистрация: 16.02.2018
Сообщений: 603
07.02.2019, 13:08 18
SomniPhobia, потому что неопределённое поведение.
1
COKPOWEHEU
1695 / 1159 / 266
Регистрация: 09.09.2017
Сообщений: 4,756
07.02.2019, 15:49 19
Цитата Сообщение от SomniPhobia Посмотреть сообщение
Я заметил, что в ряде случаев ситуации разрешаются одним компилятором так, другим иначе. Почему так в C++?
Это сделано из соображений оптимизации и переносимости. Язык Си (а следом и С++) задумывался как высокоуровневый ассемблер. То есть он должен уметь выжимать из железа все его возможности и минимально ограничивать как программиста, так и разработчика компилятора. Отсюда и неопределенный размер основных типов данных, и куча неопределенных поведений, и многое другое.
Если программист разумный (а язык Си это почему-то предполагает верным... излишне оптимистично), то он следует стандарту языка, а раз так, то все не описанное в стандарте отдается на откуп разработчикам компилятора, чтобы получаемый код оказался оптимальным. Например, в стандарте сказано, что переполнение знаковой переменной неопределено -> программист так делать не будет -> переполнение будет обрабатываться в соответствии с архитектурой процессора. В некоторых оно приводит к "закольцовыванию" значений, в других - к остановке на максимуме, в третьих - к исключению.
Второй вывод, который из этого следует: программист может воспользоваться неопределенным поведением если это приведет к существенному выигрышу. Но только при условии что он пишет строго под один процессор и под один компилятор, то есть если он точно знает что делает. И знает, что при портировании на другие процессоры и компиляторы возникнут проблемы.
1
07.02.2019, 15:49
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
07.02.2019, 15:49

Почему мне не удается заполнить двумерный булевый динамический массив?
Кносоль разрешает ввод только трех значений. #include &lt;cstdlib&gt; #include &lt;iostream&gt; using...

Не работает динамический массив
В массиве всегда один элемент. Что я делаю не так? int *p; p = new int ; p = 1; //в watch p = 1...

Как преобразовать массив в динамический? Массив вычисляет сумму элементов каждой диагонали матрицы
Ошибка : Вызвано исключение по адресу 0x00BB2F4F в Проект6.exe: 0xC0000005: нарушение прав доступа...


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

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

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