Передача массива в функцию без указания размерности
- Войдите на сайт для отправки комментариев
Пт, 02/10/2015 - 14:18
Задача состоит в следующем: Нужно передать одномерный массив в функцию таким образом, чтобы функция сама определяла размерность массива. Предполагается, что размерность передаваемых массивов может быть разной и на момент написания программы не будет известна, или известна, но чтобы не париться указывая второй аргумент функции.
Уважаемые господа, помогите решить этот вопрос. Заранее знаю что это возможно.
В языке С нельзя передать весь массив как аргумент функции. Однако можно передать указатель на массив, т.е. имя массива без индекса. Например, в представленной программе в func1() передается указатель на массив i:
int main(void)
{
int i[10];
func1(i);
/* ... */
}
Если в функцию передается указатель на одномерный массив, то в самой функции его можно объявить одним из трех вариантов: как указатель, как массив определенного размера и как массив без определенного размера. Например, чтобы функция func1() получила доступ к значениям, хранящимся в массиве i, она может быть объявлена как
void func1(int *x) /* указатель */
{
/* ... */
}
или как
void func1(int x[10]) /* массив определенного размера */
{
/* ... */
}
и наконец как
void func1(int x[]) /* массив без определенного размера */
{
/* ... */
}
Эти три объявления тождественны, потому что каждое из них сообщает компилятору одно и то же: в функцию будет передан указатель на переменную целого типа. В первом объявлении используется указатель, во втором — стандартное объявление массива. В последнем примере измененная форма объявления массива сообщает компилятору, что в функцию будет передан массив неопределенной длины. Как видно, длина массива не имеет для функции никакого значения, потому что в С проверка границ массива не выполняется. Эту функцию можно объявить даже так:
void func1(int x[32])
{
/* ... */
}
И при этом программа будет выполнена правильно, потому что компилятор не создает массив из 32 элементов, а только подготавливает функцию к приему указателя.
Один из простых вариантов:
и вторым параметром передавать в функцию array_count. Если длина массива изменится в процессе разработки, то всё равно программа останется рабочей. Возможно есть и другие варианты, только у меня за много лет не было нужды делать такое. Да и не вижу проблем передавать длину массива.
PS array_count можно объявить и как константу. const int array_count = sizeof...
UPD void foo(int *pp_array, int p_array_cnt) {}
Передать указатель на начало массива и топать до элемента-терминатора как в случае со строками.
Начнём с того, что массивов в полном понимании в этом языке нет вовсе - это синтаксическая упрощалка. То, что здесь называется массивом - ничем не отличается от указателя и никакой информации о собственой длине не содержит в принципе. Поэтому, либо он содержит информацию о длине в своих данных (терминальный символ или там длина сидит в первом элементе), то пожалуйста, если же нет - то шишеньки. Не определите.
Другое дело, что можно определить класс для массива (например Array), который будет вести себя как указатель (т.е. как СИшный массив), но на самом деле будет содержать в себе всю необходимую информацию. Тогда - пожалуйста, передавайте, определяйте на здоровье. Но класс нужно определить. Вы об этом спрашивали?
> содержит информацию о длине в своих данных (терминальный символ или там длина сидит в первом элементе
Про делфи/паскальные строки совсем забыл. Тоже вариант сообщения длины.
> содержит информацию о длине в своих данных (терминальный символ или там длина сидит в первом элементе
Про делфи/паскальные строки совсем забыл. Тоже вариант сообщения длины.
Все же решение есть, и оно работает без терминаторов и т.д.
Все началось с библиотеки EEPROM2,
https://github.com/ssvs111/ARDUINO_EEPROM2
которая по команде EEPROM_write(32, my_array); определяла длину my_array и записывала его в EEPROM-е начиная с ячейки #32.
Также переменная my_array могла иметь абсолютно любой тип – byte, int, long, array и т.д.
Попытки изучить .cpp и .h файлы библиотеки EEPRM2 мало к чему привели, т.к. использовалось неизвестное мне выражение template< typename T > которое во всем этом играет немалую роль.
Попытки вытащить нужный код из .cpp и .h также не чему не привели так как функция взятия размера sizeof() входной переменной почему-то не работает в скетче , а работает в библиотеке.
Поэтому я недолго думая скопировал нужный код из .cpp и .h библиотеки EEPROM2 в созданную библиотеку CCNET
https://github.com/ssvs111/CCNET
в библиотеке только одна функция, которая отправляет в SERIAL входной массив данных,
а вот пример, в котором показано как отправляется массив в функцию без указания размера
#include <CCNET.h> //библиотека с функцией, самостоятельно определяющей // размерность входного массива byte RESET[] = {0x02, 0x03, 0x09, 0x37, 0x00, 0x00, 0x0, 0x81, 0x1F};//9 байт byte POLL[] = {0x02, 0x03, 0x06, 0x30, 0x47, 0xC2}; //6 байт void setup () { Serial.begin(9600); //связь с компом (мониторим программу) Serial.begin(19200); //связь с внешним устройством //Библиотека CCNET запукает команду sazeof() определяет длину массива //И в цикле отправляет последовательно массив на Serial1 внешнему устройству CCNET_SEND (RESET); //Отправляем указатель на массив RESET библиотеке CCNET delay (100); CCNET_SEND (POLL); //Отправляем указатель на массив POLL библиотеке CCNET } void loop() { }У меня все получилось, все работает, код нормально оптимизирован и занимает немного места, но я не пойму каким образом это все работает.
Кто-нибудь может это объяснить?
Это называется шаблон. В 11 и 13 строках выполняются разные функции, потому и работает sizeof.
В гугле море информации, например, здесь http://cppstudio.com/post/5165/
Все же решение есть, и оно работает без терминаторов и т.д.
Боюсь Вас разочаровать, но таки нет.
скопировал нужный код из .cpp и .h библиотеки EEPROM2 в созданную библиотеку CCNET
https://github.com/ssvs111/CCNET
в библиотеке только одна функция, которая отправляет в SERIAL входной массив данных,
Не совсем так. Там два элемента. Там сидит шаблон CCNET_SEND, который на этапе компиляции разворачивается в взыов функции sendcc, которой благополучно передаётся длина данных вторым параметром
У меня все получилось, все работает, код нормально оптимизирован и занимает немного места, но я не пойму каким образом это все работает.
Кто-нибудь может это объяснить?
А чего тут объяснять. Шаблон,
template< typename T > void CCNET_SEND(const T &data) { sendcc(&data, sizeof(data)); }Будучи вызванным строкой
На этапе компиляции разворачивается в
а дальше выполняется уже эта функция, которой, как мы видим вторым параметром благополучно передаётся длина.
-----------------------------
На самом деле, пользоваться таким кодом очень опасно, особенно если Вы его толком не понимаете. Он хорош, когда массив-аргумент объявлен явно. Однако, если Вы попробуете воспользоваться этим кодом с честным указателем – Вас ждёт разочарование.
Вот смотрите:
// так работает byte RESET[] = {0x02, 0x03, 0x09, 0x37, 0x00, 0x00, 0x0, 0x81, 0x1F};//9 байт CCNET_SEND (RESET); // а вот так – уже нет void SendIt(byte * r) { CCNET_SEND (r); } byte RESET[] = {0x02, 0x03, 0x09, 0x37, 0x00, 0x00, 0x0, 0x81, 0x1F};//9 байт SendIt(RESET);Начнём с того, что массивов в полном понимании в этом языке нет вовсе - это синтаксическая упрощалка. То, что здесь называется массивом - ничем не отличается от указателя и никакой информации о собственой длине не содержит в принципе. Поэтому, либо он содержит информацию о длине в своих данных (терминальный символ или там длина сидит в первом элементе), то пожалуйста, если же нет - то шишеньки. Не определите.
Благодарю за ответ, долго думал, почему этот код работает не так, как хотелось бы.
void setup() { Serial.begin(9600); byte a[4] = {1, 2, 3, 4}; Serial.println(sizeof(a)); //Выводит 4 test(a); } void loop() { } void test(byte* param){ byte a = sizeof(param); //Выводит 2 Serial.println(a); }Это шутка такая?
В одном месте вывод количества байтов, отведенных для переменной 'a', в другом месте вывод количество байтов, которое отведено на указатель для определенной архитектуры процессора.
Благодарю за ответ, долго думал, почему этот код работает не так, как хотелось бы.
void setup() { Serial.begin(9600); byte a[4] = {1, 2, 3, 4}; Serial.println(sizeof(a)); //Выводит 4 test(a); } void loop() { } void test(byte* param){ byte a = sizeof(param); //Выводит 2 Serial.println(a); }А что Вам "хотелось бы", никому не известно, включая компилятор.
Интересно, каким образом функция println() понимает сколько символов (байт) доступно по указателю? Или после массива есть признак окончания типа 0x04 и его можно как-то извлечь?
char fileName = "201209.TXT"; void printArr(char *fileName) { println(fileName); // выводит "201209.TXT" }Интересно, каким образом функция println() понимает сколько символов (байт) доступно по указателю?
Чтоб не выставлять себя идиотом, почитай. https://server.179.ru/tasks/cpp/total/051.html