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

Определение размера массива на этапе компиляции

Ответ лежит в личке.

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

 

Всё это - палиативные решения. Окажется массив двухмерным - опять возникнут проблемы пуще прежних.

Двумерных массивов в С/С++ не бывает.

 

Если решать проблему, то на корню. Создавать класс, в которую входит указатель на массив

Не нужно придумывать условия задачи. Они уже определены и вполне однозначны и просты. И решение есть, простое и эффективное.

 

 

Так какой правильный ответ?

Давайте ещё немножко подождём, до завтра, до середины дня, пусть хотя бы сутки будут на решение.

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


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

С макросами я бы делал как-то вот так.

 

#define AR(a)    (struct ar __a = {(void *) (a), sizeof(a)}, __a)

struct ar {
void *p;
int sz;
};

#define FOO(a)   foo(AR(a))

void foo(struct ar a);

...

int A[] = {1, 2, 3};

FOO(A);

 

Точнее я бы так не делал :) Обычно можно проще, но для этого надо видеть всю задачу, в такой постановке ничего не поделать.

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

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


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

Двумерных массивов в С/С++ не бывает.

 

Шутить изволите?

 

int A[5][6];

int main()
{
  A[2][3]=123;
  A[4][2]=456;
  A[2][4]=789;
...

 

    24          int A[5][6];
  \                     A:
  \   00000000                      DS8 120
...
  \   00000004   ....               LDR.N    R0,??DataTable0
  \   00000006   7B21               MOVS     R1,#+123
  \   00000008   C163               STR      R1,[R0, #+60]
    29            A[4][2]=456;
  \   0000000A   4FF4E471           MOV      R1,#+456
  \   0000000E   8166               STR      R1,[R0, #+104]
    30            A[2][4]=789;
  \   00000010   40F21531           MOVW     R1,#+789
  \   00000014   0164               STR      R1,[R0, #+64]
...
  \                     ??DataTable0:
  \   00000000   ........           DC32     A

 

Хотя, я конечно понимаю, что суть двумерного массива всего лишь массив массивов ;)

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


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

Я бы пользовался макроподстановкой, описанной в первом сообщении

Но, если не нравится так, можно заменить саму функцию

#define a_s(array) arr_sum(array, sizeof(array)/sizeof(array[0]))

uint32_t arr_sum(uint32_t *p, uint32_t s);
...

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


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

Насколько понял, это несколько не то - эта штука чекает размер с целью контроля переполнения памяти.

По идее, она возвращает размер, тип возврата - size_t. Вроде всё ОК. А дальше по Жванецкому: включаем - не работает. :( Странно.

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


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

Шутить изволите?

Ничуть.

 

int A[5][6];

int main()
{
  A[2][3]=123;
  A[4][2]=456;
  A[2][4]=789;
...

 

[...]

Хотя, я конечно понимаю, что суть двумерного массива всего лишь массив массивов ;)

Именно. А массив массивов - это ни разу не двумерный массив. sizeof элемента массива всегда один и тот же по определению (т.к. массив - агрегатный объект, состоящий из элементов одного типа). Для настоящего (тру Ъ) двумерного массива нотация A[0] не имеет смысла, т.к. не индексирует элемент, а в С/С++ вполне имеет, но возвращает указатель на массив (точнее, указатель на указатель). Помнится, на фидоэхе su.с-сpp один очень квалифицированный человек весьма наглядно и на примерах показал ключевую разницу между этими вещами и к чему приводит неверная трактовка. Впрочем, соглашусь с тем, что сам по себе термин "двумерный массив" в C/C++ вполне имеет право быть, только нужно всегда иметь в виду контекст применения этого термина.

 

Еще бы темплейты задействовать :)

Это мысль в правильном направлении.

 

По идее, она возвращает размер, тип возврата - size_t. Вроде всё ОК. А дальше по Жванецкому: включаем - не работает. :( Странно.

Она-то возвращает, но ей надо видеть определение объекта в точке вызова. Если это сделать при вызове функции - при передаче аргументов, то оно сработает, но ровно так же работает и sizeof(), с этим нет проблем. А вот внутри функции аргумент вырождается в указатель - что оно тут сможет извлечь?

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


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

Для настоящего (тру Ъ) двумерного массива нотация A[0] не имеет смысла, т.к. не индексирует элемент, а в С/С++ вполне имеет, но возвращает указатель на массив (точнее, указатель на указатель).

 

Ну, во-первых, не указатель на указатель, а указатель на элемент. Непонятно только, чему это противоречит.

 

Хотите получить sizeof элемента - так и берите элемент - a[j].

 

sizeof(а[0]) тоже имеет вполне логичное значение, равное размеру строки в массиве.

 

Все четко и логично.

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


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

Ну, во-первых, не указатель на указатель, а указатель на элемент. Непонятно только, чему это противоречит.

 

Хотите получить sizeof элемента - так и берите элемент - a[j].

 

sizeof(а[0]) тоже имеет вполне логичное значение, равное размеру строки в массиве.

 

Все четко и логично.

Будь это настоящим двумерным массивом, операция a + 1 давала бы адрес следующего элемента, а в нашем случае это будет адрес следующего массива. Разница существенная. Повторять всё обилие различий не хочется, тут подробнее.

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


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

Повторять всё обилие различий не хочется, тут подробнее.

 

Бред там написан.

Будь А полноценным двумерным массивом - т.е. законченным, целостным

объектом, то выражение:

А + 1

означало бы адрес следующего такого двумерного массива,

 

Например, int A[10]; и последующее выражение A+1 указывает не на A[10], а на A[1], так почему там требуют другого поведения, соответствующему, кстати, указателю на двумерный массив?

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


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

Например, int A[10]; и последующее выражение A+1 указывает не на A[10], а на A[1], так почему там требуют другого поведения, соответствующему, кстати, указателю на двумерный массив?

Допустим, у нас есть настоящий честный двумерный массив А[10][20]. Что возвращает A + 1? И что возвращает это выражение в случае сишного массива?

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


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

Допустим, у нас есть настоящий честный двумерный массив А[10][20]. Что возвращает A + 1?

 

Приведите пример языка с "настоящим честным двумерным массивом", в котором допустима операция A+1.

 

Еще раз, Ваше утверждение (и утверждение по ссылке, которую Вы дали) требует поведения при операции A+1 такого, какое в Си записывается следующим образом:

 

int (*a)[5][6];

(*a)[1][2]=123; //Запись значения в первый двумерный массив
(*(a+1))[1][2]=345; //Запись значения во второй массив

 

Несмотря на то, что в 99% случаев утверждают, что в Си массивы и указатели есть одно и тоже, на самом деле между ними есть четкая разница. Непонимание порождает путаницу.

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


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

Приведите пример языка с "настоящим честным двумерным массивом", в котором допустима операция A+1.

Например, язык пакета Matlab. Или какой-нибудь другой высокоуровневый язык (тот же питон с библиотекой numpy). В низкоуровенвых языках, как С/C++, паскаль и подобных, представление соответствует уровню, т.е. является тоже низкоуровневым - непрерывная область памяти с элементами одного и того же типа. Максимум что они позволяют - массивы массивов.

 

Тем не менее концепция двумерного массива никуда не девается. В мире полно объектов, которые описываются именно двумерным массивом, а не массивом массивов. Попытка адресоваться по одной координате всегда приводит к декомпозиции исходного типа и он перестаёт существовать. Например, есть объект - точка на экране. Она характеризуется двумя координатами. И изображение на экране - это двумерный массив этих точек. Попытка обращаться только через одну координату автоматически переводит адресуемый элемент из точки в строку или столбец.

 

Нормальное представление двумерного массива вполне допускает обращение к его фрагментам - строкам, столбцам и просто прямоугольным фрагментам, и предоставляет для этого соответствующие средства (slice). Вы показывали, как ловко на С получить строку из "двумерного" массива. Покажите, как получить столбец? Или прямоугольный фрагмент?

 

 

Еще раз, Ваше утверждение (и утверждение по ссылке, которую Вы дали) требует поведения при операции A+1 такого, какое в Си записывается следующим образом:

 

int (*a)[5][6];

(*a)[1][2]=123; //Запись значения в первый двумерный массив
(*(a+1))[1][2]=345; //Запись значения во второй массив

Настоящий двумерный массив в принципе не поддерживает такую нотацию обращения - через разыменовывание.

 

Несмотря на то, что в 99% случаев утверждают, что в Си массивы и указатели есть одно и тоже, на самом деле между ними есть четкая разница. Непонимание порождает путаницу.

Самая эта тема сутью задачи подчёркивает эту разницу. :)

 

Собственно, возвращаемся к точке начала спора. Спор о терминах. Понимание сути у всех есть. Двумерных массивов в С/C++ нет. Есть массивы массивов. Разница между ними есть. В ряде случаев массив массивов может использоваться в качестве двумерного массива с некоторыми ограничениями. Учитывая этот контекст, вполне допустимо употреблять термин "двумерный массив" для краткости (и лучше оговаривать сразу, что имеется в виду), но всегда помнить, что за этим стоит. На этом предлагают дискуссию закончить, дабы не тратить время и ходить по кругу.

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


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

Наверное, пришло время обнародовать решение, как вчера было обещано.

 

Решение на самом деле очень простое и эффективное. Выглядит так:

template <typename T, size_t N>
void f(T (&a)[N])
{
    ... // используем N - это длина массива
}

 

В общем случае это не тот размер, который возвращает sizeof(), а именно длина массива, но именно она-то и нужна. А размер получается тоже элементарно - N*sizeof(T). Всё на этапе компиляции.

 

Возможно, не всем понятно, как это работает, поэтому краткое пояснение. В С++ инстанцирование функции по шаблону может быть явным, когда задаются параметры шаблона, и неявным, когда компилятор сам исходя из контекста использования, может сгенерировать реализацию. Именно вот такое неявное инстанцирование тут и используется. При этом вся информация у компилятора есть - есть объект-массив, про который всё известно - и тип элементов, и длина массива. Далее, ключевой момент: аргументом функции заявлена ссылка на массив, состоящий из элементов типа T и длиной N. Теперь в точке вызова:

 

int A[] = { 1, 2, 3, 4 };
... 

f(A);

компилятор находит использование функции f(), ищет её реализацию, находит шаблон, анализирует контекст (а по контексту требуется именно массив - ссылка на него, а не указатель) и генерирует функцию f(int &а[4]). Всё, дело сделано. Поскольку параметры шаблона доступны внутри определения функции, то доступен и тип, и длина массива. Все действия производятся на этапе компиляции, т.е. получение длины массива достигается без единой инструкции, выполняемой на рантайме. Пример:

 

template <typename T, int N>
int len(T (& a)[N])
{
    return N;
}

const int a[] = {1,1,1,1,2,3,4,5,6}; // 9 элементов
const int b[] = {1,1,1,5,6};  // 5 элементов

volatile int d;

void main()
{
    d = len(a);
    d = len(b);

}

 

Результат (MSP430/IAR):

 

              d = len(a);               
000000   B2400900.... MOV.W   #0x9, &d  
              d = len(b);               
000006   B2400500.... MOV.W   #0x5, &d

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


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

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

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

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

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

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

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

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

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

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