Несмотря на то, что холст позволит вам установить высоту = 2147483647, когда вы начнете рисовать, ничего не произойдет
Рисование происходит только тогда, когда я возвращаю высоту до 32767
Указатели функции. Можно использовать таблицу указателей функции, чтобы реализовать, например, быстро косвенно поточные интерпретаторы кода (Forth) или диспетчеры байт-кода, или моделировать подобные OO виртуальные методы.
Тогда существуют скрытые сокровища в стандартной библиотеке, такие как qsort (), bsearch (), strpbrk (), strcspn () [последние два, являющиеся полезным для реализации strtok () замена].
ошибка А C - то, что арифметическое переполнение со знаком является неопределенным поведением (UB). Таким образом каждый раз, когда Вы видите выражение, такое как x+y, и подписываемый ints, он мог бы потенциально переполнить и вызвать UB.
я раньше объявлял, что некоторые переменные с register
ключевое слово помогают вещам скорости встать. Это дало бы подсказку компилятору C для использования регистра ЦП в качестве локального устройства хранения данных. Это наиболее вероятно больше не необходимое, поскольку современные дневные компиляторы C делают это автоматически.
структура инициализации к нулю
struct mystruct a = {0};
это обнулит все stucture элементы.
gcc имеет много расширений языка C, которым я наслаждаюсь, который может быть найден здесь . Часть моего избранного функциональные атрибуты . Одним чрезвычайно полезным примером является атрибут формата. Это может использоваться при определении пользовательской функции, которая берет printf строку формата. При включении этого функционального атрибута gcc сделает, проверяет аргументы, чтобы гарантировать, чтобы строка формата и аргументы совпали и генерировали предупреждения или ошибки как соответствующие.
int my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
Мой фаворит "скрытая" функция C, использование %n в printf для обратной записи к стеку. Обычно printf выталкивает значения параметров от стека на основе строки формата, но %n может записать их обратно.
раздел Выезда 3.4.2 здесь . Может привести к большому количеству противных уязвимостей.
Ранние версии gcc попытались выполнить игру каждый раз, когда он встретился с "#pragma" в исходном коде. См. также здесь .
Мне показали это в небольшом количестве кода однажды и спросил, что он сделал:
hexDigit = "0123456789abcdef"[someNybble];
Другой фаворит:
unsigned char bar[100];
unsigned char *foo = bar;
unsigned char blah = 42[foo];
Преобразование типов при помощи необычных преобразований типа. Хотя не скрытая функция, его довольно хитрое.
Пример:
, Если необходимо было знать, как плавание хранилищ компилятора, просто попробуйте это:
uint32_t Int;
float flt = 10.5; // say
Int = *(uint32_t *)&flt;
printf ("Float 10.5 is stored internally as %8X\n", Int);
или
float flt = 10.5; // say
printf ("Float 10.5 is stored internally as %8X\n", *(uint32_t *)&flt);
Примечание умное использование преобразований типа. Преобразование адреса переменной (здесь & flt) к желаемому типу (здесь (uint32_t *)) и извлечение его содержания (применение '*').
Это работает другая сторона выражения также:
*(float *)&Int = flt;
Это могло также быть выполнено с помощью объединения:
typedef union
{
uint32_t Int;
float flt;
} FloatInt_type;
Едва ли скрытая функция, но это смотрело на меня как вуду, в первый раз, когда я видел что-то вроде этого:
void callback(const char *msg, void *data)
{
// do something with msg, e.g.
printf("%s\n", msg);
return;
data = NULL;
}
причина этой конструкции, это при компиляции этого с-Wextra и без "данных = ПУСТОЙ УКАЗАТЕЛЬ"; - строка, gcc выложит предупреждение о неиспользованных параметрах. Но с этой бесполезной строкой Вы не получаете предупреждение.
РЕДАКТИРОВАНИЕ: Я знаю, что существуют другие (лучшие) способы предотвратить те предупреждения. Это просто выглядело странным для меня, в первый раз, когда я видел это.
Мне понравились измеренные структуры переменной, которые Вы могли сделать:
typedef struct {
unsigned int size;
char buffer[1];
} tSizedBuffer;
tSizedBuffer *buff = (tSizedBuffer*)(malloc(sizeof(tSizedBuffer) + 99));
// can now refer to buff->buffer[0..99].
Также offsetof макрос, который находится теперь в ANSI C, но был частью колдовства в первый раз, я видел его. Это в основном использует операцию вычисления адреса (&) для нулевого указателя, переделанного как переменная структуры.
Выборка :
На этой странице, Вы найдете список интересного C программированием вопросов/загадок, Эти перечисленные программы являются теми, которых я принял как электронная почта вперед от моих друзей, некоторые я читал в некоторых книгах, некоторых из Интернета и некоторых на основе моих событий кодирования в C.
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t
Это дополнительный объект в стандарте, но это должна быть скрытая функция, потому что люди постоянно переопределяют их. Одна кодовая база я продолжил работать (и все еще делают, на данный момент) имеет несколько переопределений, все с различными идентификаторами. Большую часть времени это с макросами препроцессора:
#define INT16 short
#define INT32 long
И так далее. Это заставляет меня хотеть вытащить волосы. Просто используют чертовски стандартные целочисленные определения типов!
Мультисимвольные константы:
int x = 'ABCD';
Это устанавливает x
на 0x41424344
(или 0x44434241
, в зависимости от архитектуры).
РЕДАКТИРОВАНИЕ: Эта техника не является портативной, особенно при сериализации интервала Однако, может быть чрезвычайно полезно создать перечисления самодокументирования. например,
enum state {
stopped = 'STOP',
running = 'RUN!',
waiting = 'WAIT',
};
Это делает его намного более простым, если Вы смотрите на необработанный дамп памяти и должны определить значение перечисления, не имея необходимость искать его.
C99-разработайте макросы аргумента переменной, иначе
#define ERR(name, fmt, ...) fprintf(stderr, "ERROR " #name ": " fmt "\n", \
__VAR_ARGS__)
, который использовался бы как
ERR(errCantOpen, "File %s cannot be opened", filename);
Здесь, я также использую stringize оператор и строковую константу concatentation, другие функции, которые я действительно люблю.
Переменные автоматические переменные размера также полезны в некоторых случаях. Они были добавлены я nC99 и поддерживались в gcc в течение долгого времени.
void foo(uint32_t extraPadding) {
uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];
Вы заканчиваете с буфером на стеке с комнатой для заголовка протокола фиксированного размера плюс переменные данные размера. Можно получить тот же эффект с alloca (), но этот синтаксис более компактен.
необходимо удостовериться, что extraPadding является рыночной стоимостью прежде, чем назвать эту стандартную программу, или Вы заканчиваете тем, что унесли стек. У Вас были бы к проверке работоспособности аргументы прежде, чем назвать malloc или любой другой метод выделения памяти, таким образом, это не действительно необычно.
Больше приема компилятора GCC, но можно дать подсказки признака ответвления компилятору (распространенный в ядре Linux)
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
, см.: http://kerneltrap.org/node/4705
, Что я люблю приблизительно, это - то, что это также добавляет некоторую выразительность к некоторым функциям.
void foo(int arg)
{
if (unlikely(arg == 0)) {
do_this();
return;
}
do_that();
...
}
Оператор запятой широко не используется. Этим можно, конечно, злоупотребить, но это может также быть очень полезно. Это использование является наиболее распространенным:
for (int i=0; i<10; i++, doSomethingElse())
{
/* whatever */
}
, Но можно использовать этот оператор где угодно. Наблюдайте:
int j = (printf("Assigning variable j\n"), getValueFromSomewhere());
Каждый оператор оценен, но значение выражения будет значением последнего оцененного оператора.
Я никогда не использовал битовые поля , но они звучат прохладными для материала ультранизкого уровня.
struct cat {
unsigned int legs:3; // 3 bits for legs (0-4 fit in 3 bits)
unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
// ...
};
cat make_cat()
{
cat kitty;
kitty.legs = 4;
kitty.lives = 9;
return kitty;
}
Это означает, что sizeof(cat)
может быть столь же маленьким как sizeof(char)
.
Чередование структур как Устройство Вареного пудинга :
strncpy(to, from, count)
char *to, *from;
int count;
{
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}
C имеет стандарт, но не все компиляторы C полностью совместимы (я еще не видел полностью совместимого компилятора C99!).
Однако приемы, которые я предпочитаю, являются теми, которые являются неочевидными и портативными через платформы, поскольку они полагаются на семантическое C. Они обычно о макросах или укусили арифметику.
, Например: свопинг двух целых чисел без знака, не используя временную переменную:
...
a ^= b ; b ^= a; a ^=b;
...
или "расширяющийся C" для представления конечные автоматы как:
FSM {
STATE(x) {
...
NEXTSTATE(y);
}
STATE(y) {
...
if (x == 0)
NEXTSTATE(y);
else
NEXTSTATE(x);
}
}
, который может быть достигнут со следующими макросами:
#define FSM
#define STATE(x) s_##x :
#define NEXTSTATE(x) goto s_##x
В целом, тем не менее, мне не нравятся приемы, которые умны, но делают код излишне сложным для чтения (как пример подкачки), и я люблю тех, которые делают код более ясным и непосредственно передающим намерение (как пример FSM).
Я очень люблю обозначенные инициализаторы, добавленные в C99 (и поддерживаемый в gcc в течение долгого времени):
#define FOO 16
#define BAR 3
myStructType_t myStuff[] = {
[FOO] = { foo1, foo2, foo3 },
[BAR] = { bar1, bar2, bar3 },
...
инициализация массива больше не является зависимым положения. При изменении значений НЕЧТО или ПАНЕЛИ инициализация массива будет автоматически соответствовать их новому значению.
анонимные структуры и массивы являются моей любимой. (cf. http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
или
void myFunction(type* values) {
while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});
это может даже привыкнуть к instanciate связанным спискам...
Утверждения времени компиляции, как уже обсужденный здесь .
//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
typedef struct { \
char static_assertion[condition ? 1 : -1]; \
} static_assertion_t
//--- ensure structure fits in
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);
Ну, я никогда не использовал его, и я не уверен, рекомендовал ли я когда-либо его кому-либо, но я чувствую, что этот вопрос был бы неполным без упоминания о Simon Tatham прием сопрограммы.
Присвоение структуры прохладно. Многие люди, кажется, не понимают, что структуры являются значениями также и могут быть присвоены вокруг, нет никакой потребности использовать memcpy()
, когда простое присвоение добивается цели.
, Например, рассмотрите некоторую мнимую 2D графическую библиотеку, она могла бы определить тип для представления (целочисленной) координаты экрана:
typedef struct {
int x;
int y;
} Point;
Теперь, Вы делаете вещи, которые могли бы выглядеть "неправильными", как запись функция, которая создает точку, инициализированную из аргументов функции, и возвращает его, как так:
Point point_new(int x, int y)
{
Point p;
p.x = x;
p.y = y;
return p;
}
Это безопасно, настолько долго (конечно), как возвращаемое значение копируется значением с помощью присвоения структуры:
Point origin;
origin = point_new(0, 0);
Таким образом можно записать довольно чистый и код объектно-ориентированного выхода, все в простом стандарте C.
Хорошо... Я думаю, что одна из сильных сторон языка C является своей мобильностью и стандартностью, поэтому каждый раз, когда я нахожу некоторый "скрытый прием", в реализации я в настоящее время использую, я пытаюсь не использовать его, потому что я пытаюсь сохранить свой код C максимально стандартным и портативным.
использование INT (3) для установки точки останова в коде является моим всем фаворитом времени
Странная векторная индексация:
int v[100]; int index = 10;
/* v[index] it's the same thing as index[v] */
Компиляторы C реализуют один из нескольких стандартов. Однако наличие стандарта не означает, что все аспекты языка определяются. устройство Вареного пудинга , например, является фаворитом 'скрытая' функция, которая стала столь популярной, что современные компиляторы имеют код распознавания особого назначения, чтобы гарантировать, чтобы методы оптимизации не ударяли желаемый эффект этого часто используемый шаблон.
В общих скрытых функциях или приемах языка препятствуются, поскольку Вы работаете на краю бритвы того, какой бы ни стандарт (стандарты) C Ваш компилятор использует. Много таких приемов не работают от одного компилятора до другого, и часто эти виды функций перестанут работать от одной версии пакета компилятора данным производителем к другой версии.
Различные приемы, которые повредили код C, включают:
Другие проблемы и проблемы, которые возникают каждый раз, когда программисты делают предположения о моделях выполнения, которые все определяются в большинстве стандартов C как 'зависимое поведение' компилятора.
При инициализации массивов или перечислений вы можете поставить запятую после последнего элемента в списке инициализатора. например:
int x[] = { 1, 2, 3, };
enum foo { bar, baz, boom, };
Это было сделано для того, чтобы при автоматической генерации кода вам не нужно было беспокоиться об удалении последней запятой.