| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > Многомерные массивы и указатели |
| Автор: FiMa1 21.4.2007, 10:51 | ||
| Привет всем! Читаю "Программирование на языке Си" Афанасьева, появился вопрос... Автор пишет: "Следующие объявления переменных"
реализуют определенную схему размещения указателей на двумерный массив. Согласно этой схеме доступ к элементу a[0][0] можно получить по указателям a, p, pa при помощи следующих ссылок: a[0][0], *a, **a[0], *p, **pa, *p[0]. Уже на этапе инициализации массива int *pa[3] = { a, a[1], a[2] }; компилятор генерит error: 'initializing' : cannot convert from 'int [3][3]' to 'int *'. Помогите, пожалуйста, разобраться... |
| Автор: Anikmar 21.4.2007, 10:57 |
| Вот на это ругается: У вас pa имеет тип *[3], т.е. в каждом элементе указатель на int А при инициализации вы первому элементу присваиваете a, который имеет тип [3][3]; |
| Автор: FiMa1 21.4.2007, 11:51 | ||||||
Спасибо, но мне изначально было понятно, что в выражении int *pa[3] = { a, a[1], a[2] }; пытаются проинициализировать массив указателей целыми значениями.. Хорошо немного переформулирую вопрос. Есть следующие объявления переменных:
В данном случае, инициализация массива p указателями на элементы ОДНОМЕРНОГО массива a представлена второй строчкой кода. Теперь есть объявление МНОГОМЕРНОГО массива:
Как правильно проинициализировать массив указателей int *pa[3] элементами a[0][0], a[1][0] и a[2][0] ? |
| Автор: Anikmar 21.4.2007, 12:53 | ||
Попробуйте так:
А инициализация {....} на сколько я помню допускается только константами (могу ошибаться - не помню, всегда инициализировал только константами, поэтому вопроса не возникало) |
| Автор: Dov 21.4.2007, 12:56 | ||
FiMa1, выбирай:
|
| Автор: FiMa1 21.4.2007, 13:17 | ||
Ok, Dov, спасибо! До первого варианта уже сам додумался, а вот вариант int *pa[3] = { (int*)a, a[1], a[2] }; мне непонятен ... Здесь что указатель приводится к указателю на int? Не понимаю как это работает, поясните плз...
Anikmar, а что Вас смутило? Где используются НЕ константы? |
| Автор: Anikmar 21.4.2007, 13:28 |
Смутило, что a[...] может быть не константный. Но я не уверен - поэтому и написал, что точно не уверен. Хотя если массив объявлен статическим - то наверное константы... Просто ни разу так не делал - поэтому и засомневался. |
| Автор: apook 21.4.2007, 13:43 | ||||
Это ни кчемный вариант просто лажа какая-то Вот самый правильный вариант
|
| Автор: dizzy1984 21.4.2007, 13:46 | ||
| Насколько я знаю инициализатором переменной в си и си++ выступает любое выражение. Т.е вы можете, например, использовать вызов функции.
|
| Автор: FiMa1 21.4.2007, 13:53 | ||||||
apook, извини, но я не совсем согласен.... Этот вариант можно назвать самым правльным лишь только с точки зрения читабельности кода. Здесь явно задано, что мы берем адрес элемента [ 0 ][ 0 ] массива а и присваиваем его первому элементу-указателю массива и т.д. для остальных элементов. Тем не менее запись int *pa[3] = { a[0], a[1], a[2] }; это абсолютно тоже самое тому, что ты написал, т.к. символическое имя a массива - это уже по-сути "скрытый" указатель на первый элемент данного массива. Т.о. указание амперсанда & в начале каждого из элементов a не обязательно. А вот по-поводу никчемности варианта int *pa[3] = { (int*)a, a[1], a[2] }; вопрос открытый.. Ведь это работает... Dov однозначно рулит!!! Ему пасиб! Всем оставшимся не равнодушными тоже спасибо. Топик закрыт... |
| Автор: apook 21.4.2007, 14:03 | ||
| FiMa1 Да ты че не врубаешся в такую ерунду int a[ 3 ] Dov привел к int *a это сущей воды пурга,
если знаешь че тогда спрашиваещ |
| Автор: Dov 21.4.2007, 14:05 |
Ну, что же, apook, у тебя есть 3 варианта. 1. Обосновать своё утверждение и доказать свою правоту, тогда получишь +1 в репу. 2. Не обосновывать и ничего не доказывать, тогда получишь -1 в репу за необоснованый наезд. 3. Ивиниться за необоснованый наезд, тогда не получишь ничего. Желаю удачи. |
| Автор: apook 21.4.2007, 14:12 | ||||
зачем вносить путанницу вот этим
вариантом. он вообще тогда не доделан надо так
Не надо мугать меня минусами тебе во всякой ерунде наезды чудятся |
| Автор: Dov 21.4.2007, 14:22 |
Я тебя не пугаю, а учу правилам хорошего тона. Ты должен уметь отвечать за свой базар. Если ты мои слова называешь 'пургой' и 'какой-то лажей', то потрудись, хотя бы это обосновать. |
| Автор: apook 21.4.2007, 14:25 |
| Вынужден признать что моя решчь может показаться наездом тада извиняй. Но не надо ни минусов не плюсов это не тот случай Dov "Черт возьми" надеюсь ты не в обиде..... |
| Автор: Dov 21.4.2007, 14:28 | ||
Принято, проехали. |
| Автор: Rockie 21.4.2007, 14:37 | ||||
dizzy1984, если сигнатура функции позволяет, то конечно. Но в данном случае
|
| Автор: dizzy1984 21.4.2007, 14:47 | ||
И в данном случае все верно. Дело в том, что необходимость указания приведения присуща только компиляторам cи++ в си void* приводится к любому другому указателю и без явной указки. Пардон. Я писал и для того и для другого, тогда, конечно, мой пример работает только для си. Вы правы. |
| Автор: Fazil6 21.4.2007, 15:33 | ||
кто тебе сказал? двумерный массив не тоже самое что указатель на указатель |
| Автор: Dov 21.4.2007, 15:38 | ||
Fazil6, где у меня написано, что двумерный массив это указатель на указатель? |
| Автор: Fazil6 21.4.2007, 15:43 | ||
|
| Автор: Dov 21.4.2007, 15:51 |
| Fazil6, в этой цитате написано, что Имя массива а[][] имеет тип int**, т.е. указатель на указатель на int. Если ты не понимаешь разницу между словами 'массив' и 'имя массива', то почитай какие-нить книжки по этой теме, а потом спрашивай кто мне что сказал. |
| Автор: Fazil6 21.4.2007, 16:01 | ||||
ну а вот это уже точно наезд... И какая же разница в данном случае? Я применил слово массив именно к его имени. И если тебе не нравится формулировка, то имя массива не тоже самое что указатель на указатель. это скомпилится?
как ты думаешь? |
| Автор: Dov 21.4.2007, 16:16 |
Вот видишь, разницу ты не понимаешь. И ещё хочешь о чём-то спорить. Поэтому я тебе и сказал, что бы ты сначала книжки почитал, а потом уже спорил. |
| Автор: apook 21.4.2007, 16:23 |
| Осмелюсь вторгнуться в вашу дискуссию чтоб довести до вашего внимания тот факт что имя массива не является переменной. тогда может ли оно иметь тип? Тип может иметь начальный элемент массива синонимом расположения в памяти которого я вляется имя массива, тогда имя массива int а[3][3] имеет тот тип который у а[0] его элемента, а у того тип int a[3], поскольку это не массив указателей, а именно двумерный массив....или ( |
| Автор: FiMa1 21.4.2007, 16:25 | ||||||||
И (int*)a[0] и a[0] и *a и &(a[0][0]) это ОДНО И ТОЖЕ © Dov. вообще можем написать:
все элементы массива pa - указатели на первый (нулевой) элемент массива a. |
| Автор: Fazil6 21.4.2007, 16:34 | ||||||
не цепляйся к словам. Я коментируя твою фразу и в ней указано имя массива и все сказанное мной относится именно к имени массива. ок. Принимаем, что я ламер без доказательства. как насчет компиляции моего примера?
в свете того что
почему не приводится к int** ? |
| Автор: apook 21.4.2007, 16:59 | ||||||
Ну просто нелепо показалось приведение к указателному характеру одного единственного элемента массива
получилось pa[0]--*a; pa[1]--a[3]; pa[2]--a[3]; хитро как-то, хотя пускай так будет ладно...
Это почти одно и то-же, у указателя на массив и массива разный принцип сейчас подумал они все будут указателями, тогда опять же не понятно это приведение |
| Автор: vinter 21.4.2007, 17:07 | ||
Dov, ты не прав ,ты сам неверно выразился, а теперь еще и посылаешь книжки читать.
это два разных типа, а второй это как раз указатель на указатель, и это то к чему приципился Fazil6 |
| Автор: Dov 21.4.2007, 17:07 | ||
| Fazil6, если тебе так сильно хочется поспорить или, что было бы лучше, поучиться чему-нибудь хорошему, то открой соответсвующую тему, зачем же здесь оффтопить. По поводу этого. попробуй так:
Ну, а по поводу передачи двумерного массива в ф-цию в качестве аргумента, и здесь на форуме и в инете полным полно разных примеров. Нужно только поиском пользоваться. |
| Автор: vinter 21.4.2007, 17:13 | ||||
так делать нельзя, они физически в памяти по разному расположены как ты себе представляешь обращение к элементам? вспоминаем арифметику указателей и понимаем почему написали ерунду.
тебе бы как раз не мешало бы их поочитать, прежде чем так яростно спорить |
| Автор: Dov 21.4.2007, 17:44 | ||
Блин, да что за день сегодня такой. vinter, ты вопрос читал? Изначально вопрос звучал так: Я показал, что скомпилится. А описать указатель на двумерный массив можно так, например.
|
| Автор: vinter 21.4.2007, 17:46 |
нет, я читал вашь спор, и исходил только из него. |
| Автор: FiMa1 21.4.2007, 18:40 | ||
Dov, вопросик в догонку - а как выполняется прведение указателя на указатель к у просто указателю.. я не нашел.. Ну в приведении double к int понятно дробная часть откидывается, а здесь не пойму как это срабатывает.. Подскажи плз. |
| Автор: Fazil6 21.4.2007, 18:50 | ||||||
ты без конца отправляешь меня учиться чему-то (правда ты не говоришь чему именно). Я по твоему, ничего не понимаю, но внятно свою позицию ты отстоять не можешь. По твоему если я не согласен с высказываемыми тобой суждениям , то это офтоп? Мне кажется, что я могу доказать, что ты не прав, а добиться каких-то внятных объяснений почему я не прав от тебя я не могу. Постоянно отсылаешь к книгам... Можешь привести пример книги хоть одной, которая по твоему откроет мне глаза на проблемы многомерных массивов и связанных с ними указателей? Dov, Я привел конкретный пример и спрашиваю "почему он не компилится?" Я не спрашиваю "как его скомпилировать?" Чувствуешь разницу? reinterpret_cast - это ложь компилятору и с его помощью можно заставить много чего скомпилироваться. если исправить, то скомпилится , но если
|
| Автор: apook 21.4.2007, 19:54 |
| Прекращайте этот непонятный спор, я не доволен вашим поведением это что-за тон, кто вам показал дурацкий пример наезжать друг на друга. |
| Автор: JackYF 22.4.2007, 00:09 | ||||
Эта фраза мне определенно нравится... Имхо, правда больше на стороне Fazil6 здесь.
Массив вообще не имеет законченного типа, а уж тем более int**. Этот факт тоже не один раз проскакивал и выяснялся в темах. Dov, имхо, ты не прав. |
| Автор: Dov 22.4.2007, 02:26 | ||||||||
JackYF, в чём я не прав? В этом? Так я этого никогда не говорил. Я говорил только про имя массива, которое содержит адрес первого элемента массива, т.е. является указателем. Если массив состоит из элементов типа int, то имя массива имеет тип int*, т. е является указателем на тип int.
Отсюда следует, что и &ar[0] и ar имеют тип int*, т.к. являются адресами. Абсолютно то же самое можно сказать и про двумерный массив, который, по сути, является одномерным массивом, каждый элемент которого так же является одномерным массивом, и про трёхмерный и т.д.
И в том, и в другом случае имя массива имеет тип char**, так как содержит адрес первого элемента массива, который в свою очередь имеет тип char*. Разыменуем и тот и другой и посмотрим:
Оба выдают одинаковый результат. Доступ к элементам абсолютно идентичен. Можно объявить этот массив и так, это ни на что не повлияет:
Показал на пальцах, но уж извиняйте, как смог. Добавлю только, что многие не понимают разницу между массивом и именем массива. Имя массива - это указатель, хотя и константный, но всё же указатель. Почему? Да хотя бы потому, что содержит адрес, а адреса могут хранить только указатели. Массив - это последовательность элементов одного и того же типа, имеющих одно имя. Отсюда следует, что имя массива это только часть массива, и что оно указывает на начало массива. За сим откланиваюсь и иду спать, ибо лучше объяснить не смогу. |
| Автор: FiMa1 22.4.2007, 09:21 |
| Автор: JackYF 22.4.2007, 12:05 | ||
Нет. Не имеет. Читаем Страуструпа и/или стандарт. Нет, отсюда это не следует. Я тебе могу написать свой произвольный класс, который будет имитировать точно такое же поведение (возвращение элементов по индексу). Но у него явно не будет тип int* Ха. Это ты к первому элементу матрицы так можешь обратиться через [0][0], так как смещений нет. Постараюсь показать, что a[x][y] не имеет тип int** Следующий код принципиально неверен:
Вторая часть этого кода работать принципиально не будет. |
| Автор: Xenon 22.4.2007, 13:26 | ||||||
Следовательно утверждать, что имя массива имеет типа int* нельзя. К тому же похожий пример есть у Страуструпа - стр. 131 Добавлено @ 13:29 И вообще чего мы так спорим, если есть RTTI?
Выдаст int [2], но никак не int*. |
| Автор: Fazil6 22.4.2007, 17:12 | ||||||||
похоже ты как раз и относишься к тем многим, которые этого не понимают. Все, что ты привел по поводу одномерного массива, его имени и приведения его к указателю правильно, вот только когда речь зашла о двумерном массиве твоя теория поплыла... Причем сильно....
вопервых не имеет тип char** , а может использоваться в таком качестве, но опустим эти лингвистические нюансы - дело намного хуже. ar1 ну никак не char**. И все потому, что кое кто действительно не понимает разницу между массивом и именем массива.
вот именно, и что мы имеем? Имя ar1 это указатель на массив char[4]. Где здесь имя, которое можно представлять как указатель? Запомни : имя двумерного массива интов - это указатель на массив интов, но не как не указатель на указатель на int именно по причине |
| Автор: Xenon 22.4.2007, 18:05 | ||
Опять же, RTTI :
Выдает char[3][4]. Нельзя приравнять два разных типа T[] и T* - это хоть и похожие вещи, но на самом деле разные. |
| Автор: Dov 22.4.2007, 21:50 | ||||||||||||||||||||
Ух, скока накатали. Сразу говорю, что всё ни асилю.
Оставлю без комментария, хотя этому перлу прямая дорога на башорг, имхо.
JackYF, так я могу обритится к любому этементу матрицы, используя адресную арифметику указателей. Ну, например так:
Каким образом ты это пытался показать, я так и не понял.
То, что код не верен, это правда. Но я опять не понял, что именно не работает? Не компилится или не приводится или вывод не работает или выводит, но не правильно? Что не работает?
Или тебе надо, что бы в виде таблицы выводилось? Скажи "принципиально" Fazil6, весь твой пост оставляю без комментариев, кроме двух фраз, а именно:
Эти, имхо, на башорг адназначна. Да, а по поводу этого:
Я уже говорил, но для тебя ещё раз повторю. ar1 это указатель на первый элемент массива, т.е. на строку(или массив, если хочешь) "qwe". А имя, "которое можно представлять как указатель" звучит так(ты не поверишь) : ar1[0] или *ar1, как тебе больше нравится. Xenon, с тобой я соглашусь, конечно, но только не сейчас. По той причине, что ты не попадаешь в контекст текущей полемики. Здесь идёт разговор о типах данных в плане приведения типов... Что-то устал я уже, не могу правильно сформулировать, но это не важно. Ты в самом начале посмотри и всё поймёшь. Ну, типа того, что так сделать можно:
а так нельзя:
А может и можно, но я не знаю как. Тогда покажи. Камрады! Желаю всем удачи! А я из дискуссии выбываю. Притомился я тут с вами. Всем пока. |
| Автор: Fazil6 22.4.2007, 22:13 | ||||||||||||
как всетаки сильно желание выдать желаемое за действительное.... Я и код привел тебе, на который компиллятор упорно твердит, что int [][] это не int** и никто с тобой не соглашается, но ты уверенно гнешь свою линию... Почему не компиллируется всетаки мой пример?
не работает приведение типов в том виде, в котором ты утверждаешь. не приведется это
и не приведется именно потому, что твое утверждение по поводу многомерных массивов ошибочно.
повторяю, что имя многомерного массива можно использовать как указатель на его первый элемент, но 1-м элементом многомерного массива является массив, а не имя массива и рассматривать его в свою очередь указателем вкорне неправильно. Ты ведь сам просил не путать массив с его именем кстати внизу есть ссылки. Вот например и там сразу рассматривается именно этот вопрос http://forum.vingrad.ru/topic-98636.html а как же твои слова ???
|
| Автор: apook 23.4.2007, 04:30 | ||||
Dov Повторяю :указатель это переменная, а имя массива переменной не является(Не по той причине что это константный указатель) Есть имя массива a и есть обьект массива с именем a[0], так вот они синонимы, в памяти не указывает, а есть начало массива. походу так и рассматривается компилятором. Есть указатель pa который являясь указателем выступает в роли имени массива, в памяти он указывает на начальный элемент массива но сам им не является, он расположен в другом месте.
Все свелось к выяснению разницы между массвами и указателями и вот еще: Если имя массива передается функции, то последняя получает в качестве аргумента адрес его начального элемента. Внутри вызываемой функции этот аргумент является локальной переменной,содержащей адрес. т.е функция действительно всегда имеет дело с указателем. тогда уж имя массива это массив имен переменных |
| Автор: Ln78 23.4.2007, 08:50 | ||
| Dov, зачем споришь, если сам давно понял, что неправ? Помог FiMa1 разобраться - молодец. Но дальше:
это как раз ошибка, такое приведение, будь это так, недопустимо. Fazil6 как раз и указал тебе на это. И привёл короткий и наглядный пример. Допустим, элемент a[0][0] расположен по адресу 0x00123400. Получаем: &a[0][0] = 0x00123400; &a[0][1] = 0x00123404; &a[1][0] = 0x0012340C; a[0] = 0x00123400; a[1] = 0x0012340C; a = 0x00123400; *a = 11; (в исходном примере, или 0 в примере Fazil6). Именно это и означает, что a - указатель на int, а потому твоё первое приведение типа совсем не "пурга", а правильно работает. Если бы a был указателем на указатель на int, это бы означало, что для доступа к этому int нужно брать *(*a), т.е. содержимое с адресом 11 (или 0). Своим спором ты, с одной стороны, теряешь репутацию у тех, кому очевидна твоя неправота, с другой, что хуже, запутываешь начинающих. И зачем оно тебе надо? Добавлено через 10 минут и 12 секунд Для большей аккуратности вместо *a = 11; Следовало бы написать *((int*)a) = 11; |
| Автор: dizzy1984 23.4.2007, 10:30 |
| Dov, ты в самом деле не прав [][] и ** это разные типы. Будет правильно если ты разберешься в теме и тебе, похоже, стоит извениться перед Fazil6. |
| Автор: bel_nikita 23.4.2007, 11:41 |
| дабы прекратить спор раз и навсегд читаем: http://www.rsdn.ru/Forum/Message.aspx?mid=177690&only=1 (А разве char a[] не эквивалентно char* a ?) |
| Автор: JackYF 23.4.2007, 18:12 | ||||
обоснования? А ты запусти и узнаешь.
+1, поддерживаю. |
| Автор: Xenon 23.4.2007, 18:26 |
| bel_nikita имел ввиду это http://www.rsdn.ru/Forum/Message.aspx?mid=177690&only=1 |
| Автор: Dov 23.4.2007, 23:01 | ||||||||||||||||||
Ничего не нужно выдавать. Нужно всего лишь написать одну строку:
А если ты утверждаешь, что имя массива или же сам массив имеет тип int [][], то покажи мне, как нужно написать. Так, что ли, или как то иначе:
Это да, хотя никто не говорит в чём имеено. Вернее, не соглашаются в том, чего я не говорил. А я откуда знаю, это ты должен знать. Мои примеры все компиллируются.
Если рассматривать его, как одномерный массив массивов(например массив строк), то да. А если просто, как двумерный(например, двумерный массив одиночных символов), то нет. В этом случае 1-м элементом многомерного массива будет элемент с индексом[0][0], т.е. первый одиночный символ этого массива. Имя массива(например, целочисленного, в данном случае), не может быть не только первым, но и вообще никаким элементом массива, по одной простой причине. Имя массива это адрес певого элемена массива, т.е. указатель, а не целое число. Сколько раз я ещё должен это повторять, что бы до вас дошло? Ур-ра, дошло. Совершенно верно, массив это не указатель. Указателем является только адрес первого элемента массива, т.е. его(массива) имя. Fazil6, как-то полосами до тебя доходит, имхо. Так это ж я не тебе говорил, а apook`у. Или ты хочешь, что бы я это к тебе применил? Кстати, прошу всех обратить внимание на то, что apook после этого стал намного культурнее в общении, вот посмотрите на его посты до этого и после. Это две большие разницы. apook, ты молодец. Стало намного приятнее читать твои посты. Честно.
Читай выше. Ну, что тут можно сказать? Читай книжки. Пока всё.
dizzy1984, читай выше или ткни меня носом, где я говорил, что это одно и то же. Про такой тип int[][], я только в этой теме узнал, что он вобще существует. И до сих пор не могу понять, как можно его, или к нему что-либо привести. Лады, постараюсь. Не вопрос, скажи только в чём я виноват. JackYF, это моё мнение и я не должен его обосновывать или что-то доказывать, я его просто высказываю. Это не значит, что я хочу тебя чем-то обидеть, боже упаси. Просто, мне это показалось смешным. Я то запущу, конечно, но перед этим мне хотелось бы, что бы ты сказал, что твой код должен делать по твоему разумению, но, гадюка такая, не делает. Если кому-то не ответил, извините. Вас много, а я один тут за всех отбиваюсь. Всем удачи. |
| Автор: Fazil6 24.4.2007, 00:13 | ||||||||||||||||||||
Dov, Твое высокомерие переходит всякие границы...
возможно, ты сильно удивишься, но строка "qwe" имеет тип char[4] - и никакой другой (совсем не char*)
Бред... Ахинея... Приведи мне выдержку из стандарта или цитату из любой книги, где так будет написано (про двумерный массив и его имя и что его имя это указатель на указатель). Это указатель на первый элемент. А массив int a[3][3] является одномерным массивом массивов int[3], поэтому первым элементом будет массив и имя а является указателем на int[3] , а это не указатель и поэтому a не является указателем на указатель.
вот это кто говорил?
Именно с этим не соглашаются.
Ну ты же все знаешь.... Не можешь простейшие ошибки найти в пяти строках? не компилится именно потому, что имя двумерного массива int не является указателем на указатель int (int**). Именно об этом говорит компилятор. Не надо только приводить доводы, что reinterpret_cast поможет. Если бы твои домыслы были верны, то пример обязан был компилироваться.
Это мой код. Ты отвечай за себя. Скомпилится Это? a здесь имя массива. По твоему это int**, получается , что одному int** присваивается другой int**,что мешает здесь скомпилироваться?
Вот только не надо строить из себя дядю наставляющего молодежь. После будешь выглядеть очень смешно...
Я хочу чтобы ты их к себе применил, раз уж берешь на себя ответственность делать замечания, то начни с себя. |
| Автор: Fazil6 24.4.2007, 00:55 | ||||
вот еще примерчик
Добавлено @ 01:03
полный тест http://faqs.org.ru/progr/c_cpp/cfaqrus2.htm |
| Автор: vinter 24.4.2007, 08:39 |
| |
| Автор: Dov 24.4.2007, 11:59 | ||||||||||||||||||
| Причём здесь высокомерие? Fazil6, послушай меня. Вот ты не злись, когда читаешь, то, что я написал, а вдумывайся и старайся понять смысл написанного. Выкинь ты из головы эти стандарты, как будто нет их, ведь для того, что бы понять принцып работы они не нужны. Цитаты из книги я могу тебе дать, но книга написана на иврите. Если ты хочешь, я могу тебе дать цитаты с переводом на русский язык. Книгу написал Йоав Натив, израильский специалист. Он автор многих книг по программированию, в частности по С/С++, системный аналитик, имеет научную степень. Кстати, это к твоему вопросу о том, кто мне сказал. И вобще, не важно, что кто где сказал. Важно понять, как это всё работает.
Я понимаю, что компилятор тебе написал char[4], но ты пойми, он это сделал для того, что бы ты увидел и понял к чему он придрался. Иначе, как он тебе это объяснит? Но сам он воспринимает эту строку именно, как char*.
Рассмотрим подробнее подмассив а[0]. Это массив из 3 целых чисел типа int. Если а это имя для всего двумерного массива, то а[0] это имя только для этого подмассива. Ко всем трём элементам этого подмассива можно обращаться, указывая его имя а[0] и добавляя к нему индекс элемента. Что бы обратиться, например к последнему элементу, нужно написать a[0][2]. Поскольку этот подмассив, по-существу, является обычным массивом, то его имя а[0] является указателем на первый элемент а[0][0] и содержит его адрес. В этом легко убедиться.
Выведется один и тот же адрес. Делаем вывод, что имя подмассива a[0] является указателем на первый элемент, так как хранит его адрес, поэтому имеет тип указатель на инт, т.е. int*. Это понятно?
Разыменуй его:
Отсюда следует, что имя массива а, указывая на первый элемент т. е. на а[0], является указателем на указатель на инт, потому что, как мы выяснили выше, имя подмассива а[0] это указатель на инт. А поскольку имя массива а это указатель на указатель на инт, то он имеет тип int**. Все эти объяснения даны, как бы, с точки зрения компилятора, а не с точки зрения того, кто смотрит на экран монитора и видит там надпись int[][], а что это такое и как его применить он не знает. Всё, на этом пока остановимся. Если кто не понимает моих объяснений, я не виноват. Я ни кому не навязываю своё мнение. Если кто-то считает, что int[3] или int[333] или int[3][3] это какой-то тип, к которому можно хоть что-то привести, то ради бога, я не возражаю. Хотелось бы только взглянуть на это приведение. Ну, вот. Написал несколько строчек, а потратил на это несколько часов. Всем пока. Извиняюсь, забыл про пример. Если ты внимательно прочтёшь то, что я написал, то поймёшь, что нужно было бы, хотя бы, написать так:
А почему, я уже устал объяснять. Удачи. |
| Автор: bel_nikita 24.4.2007, 12:38 | ||||||
| Dov, Чтобы понять разницу между int a[] и int *a - не нужно никаких книг, и уж тем более цитат. .
Смотрим через дизасм:
Из этого всего видно, что a[3] и b[3] компилятор получает по-разному. Когда встречается а[3]: компилятор генерирует код, чтобы считать с позиции a[3]: 1) перемещается на три символа вперед 2) читает требуемый символ. В случае b[3] компилятор генерирует код, чтобы начать с позиции b: 1) считывает значение указателя 2) прибавляет к указателю 3 3) читает символ, на который указывает указатель. Т.е. int a[] != int *b. |
| Автор: Fazil6 24.4.2007, 12:57 | ||||||||||||||||
очень смешно... Сам придумал? Когда-то строки определялись char*, но это было когда-то и
Страуструп. Язык программирования С++. Специальное издание. стр. 84
там же стр.130
нифига подобного. Он воспринимает тип char[n] вот довольно простой пример
ничего я не путаю. Это ты путаешь.
именно можно использовать, но не является. И по поводу первого элемента многомерного масива - это массив ибо Многомерный массив - это массив массивов. Все мои примеры абсурдности твоих умозаключений ты игнорируешь.
ничего из этого не следует кроме того, что указатель и массив - это разные типы. |
| Автор: dizzy1984 24.4.2007, 13:55 |
| Хранятся ли в массиве char[10][10] указатели можно проверить с помощью окна Memory. |
| Автор: Daevaorn 24.4.2007, 14:23 | ||
что за окно? |
| Автор: Dov 24.4.2007, 14:28 | ||
| М-да. Как горохом об стену... bel_nikita, я тебе про ерёму, а ты мне про фому. Нет. Я вроде бы по-русски пишу, а вы не понимаете. Щас напишу большими буквами, что видно было хорошо. Я говорю, что ИМЯ ОДНОМЕРНОГО МАССИВА ЦЕЛЫХ ЧИСЕЛ - ЭТО УКАЗАТЕЛЬ НА ТИП ИНТ( ПОТОМУ ЧТО ХРАНИТ АДРЕС СВОЕГО ПЕРВОГО ЭТЕМЕНТА, КОТОРЫЙ ИМЕЕТ ТИП ИНТ) И ПО ЭТОМУ ИМЕЕТ ТИП ИНТ*(КАК И ВСЕ ОБЫЧНЫЕ УКАЗАТЕЛИ НА ТИП ИНТ). А ИМЯ ДВУМЕРНОГО МАССИВА ЭТО УКАЗАТЕЛЬ НА УКАЗАЗАТЕЛЬ НА ТИП ИНТ(ПОТОМУ ЧТО ХРАНИТ АДРЕС СВОЕГО ПЕРВОГО ЭТЕМЕНТА, КОТОРЫЙ ИМЕЕТ ТИП УКАЗАЗАТЕЛЬ НА ТИП ИНТ) И ПО ЭТОМУ ИМЕЕТ ТИП ИНТ**(КАК И ВСЕ ОБЫЧНЫЕ УКАЗАТЕЛИ НА ТИП ИНТ*). И не имеет никакого значения, как был объявлен этот массив, так или так Оба имени это указатели на тип инт, а по этому имеют тип инт*, не смотря на те разтичия, что есть между ними. Ладно, у меня такой вопрос. Есть двумерный статический массив 3х3 .И вот есть такой кусок кода. Вопрос очень простой. Какой тип, по вашему, имеет указатель pAr и как он был объявлен. Если кому не трудно допишите недостающий код в начале main.
|
| Автор: likehood 24.4.2007, 14:35 |
да хоть void *pAr; |
| Автор: Dov 24.4.2007, 14:40 |
| Вообще этот разговор похож на это. А. - дважды два равно 4. Б. - нет. трижды три не равно 4. А. - так я и не говорю, что трижды три равно 4. я говорю, что дважды два равно 4. Б. - как оно может быть равно 4, если в таблице умножения не написано, что трижды три равно 4. А. - да выкинь ты свою таблицу умножения, на пальцах прибавь 2 + 2. Будет 4. Б. - прибавляй, не прибавляй, всё-равно трижды три не равно 4. Вот так мы и разговариваем. Добавлено через 7 минут и 31 секунду Можно и так, конечно, но давайте пока в рамках int`а, что бы не путаться. И как по-русски назвать его тип? Добавлено через 10 минут и 29 секунд Вот я, например писал так: int* это указатель на целое число. int** это указатель на указатель на целое число. И т.д. А вы как назовёте? |
| Автор: Fazil6 24.4.2007, 14:52 | ||||||||||||
а мы все тебе говорим, что НЕТ, НЕ ТАК. ЭТО ТВОЕ ЗАБЛУЖДЕНИЕ.
Еще раз повторю, в первом случае имя может использоваться КАК указатель, а тип у него int[10] и правило по поводу имени массива нерекурсивно и имя многомерного массива не может использоваться как указатель на указатель и т.д. А все приемы типа явного приведения
это обман компилятора и ни к чему это привести не может. В этом примере, например, выведется совершенно разный результат. Твои примеры с строками просто неграмотные и ничего не доказывают просто из-за того что массивы char интерпретируются в соответтвующих операторах. Мой пример с шаблонами показывает что компилятор рассматривает именно как массивы, а для представления как указатель ему требуется выполнять приведение массива (или его имени как тебе будет удобнее). Добавлено через 31 секунду
и при чем здесь массивы? |
| Автор: Dov 24.4.2007, 14:58 |
Я про pAr спрашиваю. Добавлено через 3 минуты и 51 секунду Fazil6, в предыдущем своём посте я тебе показал, как нужно делать. Или в предпредыдущем. |
| Автор: Fazil6 24.4.2007, 15:15 | ||||||||||
ну вот так будет работать видимо как ты хотел показать. pAr здесь типа int[3][3], использовать можно как указатель на int[3]. Не совсем понимаю что ты хотел продемонстрировать.
правое выражение вполне нормально интерпретируется как значение инта по какому-то адресу... Ну продемонстрировал ты что элемент массива можно проинициализировать через одно место... Ну и что? Работает это потому же почему
выдаст абсолютно одно и тоже именно потому, что pAr массив Добавлено через 7 минут и 21 секунду
то чтоли?
и где здесь указатель на указатель? тут сказано, что значение в aa считать указателем на инт и вывести значение по этому адресу. И что? Ну обманул ты компилятор... Разве это что-то доказывает? |
| Автор: Dov 24.4.2007, 15:29 | ||
Fazil6, совершенно верно, именно это я и хотел увидеть. Что бы показать ещё и такой код:
Теперь ты воочию можешь убедиться, что для компилятора без разницы, как был объявлен pAr. Главное это как его воспринимает компилятор. А для него это тип int**. |
| Автор: Fazil6 24.4.2007, 15:40 | ||||
ха-ха... Зачем же ты его везде приводишь к int* ? Действительно для компилятора без разницы. Ты ведь заткнул ему рот явными приведениями совершенно к другому типу. И он его воспринимает именно как int* везде кроме объявления pAr
|
| Автор: likehood 24.4.2007, 15:40 | ||
Кстати, так тоже работает:
|
| Автор: Fazil6 24.4.2007, 15:42 |
| это как раз и доказывает, что int p[3][3]; никаким способом в int** не превратить |
| Автор: bel_nikita 24.4.2007, 15:55 | ||||||||||
Dov,
Повторюсь еще раз,
Но если по-твоему int a[3] это обычный указатель int*, тогда объясни всему миру почему не прокатывает:
Зашибись! Напиши pAr[1][1] = 1; и удивись |
| Автор: Dov 24.4.2007, 15:57 | ||
Хорошо, тогда скажи мне, что тебя может убедить в том, что имя двумерного массива это указатель на указатель? |
| Автор: likehood 24.4.2007, 16:01 | ||
Если бы его можно было присвоить указателю на указатель и потом работать с этим указателем как с обычным двумерным массивом. |
| Автор: Dov 24.4.2007, 16:04 | ||
bel_nikita, ну ёлы-палы, ну сколько можно?
Цитату в студию, где я такое говорил? Только перед тем, как её постить, посмотри, что там написано, я тебя очень прошу. |
| Автор: Fazil6 24.4.2007, 16:08 | ||||
ну, например если бы компиллировалось
то я вынужден был бы признать, что ничего не понимаю и не знаю. Не представляю что ты можешь привести для доказательства своей теории ибо она ошибочна и доказательства не имеет по определению. В противном случае никто с тобой не спорил бы. Со мной, например, кроме тебя никто не спорит в этой ветке. |
| Автор: Dov 24.4.2007, 16:12 |
| likehood, тогда скажи мне, что же это такое указатель на указатель В твоём понимании, конечно. И какой тип он имеет. А заодно скажи мне, что такое указатель, опять же в твоём понимании. Возможно, мы оперируем разными терминами? Я своё определение уже дал. Добавлено через 3 минуты и 55 секунд Fazil6, ты тоже скажи, если не трудно, про то, что я спросил у likehood. |
| Автор: dizzy1984 24.4.2007, 16:26 |
| Daevaorn, Имелось в виду средство для просмотра памяти отладчика vs. View->Debug Windows->Memory (alt+6). С его помощью можно было бы посмотреть для случая char arr[10][10]={'a','b','c','d'}. На место памяти arr. Там должен был бы быть адрес одномерного массива, если бы был прав Dov, либо 'a''b''c''d', если бы был прав Fazil6. |
| Автор: Fazil6 24.4.2007, 16:36 | ||||
меня вполне устраивает тот смысл, который ты вкладываешь в указатели. Меня неустраивает то как ты легко мешаешь указатели, массивы и их имена(которые могут использоваться как указатели).
это неверно и безграмотно. |
| Автор: bel_nikita 24.4.2007, 16:37 | ||||||||
Dov,
Итак, вернемся к дискусии. Хорошо, по-твоему int a[] == int *b, т.е. int a[] - указатель и int*b - указатель. Тогда объясни мне, пожалуйста, почему не прокатывает следующее:
И еще вернемся к твоему примеру,
Обясни, пожалуйста, почему происходит Ба-Бах! в 7-ой строке? З.Ы.: Dov, ты все просишь, чтоб другие отвечали на твои вопросы: что же это такое указатель на указатель; что такое указатель и т.д. И я тебя прошу: ответь хоть на один мой вопрос, вместо очередного увиливания от ответа и придирки к словам. |
| Автор: Dov 24.4.2007, 17:04 | ||
| bel_nikita, не в обиду тебе, скажу. Во всей этой теме есть всего десяток моих постов. Там есть все ответы на твои вопросы. Там написано, что такое массив и что такое имя массива. Там написано, что имя массива, хоть и указатель, но константный, поэтому и не будет работать твоё присваивание, ну и так далее. Сначала нужно почитать всё внимательно, а потом уже обвинять меня в том, чего я не говорил. А то получается так, читать не хотим, давай рассказывай нам в 1000-й раз одно и тоже. Вы что думаете, мне больше заняться нечем ? Убил здесь столько времени и всё заново начинать. Так что ли? Нет уж, надоело. Повторю ещё раз то, что я сказал раньше.
Всё сказанное в этой теме, моё личное, персональное имхо. всем удачи. |
| Автор: Daevaorn 24.4.2007, 17:34 |
| Dov, жесть. Чушь несешь полную. Почитай перед сном стандарт. Должно вылечить. |
| Автор: Ln78 24.4.2007, 17:34 | ||
Добавлено через 3 минуты и 13 секунд Для большей строгости, конечно, лучше
но это дело не меняет |
| Автор: Fazil6 24.4.2007, 17:46 |
на большинство нету. Ни на чьи вопросы, которые неудобны и действительно противоречат твоим теориям ты не ответил... Своих внятных примеров ты не привел. Если привел, то дай хотябы одну ссылку. |
| Автор: Xenon 24.4.2007, 19:19 | ||||||
Если бы было так, то ...
Почему-то всегда выдает false bel_nikita, в твоем примере
можно оправдаться и сказать, что a имеет тип int* const pTr, поэтому присваивание невозможно |
| Автор: Dov 24.4.2007, 23:43 | ||||||||||||||||
Ты хоть понимаешь, что ты здесь написал. Ты пытаешься указателю на указатель на инт присвоить адрес двумерного массива. То есть, грубо говоря, вместо int** ты ему подсовываешь int*** и хочешь, что бы это работало. Без приведения это работать не будет. Такому указателю ты можешь присвоить адрес указателя на подмассив массива, но не как не адрес самого массива. Например, ты можешь сделать так:
Ну, или извращаться и использовать приведение... Fazil6, я тоже с тобой не спорю, я,лишь, высказываю своё мнение.
А самому что, никак не разобраться?
Так, а теперь к делу.
Я сказал, что имя двумерного целочисленного массива имеет тип указатель на указатель на инт. Именно эти мои слова считают здесь чушью и бредом. Вот мои утверждения: 1. Указатель хранит адрес той ячейки памяти, на которую он указывает. Если есть такой код:
2. Значением указателя р будет выражение &var, то есть адрес переменной var. 3. Значение по этому адресу можно получить разыменовав указатель р. 4.Доказательством того, что р является указателем служит тот факт, что переменной var2 было присвоено значение 5, которое хранится в ячейке памяти, на которую указывает р, то есть значение переменной var. 5. Разыменование указателя р, то есть получение значения с той ячейки памяти, на которую он указывает это доказательство того, что р это указатель на int. 6. Поскольку рр содержит адрес указателя р, который является указателем на int, то это доказательтство того, что рр является указателем на указатель на int. 7. Разыменование указателя рр, то есть получение значения с той ячейки памяти, на которую он указывает это доказательство того, что рр это указатель на указатель на int. 8. Если значение переменной var3 равно значению var, то это доказательство того, что рр это укзатеть на указатель на int, поскольку через указатель рр мы получили значение ячейки, на которую указывает указатель р, являющийся указателем на int. 9. Таким образом выражение var3 = *(*рр) это доказательство того, что рр это указатель на указатель на int. Я хочу знать кто с чем не согласен. Кто хочет поучаствовать пишите всего два слова, номер утверждения и ответ на него, т.е. да или нет. Например: 1. нет 2. да и т.д. На сегодня всё, ребятки. Пошёл читать устав, или как там его. А, вспомнил. Стандарт. Всем пока. И удачи... в изучении стандарта. |
| Автор: JackYF 25.4.2007, 00:06 | ||||||
Ну где, где ты там увидел int***? Мы уже разобрались. Мы просим тебя ответить.
Тебе уже не раз говорили. Еще раз повторяю. Имя массива вообще не имеет законченного типа! Кури стандарт. У тебя в коде ни одного массива. Спасибо за работу с указателями. Мы говорим про массивы, не так ли?
Привыкай. Устав - не устав, но против стандарта не попрешь. MFC - это еще не все. |
| Автор: apook 25.4.2007, 03:54 | ||||||
Клевый спор
не имя может использоваться как указатель, а указатель может использоваться в качестве имени, а имя не есть указатель, а содержит адрес первого элемента массива.
Dov интересная гипотеза? Я думаю что 1-м элементом многомерного массива a[3][3] будет обьект с именем a[3], который в свою очередь содержит адрес элемента с индексом 0.
Dov я тебя понял Проникнувшись этим я утверждаю что не существует типа int[3][3] и int*, а есть int и все остальные значки это прибамбасы! И буду в чем-то прав. Бывает... Dov Хочется поставить плюсик но ситуация не позволяет, все же ты не прав |
| Автор: zkv 25.4.2007, 08:29 | ||
да, Dov, ты не прав |
| Автор: Kipter 25.4.2007, 08:44 |
| Да ладно вам, застыдили Dov'a... Он понимает как это работает и саму суть.... только с теорией плохо.... |
| Автор: Fazil6 25.4.2007, 08:57 | ||||||||||
Мда... Вот это уже точно жесть.... Такого отвязного бреда еще небыло в этой ветке.... Ты же все время утверждал, что имя двумерного массива является int**... Теперь это у тебя ты уж определись какой бред тебе ближе. Жесть... С приведением это будет компиллироваться. Работать это никогда не будет. Именно поэтому ты и не докажешь никому ничего. и где я сказал, что хочу чтобы это работало? Я сказал, что по твоей теории это правильный код
как правильно с массивами работать все понимают, вот только там нигде нет указателей на указатель как ни крути и не приводи.
воттолько причем здесь массивы и их имена?
эх, зря ты это написал... Добавлено через 1 минуту и 36 секунд
похоже, нихрена он не понимает... |
| Автор: bel_nikita 25.4.2007, 09:58 | ||||||||||
Dov,
Не съезжай всторону. Чушью и бредом считают твой приведенный код. Вот, некоторый фрагмент:
Dov, почитал твои посты... И скажу твоими же цитатами:
З.Ы.:
|
| Автор: Fazil6 25.4.2007, 10:02 | ||
те слова тоже бред и чушь |
| Автор: dizzy1984 25.4.2007, 12:30 |
| А что интересно так, когда скучно становится всегда знаешь что почитать... |
| Автор: FiMa1 25.4.2007, 14:41 | ||
| Программа динамического размещения трехмерного массива. Вываливается Debug Assertion Failed при "освобождении памяти, выделенной под указатели на столбцы одномерных массивов" (сорри за корявый коммент). Не пойму что не так делаю, поправьте, меня плз...
|
| Автор: JackYF 25.4.2007, 15:07 | ||||
Вот тут неверно. Инициализация делается отдельными тремя циклами. Надо так:
|
| Автор: Anikmar 25.4.2007, 15:20 | ||
| JackYF, я не уверен, что вы правильно подсказали. У вас по-моему память под строки не выделяется. Мой вариант:
Добавлено через 39 секунд Правда мой вывод отличается от того, что надо, ну и размеры 5.5.5 можно другие поставить |
| Автор: FiMa1 25.4.2007, 15:21 | ||||||||
Соответственно пишу:
Unhandled exception... Access violation writing location... при инициализации в строке
|
| Автор: apook 25.4.2007, 15:47 | ||
бросай указатели
|
| Автор: JackYF 25.4.2007, 16:42 | ||
Так нельзя! a[i][j][k] !=***(a + i + j + k ) Так что действительно бросай использовать указательную арифметику там, где она не нужна. Добавлено через 2 минуты и 38 секунд
Чем отличается от моего варианта? Правда, у меня только создание, остальное тоже надо править. Если ты имел в виду, что я не написал исправления остального кода, тогда да. |
| Автор: Partizan 25.4.2007, 20:06 |
| Даааа ) Прочёл весь топик от начала до конца ) Интересные вещи начитал ) и даже несколько утверждений себе на заметку взял) Истина рождаеццо в споре, как говорят) |
| Автор: Anikmar 26.4.2007, 09:04 | ||||||||
| JackYF, Ваш код:
Это мы выделили память под str двухмерных массивов
Это мы выделили память под массив указателей на строки двумерного массива (создали столбец) - Или создали строку с указаетлями на столбцы (как кто рассматривает)
А вот дальше я не понял. У нас есть только указатели. И сы создаем по одному элементу. А где используется третья размерность? В примере она tdim. Я ее не увидел. Получается по 3-му измерению у вас создается только 1 элемент. У меня эта часть сделана так:
Третье измерение - у меня Size2. |
| Автор: FiMa1 26.4.2007, 11:58 | ||
Всем привет! Друзья, торжественно клянусь никогда не использовать (да и не собирался) указательную арифметику для подобного запутывания кода! Однако, следует заметить, что данной код писался исключительно в целях изучения процедур доступа к i-му элементу многомерного массива посредством указателей (с этим у меня затруднения). Посему, пожалуйста, ответьте на следующие мои вопросы: Есть массив int array[i][j][k]; 1. Как ПОСРЕДСТВОМ УКАЗАТЕЛЕЙ записать в элемент [0][1][2] массива array значение, к примеру, 100? 2. Как ПОСРЕДСТВОМ УКАЗАТЕЛЕЙ прочитать значение элемента [0][1][2] массива array? Отдельное СПАСИБО тому, кто отважится закрепить ответы написанием программы динамического размещения массива array[][][] ПОСРЕДСТВОМ УКАЗАТЕЛЕЙ и его инициализации ПОСРЕДСТВОМ УКАЗАТЕЛЕЙ (не забудьте удалить массив ;). PS: Думаю, это можно будет считать хорошим логическим завершением теоритических изысканий топика. |
| Автор: Anikmar 26.4.2007, 12:56 | ||||||
Не понял. Мой пример и создает и выводит и удаляет - и все только через указатели. Чем не устраивает?
|
| Автор: FiMa1 26.4.2007, 13:19 | ||||||||||||
Да через указатели, но не "явно"... Пусть есть одномерный массив int a[3]; тогда доступ к третьему (фактически второму, нумерация с нуля) элементу массива при явной записи с использованием указателей будет выглядеть как:
Как проделать тоже самое с трехмерным массивом array[][][] ? Добавлено @ 13:30
Народ, так что ли правильно?
|
| Автор: Ln78 26.4.2007, 13:50 | ||||
| FiMa1, не так. Для первого раза можно так
На эту тему есть много хороших книг. Если очень нужно, могу попробовать написать для тебя небольшое пояснение, но только позже Добавлено через 6 минут и 56 секунд Ну вот, и сам ошибся, надо
|
| Автор: FiMa1 26.4.2007, 14:03 | ||||
Название книги в студию, пли-и-и-з. Правда сам не нашел именно про это нигде, уж извините...
Спасибо, буду ждать! |
| Автор: apook 26.4.2007, 14:12 | ||||
Ln78 Это что-то в натуре интересное, подозреваю что ты еще один запутыватель ты не преподавателем работаешь нет... а то похоже |
| Автор: Anikmar 26.4.2007, 14:26 |
| FiMa1, Имейте в виду Вариант Ln78 только для массива, занимающего единый блок памяти (только для статики). Вариант apook универсальный - сработает и на статическом масиве и на динамическом. - Я бы выбрал его. |
| Автор: FiMa1 26.4.2007, 14:28 | ||||||
Так я, кажется, тоже самое написал в строке:
apook, получается Ln78 меня не правильно поправил? PS: to apook: а как С ПОМОЩЬЮ УКАЗАТЕЛЕЙ динамически разместить данный массив в памяти и, С ПОМОЩЬЮ УКАЗАТЕЛЕЙ, реализовать инициализацию не показал (ну и удаление массива само собой). Просвяти, плз. Заранее спасибо! |
| Автор: JackYF 26.4.2007, 14:58 | ||
Кажись, да.
Все, понял, где я недописал... да, тогда действительно была ошибка. |
| Автор: apook 26.4.2007, 15:13 | ||||
Дело в том что он не поправил, а привел пример абсолютно не пвносящий ясность в твой вопрос, а где то даже наоборот.
Дело в том что я сам нахожусь в стадии изучения Cи пл пл и поэтому терпеть не могу вопиющие случаи запутывания мозгов интригующе выглядещями примерами, в коих после долгого разбирательства обнаруживается минимум смысла(хорошо если правильного). Но все-же скажу как сделал-бы apook. Пишешь просто на индексах, компелируешь, убираешь баги, любуешся, переделываешь на указатели, смотришь и думаешь нахрена ведь и так работало... Если a[0] это *(a) то a[0][0] это *(*(a)) ну и т.д Короче от простого пляшем, от простого к сложному как сказал кто-то, ведь знал же что говорил.... |
| Автор: likehood 26.4.2007, 15:16 | ||
Он показал, как этот код работает на самом деле. А использование тройного разименования как раз и запутывает мозги. а вот здесь действительно что-то напутано |
| Автор: Anikmar 26.4.2007, 16:00 | ||||
Так вы определитесь пожалуйста. Чем мой пример вам не подходит? Создание трехмерного массива - есть Присвоение элементам через указатели - есть Удаление - есть А если вы определяете массив статически, то как тогда вы планируете его еще раз создать? И как уничтожить? Если он статический - он уже создан и он сам уничтожется при выходе из программы. |
| Автор: apook 26.4.2007, 16:08 |
| likehood Компилятор встретив a[1] делает следующее *(a+1) стало быть так этот код работает на самом деле Способ с указателями я привел только потому что Fima1 с самого начала на этом настаивает(читай предыдущие посты) И отнють не говорю что это вносящий ясность способ. |
| Автор: FiMa1 26.4.2007, 16:12 | ||||
Anikmar, Ваш вариант инициализации массива:
Вариант инициализации с разыменованием через указатели: for (i=0;i<Size0;i++) for(j=0;j<Size1;j++) for(k=0;k<Size2;k++) aMas[i][j][k] = *(*(*(a + i) + j) + k);[/code] Разница понятна? По-моему мы разговариваем на разных языках... Но, вообще, по мнению большинства, этот изврат не нужен, и, видимо, пора прикрывать дискуссии... |
| Автор: likehood 26.4.2007, 16:43 | ||
Для одномерного массива все верно. Спор то с того и начался, что если размрность массива больше 1, компилятор все равно делает только одно разименование. |
| Автор: Anikmar 26.4.2007, 17:07 | ||
Указатели вам подсказал apook Как создавать массив и его удаляеть - есть в моем коде Остается поменять индексный доступ на указатели - и все есть На одном языке мы разговариваем. Суть вопроса - поменять синтаксис доступа к элементу с квадратных скобок на указатели. Больше ничего менять не надо, вот я и удивляюсь. |
| Автор: threef 26.4.2007, 17:51 |
| Посмотрите на постик, там уже есть и динамические массивы , и размышления на тему скорости их создания и обработки. А так же мысли по поводу "изврата" и других выражений людей, не желающих шевелить мозгами http://forum.vingrad.ru/topic-137278/hl/%25D0%25B4%25D0%25B8%25D0%25BD%25D0%25B0%25D0%25BC%25D0%25B8%25D1%2587%25D0%25B5%25D1%2581%25D0%25BA%25D0%25B8%25D0%25B5/15.html#st_0_view_0 |
| Автор: apook 26.4.2007, 18:01 | ||||
В догоночку, для ясности
Ну и че ты это сюда сунул что там о чем то похожем говорили? До фига мозги что-ли включены... |
| Автор: Anikmar 26.4.2007, 18:11 |
Для ясности чего? |
| Автор: apook 26.4.2007, 18:25 |
| для ясности в кавычках "" не видиш смайлик улыбающийся |
| Автор: Dov 27.4.2007, 03:32 | ||||||||||
Ы-ы-ыы, машину разбил. Вернее три машины. Сначала одному задом въехал на заправке, а потом другому в зад заехал на светофоре. Хорошо хоть страховка есть. Ладно, переживём.
Я и сейчас это утверждаю. А где ты увидел противоречие? Да, я это говорил, не отрицаю. И буду говорить. А ещё я говорил, что тип int** это ни что иное, как указатель на указатель на int. Говорил??? Говорил. И что такое указатель на указатель на int тоже говорил. Могу ещё раз повторить. Это значит, что его значением может быть только адрес. Причём не просто какой-то адрес, а именно адрес указателя на int, т.е. такого указателя, который имеет тип int*.
Имя одномерного массива А[0], который является подмассивом двумерного массива А[3][3] является указателем потому, что хранит адрес первого элемента А[0][0]. Поэтому имя подмассива А[0] имеет тип int*, точно так же, как и &А[0][0]. Но ведь подмассив А[0] сам является первым элементом массива А. Имя массива А, в свою очередь, является указателем потому, что хранит адрес первого элемента, т.е. подмассива А[0]. И если имя подмассива А[0] имеет тип int*, то его адрес имеет тип int**, так как является указателем на указатель на int. Вот и получается, что имя массива А имеет тип int** и является указателем на указатель на int. Почему, опять же? Да всё потому же, ёлы-палы. Потому что адрес его первого элемента это указатель на указатель на int, имеющий тип int**. Это понятно? Что же мы имеем? Смотрите внимательно. Показываю последний раз. Больше повторять не буду. Если объект(или его имя, или переменная, не важно) имеет тип int, то его адрес имеет тип int*. Если объект имеет тип int*, то его адрес имеет тип int**. Если объект имеет тип int**, то его адрес имеет тип int***... И так до... Не знаю сколько, но очень долго. Теперь мы видим, что если имя массива имеет тип int**, то его адрес имеет тип int***. Вот почему я говорил, что адрес двумерного массива имеет тип int***. Но это в том примере не главное. Я хотел сказать другое. Имя массива не потому указатель, что его можно или нельзя присвоить какому-нибудь указателю(указатель не этим определяется), а просто потому, что оно хранит адрес. И последнее. Разыменование указателя, т.е. получение значения с адреса можно применить только к указателям. И если это можно проделать с именем массива, то почему его нельзя считать указателем? Опять же, это моё мнение. И я его высказываю, и пытаюсь объяснить свою точку зрения. В ответ же слышу только одно. Dov, ты не прав. Ну, допустим я не прав. Ну пусть тогда мне хоть один знаток стандарта объяснит, почему самолёты летают, а крыльями не машут. То есть, почему он пользуется именем массива, как указателем, когда разыменовывает его и получает доступ к значениям, а называть его указателем не хочет. Это я не про индексы говорю, а про звёздочки... Да, впрочем, ничего мне говорить не нужно. Я уже вижу руки, тянущиеся к клавиатуре, что бы напечатать три слова. Dov, ты не прав. Ладно, развлекайтесь тут без меня. Пойду лучше FiMa1`е помогу. FiMa1, вариант Anikmar`а, тот что с индексами, очень хороший. Если есть проблемы с пониманием работы указателей, то лучше, с моей точки зрения, использовать его. Но я так понимаю, что ты просто хочешь потренироваться в использовании указателей. Это правильно. Так и нужно сделать. Вот тебе переделаный вариант Anikmar`а.
А что нам может помешать создать динамический массив?
Так, вроде всё. Всем пока. |
| Автор: Ln78 27.4.2007, 06:24 |
| FiMa1 и все желающие, первую часть статьи (случай статического распределения памяти) написал, можешь посмотреть. Dov, сочувтвия по поводу машин. В своём опусе, по странному стечению обстоятельств, я упоминал и тебя, и гаишников. Надеюсь, что в аварии я всё-таки не виноват |
| Автор: apook 27.4.2007, 08:27 |
| Во бл.. поэия! Ln78 Не ожидал от тебя такого. еще бы в стихотворной форме че полинился? А вообще наверно познавательно но длинно, я устал читать. |
| Автор: Anikmar 27.4.2007, 09:17 | ||||
Я имел в виду ограниченность применения приведенного примера в том, что там априоре предполагается размещение массива в едином блоке памяти:
Примененный метод адресации по сути использует только указатель на базовый адрес: p[(i*N1+j)*N2+k]=321;, при универсальной работе - нужно применять разименование: *(*(*(a + i) + j) + k) Вот поэтому, я и написал, что вариант Ln78 будет работать, если массив объявлен статически либо единым блоком памяти (в последнем случае нельзя будет применить скобки, а только метод вычисления смещения) В приведенном вами последнем варианте вы выделили под трехмерный массив один большой блок памяти, тем самым закрыв себе возможность доступа к массиву стандартными средствами языка - через индексы. Поэтому вы естественно будете вынуждены считать смещение самостоятельно. Я не проверял, но почему-то уверен, что такой подход гораздо медленнее чем стандартный через квадратные скобки. Мне кажется, путаница в данной теме возникла именно из-за того, что многие уверены, что массив должен занимать единый блок памяти и его адрес является адресом первого элемента массива. Во многих случаях это так, но единого блока памяти может и не быть, поэтому вычисление смещения элемента путем прибавления какого-то числа к базовому адресу заведомо ошибочно и работает только в ограниченных случаях. Разыменование вида *(*(*(a + i) + j) + k) работает при любом способе объявления и организации массива - и статического (когда это действительно один большой блок памяти) и динамического - когда подмасивы могут находится где угодно. |
| Автор: apook 27.4.2007, 09:38 | ||||
Статический массив (если он полностью статический) всегда будет занимать единый блок помяти... |
| Автор: Anikmar 27.4.2007, 09:47 | ||
А я как сказал? Именно это я и подразумевал как само собой разумеющееся: Хотя конечно это на сегодняшний день это так, может появится какой-нить чудо-компилятор, который и статические массивы будет распихивать по дыркам в памяти - кто знает. Интересно, а если я объявлю достаточно громадный статический массив, для которого будет нехватать единого блока памяти. Программа не запустится? Или линкер ругнется? Лень проверять. |
| Автор: Fazil6 27.4.2007, 10:09 | ||||||||||||||
и где я сказал, что это одно и тоже? Это ты сказал?
хотя до этого утверждал, что имя двумерного массива это указатель на указатель, т.е. int** и вдруг оно уже int***. p это имя двумерного массива.
никто не говорит, что имя массива нельзя использовать как указатель, только вот указатель на что... не думай, что если можно написать
то из этого можно сделать вывод, что p это int**. Первое разыменовывание возвращает int[3] , а второе int. ты ведь не станешь утверждать, что здесь
pa это int* или ppa - это int**... Или pa это int* ? вот тебе пример
с удовольствием ознакомлюсь с примером использования двумерного массива или его имени в качестве int**. Вполне сойдет ссылка на приводимые ранее примеры или свежачек какой-нить...
Имя двумерного массива содержит адрес инта, до значения которого можно добраться [0][0]. Т.е. по адресу, который асоциирован с именем двумерного массива лежит int. Так почему бы на основании этого не сделать вывод, что имя двумерного массива является типом int* ??? Много всякой чепухи можно напридумывать.... |
| Автор: FiMa1 27.4.2007, 12:12 | ||
Зачотная статья, имхо! Молодец! Остаток статьи, после "Он говорит про себя «Ужо тебе, озорник»" дочитывал ПАДСТАЛОМ... Небольшая очепятка на 2-й странице "И тип int, мы договорились считать тоже 4-х битным". В целом все написано доходчивым языком. Обязательно буду ждать серии второй. |
| Автор: Anikmar 27.4.2007, 12:51 | ||||||||
Для краткости: говорим о двухмерных массивах. Это нельзя. Имя массива, хоть и приводится спокойно к **, но дальше компилятор ничего не знает о размерности массива. Когда компилятор встречает конструкцию вида:
То он подразумевает, что в p[1] находится указатель на массив интов. В соответствии с этим, если мы напишем конструкцию:
То работать так не будет. Так как присваивание только базового адреса массива, а в случае со статическим массивом - по сути адреса 1-го элемента не даст никакой информации об указателях на подмассивы. Поэтому, для того, чтобы использовать двуойной указатель его необходимо проинициализировать конкретными значениями. А так, как эти конкретные значения знает только компилятор, в том блоке, в котором определен массив, то получится нечто подобное:
Я только такой способ знаю статический массив привести к динамическому. Наверное есть и другие. Конструкции вида *(p + RowCount*i+j) Я считаю медленными и громоздкими |
| Автор: JackYF 27.4.2007, 13:21 | ||
Ой... зря ты это написал. Была раньше темка, где обсуждалась косвенно эта проблема. Тему сейчас не буду искать, а предложу скомпилить следующий код:
В обоих случаях буду выданы идентичные числа. Жду возражений Dov, ты не прав. Да, кстати - здесь четыре слова |
| Автор: _stranger_ 27.4.2007, 13:48 | ||
вот что говорит стандарт ISO/IEC14882 пункт 8.3.4.8
Dov ты все таки не прав... |
| Автор: Fazil6 27.4.2007, 13:50 | ||||
по вашему reinterpret_cast это спокойно приводится???
и что? вопрос в том как это использовать наоборот
|
| Автор: Anikmar 27.4.2007, 15:16 | ||
В каком смысле? Запихать в него какие-то свои значения? Я не понял слово "наоборот"
Си хорош тем, что можно что угодно привести к чему угодно. Правда будет ли это работать - это вопрос. Именно поэтому я и написал, что привести то спокойно можно, только дальнейшее использование невозможно. Или вы с этим не согласны? Просто я так понял вопрос, что каким образом можно работать со статическим массивом как с двойным указателем. Я знаю только один способ и я его привел. Наоборот - я просто не понял, т.е. вы хотите использовать имя массива как двойной указатель? Т.е. какие действия вы хотите от него получить, чтобы такие же действия получать от двойного указателя? Имеем объявление int a[3][4]; int **p; Соответственно: Конструкция p[0] должна вернуть указатель на int Конструкция a[0] также вернет указатель на int Какие конкретно действия "наоборот" вас интересуют? |
| Автор: Fazil6 27.4.2007, 15:42 |
привести пример выражения где int a[3][4] использовался бы как int**. |
| Автор: Anikmar 27.4.2007, 15:58 | ||||||
Я такой пример привел, он вас не устроил? Например так:
Добавлено через 5 минут и 52 секунды Или так:
|
| Автор: Fazil6 27.4.2007, 16:05 |
хм... в какой строке массив или его имя используется именно как int**? Правда не вижу |
| Автор: Anikmar 27.4.2007, 16:17 |
Как тогда вы эту строку расцениваете? Как работу с целым числом? Добавлено через 1 минуту и 57 секунд Объясните, какие действия вы хотите сделать с двойным указателем? Приведите пример работы с двойным указателем, который бы вы хотели увидеть? |
| Автор: Fazil6 27.4.2007, 16:31 | ||
ну совсем не как int**, ведь если 2 раза разыменовать и в результате получить int еще не означает, что то, что разыменовывается имеет тип int**. Я привел пример когда **ppa выдает int, но ppa не является int** http://forum.vingrad.ru/index.php?showtopic=147319&view=findpost&p=1114163 ведь получаемый результат выражения (я имею в виду адрес) a + 1 разный в зависимости от того какого типа a. Ведь если a типа int** , то смещение будет 4*1 байта, а если a[2][2] , то смещение будет sizeof(int[2])*1. и в твоем примере с циклом *a возвращает массив int[4] , а потом к нему применяется смещение 0 и разыменовывание дает int Добавлено через 3 минуты и 9 секунд
мне неизвестны такие примеры, которые компиллировались бы, либо компиллировались и работали бы. Поэтому я и утверждаю, что ни массив int a[2][2] ни его имя не являются int**. Те кто это утверждают, видимо знают такие действия и примеры |
| Автор: Anikmar 27.4.2007, 16:43 | ||||||||
С такой логикой можно любой пример оспорить. К этому коду это суждение не относится. Разыменовывается без всяких приведений и искусственных преобразований (переопределений) и выдает именно то значение, которое ожидалось.
Если вы считаете, что этот результат случайный так, как размеры int и указателя совпадают попробуем по-другому:
Теперь выдаем 2-ю строку массива (или столбец - как кому удобнее) Что теперь с указательной арифметикой не устроит? Добавлено через 6 минут и 58 секунд
Я привел вам 2 работающих примера, когда имя массива можно использовать в качестве указателя. Я никогда не утверждал, что имя массива является указателем - так как он имеет тип int[3][4] - это легко увидеть использовав typeid(...).name() Я говорил, что имя массива можно использовать в качестве двойного указателя, что я вам и продемонстрировал. А вы говорите, что не увидели ни одного примера - дескать то, что я привел не является двойным указателем. У меня, право, возникло затруднение - я не увидел ни одного аргумента, опровергающего работающие правильно участки кода. Если бы вы не увидели предыдущего текста, как бы вы расценили эту строку: printf("%lf\t",*((*aa+1)+i)); У вас бы спросили какого типа aa? Что бы вы ответили? По выражению - это работа с двойным указателем и никак иначе. Строка одинаково выглядит и если бы aa была бы объявлена как ** и как двойной массив. |
| Автор: Fazil6 27.4.2007, 17:46 | ||||
это выглядит как работа с двойным указателем, но это не является работой с двойным указателем, потому что
|
| Автор: Anikmar 27.4.2007, 17:51 | ||
т.е. согласно вашей логике, указатель на конкретный тип данных и указатель на массив из таких данных отличаются? char *s; - Это указатель именно на переменную типа char (т.е. на 1 символ) или на массив таких символов? Ответьте мне, пожалуйста. Далее: char **s; - Это указатель на указатель на символ или указатель на массив указателей на символ? |
| Автор: apook 27.4.2007, 17:56 |
| Это вообще указатель на массив указателей на тип char и другое дело какие адреса взяты указателями |
| Автор: Anikmar 27.4.2007, 18:03 | ||
А просто как указатель на указатель его нельзя использовать? Можно. Тогда объяснитем мне смысл этой фразы:
Как тогда использовать double**? Как указатель на массив не подходит, а как нужно? |
| Автор: Fazil6 27.4.2007, 18:04 | ||||
да. массив и указатель это разные типы. Передай sizeof массив и указатель , который указывает на его первый элемент. Получишь разные результаты. tipeid выдаст разное. Физически - это адрес, но работа с ним и результат зависит от того как объявлено.
это указатель на символ. Массив символов при передаче в функцию передается как char *s. Строковый литерал "11" имеет тип char[3]. Я понимаю, что соблазн представлять массив как указатель достаточно велика, но если следовать логике, что в строке for (i=0;i<4;i++) printf("%d\t",*(*a)+i); a - используется в виде int** , то почему в параметрах функции двумерный массив не преобразуется в int**. Весь фокус не в том насколько велика разница между массивом и указателем, а в том что слишком большая разница между указателем на массив и указателем на указатель. |
| Автор: Anikmar 27.4.2007, 18:05 | ||
| P.S. А я как написал? По-другому?
Если конечно не придираться, что я char назвал символом - ну лень мне было язык переключать |
| Автор: Fazil6 27.4.2007, 18:08 | ||||||
допустимо только для совместимости со старым кодом. так ведь недопустимо
Добавлено @ 18:10
это указатель на указатель на char или на символ, в данном случае понятно. |
| Автор: Anikmar 27.4.2007, 18:29 | ||||||||
Потому, что он таковым не является. Тут примерно такая же разница как между адресом функции и адресом метода класса. Если массив определяется статически - то для вычисления смещения необходимо знать размерность этого массива. А в голом указателе такой информации нет. Если вы создаете массив динамически и передаете его в функцию, то что должна знать функция? Это адрес собственно массива и его размерности. Для чего? Чтобы правильно вычислить местоположение конкретного элемента. Когда массив определен явно, он выделяется прямым куском в памяти и размерности его известны. Именно поэтому он получает тип int [3][4], а не int** - т.е. в этот тип уже явно включены размерности. Если он создается динамически, то мы вынуждены создавать его многоступенчато. Сначала создаем массив указателей, потом каждый указатель инициализируем собственно массивом данных конкретного типа. Если мы создадим динамический массив единым куском, то он не будет двухмерным с точки зрения языка Си++ - вы не сможете доступиться к его элементам стандартным оператором [][]. Так как в этом большом куске будут отсутствовать указатели, которые будет искать данный оператор. А когда этот оператор пытается найти искомые указатели в статическом массиве - то ему на вход передается уже другой тип данных, не int**, а int[3][4] - соответственно адрес конкретного элемента вычисляется по-другому. Нельзя передать в функцию статический двухмерный массив как указатель именно из-за разного алгоритма вычисления смещения элемента. Функция, которая получит на вход двойной указатель будет вызывать оператор [][], относящияся именно к двойному указателю, а не к int[3][4]. Именно поэтому, работать с именем массива как с двойным указателем возможно, но он не является двойным указателем сам по себе и передавать его в функцию, преобразовав к двойному указателю будет ошибкой. Собственно также как и двойной указатель бессмыслено передавать в какую-либо функцию предварительно не проинициализировав его. Например:
Нет и еще раз нет. Если мы объявим char *s; - то это всегда указатель char s[30]; - это совсем другой тип, это явно заданный массив и имеет тип char[30] char *s = new char[100]; char *ss; // Это уже динамический массив. Причем и s и ss имеют одинаковый тип. Статически объявленные массивы всегда вносят некоторую долю путаницы, но когда полностью представляешь механизм трансляции компилятором этих типов, особенно в части вычисления адреса конкретного элемента, то в общем то всегда все понятно. Лично для меня массивом является то, с чем можно работать стандартными операторами массивов - это квадратные скобки. Поэтому варианты вычисления адреса в большом блоке я и не приемлю. Добавлено @ 18:32
Естественно недопустимо. В языке не определены константы адреса. И только из-за этого. Инициализировать можно только вещами, которые понимает компилятор. А в языке определены числовые константы, логические, а адресные - нет. Он их просто не знает. |
| Автор: Daevaorn 27.4.2007, 19:16 | ||
а вчем различие?
Это я не задал адрес? |
| Автор: Anikmar 27.4.2007, 19:30 |
Нет, это целое число. Дело в том, что исторически сложилось, что адрес и int имеют одинаковый размер - с точки зрения архитектуры процессора - все понятно. Но адресных констант в языке нет, в ранних версиях я еще встречал описание причины - дескать язык высокого уровня не должен быть зависим от архитектуры машины, в современном стандарте даже упоминаний нет. В разделе константы - там есть целые числа, числа с плавающей точкой, строковые и символьные константы и служебные константы true и false. Больше нет. Да и посудите сами: допустим процессор motorola для видеоприставок. Был еще в конце 80-х годов. На тот момент intel был еще 16 разрядным, а motorola уже имела прямую 32-битную адресацию. Ка должна выглядеть адресная константа? При всем при том, что на Си можно было писать и обработчики прерываний и драйверы. Тогда для семейства x86 она должна выглядеть как-то так: 000CC:000dd - т.е. сегмент + смещение, а для мотороловского процессора уже как 32 битное единое число без сегментации: как-то так. сссааа00 Причем int был все равно 16-разрядным. Так что отсутствие в конструкции языка константы адреса вполне объяснимы и логичны, особенно сейчас - с четким разделением памяти и управление ею операционной системой. Ну откуда программист может знать конкретный адрес на этапе разработки программы? Зачем он нужен? И с появлением 64-разрядных систем в 32 разрядный int указатель уже не запихаешь. А не за горами 128 разрядные. |
| Автор: Daevaorn 27.4.2007, 19:51 | ||
ну тут на самом деле вопрос интерпритации компилятором и процессором некого числа как адреса. хотя, кочнено, согласен что явных адресных констан нет. |
| Автор: Ln78 27.4.2007, 20:11 |
| FiMa1, хорошо, что тебе понравилось, наверное в воскресенье-понедельник попробую дописать вторую часть. К продолжающим споритьЧестно говоря, я уже перестал понимать предмет спора. Может и остальные тоже спорят просто по инерции? Зайди сюда ортодоксальный астматик, он бы долго смеялся: "Я полагал, что между плюсанутыми и аштеэмельщиками разницы особой нет, но не настолько же? Придумали какое-то мифическое чудище, и сами же его и испугались". Вот, допустим, есть у нас структуры, описывающие разные физические сущности: корабль, автомобиль, кухонный стол, кролик, десяток кроликов. И есть ещё один тип: union, элементы которого - объекты перечисленных выше структур. Структуры содержат различные элементы, занимают разный объём памяти. Определена одна переменная, тип её - как раз тот самый union. Я заполняю эту переменную данными, которые описывают автомобиль. Конечно, можно считать фокусом: объявляю указатель на переменную как на автомобиль, потом чудом (просто указанием компилятору понимать его по-другому) рассматриваю это как кухонный стол, потом как кролика, а потом опять как автомобиль, и использую в последнем значении именно как автомобиль. И выдаю это за свои сверхспособности: смотрите какой фокусник. Да нет здесь никаких фокусов, и ловкости рук никакой нет, одно примитивное мошенничество. Что я могу, чтобы находящийся в здравом уме и трезвой памяти использовал в качестве кухонного стола автомобильное колесо с фарой и не заметил разницы? Или считается, что для int* и int** это чем-то принципиально отличается (подумаешь, втихаря нарисую/сотру звёздочку, никто и не заметит)? |
| Автор: Anikmar 27.4.2007, 20:39 | ||||||
Тут чье-то конкретное мнение уже точно не влияет, вот какие допустимые литеральные константы перечислены в стандарте:
На большее стандартный компилятор не способен.
Да тут уже спор совсем о другом, чем изначально Короче спорим помаленьку, пыхтим |
| Автор: JackYF 27.4.2007, 20:59 | ||
| Две страницы я благополучно успел пропустить... И все же спор у нас был о двух вещах: 1. тип "Массив" != тип "Указатель". Это так, теоретические изыскания. 2. Неправильная работа конкретного примера. То есть, что
Пример выведет два разных числа в лучшем случае. В худшем и AV/SegFault может схлопотать. Ln78, а вообще во многом ты прав. По данным двум пунктам мы спорили, спорили... А теперь о чем спорим? |
| Автор: Anikmar 27.4.2007, 21:06 | ||
Было пожелание:
Я набросал пару примеров. И началось... |
| Автор: JackYF 28.4.2007, 13:50 |
А ничего толком не началось. Корректного примера я так и не увидел. Или я что-то пропустил? Если да, то ткните в цитату или ссылку на этот корректный рабочий пример. Начались опять какие-то разглагольствования. Итак, жду примера. |
| Автор: Anikmar 28.4.2007, 23:25 |
Ну уж нет. Привел два работающих кода. Если вам интересно - запустите их и откомпилируйте. Если вы утверждаете, что работа с именем массива отличается от работы с двойным указателем - то аргументируйте и опровергайте. А то получается какое-то барство. Сначала: А потом извольтес-с-с..: У вас в роду дворян не былос-с-с? |
| Автор: Fazil6 28.4.2007, 23:28 | ||||||||||||
ну не является... Так почему ты считаешь что в строке
показано использование a как int**, если он таковым не является? У меня такое впечатление, что ты либо не читаешь что я пишу тебе вответ, либо не читал обсуждение до этого... Поэтому не совсем понятно кому адресованы твои теоретические выкладки.
Ох... Я просил пример, который доказывает, что массив или имя его можно использовать как int** , а не пример в котором показаны действия с неким массивом и "если бы переменная была int**, то выглядело бы все именно так". что нет?
Я именно так и говорил всегда.
такое впечатление, что ты единственный, кто знает о чем спорят... Я, например, не уверен, что знаю...
ну принято считать что вот здесь пример демонстрирует http://forum.vingrad.ru/index.php?showtopic=147319&view=findpost&p=1114611 и вот тут http://forum.vingrad.ru/index.php?showtopic=147319&view=findpost&p=1114678 Добавлено через 9 минут и 47 секунд
ну выглядит одинаково... На этом сходство заканчивается... Весь топик именно об этом, что даже когда работа с двумерным массивом может выглядеть как работа с указателем на указатель, ни массив ни его имя таковым не являются и не приводятся к такому типу и не представляются как такой тип(int**). |
| Автор: Anikmar 28.4.2007, 23:44 | ||
Встречный вопрос. А как я с ним работаю с точки зрения конструкций языка? Привожу к другому типу? Я работаю с ним как с двойным указателем. Почему не так? С какими стандартными типами данных языка Си++ допустимы такие операции?
Обсуждения до этого хоть я и читал, к теме моего ответа никак не относятся. Я ни скем не спорил. Было предложение привести пример кода, в котром имя двухмерного массива использовылось бы как двойной указатель, причем правильно. Я привел пример доступа к элементу массива, используя его имя в качестве двойного указателя. В чем я спорю, кого я оспорил и что я сделал неправильно? Имя массива использовалось как двойной указатель? ДА. Без каких либо ухищрений! Без приведений типа и т.п. Доступ к элементу массива получили? ДА. Код рабочий? ДА! А потом мне объяснют, что это частный случай, что int и указатель размером одинаковы и т.п. Я всего лишь ответил на пост. Дайте мне рабочий пример. - Дал. Работает - ДА. В чем я не прав, объясните? Теперь получается, нет, он не подходит. Так как нельзя передать в функцию. Не вопрос. Передайте в функцию двойной массив не указывая его размерности? Используя только имя массива. Нельзя. Потому, что статический массив намертво привязан к своим измерениям. Таким образом, предложите мне пример функции, которая в качестве параметра получает статический двухмерный массив любой размерности. Давайте вместе разберем получившийся код. Добавлено через 2 минуты и 24 секунды P.S. Язык си, конечно, допускает вольности. Но все-таки не с точки зрения того, что "выглядит одинаково" Правила семантического разбора предложения четко оговорены. И если я использовал конструкцию со звездачками без искусственного приведения типа, то компилятор для себя не делает варинтов "наверное это похоже на..." он действует четко (слова Богу). Так что эта фраза мне несколько непонятна. |
| Автор: Fazil6 29.4.2007, 00:11 | ||||
Какие операции? Весь смысл примера сводится к тому, что **a даст в итоге значение типа int (если a это двумерный массив int). Я согласен с этим, но я говорю, что этот пример не доказвыает, что a это int**. Я просил пример, который это доказал бы. Поэтому я и говорю, что этот пример меня не устраивает. Поэтому я и вспоминаю передачу в функцию массива.
ну да действует четко и я утверждаю, что когда компиллятор видит **a, действия его будут разными в зависимости от того какого типа a хоть мы и получаем в итоге int. И в твоем примере с конструкцией **а компиллятор работает как с указателем на массив , а не указателем на указатель на int |
| Автор: Anikmar 29.4.2007, 00:21 | ||||
??? действия его будут однозначны - получить значение <type> - если a это int **a, то значение будет int. Какие зависимости? Пример:
Будет выведен результат 100 и это однозначно. имя массива использовано как двойной указатель? ДА Ваше требование: Выполнено? ДА! Не понимаю... Какие примеры еще надо привести? |
| Автор: Fazil6 29.4.2007, 00:45 | ||||
зависимости от типа a. Компиллятор использует информацию о типе а для получения результата **а Нет. Имя массива использовано как указатель на массив, а не как двойной указатель и компиллятор действует в этом случае не так как если бы это был двойной указатель. Я же писал, что **(a +1) в случае если a типа int** компиллятор выполнит смещение на sizeof(int*)*1, а если int a[2][2], то выполнит sizeof(int[2])*1 sizeof в этих случаях выдает разные результаты и поэтому компилятор никогда не рассматривает двумерный массив как двойной указатель и **a выглядит как работа с двойным указателем , но на самом деле является работой с указателем на массив в твоем примере. Поэтому я и сказал, что меня этот пример не устраивает Это не мое требование. Это смысл твоего примера. мое требование
читай внимательнее, что я пишу |
| Автор: Anikmar 29.4.2007, 01:03 |
Что вы имели в виду? |
| Автор: Fazil6 29.4.2007, 01:09 | ||||
смещение. Значение выражения a+1 при
получим адрес на 4 больше, а при
на 8 потому что sizeof(int[2]) == 8 |
| Автор: Anikmar 29.4.2007, 01:32 | ||
| А что вы хотели? Мы не получим смещение 4 или 8. Мы получим следующий элемент массива. Для того, чтобы получить следующий элемент массива в случае объявления int **p; мы должны просто прибавить размер указателя Так как у нас статический массив int a[3][50]; то чтобы получить следующий элемент согласно правилам работы с двойными указателями компилятор высчитывает значение исходя из размерностей - так как статический массив занимает единый блок памяти. И прибавляет к нему 50 * sizeof(int). Где нарущение правил работы с указателями???? Мы получили следующий элемент массива, согласно правилам языка Си. В языке Си ( в стандарте) нет понятия многомерных массивов. Там есть понятие МАССИВ, доступ к элементам которго обозначается квадратными скобками. Если вы попытаетесь найти там операцию [][] - вы ее не найдете, потому, что ее там нет. Там есть понятие только массива. И понятие имени массива, которое приводится к указателя на тип данных массива. Соответственно, если у нас есть объявление: int a[3][12]; то с точки зрения станлдарта мы имеем массив из 3элеинтов, каждый из которых имеет тип int a[12]. Надеюсь с этим утверждением никто не спорит? Далее читаем стандарт. Имя массива приводится к указателю на тип массива. (то же стандарт, а не мое мнение). Значит наш пример показывает, что мы имееем массив из 3элементов типа int[12], и что его имя приводится к указателюю на типа массива - т.е. на int[12]. При увеличении значения указателя на 1 мы должны получить следующий элемент массива. Так как у нас массив из 3элементов, каждый из которых имеет тип int[12] - то мы должны после инкрементирования укзателя получить следующий элемент массива, т.е. очереной int[12] пропуская все предыдущиие. Так как массив объявлен статически, то компилятор переставляет значение указателя так, чтобы он соответствовал правилам языка - т.е. на следующий элемент. Что мы и получаем в результате. Если и эти доводы вас не убедили, тогда уже надо обращаться в артибтраж к Страуструпу - дескать какую-то хрень ты написал... Иллюстрация к моим доводам на примере кода:
Если бы на месте b был бы инициализированный массив на базе двойного указателя, мы бы получили аналогичный результат. Т.е. имя массива и указатель ведут себя одинаково, логично и предсказуемо. Если нужен пример - я его набросаю, просто немного лень стало. А если разницу, в поведении указателей вы усмотрели в различном смещении, то опять направляемся к стандарту: При инкрементировании указателя, он увеличивается на значение, равное типу его хранения. указатель int **P p хранит массив указателей, который размещен в памяти единым блоком. Последний по измерению массив по правилам языка Си всегда размещается единым блоком (стандарт) При увеличении на единицу - он показывает следующий указатель - так как именно указатели он хранит. На какое конкретно число он увеличивается - стандартом не прописано (так как это программиста не касается). Нам гарантировано стандартом одно: при увеличении на 1 указатель устанавливается на следующий элемент (ну можно, конечно, и на 2 и на большее количество) Операция sizeof в таких случаях никого никогда колыхать не должна - у нас есть указатель на конкретный тип данных и его инкрементирование указывает на следующий элемент. При объявлении массива p[3][nnnn] Если мы инкрементируем значение p, то мы должны получить очередной элемент - т.е. указатель - т.к. p - приводится к массиву указателей по стандарту. На сколько пришлось реально инкрементировать (или декрементировать - уже стебусь В чем разница в поведении? |
| Автор: Mayk 29.4.2007, 01:43 | ||
занудутствую: Наоборот. a - это массив из трёх элементов, каждый из которых является массивом из двенадцати элементов, каждый из которых является int'ом. зы. 6 последних страниц не читал. 2 и 3 тоже. |
| Автор: Anikmar 29.4.2007, 01:45 | ||||
Эх.... Все-таки подсадили. Как правильно пишется ЗоНУДА и ЗаНУДА? Фраза из анекдота:
ПРОШУ СЧИТАТЬ ОЧЕПЯТКОЙ Добавлено @ 01:47 P.S. И ваще - в 2 часа ночи что угодно перепутать можно, а не только порядок измерений. Mayk - абсолютно прав, но смысл от этого никак не меняется P.P.S. Очепятка исправлена. |
| Автор: Fazil6 29.4.2007, 10:08 | ||||||||||||||||
вообще-то sizeof(int[50]) потому что элементом, на который указывает указатель а является int[50]. И никаких правил работы с двойными или тройными или еще какими указателями нет. Есть арифметика указателей и важно в ней только на что указывает указатель. Для двойного указателя он указывает на int*, а для массива на int[50]. Вот их размер в байтах и важен
ох.... Я разве сказал, что правила нарушены? Я разве говорил, что пример не работает? Я сразу сказал, что правила по которым этот пример работает разные в случаях двойного указателя и массива.
как солнце из-за туч!!!! Только непонятно что ты читал в топике предыдущие 10 страниц....
доводы чего? того что я тебе первым делом сказал? Теперь ты мне объясняешь как по стандарту... Именно потому что так по стандарту меня твои примеры и не устроили. Я тебе так сразу и сказал.
в том как компиллятор получает этот очередной элемент |
| Автор: Anikmar 29.4.2007, 11:08 | ||||||||||||
С этого момента по подробнее. Итак имеем арифметику указателей. Имеем char *s; При операции s+1 указатель увеличивается так, чтобы указать на следующий элемент массива. По сути Имеем char * *s; Теперь s указывает на указатель. s+1 прибавляет так же 1 адрес, чтобы указать на следующий элемент Имеем char s[3][30]; При операции s+1 он должен указать на следующий указатель. Именно для того, чтобы имя двухмерного массива соответствовало двойному указателю компилятор прибавляет к значению указателя сразу целую строку. Именно для того, чтобы арифметика указателей была одинаковой в поведении со статическими массивами и двойными указателями. А так как значение адреса статического массива по сути "только для чтения", то компилятор нне рискует тем, что если обратиться к имени массива как к указателю и попытаться что-нибудь ему присвоить, то естественно вылезет ошибка - так как указатели на подмассивы нигде не хранятся, а вычисляются по мере обращения. Это как раз и есть то отличие, почему имя двухмерного массива не является двойным указателем в чистом виде.
Никаких разных! Статический массив инициализирован! Поэтому прибавление к имени массива 1 позволяет сразу указать на след. элемент! char a[3][30]; a - приводится к двойному указателю на 1-й элемент массива. При прибавлении 1 а увеличивается на размер элемента хранения, в данном случае char[30] так как a имеет явный тип 3 указателя на массивы из 30 элементов Вы же не будете спорить, что void *p; - является указателем? Будьте добры, приведите арифметику указателей примениму к типу void? Арифметика указателей обязательно будет работать по-разному в зависимости от типа указателя. Кто с этим спорит? Я утверждаю, что двухмерные массивы С++ приводятся к двойному указателю согласно правилам стандарта.
Я не увидел ни одного вашего встречного примера - как должно быть. Я спокойно взял имя массива, произвел с ним согласно правила языка операции разыменования и доступа к элементу, а вы говорите, что согласно стандарта мои примеры не устроили. Приведите, пожалуйста главу стандарта, согласно которой вас не устроили мои примеры? Откуда такая уверенность? Вы видели как выделилась память? Это указатель на двухмерный динамический массив. Посмотрите на объявление функции. void PrintMas(int**P,int a, int b) Скажите с такой же уверенностью: что храниться в переменной P? Возьмем одномерный массив. char *s; Это указатель или массив? Пока мы непроинициализировали указатель - это неизвестно. В зависимости от того, что мы присвоим это может быть адрес переменной и адрес первого элемента массива.
Т.е. вы говорите, что приведенное мной объявление int **p; приводится к указателю на массив? У нас всего лишь двойной указатель - вы же сами говорили:
К какому массиву указателей? Он может содержать адрес конкретной переменной типа "Указатель на int" или адрес первого элемента массива таких указателей. По аналогии с одномерными массивами, согласно стандарта языка Си. int p; - одиночное значение int int *p; - указатель на int или на массив из таких int-ов int **p - указатель на указатель на int или на массив таких указателей Приведите пример, как вы объявляете динамический массив? Давайте посмотрим и обсудим ваш код.
Разница поведения компилятора - это не разница поведения указателей. Именно для того, чтобы привести имя двухмерного массива к двойному указателю компилятор и ведет себя по-разному. Как же иначе? Зато указатели ведут себя адекватно. |
| Автор: Fazil6 29.4.2007, 15:38 | ||||||||||||||||||||||||||||
С чего ты взял, что компиллятор стремится чтобы
???? Ему до лямпочки ваши инсинуации по поводу того как массив или его имя представить как двойной указатель. Ему говорят "прибавь к указателю", он вычисляет размер того на что он указывает и прибавляет нужное количество байт. Он совершенно не стремится , чтобы это было как с двойным указателем. Зачем ему это? Он работает с указателем на массив , а это не указатель на указатель.
хм... она и так везде одинаковая... Повторяю, что компилятор четко разделяет статические массивы и двойными указателями.
Вот я и говорю, какого художника вы утверждаете что **(a + 1) работает с int**, если a + 1 дает смещение не на размер int* , а на размер подмассива? Только не надо говорить , что смещение на размер подмассива для того чтобы соответствовало двойному указателю.
нет. Нет такого правила в стандарте... Есть правило по поводу просто массива, но это правило не рекурсивно и переносить его на многомерные массивы нельзя.
Дискуссия с тобой началась с моих примеров....
ну применил ты разыменовывание, ну получил ты в итоге int. По твоему это доказывет, что тип разыменовываемого int**? Я говорю, что тип того, что ты разымновывал int (*)[n] (указатель на массив из n элементов). Если его имя разыменовать 2 раза получим int. Вот меня и не устраивает твой пример в котором двойным указателем и не пахнет.
я вижу объявление и этого достаточно, чтобы однозначно определить тип. Нет такого типа указатель на двухмерный динамический массив и в арифметике указателей твое p будет использоваться как указатель на int* и никак подругому. Вообще причем здесьэти экскурсы в динамическое выделение. Твой двухмерный динамический массив совершенно подругому расположен в памяти и то, что тут именно двойной указатель я не спорю
как его не инициализируй, но тип его не изменится. Это указатель на char. Даже new[] не сделает из него массива и его арифментика будет основываться на размере char. Мы говорим о нормальных класических статических массивах и динамические массивы идут в сад...
повторяю, динамические массивы нервно курят в саду... Речь не о них.
ну не надо передергивать... было ведь так
Компилятор ведет себя поразному потому что это разные типы, а не потому что ему нужно чтобы они привелись один другому |
| Автор: Xenon 29.4.2007, 16:15 | ||
Anikmar,
Это что, работать должно? p - не l-value, к нему не может быть применем инкремент. |
| Автор: Anikmar 29.4.2007, 17:00 | ||
В моем примере p был указан как **p Инкремент я имел саму операцию увеличения указателя на 1 без его изменения в таком смысле: int p[3][4]; *(p+1).... Так мы же можем делать и никто нас не убьет за это Fazil6, Я честно говоря тольо отвечаю на ваши вопросы, никак не получив одного единственного ответа: Каких действий вы ждете от работы с именем массива как с двойным указателем, которые вас не устраивают. Поэтому дальнейшее обсуждение возможно только по приведению вашего кода с двойным указателем, которых вы не можете добиться от имени двойного массива. Если вас интересует именно инкремент в чистом виде, чтобы он прибавлялся именно на 1 - это конечно очень интересно, но как выше уже обсуждалось числовое значение адреса никого особо не трогает - так как не нужно. Я просил вас привести некоторые примеры - вы настойчиво заняли позицию нет это не то. Понимаете, у меня нет цели вам продать какой-либо продукт, а вы как придирчивый покупатель не хотите его покупать. Я прошу вас, показать пример объявления функции, которая получает на вход статический двухмерный массив, размерности которого заранее неизвестны. Если делать это по рекомендациям Страуструпа вы все равно его приведете именно к двойному указателю. Изначально ваш вопрос звучал так: Покажите мне примеры, где с именем массива работают так же как с двойным указателем. Я вам показал рабочие примеры, которые не просто показывали что это работает, но и несли смысловую нагрузку - доступ к строке матрицы и к конкретному элементу. Сейчас я уже начинаю себя ловить на мысли, что мне надо доказать вам, что имя двойного массива ЯВЛЯЕТСЯ двойным указателем. Нет. Двухмерный массвив не является двойным указателем. Его лишь можно так рассматривать по одной причине: В языке Си имя одномерного массива можно рассматривать как указатель на его первый элемент. Т.е. как указатель на тип данных массива: Утверждение 1 <type> Ar[NNN] - т.е. имя такого массива можно рассматривать как тип <type> * Переходим к второму измерению <type>Ar[MMM][NNN] Теперь Ar имеет тип Массив из MMM элементов, каждый из которых является массивоим из NNN элементов типа type. Согласно утверждению 1, имя массива можно рассматривать как указатель на его первый элемент. Т.е. в нашем случае имя массива это указатель на 1-й массив (ну вернее нулевой) из NNN Элементов. Т.е. указатель на <type>[NNN] <type>.[NNN] в первой части нашего утверждения ужы была рссмотрено как <type>*. Значит мы можем рассматривать имя двухмерного массива как указатель на <type>* что является <type>** Если эти же рассуждения перевести к другой логике, то можно рассмотреть ее так: typedef a10 int[10]; a10 a; // Если взять отдельно a, то она может рассматривать как указатель на int и может вести себя именно так. //Имя одномерного массива является указателем на его первый элемент, значит int* a10 aa[10]; // Имя массива является указателем на 1-й элемент. Т.е. имеет тип *int[10]. // Последний в свою очередь может быть рассмотрен как int* Следовательно aa можно рассмотреть как int** Еще раз повторю, фразы : "может быть рассмотрен", "с ним можно работать как" не синонимамы фраз "является" Что собственно я вам показал и доказал примерами своего кода. А варианты, "А покажите мне код где с именем массива работают как с двойным указателем, и чтобы еще его значения соответсвовали двойному указателю" совершенно нелогичны. Вообще, тип *int[40] не является стандартным типом языка C. Это уже инициализированный тип. Поэтому всегда можно сказать, что операции с ними отличаются от опреаций int **. Ну так int [][45] будут отличаться от int [][50]. Это что, тоже разные типы? Или все-таки частные случаи? Общия то базовый подоход операции [][] - это именно двойной указатель. И Именно к этому базовому подходу и подгоняет компилятор свои действия. Конкретно операция [][], применяемая к двойному указателю является аналогом разыменования. В случае со статтическим массив (но это является как раз именно частным случаем общего подхода) арифметика несколько упрощается, но не более. Опять таки пока мы не попробовали передать статический двухмерный массив произвольного размера в функцию. Там экономия на отсутствии реально хранящихся указателей вылиывается в проблему рассчета смещения. За все надо платить. Но общий подход всегда базируется на каком-либо базовом типе, а базовый тип - это двойной указатель, так как нету в языке си типа [][]. |
| Автор: nickless 29.4.2007, 17:25 |
| Немного статистики для сравнения: "Win vs. Lin": 122 дня, 453 поста = 3,71 поста/день "Собираем поклонников висты": 12 дней, 147 постов = 12,25 поста/день (это при активном участии тролля) Эта тема: 9 дней, 172 поста = 19,11 поста/день Вывод: религиозные войны отдыхают Ладно, больше не буду вам мешать |
| Автор: Fazil6 29.4.2007, 18:04 | ||||||||||||||||||||||||||
такие, когда будет видно, что имя используется именно как тип int**. Я тебе уже говорил, что таких примеров мне неведомо ибо двумерный массив никак не связан с двойным указателем. И имя двумерного массива никогда не используется как переменная типа int**. например это
доказывает, что a может использоваться как int**, и если бы такое выражение было корректно в С++ , то этого хватило бы для доказательства моей НЕПРАВОТЫ и безграмотности.
какие еще конкретные примеры??? С чего твои наезды на меня начались??? Посмотри внимательно.... http://forum.vingrad.ru/index.php?showtopic=147319&view=findpost&p=1114163 там полно примеров.
Неправда. Вот как он прозвучал.
ты показал как двойной указатель использовать в качестве двумерного массива. Я не это спрашивал.
ну вобщем смысл был таким. Если вы утверждаете что int[2][2] и int** както связаны (имя или еще что-то) то приведите примеры из которых это будет видно.
вот здесь стоило остановиться ибо на этом рассуждения заканчиваются и дальнейшие умозаключения неправильны
ну вот это и неправильно. Это заблуждение. Я уже раз пять повторил!!!! Правило относящееся к одномерному массиву нерекурсивно!!!! Если мы можем рассматривать имя двумерного массива, то приведите мне пример где оно рассматривается компилятором как int**. Не надо хвататься за клаву. Нет таких примеров... Ты говоришь, что в примере **a - это работа с двойным указателем потому что получили int. Я говорю, что это работа с указателем на int[n] , а двойным указателем здесь не пахнет.
конечно это разные типы.
Бред.... Ничего компилятор никуда не подгоняет.
ну договорились... Получается есть в языке си тип "двойной указатель"... |
| Автор: Anikmar 29.4.2007, 19:57 | ||||
Наконец то из нашего баяна только один пост более мнее выделить можно. Откуда такая уверенность в этом: Правило относящееся к одномерному массиву нерекурсивно!!!! Это вы прочитали в стандарте? Еще раз рассмотрим int[n]. Это массив целых чисел, который может быть приведен к указателю на int. Причем приведен имено по стандарту - без всяких приведений типа. По сути компилятор это делает неявно: int a[45]; int *b=a; Так что имя одномерного массива практически является указателм на тип - так как приводится неявно. И почему это правило должно быть нерекурсивно? И главное где это написано?
Предлагаю остановиться на этом примере: int a[3][4]; int b = **a; |
| Автор: MAKCim 29.4.2007, 20:06 |
| господа посмотрите ассемблерный код и все станет понятно |
| Автор: Fazil6 29.4.2007, 21:23 | ||
будем ходить по кругу? Нет здесь int** Для наглядности
если а используется как указатель на int* , то выражение (a + 1) должно дать адрес на sizeof(int*) больший чем a, но мы получаем адрес больший на sizeof(int[4]) поэтому делаем вывод, что a здесь используется как указатель на int[4] , который в свою очередь никак не приводится к int** ибо тогда неправильно будет работать арифметика указателей и поэтому правило нерекурсивно. |
| Автор: Dov 29.4.2007, 22:50 | ||
| Никакой такой вывод мы не делаем. А просто вспоминаем, что имя массива не обычный указатель, а особенный, имеющий несколько ограничений, по сравнению с обычным указателем. Могу перечислить те, что знаю и помню.
И последнее. Оператор sizeof(ar), используя имя массива в качестве аргумента возвращает размер всего массива в байтах, а не размер указателя. То есть в данном примере вернёт 16, а не 4. |
| Автор: Anikmar 29.4.2007, 23:52 |
Если вам будет легче, то int** является базовым типом для массива. Массив это частный случай поведения, в том числе и рассчета указателя. Еще раз повторю, в связи с тем что массив не является int**, но должен вести себя как int арифметика в нем естественно работает по-другому. Если говорить о нерекурсивности (я вообще не особо понимаю при чем здесь этот термин, но в общем то понятно) то: int a[5]; int *aa = a; Преобразование стандартное, все в порядке, но sizeof(a) и sizeof(aa) также будут отличаться. Однако это ведь вас не смущает? Тем не менее a ведет себя как указатель на int, тогда согласно вашей логике это не так - так как sizeof дают разные результаты? Еще раз повторяю: Я не говорю, что имя двухмерного массива является двойным указателем. Можно сказать, что это производный тип двойного указателя с перегруженной арифметикой. Однако если работать с ним как с двойным указателем он выдает именно тот результат, который ожидается. Этому посвящена отдельная глава стандарта (8.3) Там описана схема преобразования имени массива в указатель |
| Автор: JackYF 30.4.2007, 05:01 | ||||
Попробую оспорить эту фразу таким кодом:
Если двумерные массива приводятся к двойному указателю, то объясни, почему не компилируется часть 1 моего примера. И почему, если даже сделать reinterpret_cast<int**>, то функция будет работать неверно? |
| Автор: Fazil6 30.4.2007, 09:31 | ||||||
Что дает разные результаты? при чем здесь sizeof(a) и sizeof(aa) ??? Вы Вместо того чтобы с пеной у рта доказывать что я дурак подумали бы оба что вы пишете и внимательно почитали что я пишу. int a[5]; - а как указатель на int -> в вычислениях указателя a + sizeof(int) int *aa - > в вычислениях aa + sizeof(int) где здесь будет тличие и разный результат????
фантазии.... |
| Автор: Anikmar 30.4.2007, 09:44 | ||
| Мы уже этот вопрос обсуждали. Нельзя напрямую передать инициализированный массив в функцию. Хоть имя массива и можно передать как двойной указатель, но хранение динамических массивов и статических отличается. Поэтому функции необходимо знать размерности и способ хранения. Если мы хотим создать функцию, которая бы принимала как статический так и динамический массив в качестве параметров придется делать дополнительную работу: так как способ хранения в памяти отличается. Мне кажется это возникло из-за совместимости снизу вверх - в первых версиях Си об этом не подумали, а дальше уже вроде как деваться не было. Когда я начал изучать этот вопрос для меня это тоже было загадкой: зачем два разных механизма сделали для реализации одной и той же вещи. Причем достаточно неудобную. С другой сторону, статические двухмерные массивы переменного размера вещь достаточно редко используемая, для этого существуют динамические массивы.
Тут немного не так звучит моя фраза. Весь наш спор был об использовании имени массива. Не передачи его в функцию. В рамках функции это уже чисто двойной указатель и функция с ним работает уже по правилам двойного указателя (т.е. с базовым классом) и ничего не знает о статическом массиве (производном классе). Если мы хотим сделать функцию, куда мы можем передать как статический массив переменного размера так и динамический - без дополнительной работы не выйдет. Что предлагает Страуструп. Он предлагает преобразовывать статический двухмерный массив в одномерный, передавать в функцию его базовый адрес и размерности, а смещение элемента вычислять динамически по мере надобности. Достаточно странная рекомендация человека, разработавшего этот язык. Если матрица достаточно большая, а элементы нужны не по порядку а вразброс - то это будет накладка по времени и причем серьезная. И внутри функции этот массив уже даже не выглядит массивом - так как нельзя использовать операции извлечения элемента по индексу. Для меня это полная загадка - сделали бы инициализированный массив указателей как сами же написали в стандарте и все было бы замечательно (правда кроме этого спора - его не было бы). Вместо этого этой теме мало того, что посвящают достаточно большую главу в стандарте, еще и в книгах кто как горазд пишет. И ведь действительно, идиотская ситуация: один и тот же логический объект (двухмерный массив) по-разному храниться при статическом и при динамическом варианте создания. По меньшей мере странно. Лично я никогда не пользуюсь статическими двухмерными массивами - они мне видятся достаточно неудобными как раз по причине их передачи в функции. Я для передачи статического массива в универсальную функцию вручную привожу его к двойному указателю, чтобы функция работала как со статическим массивом любого размера так и с динамическим - т.е. для функции по сути это двойной указатель, к которому спокойно применима операция извлечения элемента из массива. Ну это все вода. К сожалению по сути вашего вопроса говорю, что имя двухмерного массива можно использовать как двойной указатель только в рамках блока, в котором он определен. Дальше уже хлопоты - хотите делать функцию, которая работает и со статическими массивами и с динамическими - без накладных расходов не выйдет. Так уж устроен язык. Либо теряем возможность удобной работы через квадратные скобки, либо перед передачей статического массива приводим его полностью через промежуточный массив указателей к двойному указателю. Можно конечно и наоборот сказать, что это двойной указатель ведет себя как статический массив. Но вот тут как раз и есть костяк моего мнения: для того, чтобы сделать работу хотя бы в рамках блока универсальной и перегружена указательная арифметика статического массива. Для чего так было наворачивать я не знаю - было бы все одинаково было бы проще. Всяк жизнь прграммисту наровит испротить |
| Автор: MAKCim 30.4.2007, 09:54 |
| <type> a[N] - массив размерности 1 - есть суть последовательность элементов одного типа <type> a[N][N] - массив размерности 2 - есть суть последовательность массивов размерности 1 элементов одного типа <type> *a - указатель на элемент определенного типа - есть суть адрес элемента <type> **a - указатель на указатель элемента определенного типа - есть суть адрес элемента, содержащего адрес элемента <type> **a != <type> a[N][N] потому как <type> a[N][N] = {a11, a12, a13, ... ,a1N, a21, ..., a2N, ..., aN1, ..., aNN} если <type> **a содержит адрес элемента <type>* и, предположем, этот элемент первый элемент массива из N элементов <type>*, то <type> **a = <type> *a[N] = {a1*, a2*, ..., aN*}, из этого случая видно, что <type> a[N][N] != <type> **a, но т. к арифметика указателей гласит, что, <N раз *>a = a[0]...[0] (N раз), то получаем, что любой массив вида <type> a[A][B][C]... может быть представлен как int<A * B * C * ... раз *>, обратное (как уже показали) не верно |
| Автор: Anikmar 30.4.2007, 12:07 |
| У меня сложилось впечатление, что я всем доказываю, что двухмерный массив является двойным указателем, а со мной спорят. Причем иногда мне самому уже так кажется (что я это доказываю) Итак подвожу итого: Все участники спора прекрасно понимают как организованы в памяти массивы как статические так и динамические. Это на самом деле главное - никто не наворотит ошибок, связанных с непониманием вопроса. Пролистал ответы и что-то не увидел внятного примера передачи любого массива в функцию - сразу какой-то спор пошел, а тема именно с этого началась. Я влез в спор по другому поводу именно с примером - как можно имя двухмерного массива использовать как будто это был бы двойной указатель. Приведенные примеры опонента не устроили - и растянулся теоритический баян, когда в процессе спора уже забыли о чем начали. Да и ладно, в общем то. Не устроили и ладно - главное, что мои примеры рабочие, правильные и могут кому-нибудь пригодиться, а как конкретно это назвать - дело десятое. Я назову получение необходимой информации о статическом массиве - и не более. Исходя из этого, я подведу итого - так как окончательно точку в споре может поставить только самый свежий стандарт C++, его я не нашел, нашел только теоретические статьи, где про нашу тему скромно говориться "вопрос в стадии решения". Может где-то что-то и есть - просто я не нашел. Итак ИТОГО, что я собрал из всей темы: 1. Имя двухмерного статического массива двойным указателем не является. 2. Для передачи статически объявленного массива в функцию объявление такого массива в основной программе и в функции должно совпадать. Т.е. статический массив переменной размерности в функцию без дополнительных ухищрений и приведения типа не передать. По сути это означает, что в языке нет типа "двухмерный массив" - так как универсальное описание двухмерного массива переменного размера отсутствует. Это же относится к оличеству измерений больше 2. 3. Для получения информации об указателях на строки (или столбцы) статического двухмерного массива можно использовать его имя и синтаксис, аналогичный применяемому к двойным указателям. 4. Двойной указатель можно представить при надлежащей инициализации двухмерным массивом. 5. Физическая организация многомерного массива при статическом объвлении и динамическом создании отличается как минимум на дополнительный массив указателей для каждого измерения свыше 1. Всем спасибо за участие в споре, как минимум народ вокруг повеселили и ладно. Думаю надо переключаться на более продуктивные вещи. Еще раз всем спасибо. |
| Автор: Fazil6 30.4.2007, 14:14 | ||||||||||||||
ты сам себе противоречишь. В месте вызова функции массив определен и размерности его известны. То что будет дальше (внутри функции) нас вообще не интересует потому что такой код все равно не компилится. Значит нельзя использовать имя масива как двойной указатель. Ты утверждаешь, что если по стандарту имя int а[2] может использоваться как int* , то продолжая эту аналогию int aa[2][2] можно использовать как int**. У нас (несогласных с этой точкой зрения и считающих, что это заблуждение и необоснованые домыслы) возникает справедливый вопрос : почему тогда это не переносится на примеры кода?
По поводу стандарта. Нет НИГДЕ в стандарте упоминаний того, что имя двумерного массива двойной указатель. Есть пункт 8.3.4.8, который здесь приводили. Есть еще 8.3.4.7
сказано совершенно четко, что имя многомерного массива конвертируется в указатель на массив меньшей размерности. Все! Больше никаких конвертаци дальше нет! Сказано, что если к нему применить *operator , то результат этой операции конвертируется тоже в указатель. Для того чтобы получить этот результат (к примеру для двумерного массива) имя массива используется как уазатель на массив, а не на int*. Т.е. уже все, он уже использовался не просто как указатель , а именно как указатель на массив и дальше разглогольствовать, что дескать там тоже конвертируется в указатель не имеет смысла потому, что к имени двумерного массива это уже отношения не имеет. вчем прав? если
Добавлено через 5 минут и 5 секунд
ох... неправда... я сразу тебе сказал, что это выглядит только так и поэтому меня не устроило... Ладно... Закончили и забыли PS но Dov неправ |
| Автор: apook 30.4.2007, 15:41 | ||||||||||
пошагам
что такое pa[ 0 ], pa[ 1 ], pa[ 2 ]- - это указатели которым надо присвоить адреса(т.к pa это массив из 3-х указателей ) напрашивается
т.е напрямую или косвенно имеется ввиду значок & теперь
абсолютно ненужный (int *) * и & означают ведь разные вещи не пример конечно работает.. Вот и все, а вообще просто было плохое настроение вот я и возмутился на такую совсем мелочь, смысл значил крайне мало, надеюсь не из-за этого так раздулась данная тема |
| Автор: Anikmar 30.4.2007, 16:07 | ||||
| ЭТО МОЙ ПОСЛЕДНИЙ ПОСТ В ЭТОЙ ТЕМЕ - иначе мы ее на завершим. Вот как советует приводить первый элемент двойного массива господин Страуструп:
Там много текста - это конкретно выражение у него используется для получения указателя на первую строку. m - двухмерный массив Самое смешное, в его примерах ничего несказано, что статический двухмерный массив передается в функцию с явным преобразованием к двойному указателю. ИМХО Код Страуструпа сомнителен с точки зрения быстродействия.
Ну не устроило - так не устроило, что я могу сказать. Автора темы - пусть галочку поставит "ТЕМА ЗАКРЫТА" Хоть как тут меня называйте и обзывайте - в этой теме больше нислова не скажу |
| Автор: Ln78 30.4.2007, 18:04 |
| FiMa1 и все желающие – получите обещанное окончание. Там есть обращение и к apook. Likehood, threef – основное содержание вам вряд ли будет интересно, но последние 2 страницы, возможно, вам понравятся. Fazil6. У меня недостаточно постов, чтобы поставить тебе +, поэтому скажу здесь - молодец. 10 раз объяснять одно и то же, когда и первое объяснение было простым и понятным, сохраняя при этом вежливость и видимое спокойствие, - я бы, наверное, так не смог. Хотя и у тебя уже динамические массивы начали нервно курить. В одном из соседних топиков обсуждался, в частности, вопрос выхода из вложенных циклов. Не знаю, какой вложенности здешний «цикл» обсуждений, но это – моя 4-я попытка вставить break. Хотел было обратиться к многоуважаемой Earnest с просьбой поставить здесь свой весомый return, да прочитал абзац про ничего не понимающего в массивах Страуструпа и передумал. Да… В общем, береги невры Fazil6, ты нам нужен живым и здоровым |
| Автор: JackYF 1.5.2007, 23:39 |
| Подведу и я свой мини-итог. Тип "двойного массива" != тип "двойной указатель". Внутренности двойного массива не тождественны внутренностям двойного указателя. Ни неявное, ни явное преобразования не приводят один тип к другому. То, что объединяет эти два типа - возможность обращения к элементу через двойную индексацию. Надеюсь, от меня в этой теме тоже все. |
| Автор: zkv 2.5.2007, 00:47 |
| Fazil6, apook, Anikmar, JackYF, что это вы на 13-й странице остановились? Примета плохая, надо еще одну-другую накатать |
| Автор: FiMa1 3.5.2007, 13:16 | ||||||||||
Ln78, спасибо тебе огромное! Старался читать с особым пристрастием. Есть вопросы...
Ну с arA[0], допустим, понятно. Это имя переменной, значением которой м.б. только целое число. arA, насколько я понял - имя указателя, т.е, по-сути, тойже переменной, значением которой, однако, может быть только адрес (ну и другие, в данном контексте несущественные, отличия обычной переменной от указателя). Далее выполняем следующие действия:
Действия в обоих строках абсолютно одинаковые? Если да, то вопрос2 - как таки работает приведение, использованное в первой строке? Фактически берем адрес указателя arA, приводим его к указателю.. ну не понятно.. В противном случае, если arA - не указатель, а просто переменная, то почему нельзя записать:
Ну и в догонку - что за тип такой 'int (*__w64 )[10]' ? ДРУЗЬЯ, БОЛЬШАЯ БРОСЬБА В ДАННОМ ТОПИКЕ ДИСКУССИЙ НЕ РАЗВОДИТЬ! РЕЖИМ ВОПРОС-ОТВЕТ ЛИБО РИДОНЛИ ;) СПАСИБО! |
| Автор: Ln78 3.5.2007, 14:27 |
| FiMa1, как бы тебе ответить, чтобы не спугнуть здешнее затишье? Может, если у тебя ко мне появятся по этой теме ещё вопросы, лучше задавать их в личку? Хотя я на форуме новичок, не знаю, как лучше. Собственно, ответ на вопрос. Тебе можно считать, что 'int (*__w64 )[10]’ это есть адрес массива из 10 элементов, т.е. собственно то, что написано буквально. Модификатор __w64 это специфическое для VS определение, связанное с вопросами портирования из 32-х в 64-х разрядный компилятор, для тебя, наверное, это неважно. Если его убрать, то получим просто указатель на массив из 10 элементов. Т.е. если мы напишем так: typedef int ArrayInt10[10]; ArrayInt10 *pAr = &arA; то никаких замечаний от компилятора, естественно, не получим. Как я там писал, единственная причина, зачем я написал эту строчку, показав, что и такое сработает: Только для того, чтобы продемонстрировать один нюанс: не следует считать, что для компилятора arA и &arA[0] абсолютно одно и то же. Т.е. здесь к arA мы можем применить операцию взятия адреса, а к &arA[0] – нет. Основное, что ты должен запомнить, когда делаешь явные приведения: существует только память под 10 элементов int, к этой памяти можно адресоваться. И на все три варианта будет получен только один ответ: адрес начального элемента массива. Ничего больше нет. Компилятор не выдумщик, ему просто нечего больше возвращать. Все примеры преобразований, которые я приводил, например, с «превращением» двумерного массива в трёхмерный, рисуя таблички, как оно находится в памяти, я приводил для демонстрации этого факта (ни один бит при этих преобразованиях даже не пошевелился, это просто указание компилятору воспринимать область памяти так или иначе). В примере с тем одномерным массивом: есть массив из 10 чисел. Адрес первого (нулевого) элемента этого массива мы в начале привели чёрт знает к чему (могли привезти к указателю на рыжую корову, это сути не меняет), а потом явно сказали, что это всё-таки указатель на целое число (или несколько чисел), т.е. к тому, как оно есть на самом деле. Ещё раз повторюсь, так как написано в первой строке – никогда не делай. «Просто переменная» может быть чем угодно и указателем, и указателем на указатель, да мало ли ещё чем. Но при присваивании типы операндов должны совпадать (или для них в языке должны быть определены правила преобразования), иначе компилятор выдаст ошибку. Силой компилятор можно заставить считать, что это другой тип, но дальнейшая ответственность на том, кто такое приведение написал. |
| Автор: FiMa1 3.5.2007, 15:01 | ||||
Под "привели чёрт знает к чему" ты подразумеваешь то, что мы зачем-то взяли адрес указателя? В то время, как могли бы просто написать int *pArr = (int *)arr; или и подавно int *pArr = arr; вместо:
PS: Ln78, не серчай, если шибко надоедаю, заглядывай периодически в топик, вопросы, имхо, будут... |
| Автор: Ln78 3.5.2007, 15:15 |
| FiMa1, я совсем "не серчаю", что ты. Мы все не экспертами родились, вопросы задавать нормально. "Черт знает к чему" - не от "серчания", а просто, чтобы было понятнее. Я просто боюсь опять поднять волну в этом многострадальном топике. Именно это я и имел в виду. Единственное, всё-таки, нет там указателя (как такового, для которого компилятор отвёл место, для которого можно писать что-то типа int**), есть имя массива, о чём эти 13 страниц и говорили, и примерно столько же я написал тебе в комментариях. |
| Автор: FiMa1 3.5.2007, 16:00 | ||||||||
Очередной вопрос..
Строку инициализации указателя pArr[0] компилятор приводит к виду *(*(pArr + 0) + 1) = 4; ? Нагло воспользовавшись твоими знаниями попытаюсь удовлетворить свою любознательность..
Почему компилятор MS VS.NET 2003 разрешает создать такую структуру, ведь обратиться к ее члену будет невозможно, т.к. имя создаваемому структурой типу не дано?
Если верить codelord, то не рулит компилятор MS.. ? |
| Автор: codelord 3.5.2007, 16:25 | ||
gcc
|
| Автор: Ln78 3.5.2007, 16:40 |
| На первый вопрос - да. На второй (несколько выходит за тему топика) строгого формального ответа не знаю, но как бы я это объяснил для себя: это объявление сродни описанию неиспользованной переменной: пользы от неё никакой, но и вреда тоже. В случае с константами ситуация другая: если компилятор обработал такое определение переменной, то предполагается, что к ней можно обращаться за значением, а поскольку у неё значения нет - воспринимается как ошибка. Формально правильнее, наверное, можно было бы сказать: синтаксис такой, то нельзя, а это можно. Но, учитывая, что синтаксис создавался предварительно подумав, логика какая-то быть должна. А поскольку не я определял синтаксис, точного объяснения дать не могу. Можешь создать ещё один топик по этой теме, возможно, получишь много интересных вариантов в ответ. |
| Автор: FiMa1 4.5.2007, 10:32 |
| Имя одномерного массива - это константа, представляющая собой указатель на 0-ой элемент массива. Этот указатель отличается от обычных тем, что его нельзя изменить (установить на другую переменную), поскольку он сам хранится не в переменной, а является просто некоторым постоянным адресом. Андрей Богатырев. Хрестоматия по программированию на Си в Unix Как таки будет выглядеть определение для МНОГОМЕРНОГО массива? |
| Автор: Dov 4.5.2007, 10:41 |
FiMa1, а сам-то ты как думаешь? Намёк: многомерный массив представляет собой одномерный массив массивов. |
| Автор: FiMa1 4.5.2007, 11:05 | ||
Имя многомерного массива - это константный указатель на 0-ой элемент данного многомерного массива (здесь одномерный массив - константный указатель на 0-ой элемент данного одномерного массива). |
| Автор: Fazil6 4.5.2007, 11:25 | ||||
какой 0 в последней строке? Вся предыдущая дискуссия была о том, что представлять
неправильно. Правильно сказать Имя многомерного массива - это константный указатель на 0-ой элемент данного многомерного массива (здесь одномерный массив) поэтому и не будет работать
|
| Автор: FiMa1 4.5.2007, 13:17 | ||
Ок, если ma – обычный указатель, то зачем нужно явное его приведение к int*: int *p = (int *)ma; ?
В общем-то всё понятно. Не очевидным кажется только то, что имя массива ma - по определению указатель, но мы не можем использовать его как указатель без предварительного приведения к *int. Вот это прокомментируйте. пожалуйста. |
| Автор: Fazil6 4.5.2007, 15:05 | ||||
ma - это не указатель по определению. Это имя массива можно использовать как указатель. В Твоем примере ma можно использовать как указатель на массив int[5] и поэтому
компилироваться не будет, но с приведением к int* работать будет потому, что адрес, с которым асоциировано имя ma - это, кроме всего прочего, и адрес первого элемента первого массива в твоем многомерном массиве, и через этот адрес посредством указателя int *p можно получить дотуп. |
| Автор: FiMa1 4.5.2007, 15:44 | ||||
Почему когда мы пишем
|
| Автор: Fazil6 4.5.2007, 15:52 |
он не инициализируется нулем. Он инициализируется адресом, асоциированным с ma, а по этому адресу лежит значение ma[0][0] , а так как ma[0][0] равно нулю и поэтому *p2 равно 0 , и поэтому **p2 - это попытка разыменовать нулевой адрес. |
| Автор: FiMa1 4.5.2007, 16:14 | ||
С этим разобрался, спасибо! Далее Ln78 пишет (комменты мои):
|
| Автор: Ln78 4.5.2007, 18:19 |
| FiMa1, я рисовал таблички, чтобы пояснить, что там делается, посмотри на них внимательнее. Здесь мы указываем транслятору, чтобы он мог ту область памяти (3*5*4 байт) адресовать другим способом, как будто там расположен трёхмерный массив. |
| Автор: FiMa1 10.5.2007, 10:58 |
| Надеюсь, что во всем разобрался... Сделал для себя ряд очевидных, для взгляда со стороны, выводов. После объявления int arr[SIZE], получить доступ к элементам массива arr можно посредством имени переменной arr[0] и имени переменной-указателя arr. Такое многообразие дополнительных имен переменных дает просто больше возможностей для "общения" с компилятором. С другой стороны, важно знать, что ни для хранения самого имени arr[0], ни для имени arr никакой памяти не выделяется!!! С точки зрения компилятора, имя переменной - это синоним имени определенной ячейки памяти, ее символический адрес. Эта ячейка памяти может хранить данные — значение переменной. В то время, как сам объявленный массив arr имеет тип ТИП[SIZE], имя массива может быть использовано в качестве переменной типа КОНСТАНТНЫЙ_УКАЗАТЕЛЬ, который, по умолчанию проинициализирован нулевым элементом данного массива. Рассмотрим определение двумерного массива - int ma[3][3]; После данного определения вводится три имени переменных - ma, ma[0], ma[0][0]. Соответственно типов int[3][3], int[3] и int. Теперь имя массива ma может быть использовано в качестве указателя на его нулевой элемент, вместе с этим на разработчика ложится отвественность четко понимать, что, в данном случае, нулевым элементом массива ma является также массив, только одномерный – int ma[3], имя которого также может быть использовано в качестве указателя... Поэтому для доступа к нулевому элементу двумерного массива через указатель необходимо предварительное приведение к типу указатель: int *pa = (int *)ma; В общем, ОГРОМНОЕ всем спасибо за участие! Отдельная благодарность Ln78 за потраченное время и усилия на составление документов! |
| Автор: unicuum 20.4.2008, 04:48 | ||||
Простой код без каких-либо фишек и хитростей языка:
Распечатка:
Таким образом с многомерными массивами можно работать и посредством указателя, указывающего на первый элемент массива. Иными словами для нашего случая это int*, а не int**. int** является указателем на указатель, следующая картинка схематически изображает память и нарисована мною чтобы разъяснить данный момент. ![]() 1. указатель на указатель содержит адрес указателя на целочисленное значение 2. указатель содержит адрес целочисленного значения 3. внутри самого первого элемента массива содержится конкретное значение, а не адрес |