|
Программирование >> Перегруженные имена функций и идентификаторы
Как определить, сколько аргументов передано функции? Для переносимых программ такая информация недоступна. Некоторые старые системы имели нестандартную функцию 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, ведь в Си любое не равное нулю значение рассматривается как истинное? А если оператор сравнения или встроенный булев оператор возвратит нечто, отличное от Истинно (да-да!), что любое ненулевое значение рассматривается в Си как значение ИСТИНА , но это применимо только на входе , где ожидается булева переменная. Когда булева переменная генерируется встроенным оператором, гарантируется, что она равна 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 утверждает, что произвольному числу элементов перечисления могут быть явно присвоены целочисленные значения. (Запрет на присвоение значений без явного приведения типов, позволил бы при разумном использовании перечислений избежать некоторых ошибок.) Некоторые преимущества перечислений в том, что конкретные значения задаются автоматически, что отладчик может представлять значения перечислимых переменных в символьном виде, а также в том, что перечислимые переменные подчиняются обычным правилам областей действия. (Компилятор может также выдавать предупреждения, когда перечисления необдуманно смешиваются с целочисленными переменными. Такое смешение может рассматриваться как проявление плохого стиля, хотя формально это не запрещено). Недостаток перечислений в том, что у программиста мало возможностей управлять размером переменных (и предупреждениями компилятора тоже).
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |