Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Многомерные массивы и указатели, Как получить адрес элемента массива 
V
    Опции темы
Fazil6
Дата 27.4.2007, 15:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Anikmar @  27.4.2007,  15:16 Найти цитируемый пост)
Какие конкретно действия "наоборот" вас интересуют?

привести пример выражения где int a[3][4] использовался бы как int**. 
PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 15:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



Цитата(Fazil6 @  27.4.2007,  15:42 Найти цитируемый пост)
привести пример выражения где int a[3][4] использовался бы как int**.  


Я такой пример привел, он вас не устроил? Например так:
Код

int a[3][4];
int **p;
int *d;

d = p[0];
d = a[0];


Добавлено через 5 минут и 52 секунды
Или так:
Код

    int a[3][4] = {{100,101,102,103},{110,111,112,113},{120,121,122,123}};
    int i;

    for (i=0;i<4;i++) printf("%d\t",*(*a)+i);
    printf("\n");


PM MAIL ICQ   Вверх
Fazil6
Дата 27.4.2007, 16:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Anikmar @  27.4.2007,  15:58 Найти цитируемый пост)
Я такой пример привел, он вас не устроил? Например так:

хм... в какой строке массив или его имя используется именно как int**? Правда не вижу
PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 16:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



Цитата(Anikmar @  27.4.2007,  15:58 Найти цитируемый пост)
*(*a)+i


Как тогда вы эту строку расцениваете? Как работу с целым числом?

Добавлено через 1 минуту и 57 секунд
Объясните, какие действия вы хотите сделать с двойным указателем? Приведите пример работы с двойным указателем, который бы вы хотели увидеть?
PM MAIL ICQ   Вверх
Fazil6
Дата 27.4.2007, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Anikmar @  27.4.2007,  16:17 Найти цитируемый пост)
Как тогда вы эту строку расцениваете? Как работу с целым числом?

ну совсем не как int**, ведь если 2 раза разыменовать и в результате получить int еще не означает, что то, что разыменовывается имеет тип int**. Я привел пример когда **ppa выдает int, но ppa не является int** 

http://forum.vingrad.ru/index.php?showtopi...t&p=1114163
 
ведь получаемый результат выражения (я имею в виду адрес) a + 1 разный в зависимости от того какого типа a. Ведь если a типа int** , то смещение будет 4*1 байта, а если a[2][2] , то смещение будет sizeof(int[2])*1. 
и в твоем примере с циклом *a возвращает массив int[4] , а потом к нему применяется смещение 0 и разыменовывание дает int

Добавлено через 3 минуты и 9 секунд
Цитата(Anikmar @  27.4.2007,  16:17 Найти цитируемый пост)
Объясните, какие действия вы хотите сделать с двойным указателем? Приведите пример работы с двойным указателем, который бы вы хотели увидеть?

мне неизвестны такие примеры, которые компиллировались бы, либо компиллировались и работали бы. Поэтому я и утверждаю, что ни массив int a[2][2] ни его имя не являются int**. Те кто это утверждают, видимо знают такие действия и примеры
PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 16:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



Цитата(Fazil6 @  27.4.2007,  16:31 Найти цитируемый пост)
ну совсем не как int**, ведь если 2 раза разыменовать и в результате получить int еще не означает, что то, что разыменовывается имеет тип int**. Я привел пример когда **ppa выдает int, но ppa не является int**

С такой логикой можно любой пример оспорить.

К этому коду это суждение не относится. Разыменовывается без всяких приведений и искусственных преобразований (переопределений) и выдает именно то значение, которое  ожидалось.


Цитата(Fazil6 @  27.4.2007,  16:31 Найти цитируемый пост)
ведь получаемый результат выражения (я имею в виду адрес) a + 1 разный в зависимости от того какого типа a. Ведь если a типа int** , то смещение будет 4*1 байта, а если a[2][2] , то смещение будет sizeof(int[2])*1. 
и в твоем примере с циклом *a возвращает массив int[4] , а потом к нему применяется смещение 0 и разыменовывание дает int


Если вы считаете, что этот результат случайный так, как размеры int и указателя совпадают попробуем по-другому:
Код

    double aa[3][4] = {{100.1,101.2,102.3,103.4},{110.1,111.2,112.3,113.4},
        {120.1,121.2,122.3,123.4}};

    for (i=0;i<4;i++) printf("%lf\t",*((*aa+1)+i));



Теперь выдаем 2-ю строку массива (или столбец - как кому удобнее)
Что теперь с указательной арифметикой не устроит?

Добавлено через 6 минут и 58 секунд
Цитата(Fazil6 @  27.4.2007,  16:31 Найти цитируемый пост)
мне неизвестны такие примеры, которые компиллировались бы, либо компиллировались и работали бы. Поэтому я и утверждаю, что ни массив int a[2][2] ни его имя не являются int**. Те кто это утверждают, видимо знают такие действия и примеры 


Я привел вам 2 работающих примера, когда имя массива можно использовать в качестве указателя. Я никогда не утверждал, что имя массива является указателем - так как он имеет тип int[3][4] - это легко увидеть использовав typeid(...).name()

Я говорил, что имя массива можно использовать в качестве двойного указателя, что я вам и продемонстрировал. А вы говорите, что не увидели ни одного примера - дескать то, что я привел не является двойным указателем. У меня, право, возникло затруднение - я не увидел ни одного аргумента, опровергающего работающие правильно участки кода.

Если бы вы не увидели предыдущего текста, как бы вы расценили эту строку:
printf("%lf\t",*((*aa+1)+i));

У вас бы спросили какого типа aa? Что бы вы ответили? По выражению - это работа с двойным указателем и никак иначе. Строка одинаково выглядит и если бы aa была бы объявлена как ** и как двойной массив.

PM MAIL ICQ   Вверх
Fazil6
Дата 27.4.2007, 17:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Anikmar @  27.4.2007,  16:43 Найти цитируемый пост)
У вас бы спросили какого типа aa? Что бы вы ответили? По выражению - это работа с двойным указателем и никак иначе. Строка одинаково выглядит и если бы aa была бы объявлена как ** и как двойной массив.

это выглядит как работа с двойным указателем, но это не является работой с двойным указателем, потому что

Цитата(Fazil6 @  27.4.2007,  16:31 Найти цитируемый пост)
ведь получаемый результат выражения (я имею в виду адрес) a + 1 разный в зависимости от того какого типа a. Ведь если a типа int** , то смещение будет 4*1 байта, а если a[2][2] , то смещение будет sizeof(int[2])*1. и в твоем примере с циклом *a возвращает массив int[4] , а потом к нему применяется смещение 0 и разыменовывание дает int
 и aa в твоем примере используется как указатель на массив , а не как double**

PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 17:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



Цитата(Fazil6 @  27.4.2007,  17:46 Найти цитируемый пост)
и aa в твоем примере используется как указатель на массив , а не как double**


т.е. согласно вашей логике, указатель на конкретный тип данных и указатель на массив из таких данных отличаются?

char *s; - Это указатель именно на переменную типа char (т.е. на 1 символ) или на массив таких символов? Ответьте мне, пожалуйста.

Далее:

char **s; - Это указатель на указатель на символ или указатель на массив указателей на символ?


PM MAIL ICQ   Вверх
apook
Дата 27.4.2007, 17:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 794
Регистрация: 12.7.2006

Репутация: 9
Всего: 23



Это вообще указатель на массив указателей на тип char
и другое дело какие адреса взяты указателями



--------------------
Мои руки из дуба, голова из свинца ну и пусть ...
PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 18:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



Цитата(apook @  27.4.2007,  17:56 Найти цитируемый пост)
Это вообще указатель на массив указателей на тип char

А просто как указатель на указатель его нельзя использовать? Можно.

Тогда объяснитем мне смысл этой фразы:

Цитата(Fazil6 @  27.4.2007,  17:46 Найти цитируемый пост)
и aa в твоем примере используется как указатель на массив , а не как double**


Как тогда использовать double**? Как указатель на массив не подходит, а как нужно?

PM MAIL ICQ   Вверх
Fazil6
Дата 27.4.2007, 18:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Цитата(Anikmar @  27.4.2007,  17:51 Найти цитируемый пост)
т.е. согласно вашей логике, указатель на конкретный тип данных и указатель на массив из таких данных отличаются?

да. массив и указатель это разные типы. Передай sizeof массив и указатель , который указывает на его первый элемент. Получишь разные результаты. tipeid выдаст разное.  Физически - это адрес, но работа с ним и результат зависит от того как объявлено.
Цитата(Anikmar @  27.4.2007,  17:51 Найти цитируемый пост)
char *s; - Это указатель именно на переменную типа char (т.е. на 1 символ) или на массив таких символов? Ответьте мне, пожалуйста.

это указатель на символ. Массив символов при передаче в функцию передается как char *s.

Строковый литерал "11" имеет тип char[3].

Я понимаю, что соблазн представлять массив как указатель достаточно велика, но если следовать логике, что  в строке
for (i=0;i<4;i++) printf("%d\t",*(*a)+i); a - используется в виде int** , то почему в параметрах функции двумерный массив не преобразуется в int**.
Весь фокус не в том насколько велика разница между массивом и указателем, а в том что слишком большая разница между указателем на массив и указателем на указатель.
PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 18:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



P.S.

Цитата(apook @  27.4.2007,  17:56 Найти цитируемый пост)
Это вообще указатель на массив указателей на тип char


А я как написал? По-другому?

Цитата(Anikmar @  27.4.2007,  17:51 Найти цитируемый пост)
Это указатель на указатель на символ или указатель на массив указателей на символ?


Если конечно не придираться, что я char назвал символом - ну лень мне было язык переключать   smile 
PM MAIL ICQ   Вверх
Fazil6
Дата 27.4.2007, 18:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1653
Регистрация: 3.5.2006
Где: Минск

Репутация: 35
Всего: 60



Код

char *s = "1111";

допустимо только для совместимости со старым кодом.
так ведь недопустимо
Код

int *pi = {1,2,3};


Добавлено @ 18:10
Цитата(Anikmar @  27.4.2007,  17:51 Найти цитируемый пост)
char **s; - Это указатель на указатель на символ или указатель на массив указателей на символ?

это указатель на указатель на char или на символ, в данном случае понятно.


Это сообщение отредактировал(а) Fazil6 - 27.4.2007, 18:11
PM MAIL   Вверх
Anikmar
Дата 27.4.2007, 18:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2513
Регистрация: 26.11.2006
Где: Санкт-Петербург

Репутация: 9
Всего: 59



Цитата(Fazil6 @  27.4.2007,  18:04 Найти цитируемый пост)
то почему в параметрах функции двумерный массив не преобразуется в int

Потому, что он таковым не является.

Тут примерно такая же разница как между адресом функции и адресом метода класса.
Если массив определяется статически - то для вычисления смещения необходимо знать размерность этого массива. А в голом указателе такой информации нет.

Если вы создаете массив динамически и передаете его в функцию, то что должна знать функция? Это адрес собственно массива и его размерности. Для чего? Чтобы правильно вычислить местоположение конкретного элемента.

Когда массив определен явно, он выделяется прямым куском в памяти и размерности его известны. Именно поэтому он получает тип int [3][4], а не int** - т.е. в этот тип уже явно включены размерности.

Если он создается динамически, то мы вынуждены создавать его многоступенчато. Сначала создаем массив указателей, потом каждый указатель инициализируем собственно массивом данных конкретного типа.

Если мы создадим динамический массив единым куском, то он не будет двухмерным с точки зрения языка Си++ - вы не сможете доступиться к его элементам стандартным оператором [][]. Так как в этом большом куске будут отсутствовать  указатели, которые будет искать данный оператор.

А когда этот оператор пытается найти искомые указатели в статическом массиве - то ему на вход передается уже другой тип данных, не int**, а int[3][4] - соответственно адрес конкретного элемента вычисляется по-другому.

Нельзя передать в функцию статический двухмерный массив как указатель именно из-за разного алгоритма вычисления смещения элемента. Функция, которая получит на вход двойной указатель будет вызывать оператор [][], относящияся именно к двойному указателю, а не к int[3][4].  

Именно поэтому, работать с именем массива как с двойным указателем возможно, но он не является двойным указателем  сам по себе и передавать его в функцию, преобразовав к двойному указателю будет ошибкой. Собственно также как и двойной указатель бессмыслено передавать в какую-либо функцию предварительно не проинициализировав его.
Например:

Код

// Объявляем статический массив
int a[3][4] = {{100,101,102,103},{110,111,112,113},{120,121,122,123}};
// Объявляем двойной указатель
int *pp;

pp = (int**)a;
pp[0][1] = 100; // Так нельзя, компилятор ждет, что в pp[0] находится указатель, а его там нет - так как для статических массивов они не определяются.

// Но если сделать так:
pp = new int*[3];
pp[0] = a[2];
pp[1] = a[1];
pp[2] = a[0];

// То мало того, что pp теперь правильный массив с точки зрения Си++, но в нем еще и строки исходного массива перемешаны.
// Так что можно с ним работать как с двухмерным массивом, передавать в функцию и т.п.

// теперь pp[0][0] == 120 и это абсолютно ожидаемый результат.



Цитата(Fazil6 @  27.4.2007,  18:04 Найти цитируемый пост)
Передай sizeof массив и указатель , который указывает на его первый элемент. Получишь разные результаты. tipeid выдаст разное.  Физически - это адрес, но работа с ним и результат зависит от того как объявлено.


Нет и еще раз нет.
Если мы объявим 
char *s; - то это всегда указатель
char s[30]; - это совсем другой тип, это явно заданный массив и имеет тип char[30]

char *s = new char[100];
char *ss;
// Это уже динамический массив. Причем и s и ss имеют одинаковый тип.

Статически объявленные массивы всегда вносят некоторую долю путаницы, но когда полностью представляешь механизм трансляции компилятором этих типов, особенно в части вычисления адреса конкретного элемента, то в общем то всегда все понятно.

Лично для меня массивом является то, с чем можно работать стандартными операторами массивов - это квадратные скобки. Поэтому варианты вычисления адреса в большом блоке я и не приемлю.

Добавлено @ 18:32
Цитата(Fazil6 @  27.4.2007,  18:08 Найти цитируемый пост)
допустимо только для совместимости со старым кодом.
так ведь недопустимо

код C++
1:

int *pi = {1,2,3};


Естественно недопустимо. В языке не определены константы адреса. И только из-за этого. Инициализировать можно только вещами, которые понимает компилятор. А в языке определены числовые константы, логические, а адресные - нет. Он их просто не знает.




Это сообщение отредактировал(а) Anikmar - 27.4.2007, 18:34
PM MAIL ICQ   Вверх
Daevaorn
Дата 27.4.2007, 19:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2155
Регистрация: 29.11.2004
Где: Москва

Репутация: 51
Всего: 70



Цитата(Anikmar @  27.4.2007,  19:29 Найти цитируемый пост)
В языке не определены константы адреса

Цитата(Anikmar @  27.4.2007,  19:29 Найти цитируемый пост)
А в языке определены числовые константы

а вчем различие?
Код

int* p = 234;

Это я не задал адрес?
PM MAIL WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0941 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.