#c #функции #переменные
Навеяно вопросом Как работает execlp, что за последний аргумент NULL? Полистал ответы на схожие вопросы, но подробного описания не нашёл: Функции с переменным числом параметров Как в C объявить функцию с переменным числом аргументов? С и переменное число аргументов И т.д. P.S Предполагается, что читатель этого текста уже выпустился из детсада, и не задаёт вопросов уровня "Мне тут какую-то ошибку выдали, чо это?". И не путает C с C++. Пинки, подзатыльники и прочие указания на "ты неправ" приветствуются.
Ответы
Ответ 1
В языке C есть возможность объявлять и использовать функции с переменным числом аргументов. Возможность эта обеспечивается особенностями работы со стеком при вызове функций, но сейчас они подробно рассматриваться не будут, только практическая сторона вопроса. Магия, происходящая втоже за рамками, любопытный да найдёт объяснение этому шаманству. Прототип такой функции может выглядеть так, например: void print_messages( const char * title, ... ); А вызов - так: print_messages( "Вот что имею сообщить", "это раз", "это два", "это три", NULL ); И печатать, соответственно: Вот что имею сообщить: - это раз. - это два. - это три. Ну и её реализация: void print_messages( const char * title, ... ) { va_list ap; const char * message; va_start( ap, title ); printf( "%s:\n", title ); message = va_arg( ap, const char * ); while( message ) { printf( " - %s.\n", message ); message = va_arg( ap, const char *); } va_end( ap ); } Самой важной проблемой тут является определение конца списка аргументов. В данном случае использовался NULL для его отметки. Но это не всегда приемлемо. Например, когда NULL является допустимым значением аргумента. Или, скажем, 0/-1 в случае целых чисел. Первое решение этой проблемы - передать количество аргументов первым параметром: void print_numbers( size_t amount, ... ) { va_list ap; int number; va_start( ap, amount ); printf( "Total numbers: %zu, let's go! ", amount ); while( amount-- ) { number = va_arg( ap, int ); printf( " [%d]", number ); } va_end( ap ); } Вызов: print_numbers( 3, 11, 22, 33 ); Этот метод может применяться только тогда, когда в функцию передаются аргументы одного типа. Того же можно добиться, передавая массив значений с его размером. Но это не всегда оправдано. Да и рассматривается сейчас технологическая демка, а не целесообразность (которая всегда на совести программиста, но на то к нему /dev/brain и прилагается). Шёпотом: ну и TMTOWTDI в сях тоже бывает, хи-хи... А что делать, если аргументы могут быть разных типов? Тут на помощь приходит метод под названием "формат". Любой сишник с ним знаком по *printf*. Но это не интересно. Придумаем своё, на том же принципе. Определимся: символ 'c' означает char 's' - short 'l' - long 'z' - char * Прототип: void print_something( const char * format, ... ); Реализация: void print_something(const char * fmt, ...) { va_list ap; va_start( ap, fmt ); /* Гусары, молчать! */ long l; int i; char c; char *z; while( *fmt ) { switch( *fmt ) { case 'c': /* см дальше */ c = (char)va_arg( ap, char ); printf( "char: '%c'\n", c ); break; case 's': /* см дальше */ s = (short)va_arg( ap, short ); printf( "short: '%d'\n", s ); break; case 'i': i = va_arg( ap, int ); printf( "int: '%d'\n", i ); break; case 'l': l = va_arg( ap, long ); printf( "long: '%lu'\n", l );; break; case 'z': z = va_arg( ap, char * ); printf( "char *: '%s'\n", z );; break; default: printf( "Хрен знает что передали: '%c'\n", *fmt ); break; } fmt++; } } Дальше: char, short и прочее, которое меньше int. Эти строчки возбуждают компилятор: c = (char)va_arg( ap, char ); /* 'char' is promoted to 'int' when passed through '...' */ s = (short)va_arg( ap, short ); /* 'short' is promoted to 'int' when passed through '...' */ Выводы делать не буду, оставлю на домашнее задание.
Комментариев нет:
Отправить комментарий