Например, я недавно сталкивался с этим в ядре Linux:
/ * Принудительная ошибка компиляции, если условие истинно * / #define BUILD_BUG_ON (условие) ((недействительно) sizeof (символ [1 - 2 * !! (условие)]))
Итак, в вашем коде, если у вас есть какая-то структура, которая должна иметь размер, скажем, кратный 8 байтам, возможно, из-за некоторых аппаратных ограничений, вы можете сделать следующее:
BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);
и он не скомпилируется, если размер struct mystruct не кратен 8, а если он кратен 8, код времени выполнения вообще не генерируется.
Другой известный мне трюк из книги «Графические жемчужины», которая позволяет одному заголовочному файлу как объявлять, так и инициализировать переменные в одном модуле, тогда как в других модулях, использующих этот модуль, просто объявлять их как внешние.
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL #define INIT (x, y) (x) = (y) #else #define GLOBAL extern #define INIT (x, y) #endif GLOBAL int INIT (x, 0); GLOBAL int somefunc (int a, int b);
При этом код, который определяет x и somefunc:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
в то время как код, который просто использует x и somefunc (), делает:
#include "the_above_header_file.h"
Таким образом, вы получаете один заголовочный файл, который объявляет как экземпляры глобальных переменных, так и прототипы функций, где они необходимы, и соответствующие объявления extern.
Итак, какие у вас любимые приемы программирования на С в этом направлении?
BUILD_BUG_ON
макросе, что не так с использованием#error
inside и#if
?Ответы:
C99 предлагает некоторые действительно интересные вещи, используя анонимные массивы:
Удаление бессмысленных переменных
становится
Передача переменного количества аргументов
Статические связанные списки
Во всяком случае, я уверен, что многие другие классные приемы, о которых я не думал.
источник
&(int){1}
, если вы хотите прояснить немного, каково ваше намерение здесь.Читая исходный код Quake 2, я придумал что-то вроде этого:
(более или менее, у меня нет кода, чтобы проверить это сейчас).
С тех пор перед моими глазами открылся новый мир творческого использования препроцессора. Я больше не включаю только заголовки, а целые куски кода время от времени (это значительно улучшает возможность повторного использования) :-p
Спасибо Джон Кармак! XD
источник
Я люблю использовать
= {0};
для инициализации структур без необходимости вызывать memset.Это инициализирует все члены структуры (или массива) нулями (но не байтами заполнения - используйте memset, если вам нужно также обнулить их).
Но вы должны знать, что есть некоторые проблемы с этим для больших, динамически размещаемых структур .
источник
const struct something zero_something = { 0 };
и затем я могу сбросить переменную на лету с помощьюstruct something X = zero_something;
или частично пройдя процедуру, я могу использовать 'X = zero_something;'. Единственное возможное возражение состоит в том, что это предполагает чтение данных откуда-то; в наши дни memset () может быть быстрее, но мне нравится ясность назначения, и в инициализаторе также можно использовать значения, отличные от нуля (и memset (), за которыми следуют настройки для отдельного члена может быть медленнее, чем простая копия).Если мы говорим о трюках, то моим любимым должен быть прибор Даффа для раскрутки петли! Я просто жду подходящей возможности для меня, чтобы фактически использовать это в гневе ...
источник
используя
__FILE__
и__LINE__
для отладкиисточник
__FUNCTION__
это просто псевдоним для__func__
, и__func__
в C99. Довольно удобно.__PRETTY_FUNCTION__
в C (GCC) это просто еще один псевдоним__func__
, но в C ++ он получит полную сигнатуру функции.В C99
источник
Однажды мой друг и я пересмотрели возвращение, чтобы найти хитрую ошибку с повреждением стека.
Что-то вроде:
источник
DoSomeStackCheckStuff
функциям, которые вы хотите отследить.#define return if((DoSomeStackCheckStuff) && 0) ; else return
... так же безумно, я думаю!Мне нравится "взлом структуры" для наличия объекта динамического размера. Этот сайт также объясняет это довольно хорошо (хотя они ссылаются на версию C99, где вы можете написать «str []» как последний член структуры). Вы можете сделать строку "объект", как это:
здесь мы выделили структуру типа X в куче, которая является размером с int (для len), плюс длина «hello world», плюс 1 (так как str 1 включена в sizeof (X).
Это обычно полезно, когда вы хотите иметь «заголовок» прямо перед некоторыми данными переменной длины в одном и том же блоке.
источник
str[1]
(неstr[]
), 1 байт строки включен вsizeof(struct X)
. это включает в себя любые отступы междуlen
иstr
.str
. Хорошо, когда я выделяю, тоsizeof(struct X) + 10
это делаетstr
эффективно10 - sizeof(int)
(или больше, так как мы сказали, что есть отступ) большим. Это накладываетсяstr
и любой отступ после него. Единственный способ, которым это будет иметь какое-либо различие, состоит в том, что, если бы был член, послеstr
которого все равно ломается, гибкие члены должны быть последними. Любое заполнение в конце может привести к слишком большому выделению. Пожалуйста, приведите конкретный пример того, как это может пойти не так.Объектно-ориентированный код с C, эмулируя классы.
Просто создайте структуру и набор функций, которые принимают указатель на эту структуру в качестве первого параметра.
источник
Вместо того
использование
источник
Использование глупого трюка с макросами для упрощения поддержки определений записей.
источник
Для создания переменной, которая доступна только для чтения во всех модулях, кроме той, в которой она объявлена:
источник
Source2.c
компилятор может предположить, чтоMyVar
это не меняется даже при вызове функцииSource1.c
. (Обратите внимание, что это, как фактическая переменная const, отличается от указателя на const. В последнем случае объект, на который указывает указатель, все еще может быть изменен с помощью другого указателя.)Сдвиги битов определяются только до величины сдвига 31 (для 32-битного целого).
Что вы делаете, если хотите иметь вычисленный сдвиг, который также должен работать с более высокими значениями сдвига? Вот как это делает видеокодек Theora:
Или гораздо более читабельным:
Выполнение задачи, как показано выше, намного быстрее, чем использование такой ветки:
источник
v
, в то время какhalfshift
уловка только удваивает допустимый диапазон до 63 в 32-разрядной архитектуре и до 127 в 64-разрядной.Объявление массива указателей на функции для реализации конечных автоматов.
Самое приятное преимущество заключается в том, что каждый стимул / состояние просто заставляет проверять все пути кода.
Во встроенной системе я часто сопоставляю ISR, чтобы указывать на такую таблицу и обнаруживать ее по мере необходимости (за пределами ISR).
источник
Еще один приятный трюк препроцессора - использовать символ «#» для печати выражений отладки. Например:
редактировать: приведенный ниже код работает только на C ++. Благодаря smcameron и Эван Теран.
Да, время компиляции всегда хорошее. Это также может быть записано как:
источник
Я бы не стал называть это любимым трюком, так как никогда не использовал его, но упоминание об устройстве Даффа напомнило мне об этой статье о реализации Coroutines в C. Это всегда вызывает у меня смех, но я уверен, что это может быть полезным некоторое время
источник
static
переменной, а вместо этого динамически выделяю структуру и передаю указатель на нее в функцию сопрограммы. Куча макросов делает это более приемлемым. Это не хорошо, но лучше, чем версия с асинхронным вызовом / обратным вызовом, которая скачет повсюду. Я бы использовал зеленые темы (черезswapcontext()
on * nixes), если бы мог.В то время как (0); не влияет на программу, но компилятор выдаст предупреждение о том, что «это ничего не делает», и этого достаточно, чтобы я посмотрел на строку с ошибками и увидел реальную причину, по которой я хотел обратить на это внимание.
источник
Я фанат xor хаков
Поменяйте местами 2 указателя без третьего временного указателя:
Или мне действительно нравится список ссылок xor только с одним указателем. (Http://en.wikipedia.org/wiki/XOR_linked_list)
Каждый узел в связанном списке - это Xor предыдущего узла и следующего узла. Чтобы перейти вперед, адреса узлов находятся следующим образом:
и т.п.
или пройти назад:
и т.п.
Хотя это не очень полезно (вы не можете начать обход с произвольного узла), я считаю, что это очень круто.
источник
Этот из книги «Хватит веревки, чтобы выстрелить себе в ногу»:
В шапке заявляю
В вашем коде поместите операторы тестирования, например:
Функция do / while помогает в случае, если содержимое макроса расширяется до нескольких операторов.
Оператор будет напечатан только в том случае, если флаг компилятора «-D RELEASE» не используется.
Вы можете тогда, например. передать флаг вашему make-файлу и т. д.
Не уверен, как это работает в Windows, но в * nix это работает хорошо
источник
#define D(x) do { } while(0)
вместо этого обрабатывает этот случай (и может быть применено к ветви, которая также вставляетx
для согласованности)Rusty на самом деле создал целый набор условных выражений сборки в ccan , проверьте модуль build assert:
В самом заголовке есть много других полезных макросов, которые легко установить на свои места.
Я стараюсь изо всех сил сопротивляться притяжению темной стороны (и злоупотреблению препроцессором), придерживаясь в основном встроенных функций, но мне нравятся умные, полезные макросы, подобные тем, которые вы описали.
источник
Двумя хорошими исходными книгами для такого рода вещей являются «Практика программирования и написания твердого кода» . Один из них (я не помню, какой именно) говорит: предпочитайте enum вместо #define, где вы можете, потому что enum проверяется компилятором.
источник
Не относится к C, но мне всегда нравился оператор XOR. Одна крутая вещь, которую он может сделать, это «своп без временного значения»:
Результат:
источник
Смотрите вопрос «Скрытые возможности С» .
источник
Мне нравится понятие,
container_of
используемое, например, в списках. В принципе, вам не нужно указыватьnext
иlast
поля для каждой структуры, которые будут в списке. Вместо этого вы добавляете заголовок структуры списка к фактическим связанным элементам.Посмотрите на
include/linux/list.h
реальные примеры.источник
Я думаю, что использование указателей userdata довольно аккуратно. Мода сдает позиции в наши дни. Это не столько функция C, но довольно простая в использовании в C.
источник
Я использую X-Macros, чтобы позволить прекомпилятору генерировать код. Они особенно полезны для определения значений ошибок и связанных строк ошибок в одном месте, но они могут пойти гораздо дальше.
источник
Наша кодовая база имеет трюк, похожий на
который позволяет отслеживать утечки памяти в режиме отладки. Я всегда думал, что это круто.
источник
Приколы с макросами:
источник
Вот пример того, как сделать код на C полностью не осведомленным о том, что на самом деле используется HW для запуска приложения. Main.c выполняет настройку, и затем свободный слой может быть реализован на любом компиляторе / арке. Я думаю, что это довольно удобно для абстрагирования кода на C, так что оно не будет особенным.
Добавляем полный скомпилированный пример здесь.
источник
Заполните пропуски, чтобы ни привет, ни привет не появлялись на выходе.
анс:
fclose(stdout)
источник
{}
кнопки панели инструментов (я сделал это для вас). Кнопка «Цитировать» не сохраняет пробелы и не применяет подсветку синтаксиса.