Программирование >>  Арифметические и логические операции 

1 ... 46 47 48 [ 49 ] 50 51 52 53


Очевидный недостаток такого подхода в том, что нужно помнить о дополнительных круглых скобках. Другие решения - использовать различные макросы (DEBUG1, DEBUG2, и т.п.) в зависимости от количества аргументов, или манипулировать запятыми:

#define DEBUG(args) (printf( DEBUG: ), printf(args)) #define , DEBUG( i = %d i)

Часто предпочтительнее использовать настоящую функцию, которая стандартным способом может использовать переменное число аргументов.

Как реализовать функцию с переменным числом аргументов?

Используйте головной файл <stdarg.h> (или, если необходимо, более старый <varargs.h>).

Вот пример функции, которая объединяет произвольное количество стрингов, помещая результат в выделенный с помощью malloc участок памяти.

#include <stdlib.h> /* для malloc, NULL, size t */ #include <stdarg.h> /* для va макросов */ #include <string.h> /* для strcat и т.п. */

char *vstrcat(char *first, ... )

size t len = 0; char *retbuf; va list argp; char *p;

if(first == NULL)

return NULL; len = strlen(first);

va start(argp, first);

while((p = va arg(argp, char *)) != NULL)

len += strlen(p);

va end(argp);

retbuf = malloc(len + 1); /* +1 для \0 */

if(retbuf == NULL)

return NULL; /* ошибка */

(void)strcpy(retbuf, first);

va start(argp, first);

while((p = va arg(argp, char *)) != NULL)

(void)strcat(retbuf, p);

va end(argp);

return retbuf;

Вызывается функция примерно так:

char *str = vstrcat( Hello, , world! , (char *)NULL);

Обратите внимание на явное приведение типа в последнем аргументе. (Помните также, что после вызова такой функции нужно освободить память).

Если компилятор разрабатывался до приема стандарта ANSI, перепишите определение функции без прототипа ( char *vstrcat(first) char *first; { ) включите <stdio.h> вместо <stdlib.h>, добавьте extern char *mal-loc(); , и используйте int вместо size t. Возможно, придется удалить приведение (void) и использовать varargs.h вместо stdarg.

Помните, что в прототипах функций со списком аргументов переменной длины не указывается тип аргументов. Это значит, что по умолчанию будет происходить расширение типов аргументов.

Это также значит, что тип нулевого указателя должен быть явно указан.

Как написать функцию, которая бы, подобно printf, получала строку формата и переменное число аргументов, а затем для выполнения большей части работы передавала бы все это printf?

Используйте vprintf, vfprintf или vsprintf.

Перед вами подпрограмма error , которая после строки error: печатает сообщение об ошибке и символ новой строки.

#include <stdio.h> #include <stdarg.h>

error(char *fmt, ...)

va list argp;

fprintf(stderr, error: ); va start(argp, fmt); vfprintf(stderr, fmt, argp); va end(argp); fprintf(stderr, \n );

Чтобы использовать старый головной файл <varargs.h> вместо <stdarg.h>, измените заголовок функции:

void error(va alist)



va dcl

char *fmt; измените строку с va start:

va start(argp); и добавьте строку

fmt = va arg(argp, char *);

между вызовами va start и vfprintf. Заметьте, что после va dcl нет точки с запятой.

Как определить, сколько аргументов передано функции?

Для переносимых программ такая информация недоступна. Некоторые старые системы имели нестандартную функцию nargs(), но ее полезность всегда была сомнительна, поскольку обычно эта функция возвращает количество передаваемых машинных слов, а не число аргументов. (Структуры и числа с плавающей точкой обычно передаются в нескольких словах).

Любая функция с переменным числом аргументов должна быть способна по самим аргументам определить их число. Функции типа printf определяют число аргументов по спецификаторам формата (%d и т.п.) в строке формата (вот почему эти функции так скверно ведут себя при несовпадении списка аргументов и строки формата). Другой общепринятый прием - использовать признак конца списка (часто это числа 0, -1, или нулевой указатель, приведенный к нужному типу).

Мне не удается добиться того, чтобы макрос va arg возвращал аргумент типа указатель-на-функцию

Манипуляции с переписыванием типов, которыми обычно занимается va arg, кончаются неудачей, если тип аргумента слишком сложен - вроде указателя на функцию. Если, однако, использовать typedef для определения указателя на функцию, то все будет нормально.

Как написать функцию с переменным числом аргументов, которая передает их другой функции с переменным числом аргументов?

В общем случае задача неразрешима. В качестве второй функции нужно использовать такую, которая принимает указатель типа va list, как это делает vfprintf в приведенном выше примере.

Если аргументы должны передаваться непосредственно (a не через указатель типа va list), и вторая функция принимает переменное число аргументов (и нет возможности создать альтернативную функцию, принимающую указатель va list), то создание переносимой программы

невозможно. Проблема может быть решена, если обратиться к языку ассемблера соответствующей машины.

Как вызвать функцию со списком аргументов, создаваемым в процессе выполнения?

Не существует способа, который бы гарантировал переносимость. Если у вас пытливый ум, раздобудьте редактор таких списков, в нем есть несколько безрассудных идей, которые можно попробовать...

Переменные какого типа правильнее использовать как булевы? Почему в языке С нет стандартного типа логических переменных? Что использовать для значений true и false - #define или enum?

В языке С нет стандартного типа логических переменных, потому что выбор конкретного типа основывается либо на экономии памяти, либо на выигрыше времени. Такие вопросы лучше решать программисту (использование типа int для булевой переменной может быть быстрее, тогда как использование типа char экономит память).

Выбор между #define и enum - личное дело каждого, и споры о том, что лучше, не особенно интересны.

Используйте любой из вариантов:

#define TRUE 1 #define YES 1

#define FALSE 0 #define NO 0

enum bool {false, true}; enum bool {no, yes}; или последовательно в пределах программы или проекта используйте числа 1 и 0. (Возможно, задание булевых переменных через enum предпочтительнее, если используемый вами отладчик раскрывает содержимое enum-переменных).

Некоторые предпочитают такие способы задания:

#define TRUE (1==1) #define FALSE (!TRUE)

или задают вспомогательный макрос:

#define Istrue(e) ((e) != 0)

Не видно, что они этим выигрывают.

Разве не опасно задавать значение TRUE как 1, ведь в С любое не равное нулю значение рассматривается как истинное? А если оператор сравнения или встроенный булев оператор возвратит нечто, отличное от 1?

Истинно (да-да!), что любое ненулевое значение рассматривается в С как значение ИСТИНА , но это применимо только на входе , где



ожидается булева переменная. Когда булева переменная генерируется встроенным оператором, гарантируется, что она равна 0 или 1.

Следовательно, сравнение

if((a == b) == TRUE)

как ни смешно оно выглядит, будет вести себя, как ожидается, если значению TRUE соответствует 1. Как правило, явные проверки на TRUE и FALSE нежелательны, поскольку некоторые библиотечные функции (стоит упомянуть isupper, isalpha и т.п.), возвращают в случае успеха ненулевое значение, которое не обязательно равно 1. (Кроме того, если вы верите, что if((a == b) == TRUE) лучше чем if(a == b) , то почему не пойти дальше и не написать:

if(((a == b) == TRUE) == TRUE)

Хорошее пальцевое правило состоит в том, чтобы использовать TRUE и FALSE (или нечто подобное) только когда булевым переменным или аргументам функции присваиваются значения или когда значение возвращается булевой функцией, но никогда при сравнении.

Макроопределения препроцессора TRUE и FALSE используются для большей наглядности, а не потому, что конкретные значения могут измениться.

Какова разница между enum и рядом директив препроцессора

#define?

В настоящее время разница невелика. Хотя многие, возможно, предпочли бы иное решение, стандарт ANSI утверждает, что произвольному числу элементов перечисления могут быть явно присвоены целочисленные значения. (Запрет на присвоение значений без явного приведения типов, позволил бы при разумном использовании перечислений избежать некоторых ошибок.)

Некоторые преимущества перечислений в том, что конкретные значения задаются автоматически, что отладчик может представлять значения перечислимых переменных в символьном виде, а также в том, что перечислимые переменные подчиняются обычным правилам областей действия. (Компилятор может также выдавать предупреждения, когда перечисления необдуманно смешиваются с целочисленными переменными.

Такое смешение может рассматриваться как проявление плохого стиля, хотя формально это не запрещено). Недостаток перечислений в том, что у программиста мало возможностей управлять размером переменных (и предупреждениями компилятора тоже).

Я слышал, что структуры можно копировать как целое, что они могут быть переданы функциям и возвращены ими, но в K&R I сказано, что этого делать нельзя

В K&R I сказано лишь, что ограничения на операции со структурами будут сняты в следующих версиях компилятора; эти операции уже были возможны в компиляторе Денниса Ритчи, когда издавалась книга K&R I.

Хотя некоторые старые компиляторы не поддерживают копирование структур, все современные компиляторы имеют такую возможность, предусмотренную стандартом ANSI C, так что не должно быть колебаний при копировании и передаче структур функциям.

Каков механизм передачи и возврата структур?

Структура, передаваемая функции как параметр, обычно целиком размещается на стеке, используя необходимое количество машинных слов. (Часто для снижения ненужных затрат программисты предпочитают передавать функции указатель на структуру вместо самой структуры).

Структуры часто возвращаются функциями в ту область памяти, на которую указывает дополнительный поддерживаемый компилятором скрытый аргумент. Некоторые старые компиляторы используют для возврата структур фиксированную область памяти, хотя это делает невозможным рекурсивный вызов такой функции, что противоречит стандарту ANSI.

Эта программа работает правильно, но после завершения выдает дамп оперативной памяти. Почему? struct list { char *item; struct list *next; } /* Здесь функция main */ main(argc, argv) ...

Из-за пропущенной точки с запятой компилятор считает, что main возвращает структуру. (Связь структуры с функцией main трудно определить, мешает комментарий). Так как для возврата структур компилятор обычно использует в качестве скрытого параметра указатель, код, сгенерированный для main() пытается принять три аргумента, хотя передаются (в данном случае стартовым кодом Си) только два.

Почему нельзя сравнивать структуры?

Не существует разумного способа сделать сравнение структур совместимым с низкоуровневой природой языка Си. Побайтовое сравнение может быть неверным из-за случайных бит в неиспользуемых дырках (такое заполнение необходимо, чтобы сохранить выравнивание для последующих полей). Почленное сравнение потребовало бы неприемлемого количества повторяющихся машинных инструкций в случае больших структур.



1 ... 46 47 48 [ 49 ] 50 51 52 53

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика