|
Программирование >> Арифметические и логические операции
Если необходимо сравнить две структуры, напишите для этого свою собственную функцию. C++ позволит создать оператор ==, чтобы связать его с вашей функцией. Как читать/писать структуры из файла/в файл? Писать структуры в файл можно непосредственно с помощью fwrite: fwrite((char *)&somestruct, sizeof(somestruct), 1, fp); а соответствующий вызов fread прочитает структуру из файла. Однако файлы, записанные таким образом будут не особенно переносимы. Заметьте также, что на многих системах нужно использовать в функции fopen флаг b . Мне попалась программа, в которой структура определяется так: struct name { int namelen; char name(1); }; затем идут хитрые манипуляции с памятью, чтобы массив name вел себя будто в нем несколько элементов. Такие манипуляции законны/мобильны? Такой прием популярен, хотя Деннис Ритчи назвал это слишком фамильярным обращением с реализацией Си . ANSI полагает, что выход за пределы объявленного размера члена структуры не полностью соответствует стандарту, хотя детальное обсуждение всех связанных с этим проблем не входит в задачу данных вопросов и ответов. Похоже, однако, что описанный прием будет одинаково хорошо принят всеми известными реализациями Си. (Компиляторы, тщательно проверяющие границы массивов, могут выдать предупреждения). Для страховки будет лучше объявить переменную очень большого размера чем очень малого. В нашем случае char name[MAXSIZE]; где MAXSIZE больше, чем длина любого имени, которое будет сохранено в массиве name[]. (Есть мнение, что такая модификация будет соответствовать Стандарту). Как определить смещение члена структуры в байтах? Если это возможно, необходимо использовать макрос offsetof, который определен стандартом ANSI. Если макрос отсутствует, предлагается такая (не на все 100% мобильная) его реализация: #define offsetof(type, mem) ((size t) \ ((char *)&((type *) 0)->mem - (char *)((type *) 0))) Для некоторых компиляторов использование этого макроса может оказаться незаконным. Как осуществить доступ к членам структур по их именам во время выполнения программы? Создайте таблицу имен и смещений, используя макрос offsetof(). Смещение члена структуры b в структуре типа a равно: offsetb = offsetof(struct a, b) Если structp указывает на начало структуры, а b - член структуры типа int, смещение которого получено выше, b может быть установлен косвенно с помощью: *(int *)((char *)structp + offsetb) = value; Почему sizeof выдает больший размер структурного типа, чем я ожидал, как будто в конце структуры лишние символы? Это происходит (возможны также внутренние дыры , когда необходимо выравнивание при задании массива непрерывных структур. Мой компилятор оставляет дыры в структурах, что приводит к потере памяти и препятствует двоичному вводу/выводу при работе с внешними файлами. Могу я отключить дырообразование или как-то контролировать выравнивание? В вашем компиляторе, возможно, есть расширение, (например, #pragma), которое позволит это сделать, но стандартного способа не существует. Можно ли задавать начальные значения объединений? Стандарт ANSI допускает инициализацию первого члена объединения. Не существует стандартного способа инициализации других членов (и тем более нет такого способа для старых компиляторов, которые вообще не поддерживают какой-либо инициализации). Как передать функции структуру, у которой все члены - константы? Поскольку в языке С нет возможности создавать безымянные значения структурного типа, необходимо создать временную структуру. Какой тип целочисленной переменной использовать? Если могут потребоваться большие числа, (больше 32767 или меньше -32767), используйте тип long. Если нет, и важна экономия памяти (большие массивы или много структур), используйте short. Во всех ос- тальных случаях используйте int. Если важно точно определить момент переполнения и/или знак числа не имеет значения, используйте соответствующий тип unsigned. (Но будьте внимательны при совместном использовании типов signed и unsigned в выражениях). Похожие соображения применимы при выборе между float и double. Хотя тип char или unsigned char может использоваться как целочисленный тип наименьшего размера, от этого больше вреда, чем пользы из-за непредсказуемых перемен знака и возрастающего размера программы. Эти правила, очевидно, не применимы к адресам переменных, поскольку адрес должен иметь совершенно определенный тип. Если необходимо объявить переменную определенного размера, (единственной причиной тут может быть попытка удовлетворить внешним требованиям к организации памяти), непременно изолируйте объявление соответствующим typedef. Каким должен быть новый 64-битный тип на новых 64-битных машинах? Некоторые поставщики С компиляторов для 64-битных машин поддерживают тип long int длиной 64 бита. Другие же, опасаясь, что слишком многие уже написанные программы зависят от sizeof(int) == sizeof(long) == 32 бита, вводят новый 64-битный тип long long (или lon- glong). Программисты, желающие писать мобильные программы, должны, следовательно, изолировать 64-битные типы с помощью средства typedef. Разработчики компиляторов, чувствующие необходимость ввести новый целочисленный тип большего размера, должны объявить его как имеющий по крайней мере 64 бит (это действительно новый тип, которого нет в традиционном Си), а не как имеющий точно 64 бит . У меня совсем не получается определение связанного списка. Я пишу: typedef struct { char *item; NODEPTR next; } *NODEPTR; но компилятор выдает сообщение об ошибке. Может структура в С содержать ссылку на себя? Структуры в Си, конечно же, могут содержать указатели на себя. В приведенном тексте проблема состоит в том, что определение NODEPTR не закончено в том месте, где объявляется член структуры next . Для исправления, снабдите сначала структуру тегом ( struct node ). Далее объявите next как struct node *next; , и/или поместите декларацию typedef целиком до или целиком после объявления структуры. Одно из возможных решений будет таким: struct node char *item; struct node *next; typedef struct node *NODEPTR; Есть по крайней мере три других одинаково правильных способа сделать то же самое. Сходная проблема, которая решается примерно так же, может возникнуть при попытке определить с помощью средства typedef пару ссылающихся друг на друга структур. Как объявить массив из N указателей на функции, возвращающие указатели на функции возвращающие указатели на char? Есть по крайней мере три варианта ответа: char .(.(.a[N])())(); 2. Писать декларации по шагам, используя typedef: typedef char *pc; /* указатель на char */ typedef pc fpc(); /* функция,возвращающая указатель на char */ typedef fpc *pfpc; /* указатель на.. см. выше */ typedef pfpc fpfpc(); /* функция, возвращающая... */ typedef fpfpc *pfpfpc; /* указатель на... */ pfpfpc a[N]; /* массив... */ 3. Использовать программу cdecl, которая переводит с английского на С и наоборот. cdecl> declare a as array of pointer to function returning pointer to function returning pointer to char char *(*(*a[])())() cdecl может также объяснить сложные декларации, помочь при явном приведении типов, и, для случая сложных деклараций, вроде только что разобранного, показать набор круглых скобок, в которые заключены аргументы. Версии cdecl можно найти в comp.sources.unix и в K&R II. Я моделирую Марковский процесс с конечным числом состояний, и у меня есть набор функций для каждого состояния. Я хочу, чтобы смена состояний происходила путем возврата функцией указателя на функцию, соответствующую следующему состоянию. Однако, я обнаружил ограничение в механизме деклараций языка Си: нет возможности объявить функцию, возвращающую указатель на функцию, возвращающую указатель на функцию, возвращающую указатель на функцию... Да, непосредственно это сделать нельзя. Пусть функция возвращает обобщенный указатель на функцию, к которому перед вызовом функции будет применен оператор приведения типа, или пусть она возвращает структуру, содержащую только указатель на функцию, возвращающую эту структуру. Мой компилятор выдает сообщение о неверной повторной декларации, хотя я только раз определил функцию и только раз вызвал Подразумевается, что функции, вызываемые без декларации в области видимости (или до такой декларации), возвращают значение типа int. Это приведет к противоречию, если впоследствии функция декларирована иначе. Если функция возвращает нецелое значение, она должна быть объявлена до того как будет вызвана. Как наилучшим образом декларировать и определить глобальные переменные? Прежде всего заметим, что хотя может быть много деклараций (и во многих файлах) одной глобальной (строго говоря внешней ) переменной, (или функции), должно быть всего одно определение. (Определение - это такая декларация, при которой действительно выделяется память для переменной, и присваивается, если нужно, начальное значение). Лучше всего поместить определение в какой-то главный (для программы или ее части). c файл, с внешней декларацией в головном файле .h, который при необходимости подключается с помощью #include. Файл, в котором находится определение переменной, также должен включать головной файл с внешней декларацией, чтобы компилятор мог проверить соответствие декларации и определения. Это правило обеспечивает высокую мобильность программ и находится в согласии с требованиями стандарта ANSI C. Заметьте, что многие компиляторы и компоновщики в системе UNIX используют общую модель , которая разрешает многократные определения без инициализации. Некоторые весьма странные компиляторы могут требовать явной инициализации, чтобы отличить определение от внешней декларации. С помощью препроцессорного трюка можно устроить так, что декларация будет сделана лишь однажды, в головном файле, и она c помощью #define превратится в определение точно при одном включении головного файла. Что означает ключевое слово extern при декларации функции? Слово extern при декларации функции может быть использовано из соображений хорошего стиля для указания на то, что определение функции, возможно, находится в другом файле. Формально между: extern int f(); int f(); нет никакой разницы. Я, наконец, понял, как объявлять указатели на функции, но как их инициализировать? Используйте нечто такое: extern int func(); int (*fp)() = func; Когда имя функции появляется в выражении, но функция не вызывается (то есть, за именем функции не следует ( ), оно сворачивается , как и в случае массивов, в указатель (т.е. неявным образом записанный адрес). Явное объявление функции обычно необходимо, так как неявного объявления внешней функции в данном случае не происходит (опять-таки из-за того, что за именем функции не следует ( ). Я видел, что функции вызываются с помощью указателей и просто как функции. В чем дело? По первоначальному замыслу создателя С указатель на функцию должен был превратиться в настоящую функцию с помощью оператора * и дополнительной пары круглых скобок для правильной интерпретации. int r, func(), (*fp)() = func; r = (*fp)(); На это можно возразить, что функции всегда вызываются с помощью указателей, но что настоящие функции неявно превращаются в указатели (в выражениях, как это происходит при инициализациях) и это
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |