|
Определение размера массива на этапе компиляции, Задачка |
|
|
|
Sep 10 2012, 07:02
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Не уверен, в тот ли форум написал - не нашёл специализированного подфорума по программированию, есть этот и ещё в разделе по DSP. Вопрос чисто по программированию на С/С++. Задача. Есть энное количество массивов. Тип элемента массива в данном случае не важен (на практике он может быть любым - в частности, у меня это небольшая структура), поэтому в примере будет просто int. Длины массивов произвольные. Обработка данных из массивов возложена на функцию. Вариант на голом С. CODE int A[] = { 1, 2, 3 }; int B[] = { 4, 5 }; int C[] = { 6 }; int D[] = { 7, 8, 9, 0 }; ... // и т.д.
void f(int *p, size_t size) { ... } вызов (пример): CODE f(A, sizeof(A)); Очевидный недостаток - нужно руками задавать правильный размер массива. При достаточно большом количестве массивов и вызовов, а также при интенсивном редактировании этого кода (особенно, когда обычным спутником такого редактирования является метод copy'n'paste), можно легко передать адрес одного массива, а размер другого, просто не уследив глазами за именами в аргументах. Хочется избавиться от этого недостатка. Очевидное решение в стиле С - использовать макроподстановку препроцессора: CODE #define PASS_ARRAY(a) a, sizeof(a)
f(PASS_ARRAY(A)); Это в некотором смысле получше, но есть свои минусы: препроцессор игнорирует пространства имён - возможен конфликт имён, макросу лучше задавать такое имя, чтобы оно было поуникальнее (а значит, подлиннее, поуродливее, сильнее загромождающее код), но самое главное то, что препроцессор не производит контроль типов и если на вход подать не массив, а указатель, то результат работы функции будет ошибочным при полном отсутствии ругани со стороны компилятора. В общем, основная проблема в низкоуровневом представлении массива на С, который при передаче в функцию даунгрейдится до указателя и внутри функции уже видится просто как адрес на объект своего типа. Хочется обойти этот недостаток - ведь в точке вызова-то у компилятора вся информация о массиве, переданном в качестве аргумента, есть. Вопрос. Имеется ли тут хорошее решение на С или С++. И если имеется, то какое? P.S. Забегая вперёд, скажу, от ответ знаю, но, во-первых, возможно есть и другие, получше, поинтереснее, во-вторых, сама по себе задачка, имхо, очень интересная и достойная того, чтобы поломать немножко над ней голову, тем более, что практическое решение явно полезно, а не просто академическая головоломка.  P.P.S Если кто-то знает решение с ходу, просьба не сообщать его сразу - дайте другим тоже потерзать задачку. Сообщите только, что знаете решение (это, возможно, лучше мотивирует других решателей).
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
 |
Ответов
(1 - 45)
|
Sep 10 2012, 09:05
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (gerber @ Sep 10 2012, 15:40)  Для работы с массивом на С совсем необязательно знать его размер ...  Вот как! А как, например, обработать все элементы массива? Скажем, подсчитать сумму элементов (если это инты)? QUOTE (Сергей Борщ @ Sep 10 2012, 15:13)  Вроде бы знаю, на плюсах... Гуд, у тебя в асе спрошу.  Добавлено: Сергей Борщ - зачёт!
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 10 2012, 12:46
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(dxp @ Sep 10 2012, 13:05)  Вот как! А как, например, обработать все элементы массива? Скажем, подсчитать сумму элементов (если это инты)? Элементарно. Должен быть маркер последнего элемента массива. Как у "нуль-терминированных" строк. Если это массив указателей - нулевой указатель. Тогда перебор всех элементов массива идёт не циклом for, а циклом while. С массивом произвольных интов сложнее, но на практике совсем произвольных данных не бывает  , поэтому всегда по смыслу можно подобрать значение, годящееся на роль терминатора массива.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Sep 10 2012, 13:14
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(dxp @ Sep 10 2012, 16:26)  А как в GCC? Там какой-то свой язык С/С++? B GCC4.7 заявлена поддержка С++0х, в котором заявлена реализация циклов foreach Но это по слухам. Что ещё приходит в голову... Завернуть массив в структуру, состоящую из собственно массива и константного поля длины. Надеяться, что оптимизатор выкинет второе поле, подставив длину по месту. Это если просто в си
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Sep 10 2012, 13:41
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(dxp @ Sep 10 2012, 15:26)  А как в GCC? Там какой-то свой язык С/С++? Ответ лежит в личке. Цитата(MrYuran @ Sep 10 2012, 16:14)  Завернуть массив в структуру, состоящую из собственно массива и константного поля длины. Надеяться, что оптимизатор выкинет второе поле, подставив длину по месту. Это если просто в си Если сравнить с дефайном, то это хуже.
|
|
|
|
|
Sep 10 2012, 15:13
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (gerber @ Sep 10 2012, 19:46)  Элементарно. Должен быть маркер последнего элемента массива. Как у "нуль-терминированных" строк. Если это массив указателей - нулевой указатель. Тогда перебор всех элементов массива идёт не циклом for, а циклом while. С массивом произвольных интов сложнее, но на практике совсем произвольных данных не бывает  , поэтому всегда по смыслу можно подобрать значение, годящееся на роль терминатора массива. Это вы придумываете условия задачи. Исходно массивы есть как есть, и тип элемента в них может быть произвольным. Никаких предположений о возможности изменения содержимого первого или терминальнго элементов делать нельзя. Да и не нужно тут это - применять рантаймные проверки там, где вся информация о размере есть у комилятора на этапе компиляции и доступна в точке вызова. Задача лишь в том, чтобы автоматом доставить эту информацию внутрь функции. QUOTE (ig_z @ Sep 10 2012, 19:59)  На с++ есть, и это решение давно присутствует в бусте ессно  Вам зачёт!  Вижу, что решение многим известно, но пока не будем открывать, пусть для желающих останется некий challenge. В принципе, уже ясно, что в сети решение найти не проблема, но у нас тут спорт и от всех ожидается спортивное поведение.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 10 2012, 15:29
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Всё это - палиативные решения. Окажется массив двухмерным - опять возникнут проблемы пуще прежних. Если решать проблему, то на корню. Создавать класс, в которую входит указатель на массив (который при необходимости аллокируется на нужную длину в конструкторе и деаллокируется в деструкторе), его габариты (их удобнее выразить в числе элементов, а не в общей байтовой длине). При желании можно указать еще тип элемента и т.п. Указатели или ссылки на такие классы компактно передаются в любые функции одним параметром, из которого функция сможет при необходимости узнать то, что ей нужно. А в пределе можно встроить в этот класс частно используемые фукции, типа обнуления, инициализации константой, умножения на число, нахождение суммы, минимального и максимального элемента и т.д. А при должном уровне упорства  определить операции над такими массивами на матричный манер - реализовать их умножение, разложение на множители, FFT и прочие преобразования, и очень много еще чего другого на собственный вкус. Если не лезть на рожон (не определять функции вируальными), то компилятор будет компилировать только те функции класса, которые находят спрос в конкретной программе. Поэтому "излишества" не помешают, зато создадут удобную среду для работы.
|
|
|
|
|
Sep 10 2012, 15:54
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (_Pasha @ Sep 10 2012, 20:41)  Ответ лежит в личке. Посмотрел, спасибо. Насколько понял, это несколько не то - эта штука чекает размер с целью контроля переполнения памяти. Но она никак не поможет узнать размер массива внутри функции, которая видит указатель. Ну, и весьма частное решение, даже если бы делало то, что надо. QUOTE (Xenia @ Sep 10 2012, 22:29)  Всё это - палиативные решения. Окажется массив двухмерным - опять возникнут проблемы пуще прежних. Двумерных массивов в С/С++ не бывает. QUOTE (Xenia @ Sep 10 2012, 22:29)  Если решать проблему, то на корню. Создавать класс, в которую входит указатель на массив Не нужно придумывать условия задачи. Они уже определены и вполне однозначны и просты. И решение есть, простое и эффективное. QUOTE (kurtis @ Sep 10 2012, 22:36)  Так какой правильный ответ? Давайте ещё немножко подождём, до завтра, до середины дня, пусть хотя бы сутки будут на решение.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 10 2012, 16:12
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
С макросами я бы делал как-то вот так. Код #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 - Sep 10 2012, 16:16
|
|
|
|
|
Sep 10 2012, 17:02
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
QUOTE Двумерных массивов в С/С++ не бывает. Шутить изволите? CODE int A[5][6];
int main() { A[2][3]=123; A[4][2]=456; A[2][4]=789; ... CODE 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
Хотя, я конечно понимаю, что суть двумерного массива всего лишь массив массивов
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 11 2012, 00:59
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (Rst7 @ Sep 11 2012, 00:02)  Шутить изволите? Ничуть. QUOTE (Rst7 @ Sep 11 2012, 00:02)  CODE 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++ вполне имеет право быть, только нужно всегда иметь в виду контекст применения этого термина. QUOTE (Xenia @ Sep 11 2012, 01:31)  Еще бы темплейты задействовать  Это мысль в правильном направлении. QUOTE (_Pasha @ Sep 11 2012, 01:35)  По идее, она возвращает размер, тип возврата - size_t. Вроде всё ОК. А дальше по Жванецкому: включаем - не работает.  Странно. Она-то возвращает, но ей надо видеть определение объекта в точке вызова. Если это сделать при вызове функции - при передаче аргументов, то оно сработает, но ровно так же работает и sizeof(), с этим нет проблем. А вот внутри функции аргумент вырождается в указатель - что оно тут сможет извлечь?
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 11 2012, 03:35
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
QUOTE Для настоящего (тру Ъ) двумерного массива нотация A[0] не имеет смысла, т.к. не индексирует элемент, а в С/С++ вполне имеет, но возвращает указатель на массив (точнее, указатель на указатель). Ну, во-первых, не указатель на указатель, а указатель на элемент. Непонятно только, чему это противоречит. Хотите получить sizeof элемента - так и берите элемент - a[i][j]. sizeof(а[0]) тоже имеет вполне логичное значение, равное размеру строки в массиве. Все четко и логично.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 11 2012, 04:37
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (Rst7 @ Sep 11 2012, 10:35)  Ну, во-первых, не указатель на указатель, а указатель на элемент. Непонятно только, чему это противоречит.
Хотите получить sizeof элемента - так и берите элемент - a[i][j].
sizeof(а[0]) тоже имеет вполне логичное значение, равное размеру строки в массиве.
Все четко и логично. Будь это настоящим двумерным массивом, операция a + 1 давала бы адрес следующего элемента, а в нашем случае это будет адрес следующего массива. Разница существенная. Повторять всё обилие различий не хочется, тут подробнее.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 11 2012, 05:04
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
QUOTE Повторять всё обилие различий не хочется, тут подробнее. Бред там написан. QUOTE Будь А полноценным двумерным массивом - т.е. законченным, целостным объектом, то выражение: А + 1 означало бы адрес следующего такого двумерного массива, Например, int A[10]; и последующее выражение A+1 указывает не на A[10], а на A[1], так почему там требуют другого поведения, соответствующему, кстати, указателю на двумерный массив?
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 11 2012, 05:31
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (Rst7 @ Sep 11 2012, 12:04)  Например, int A[10]; и последующее выражение A+1 указывает не на A[10], а на A[1], так почему там требуют другого поведения, соответствующему, кстати, указателю на двумерный массив? Допустим, у нас есть настоящий честный двумерный массив А[10][20]. Что возвращает A + 1? И что возвращает это выражение в случае сишного массива?
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 11 2012, 07:47
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
QUOTE Допустим, у нас есть настоящий честный двумерный массив А[10][20]. Что возвращает A + 1? Приведите пример языка с "настоящим честным двумерным массивом", в котором допустима операция A+1. Еще раз, Ваше утверждение (и утверждение по ссылке, которую Вы дали) требует поведения при операции A+1 такого, какое в Си записывается следующим образом: CODE int (*a)[5][6];
(*a)[1][2]=123; //Запись значения в первый двумерный массив (*(a+1))[1][2]=345; //Запись значения во второй массив Несмотря на то, что в 99% случаев утверждают, что в Си массивы и указатели есть одно и тоже, на самом деле между ними есть четкая разница. Непонимание порождает путаницу.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Sep 11 2012, 08:31
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (Rst7 @ Sep 11 2012, 14:47)  Приведите пример языка с "настоящим честным двумерным массивом", в котором допустима операция A+1. Например, язык пакета Matlab. Или какой-нибудь другой высокоуровневый язык (тот же питон с библиотекой numpy). В низкоуровенвых языках, как С/C++, паскаль и подобных, представление соответствует уровню, т.е. является тоже низкоуровневым - непрерывная область памяти с элементами одного и того же типа. Максимум что они позволяют - массивы массивов. Тем не менее концепция двумерного массива никуда не девается. В мире полно объектов, которые описываются именно двумерным массивом, а не массивом массивов. Попытка адресоваться по одной координате всегда приводит к декомпозиции исходного типа и он перестаёт существовать. Например, есть объект - точка на экране. Она характеризуется двумя координатами. И изображение на экране - это двумерный массив этих точек. Попытка обращаться только через одну координату автоматически переводит адресуемый элемент из точки в строку или столбец. Нормальное представление двумерного массива вполне допускает обращение к его фрагментам - строкам, столбцам и просто прямоугольным фрагментам, и предоставляет для этого соответствующие средства (slice). Вы показывали, как ловко на С получить строку из "двумерного" массива. Покажите, как получить столбец? Или прямоугольный фрагмент? QUOTE (Rst7 @ Sep 11 2012, 14:47)  Еще раз, Ваше утверждение (и утверждение по ссылке, которую Вы дали) требует поведения при операции A+1 такого, какое в Си записывается следующим образом: CODE int (*a)[5][6];
(*a)[1][2]=123; //Запись значения в первый двумерный массив (*(a+1))[1][2]=345; //Запись значения во второй массив Настоящий двумерный массив в принципе не поддерживает такую нотацию обращения - через разыменовывание. QUOTE (Rst7 @ Sep 11 2012, 14:47)  Несмотря на то, что в 99% случаев утверждают, что в Си массивы и указатели есть одно и тоже, на самом деле между ними есть четкая разница. Непонимание порождает путаницу. Самая эта тема сутью задачи подчёркивает эту разницу.  Собственно, возвращаемся к точке начала спора. Спор о терминах. Понимание сути у всех есть. Двумерных массивов в С/C++ нет. Есть массивы массивов. Разница между ними есть. В ряде случаев массив массивов может использоваться в качестве двумерного массива с некоторыми ограничениями. Учитывая этот контекст, вполне допустимо употреблять термин "двумерный массив" для краткости (и лучше оговаривать сразу, что имеется в виду), но всегда помнить, что за этим стоит. На этом предлагают дискуссию закончить, дабы не тратить время и ходить по кругу.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 11 2012, 10:09
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
Наверное, пришло время обнародовать решение, как вчера было обещано. Решение на самом деле очень простое и эффективное. Выглядит так: CODE template <typename T, size_t N> void f(T (&a)[N]) { ... // используем N - это длина массива } В общем случае это не тот размер, который возвращает sizeof(), а именно длина массива, но именно она-то и нужна. А размер получается тоже элементарно - N*sizeof(T). Всё на этапе компиляции. Возможно, не всем понятно, как это работает, поэтому краткое пояснение. В С++ инстанцирование функции по шаблону может быть явным, когда задаются параметры шаблона, и неявным, когда компилятор сам исходя из контекста использования, может сгенерировать реализацию. Именно вот такое неявное инстанцирование тут и используется. При этом вся информация у компилятора есть - есть объект-массив, про который всё известно - и тип элементов, и длина массива. Далее, ключевой момент: аргументом функции заявлена ссылка на массив, состоящий из элементов типа T и длиной N. Теперь в точке вызова: CODE int A[] = { 1, 2, 3, 4 }; ...
f(A); компилятор находит использование функции f(), ищет её реализацию, находит шаблон, анализирует контекст (а по контексту требуется именно массив - ссылка на него, а не указатель) и генерирует функцию f(int &а[4]). Всё, дело сделано. Поскольку параметры шаблона доступны внутри определения функции, то доступен и тип, и длина массива. Все действия производятся на этапе компиляции, т.е. получение длины массива достигается без единой инструкции, выполняемой на рантайме. Пример: CODE 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): CODE d = len(a); 000000 B2400900.... MOV.W #0x9, &d d = len(b); 000006 B2400500.... MOV.W #0x5, &d
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 11 2012, 11:57
|
Местный
  
Группа: Свой
Сообщений: 466
Регистрация: 21-06-05
Пользователь №: 6 205

|
Опять не понял Код #include <iostream>
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;
int foo(const int arr[]) { std::cout << "len:" << len(arr) << "\n"; }
int main() { foo(a); foo(b); } Цитата mike@mike-work:/tmp$ LC_ALL=C g++ test.cpp test.cpp: In function 'int foo(const int*)': test.cpp:15:33: error: no matching function for call to 'len(const int*&)' test.cpp:15:33: note: candidate is: test.cpp:4:5: note: template<class T, int N> int len(T (&)[N])
|
|
|
|
|
Sep 11 2012, 15:22
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (kurtis @ Sep 11 2012, 18:57)  Опять не понял Что тут не понятного? Вы слепили свою функцию, скормили ей массив, внутри функции массив уже не виден, а виден только указатель на него - и что, вы хотите, чтобы компилятор восстановил из указателя массив? Заведите массив внутри вашей функции и передавайте его функции len(). Массив, а не указатель. Именно тут и проявилась фича метода - невозможно по ошибке передать в функцию указатель - только массив. Такая ошибка гарантировано отлавливается на этапе компиляции.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 11 2012, 17:20
|
Местный
  
Группа: Свой
Сообщений: 466
Регистрация: 21-06-05
Пользователь №: 6 205

|
Цитата Заведите массив внутри вашей функции и передавайте его функции len(). Массив, а не указатель. Можете, пожалуйста, привести код, который решает проблему из первого сообщения?
|
|
|
|
|
Sep 11 2012, 18:30
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(kurtis @ Sep 11 2012, 23:20)  Можете, пожалуйста, привести код, который решает проблему из первого сообщения? Код #include <iostream>
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 элементов
int main() { std::cout << "len a:" << len(a) << "\n"; std::cout << "len b:" << len(b) << "\n"; }
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Sep 11 2012, 19:17
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(ViKo @ Sep 11 2012, 20:03)  Такая же особенность будет в простеньком #define a_s(array) arr_sum(array, sizeof(array)/sizeof(array[0])) Это для числа элементов. А для просто размера - пойдем в обход, чтобы выловить передачу не-массива Код #define a_s(array) arr_sum(array, (sizeof(array)/sizeof(array[0])*sizeof(array[0]))  PS Задумался... в "чиста Си" от GCC уже столько костылей плюсовых... ужас - бери и делай закат Солнца.
Сообщение отредактировал _Pasha - Sep 11 2012, 19:20
|
|
|
|
|
Sep 12 2012, 01:36
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (ViKo @ Sep 12 2012, 00:03)  Такая же особенность будет в простеньком #define a_s(array) arr_sum(array, sizeof(array)/sizeof(array[0])) В том-то и дело, что нет.  Когда вы скормите указатель в тут шаблонную функцию, вы получите ругань компилятора. А в случае макроса код будет молча скомпилирован, но размер будет получен неверный - не длина массива, а sizeof() указателя. В этом и состоит подлость, что закрадывается ошибка, которую компилятор отловить не в силах. Шаблонная функция лишена этого недостатка. QUOTE (kurtis @ Sep 12 2012, 02:04)  Чем это принципиально отличается от sizeof и как мне внутри функции узнать размер переданого массива? (ведь в этом смысл вопроса был?) Вы разницу между f(A) и f(A, sizeof(A)/sizeof(A[0]) видите? Перечитайте, пожалуйста, самый первый пост внимательно, там подробно описано, чем не устраивает второй вариант и чего хочется добиться. Для конкретности, представьте, что задача состоит не в определении длины массива, а в подсчёте суммы его элементов, функция должна вернуть эту сумму.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
Sep 12 2012, 03:41
|
Местный
  
Группа: Свой
Сообщений: 466
Регистрация: 21-06-05
Пользователь №: 6 205

|
Медлено начинаю понимать... Код #include <iostream>
template <typename T, int N> int sum(T (& a)[N]) { int sum, i; for (i = 0, sum = 0; i < N; i++) sum += a[i];
return sum; }
const int a[] = {1,1,1,1,2,3,4,5,6}; // 9 элементов const int b[] = {1,1,1,5,6}; // 5 элементов
int main() { std::cout << sum(a) << "\n"; std::cout << sum(b) << "\n"; } Цитата mike@mike-P55A-UD4:/tmp$ for i in $(nm a.out | grep sum | cut -d" " -f 3) ; do echo $i ; c++filt $i ; done _Z3sumIKiLi5EEiRAT0__T_ int sum<int const, 5>(int const (&) [5]) _Z3sumIKiLi9EEiRAT0__T_ int sum<int const, 9>(int const (&) [9]) При выключенной оптимизации, получается 2 экземпляра функции sum(), для каждого из параметров. Конечно, это все хорошо если функции простые, и компилятор может их оптимизировать, а если нет?
|
|
|
|
|
Sep 12 2012, 03:51
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(kurtis @ Sep 12 2012, 01:04)  Чем это принципиально отличается от sizeof и как мне внутри функции узнать размер переданого массива? (ведь в этом смысл вопроса был?) Вы не совсем поняли идею. Идея именно в том, что это аналог sizeof(), но за программиста этот sizeof() автоматически подставляет компилятор. То есть, вы вместо функции Код void fome_func(int arr[], size_t arr_len) { for (int i = 0; i < arr_len; i++) foo(arr[i]); } пишете шаблон: Код template <typename T, int arr_len> void fome_func(T (& arr)[arr_len]) { for (int i = 0; i < arr_len; i++) foo(arr[i]); } Здесь исключена возможность передать размер, не соответствующий массиву - это делает компилятор. Однако при таком подходе возможно, что у вас для каждого размера массива будет сгенерирована своя функция. Если вы этого не желаете, то можно совместить подходы: Код template <typename T, int arr_len> inline void call_fome_func(T (& arr)[arr_len]) { fome_func(arr, arr_len); } Это практически полный аналог варианта с #define, но более безопасный с точки зрения контроля параметров.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Sep 12 2012, 04:38
|

Универсальный солдатик
     
Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362

|
Цитата(dxp @ Sep 12 2012, 04:36)  В том-то и дело, что нет.  Когда вы скормите указатель в ту шаблонную функцию, вы получите ругань компилятора. А в случае макроса код будет молча скомпилирован, но размер будет получен неверный - не длина массива, а sizeof() указателя. В этом и состоит подлость, что закрадывается ошибка, которую компилятор отловить не в силах. Шаблонная функция лишена этого недостатка. Вы правы. Передал в функцию a_s() указатель, получилась длина 1. И отсутствие элемента array[0] компилятор не беспокоит. Keil не выдает даже предупреждения. Видимо, это должно подтверждать, что разница между массивами и указателями меньше, чем хотелось бы.  P.S. можно даже sizeof(array[1]) использовать, и все равно компилятор не ругается. Можно даже sizeof(array[1000000]).
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|