|
|
  |
Определение размера массива на этапе компиляции, Задачка |
|
|
|
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 Если кто-то знает решение с ходу, просьба не сообщать его сразу - дайте другим тоже потерзать задачку. Сообщите только, что знаете решение (это, возможно, лучше мотивирует других решателей).
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
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 и прочие преобразования, и очень много еще чего другого на собственный вкус. Если не лезть на рожон (не определять функции вируальными), то компилятор будет компилировать только те функции класса, которые находят спрос в конкретной программе. Поэтому "излишества" не помешают, зато создадут удобную среду для работы.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|