Перейти к содержанию
    

    int dim1=5;
    int dim2=10;

    int** my_array=GenerateArray(dim1,dim2);

    my_array[i][j] = ...

 

Забавно. Никогда так не делал. Первая реакция была - "ерунда". Потом подумал - а ведь должно работать....

Изменено пользователем igorle

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Забавно. Никогда так не делал. Первая реакция была - "ерунда".
Это один из классических способов реализаций многомерных динамических массивов (я бы даже сказал, что самый первый из классических)

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, оч. интересно! Правда не совсем ппонимаю, как освобождать память из под такого массивчика? :)

Если указатель связанный с данными через "new" погибнет, потеряется - память "потечет"!?

 

Если я использую библиотечные функции, ну например "strcat" библиотеки cLib, прототип описан в string.h, выделить память для результирующей строки должен программист? о_О

 

/*
* Copyright (C) 2002     Manuel Novoa III
* Copyright (C) 2000-2005 Erik Andersen <[email protected]>
*
* Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
*/

#include "_string.h"

#ifdef WANT_WIDE
# define Wstrcat wcscat
#else
# define Wstrcat strcat
#endif

Wchar *Wstrcat(Wchar * __restrict s1, register const Wchar * __restrict s2)
{
    register Wchar *s = s1;

    while (*s++);
    --s;
    while ((*s++ = *s2++) != 0);

    return s1;
}
libc_hidden_def(Wstrcat)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дьявол в мелочах (с).
Вот именно™

Тот случай, когда пренебежение терминологией, привело к не верному выводу. А именно, "указатель" != "адрес". Имя массива - не "указатель" на первый элемент, а "адрес" первого элемента.
Имя массива — это имя массива. Оно имеет тип «массив вон-того» (в зависимости от объявления).

Почти во всех выражениях оно автоматически приводится к указателю на первый элемент.

6.3 Conversions

...

6.3.2 Other operands

6.3.2.1 Lvalues, arrays, and function designators

...

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Это один из классических способов реализаций многомерных динамических массивов (я бы даже сказал, что самый первый из классических)

Я считаю что хороший способ запутать (и запугать) новичка. Вы должны теперь объяснить, что my_array и your_array следующем примере:

int** my_array=GenerateArray(6, 9);
int  your_array[6][9];

это две большие разницы. И вообще эти переменные - разного типа. Это неочевидно ученику.

Давайте спросил Буратино (задачу решить на листочке, не используя компилятор):

- какого типа your_array?

- что вернет sizeof(my_array) и sizeof(your_array)?

- можно ли сделать такое присвоение int **his_array = your_array; ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, оч. интересно! Правда не совсем ппонимаю, как освобождать память из под такого массивчика? :)
«наоборотом». Сначала удалить по каждому из указателей из массива указателей, потом удалить по указателю на массив.

 

Такие массивы удобны тем, что каждая «строка» выделена отдельно и хранится отдельно. Можно менять месстами строки, перемещая только значения указателей, не трогая данные.

 

Например, было актуально во времена «640 килобайт»

При фильтрации изображений, скажем, окном 3х3, можно выделить место на две дополнительных строки, начать фильтрацию в них. При получении третьей (номер 2) строки выходного изображения первая строка (номер 0) входного изображения уже не нужна, результат можно помещать в неё, потом в нужное место переписать указатель. В итоге через какое-то время строки изображений в памяти разбросаны как попало, но все работает :-)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если я использую библиотечные функции, ну например "strcat" библиотеки cLib, прототип описан в string.h, выделить память для результирующей строки должен программист? о_О

Да, и узнать это из исходников - похвальный, но не самый простой путь. Лучше читать man pages

Кстати - многие функции еще и не проверяют корректность параметров. Например strcat(NULL, "basa"); просто покрошится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

это две большие разницы. И вообще эти переменные - разного типа. Это неочевидно ученику.

Давайте спросил Буратино

Давайте сначала Вы объясните ученику, как при помощи записи вида int your_array[что-то-одно][что-то-другое]; создать массив неизвестного заранее размера с времением жизни, выходящим за рамки функции, в которой он создан.

 

Был вопрос «зачем такое может быть нужно». Дан ответ. Дальше ученик должен думать. Различия можно объяснять.

 

- что вернет sizeof(my_array) и sizeof(your_array)?

sizeof(my_array) вернёт в точности то же самое, что тут вернёт sizeof(your_array)

int foo(int your_array[6][9])
{
    return sizeof(your_array);
}

и всё равно придётся объяснять, почему sizeof(your_array) в двух разных местах возвращает разные значения.

 

 

Кстати, я считаю, что запись foo(char *arr[]) гораздо хуже, чем foo(char **arr).

В итоге-то одно и то же, у функций одинаковый прототип, принимают pointer to pointer to char.

В режиме компиляции С++ должно быть одинаковое mangled-имя.

В первой записи arr по сути тот же char **, только этого не видно (пресловутому ученику) и этот arr невозможно сделать константным.

foo(char const * const arr[])

Вот тут аж дальние char — константные. Указатели на них, хранящиеся в массиве, на который указывает аргумент arr (в С ведь массивы не передаются как параметры, не правда ли?) — тоже константные. А сам аргумент - не константный, хоть плачь.

 

int foo(char const * const arr[])
{
        int i = 0;
        while( *arr++) ++i; // Тут всё отлчино!
        return i;
}

int moo(char const * const * const arr)
{
        int i = 0;
        while(*arr++) ++i; // А вот тут компилятор говорит об ошибке
        return i;
}

всё, что имеем:

arrn.c: In function ‘moo’:

arrn.c:11: error: increment of read-only location ‘arr’

А с первой функцией все нормально, компилятор молчит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Имя массива — это имя массива. Оно имеет тип «массив вон-того» (в зависимости от объявления).Почти во всех выражениях оно автоматически приводится к указателю на первый элемент.

Ещё раз. Указатель, это переменная, занимающая место в памяти. Под адрес же, память не выделяется. Что там происходит дальше, на что похоже, как оптимизируется и т.п. - дело десятое.

 

Не знаю как сейчас, а еще 3-4 года назад тот же IAR/ARM, на максимальной оптимизации, генерил совершенно разный код в одном и том же цикле, для Array и *(pArray+i). Т.е. работал совершенно по-разному ( через указатель гораздо быстрее ). "Хотя казалось бы..." (с). Пришлось ручками ковырять libavcodec, на предмет переделки обращения a[] в *pa, потому что иначе SAM7S64 не успевал размотать два канала честного ADPCM.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ещё раз. Указатель, это переменная, занимающая место в памяти.
Ну хорошо.

«is converted to an expression with type ‘‘pointer to type’’»

приводится к выражению типа «указатель на»

Не к «переменной типа „указатель на”». А к выражению. Приводится, а не есть им.

Результат этого выражения может не сохраняться в памяти. Может сохраняться во временной переменной в регистре. Может в результате оптимизации сразу компилироваться в тело команды.

Но сначала имя массива приводится к указателю на. Везде, кроме & и sizeof.

Только в результате этого приведения выражения

array[i]

и

i[array]

эквивалентны.

 

Да, кстати. Возьмём char a, b;. Теперь возьмём выражение a+b.

Приянто говорить «перед сложением a и b приводятся к целому».

Вы и здесь будете говорить «Ещё раз. Целое это переменная, занимающая место в памяти.» ?

«Целое», «указатель на» — это типы. А не переменные этих типов.

 

Кстати, и «переменная типа „указатель на”» может не занимать место в памяти. И даже в регистре. После оптимизации, конечно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Друзья. Эта тема создана Буратиной для Буратино. Он изучает С. Но в результате ветка превратилась в спор о дистнинкциях между несколькими опытными профессионалами. Это то, чего я опасался - вместо обучения получается запугивание.

 

Давайте вернемся а TC. Если человек пришел с вопросом, то надо отвечать на его языке. Любое знание должно прорасти. И его надо оберегать на начальных периодах. А если начинать тригонометрию не с прямоугольных прямоугольников, а с тригонометрических функций комплексных переменных, то толку не будет.

 

То что очевидно опытному - не сразу понятно начинающему. Время нужно.

 

Кстати, никто не ответил ясно на мой вопрос. Повторю. Что будет напечатано на 32 битном процессоре (решить на листочке, без компилятора):

int** my_array=GenerateArray(6, 9);
int  your_array[6][9];

printf("%z %z\n", sizeof(my_array), sizeof(your_array));

 

К вопросу о споре. Я понимаю обе стороны. Позиция ReAl мне ближе. Просто Буратино жалко. Он уже сбежал из своей ветки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Забавно. Никогда так не делал. Первая реакция была - "ерунда".

 

Причем первая реакция была правильной :) это не массив а мутант

 

int** GenerateArray(int dim1, int dim2)
{
int** rv=new int*[dim1];
for(int i=0;i<dim1;++i)
  rv[i]=new int[dim2];
return rv;
}

 

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type.

 

и даже динамическим мутантом его назвать сложно - нужно еще специальную ф-цию написать чтобы изменить его размерность в динамике.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну хорошо.

«is converted to an expression with type ‘‘pointer to type’’»

приводится к выражению типа «указатель на»

Не к «переменной типа „указатель на”». А к выражению. Приводится, а не есть им.

Ну хорошо. (с) Потому что:

Что там происходит дальше, на что похоже, как оптимизируется и т.п. - дело десятое.

 

И что бы стало совсем понятно, о чем речь, наглядная демонстрация, что "адрес" != "указатель":

uint8_t    Array[8];
Array++;

на выходе получим:

Error[Pe137]: expression must be a modifiable lvalue

 

 

Друзья. Эта тема создана Буратиной для Буратино. Он изучает С. Но в результате ветка превратилась в спор о дистнинкциях между несколькими опытными профессионалами. Это то, чего я опасался - вместо обучения получается запугивание.

Вот я как раз именно об этом. Да не обидится ReAl, но объяснять, это отдельное искусство. Знаю много грамотных спецов, однако, дай Б-г, если четверть из них в состоянии внятно объяснить то, чем сами отлично владеют.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И что бы стало совсем понятно, о чем речь, наглядная демонстрация, что "адрес" != "указатель":

 

это не совсем наглядно - с константным указателем тоже ничего хорошего не получим.

A pointer type describes an object whose value provides a reference to an entity of the referenced type.

 

имя массива объектом не является, его можно использовать только в выражениях, в этом его отличие от указателя.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

это не совсем наглядно - с константным указателем тоже ничего хорошего не получим.

"И о чем нам это должно говорить?" (с) Константный указатель, это по-прежнему указатель, даже если мы сознательно запретили его изменять.

Собсно, зачем я буду пересказывать классиков своими словами... K&R - Язык C - 5.3. Указатели и массивы.

 

 

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

 

 

UPD

На данном этапе, новичку надо не про оптимизации рассказывать, а вбить в голову простую мысль, что адрес и указатель, вещи тесно связанные, но это не одно и то же!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...