![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
Anikmar |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2513 Регистрация: 26.11.2006 Где: Санкт-Петербург Репутация: 9 Всего: 59 |
А что вы хотели? Мы не получим смещение 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 - приводится к массиву указателей по стандарту. На сколько пришлось реально инкрементировать (или декрементировать - уже стебусь ![]() В чем разница в поведении? Это сообщение отредактировал(а) Anikmar - 29.4.2007, 03:39 |
|||
|
||||
Mayk |
|
|||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 45 Всего: 134 |
занудутствую: Наоборот. a - это массив из трёх элементов, каждый из которых является массивом из двенадцати элементов, каждый из которых является int'ом. зы. 6 последних страниц не читал. 2 и 3 тоже. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
|||
|
||||
Anikmar |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2513 Регистрация: 26.11.2006 Где: Санкт-Петербург Репутация: 9 Всего: 59 |
Эх.... Все-таки подсадили. ![]() Как правильно пишется ЗоНУДА и ЗаНУДА? ![]() Фраза из анекдота:
ПРОШУ СЧИТАТЬ ОЧЕПЯТКОЙ ![]() Добавлено @ 01:47 P.S. И ваще - в 2 часа ночи что угодно перепутать можно, а не только порядок измерений. Mayk - абсолютно прав, но смысл от этого никак не меняется P.P.S. Очепятка исправлена. Это сообщение отредактировал(а) Anikmar - 29.4.2007, 03:41 |
||||
|
|||||
Fazil6 |
|
||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
вообще-то sizeof(int[50]) потому что элементом, на который указывает указатель а является int[50]. И никаких правил работы с двойными или тройными или еще какими указателями нет. Есть арифметика указателей и важно в ней только на что указывает указатель. Для двойного указателя он указывает на int*, а для массива на int[50]. Вот их размер в байтах и важен
ох.... Я разве сказал, что правила нарушены? Я разве говорил, что пример не работает? Я сразу сказал, что правила по которым этот пример работает разные в случаях двойного указателя и массива. как солнце из-за туч!!!! Только непонятно что ты читал в топике предыдущие 10 страниц....
доводы чего? того что я тебе первым делом сказал? Теперь ты мне объясняешь как по стандарту... Именно потому что так по стандарту меня твои примеры и не устроили. Я тебе так сразу и сказал. хранит он указатель на указатель. Все. Никаких массивов здесь нет.
в том как компиллятор получает этот очередной элемент |
||||||||||
|
|||||||||||
Anikmar |
|
||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2513 Регистрация: 26.11.2006 Где: Санкт-Петербург Репутация: 9 Всего: 59 |
С этого момента по подробнее. Итак имеем арифметику указателей. Имеем 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 |
|
||||||||||||||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
С чего ты взял, что компиллятор стремится чтобы
???? Ему до лямпочки ваши инсинуации по поводу того как массив или его имя представить как двойной указатель. Ему говорят "прибавь к указателю", он вычисляет размер того на что он указывает и прибавляет нужное количество байт. Он совершенно не стремится , чтобы это было как с двойным указателем. Зачем ему это? Он работает с указателем на массив , а это не указатель на указатель.
хм... она и так везде одинаковая... Повторяю, что компилятор четко разделяет статические массивы и двойными указателями.
Вот я и говорю, какого художника вы утверждаете что **(a + 1) работает с int**, если a + 1 дает смещение не на размер int* , а на размер подмассива? Только не надо говорить , что смещение на размер подмассива для того чтобы соответствовало двойному указателю.
нет. Нет такого правила в стандарте... Есть правило по поводу просто массива, но это правило не рекурсивно и переносить его на многомерные массивы нельзя.
Дискуссия с тобой началась с моих примеров.... ну применил ты разыменовывание, ну получил ты в итоге int. По твоему это доказывет, что тип разыменовываемого int**? Я говорю, что тип того, что ты разымновывал int (*)[n] (указатель на массив из n элементов). Если его имя разыменовать 2 раза получим int. Вот меня и не устраивает твой пример в котором двойным указателем и не пахнет.
я вижу объявление и этого достаточно, чтобы однозначно определить тип. Нет такого типа указатель на двухмерный динамический массив и в арифметике указателей твое p будет использоваться как указатель на int* и никак подругому. Вообще причем здесьэти экскурсы в динамическое выделение. Твой двухмерный динамический массив совершенно подругому расположен в памяти и то, что тут именно двойной указатель я не спорю
как его не инициализируй, но тип его не изменится. Это указатель на char. Даже new[] не сделает из него массива и его арифментика будет основываться на размере char. Мы говорим о нормальных класических статических массивах и динамические массивы идут в сад...
повторяю, динамические массивы нервно курят в саду... Речь не о них.
ну не надо передергивать... было ведь так ни по какому стандарту он к массиву указателей не приводится.
|
||||||||||||||||||||||
|
|||||||||||||||||||||||
Xenon |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1529 Регистрация: 12.4.2006 Репутация: 11 Всего: 50 |
Anikmar,
Это что, работать должно? p - не l-value, к нему не может быть применем инкремент. |
|||
|
||||
Anikmar |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2513 Регистрация: 26.11.2006 Где: Санкт-Петербург Репутация: 9 Всего: 59 |
В моем примере 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 |
|
|||
![]() Гентозавр ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 2976 Регистрация: 29.8.2005 Где: Germany Репутация: 19 Всего: 181 |
![]() ![]() Немного статистики для сравнения: "Win vs. Lin": 122 дня, 453 поста = 3,71 поста/день "Собираем поклонников висты": 12 дней, 147 постов = 12,25 поста/день (это при активном участии тролля) Эта тема: 9 дней, 172 поста = 19,11 поста/день ![]() Вывод: религиозные войны отдыхают ![]() ![]() Ладно, больше не буду вам мешать ![]() -------------------- ![]() Real men don't use backups, they post their stuff on a public ftp server and let the rest of the world make copies - Linus Torvalds |
|||
|
||||
Fazil6 |
|
||||||||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
такие, когда будет видно, что имя используется именно как тип int**. Я тебе уже говорил, что таких примеров мне неведомо ибо двумерный массив никак не связан с двойным указателем. И имя двумерного массива никогда не используется как переменная типа int**. например это
доказывает, что a может использоваться как int**, и если бы такое выражение было корректно в С++ , то этого хватило бы для доказательства моей НЕПРАВОТЫ и безграмотности. какие еще конкретные примеры??? С чего твои наезды на меня начались??? Посмотри внимательно.... http://forum.vingrad.ru/index.php?showtopi...t&p=1114163 там полно примеров.
Неправда. Вот как он прозвучал.
ты показал как двойной указатель использовать в качестве двумерного массива. Я не это спрашивал.
ну вобщем смысл был таким. Если вы утверждаете что int[2][2] и int** както связаны (имя или еще что-то) то приведите примеры из которых это будет видно. да. Только не рассматривать а использовать где это нужно. вот здесь стоило остановиться ибо на этом рассуждения заканчиваются и дальнейшие умозаключения неправильны ну вот это и неправильно. Это заблуждение. Я уже раз пять повторил!!!! Правило относящееся к одномерному массиву нерекурсивно!!!! Если мы можем рассматривать имя двумерного массива, то приведите мне пример где оно рассматривается компилятором как int**. Не надо хвататься за клаву. Нет таких примеров... Ты говоришь, что в примере **a - это работа с двойным указателем потому что получили int. Я говорю, что это работа с указателем на int[n] , а двойным указателем здесь не пахнет.
конечно это разные типы.
Бред.... Ничего компилятор никуда не подгоняет.
ну договорились... Получается есть в языке си тип "двойной указатель"... |
||||||||||||||||
|
|||||||||||||||||
Anikmar |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2513 Регистрация: 26.11.2006 Где: Санкт-Петербург Репутация: 9 Всего: 59 |
Наконец то из нашего баяна только один пост более мнее выделить можно. Откуда такая уверенность в этом: Правило относящееся к одномерному массиву нерекурсивно!!!! Это вы прочитали в стандарте? Еще раз рассмотрим int[n]. Это массив целых чисел, который может быть приведен к указателю на int. Причем приведен имено по стандарту - без всяких приведений типа. По сути компилятор это делает неявно: int a[45]; int *b=a; Так что имя одномерного массива практически является указателм на тип - так как приводится неявно. И почему это правило должно быть нерекурсивно? И главное где это написано?
Предлагаю остановиться на этом примере: int a[3][4]; int b = **a; |
|||
|
||||
MAKCim |
|
|||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 52 Всего: 207 |
господа
посмотрите ассемблерный код и все станет понятно ![]() -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
|||
|
||||
Fazil6 |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
будем ходить по кругу? Нет здесь int** Для наглядности
если а используется как указатель на int* , то выражение (a + 1) должно дать адрес на sizeof(int*) больший чем a, но мы получаем адрес больший на sizeof(int[4]) поэтому делаем вывод, что a здесь используется как указатель на int[4] , который в свою очередь никак не приводится к int** ибо тогда неправильно будет работать арифметика указателей и поэтому правило нерекурсивно. Это сообщение отредактировал(а) Fazil6 - 29.4.2007, 21:23 |
|||
|
||||
Dov |
|
|||
![]() аСинизатор ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1721 Регистрация: 10.5.2003 Где: Эрец-Исраэль Репутация: 15 Всего: 88 |
Никакой такой вывод мы не делаем. А просто вспоминаем, что имя массива не обычный указатель, а особенный, имеющий несколько ограничений, по сравнению с обычным указателем.
Могу перечислить те, что знаю и помню.
И последнее. Оператор sizeof(ar), используя имя массива в качестве аргумента возвращает размер всего массива в байтах, а не размер указателя. То есть в данном примере вернёт 16, а не 4. -------------------- Тут вечности запах томительный, И свежие фрукты дешевые, А климат у нас – изумительный, И только соседи – #уевые. Игорь Губерман. |
|||
|
||||
Anikmar |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2513 Регистрация: 26.11.2006 Где: Санкт-Петербург Репутация: 9 Всего: 59 |
Если вам будет легче, то int** является базовым типом для массива. Массив это частный случай поведения, в том числе и рассчета указателя. Еще раз повторю, в связи с тем что массив не является int**, но должен вести себя как int арифметика в нем естественно работает по-другому. Если говорить о нерекурсивности (я вообще не особо понимаю при чем здесь этот термин, но в общем то понятно) то: int a[5]; int *aa = a; Преобразование стандартное, все в порядке, но sizeof(a) и sizeof(aa) также будут отличаться. Однако это ведь вас не смущает? Тем не менее a ведет себя как указатель на int, тогда согласно вашей логике это не так - так как sizeof дают разные результаты? Еще раз повторяю: Я не говорю, что имя двухмерного массива является двойным указателем. Можно сказать, что это производный тип двойного указателя с перегруженной арифметикой. Однако если работать с ним как с двойным указателем он выдает именно тот результат, который ожидается. Этому посвящена отдельная глава стандарта (8.3) Там описана схема преобразования имени массива в указатель |
|||
|
||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |