Скрытые возможности C

Несмотря на то, что холст позволит вам установить высоту = 2147483647, когда вы начнете рисовать, ничего не произойдет

Рисование происходит только тогда, когда я возвращаю высоту до 32767

141
задан 6 revs, 5 users 50% 25 September 2017 в 20:52
поделиться

48 ответов

В Gcc (c) есть несколько интересных функций, которые вы можете включить, например, объявления вложенных функций и форму a?: B оператора?:, Который возвращает a, если a не ложно.

8
ответ дан 23 November 2019 в 23:06
поделиться

(скрытая) особенность, которая "шокировала" меня, когда я впервые увидел, касается printf. эта функция позволяет вам использовать переменные для форматирования самих спецификаторов формата. ищите код, вы увидите лучше:

#include <stdio.h>

int main() {
    int a = 3;
    float b = 6.412355;
    printf("%.*f\n",a,b);
    return 0;
}

символ * достигает этого эффекта.

24
ответ дан 23 November 2019 в 23:06
поделиться

C99 имеет отличную инициализацию структуры любого порядка.

struct foo{
  int x;
  int y;
  char* name;
};

void main(){
  struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}

28
ответ дан 23 November 2019 в 23:06
поделиться

Проверка допущений во время компиляции с использованием перечислений: Глупый пример, но он может быть действительно полезен для библиотек с константами, настраиваемыми во время компиляции.

#define D 1
#define DD 2

enum CompileTimeCheck
{
    MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
    MAKE_SURE_DD_IS_POW2    = 1/((((DD) - 1) & (DD)) == 0)
};
8
ответ дан 23 November 2019 в 23:06
поделиться

Конкатенация констант

Я был очень удивлен, не увидев этого уже в ответах, поскольку все известные мне компиляторы поддерживают его, но многие программисты, кажется, игнорируй это. Иногда это действительно удобно, и не только при написании макросов.

Пример использования в моем текущем коде: У меня есть #define PATH "/ some / path /" в файле конфигурации (на самом деле он задается файлом makefile). Теперь я хочу построить полный путь, включая имена файлов, для открытия ресурсов. Это просто:

fd = open(PATH "/file", flags);

Вместо ужасного, но очень распространенного:

char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);

Обратите внимание, что обычное ужасное решение:

  • в три раза длиннее
  • гораздо труднее читать
  • гораздо медленнее
  • менее эффективен при установке на произвольный предел размера буфера (но вам придется использовать еще более длинный код, чтобы избежать этого без конкатенации константных строк).
  • использовать больше места в стеке
16
ответ дан 23 November 2019 в 23:06
поделиться

Для очистки входного буфера нельзя использовать fflush (stdin) . Правильный способ выглядит следующим образом: scanf ("% * [^ \ n]% * c") Это приведет к удалению всего из входного буфера.

4
ответ дан 23 November 2019 в 23:06
поделиться

Допустим, у вас есть структура с членами одного типа:

struct Point {
    float x;
    float y;
    float z;
};

Вы можете преобразовать ее экземпляры в указатель с плавающей точкой и использовать индексы массива:

Point a;
int sum = 0, i = 0;
for( ; i < 3; i++)
    sum += ((float*)a)[i];

Довольно элементарно, но полезно при написании краткий код.

1
ответ дан 23 November 2019 в 23:06
поделиться

Вот три хороших примера в gcc:

__FILE__ 
__FUNCTION__
__LINE__
1
ответ дан 23 November 2019 в 23:06
поделиться

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

struct foo
{
  int a;
  int b;
  char b[1]; // using [0] is no longer correct
             // must come at end
};

char *str = "abcdef";
int len = strlen(str);
struct foo *bar = malloc(sizeof(foo) + len);

strcpy(bar.b, str); // try and stop me!
0
ответ дан 23 November 2019 в 23:06
поделиться

Оберните malloc и realloc следующим образом:

#ifdef _DEBUG
#define mmalloc(bytes)                  malloc(bytes);printf("malloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define mrealloc(pointer, bytes)        realloc(pointer, bytes);printf("realloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#else //_DEBUG
#define mmalloc(bytes)                  malloc(bytes)
#define mrealloc(pointer, bytes)        realloc(pointer, bytes)

Фактически, вот мой полный арсенол (BailIfNot для OO c):

#ifdef _DEBUG
#define mmalloc(bytes)                  malloc(bytes);printf("malloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define mrealloc(pointer, bytes)        realloc(pointer, bytes);printf("realloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define BAILIFNOT(Node, Check)  if(Node->type != Check) return 0;
#define NULLCHECK(var)          if(var == NULL) setError(__FILE__, __LINE__, "Null exception", " var ", FATAL);
#define ASSERT(n)               if( ! ( n ) ) { printf("<ASSERT FAILURE@%s:%d>", __FILE__, __LINE__); fflush(0); __asm("int $0x3"); }
#define TRACE(n)                printf("trace: %s <%s@%d>\n", n, __FILE__, __LINE__);fflush(0);
#else //_DEBUG
#define mmalloc(bytes)                  malloc(bytes)
#define mrealloc(pointer, bytes)        realloc(pointer, bytes)
#define BAILIFNOT(Node, Check)  {}
#define NULLCHECK(var)          {}
#define ASSERT(n)               {}
#define TRACE(n)                {}
#endif //_DEBUG

Вот пример вывода:

malloc: 12      <hash.c@298>
trace: nodeCreate <hash.c@302>
malloc: 5       <hash.c@308>
malloc: 16      <hash.c@316>
malloc: 256     <hash.c@320>
trace: dataLoadHead <hash.c@441>
malloc: 270     <hash.c@463>
malloc: 262144  <hash.c@467>
trace: dataLoadRecursive <hash.c@404>
0
ответ дан 23 November 2019 в 23:06
поделиться

Я только что прочитал эту статью . В нем есть некоторые «скрытые функции» C и некоторых других языков.

0
ответ дан 23 November 2019 в 23:06
поделиться

Объектно-ориентированные макросы C: Вам нужен конструктор (init), деструктор (dispose), равный (равный), копировальный (copy) и некоторый прототип для создания экземпляра (prototype).

С помощью объявления вам необходимо объявить постоянный прототип для копировать и выводить из. Затем вы можете сделать C_OO_NEW . Если нужно, я могу разместить больше примеров. LibPurple - это большая объектно-ориентированная база кода C с системой обратного вызова (если вы хотите увидеть ее в использовании)

#define C_copy(to, from) to->copy(to, from)

#define true 1
#define false 0
#define C_OO_PROTOTYPE(type)\
void type##_init (struct type##_struct *my);\
void type##_dispose (struct type##_struct *my);\
char type##_equal (struct type##_struct *my, struct type##_struct *yours); \
struct type##_struct * type##_copy (struct type##_struct *my, struct type##_struct *from); \
const type type##__prototype = {type##_init, type##_dispose, type##_equal, type##_copy

#define C_OO_OVERHEAD(type)\
        void (*init) (struct type##_struct *my);\
        void (*dispose) (struct type##_struct *my);\
        char (*equal) (struct type##_struct *my, struct type##_struct *yours); \
        struct type##_struct *(*copy) (struct type##_struct *my, struct type##_struct *from); 

#define C_OO_IN(ret, type, function, ...)       ret (* function ) (struct type##_struct *my, __VA_ARGS__);
#define C_OO_OUT(ret, type, function, ...)      ret type##_##function (struct type##_struct *my, __VA_ARGS__);

#define C_OO_PNEW(type, instance)\
        instance = ( type *) malloc(sizeof( type ));\
        memcpy(instance, & type##__prototype, sizeof( type ));

#define C_OO_NEW(type, instance)\
        type instance;\
        memcpy(&instance, & type ## __prototype, sizeof(type));

#define C_OO_DELETE(instance)\
        instance->dispose(instance);\
        free(instance);

#define C_OO_INIT(type)         void type##_init (struct type##_struct *my){return;}
#define C_OO_DISPOSE(type)      void type##_dispose (struct type##_struct *my){return;}
#define C_OO_EQUAL(type)        char type##_equal (struct type##_struct *my, struct type##_struct *yours){return 0;}
#define C_OO_COPY(type)         struct type##_struct * type##_copy (struct type##_struct *my, struct type##_struct *from){return 0;}
0
ответ дан 23 November 2019 в 23:06
поделиться

Мне нравится оператор typeof (). Он работает как sizeof () в том смысле, что разрешается во время компиляции. Вместо того, чтобы возвращать количество байтов, он возвращает тип.

0
ответ дан 23 November 2019 в 23:06
поделиться

Использовать NaN для связанных вычислений / возврата ошибки:

// # include
статический uint64_t iNaN = 0xFFF8000000000000;
константный двойной NaN = * (двойной *) & iNaN; // тихо NaN

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

примечание: проверка на NaN сложна, поскольку NaN! = NaN ... используйте isnan (x) или выберите свой собственный.
x! = x математически правильно, если x равно NaN, но имеет тенденцию быть оптимизированным некоторыми компиляторами

0
ответ дан 23 November 2019 в 23:06
поделиться

Я обнаружил это только после 15 с лишним лет программирования на C:

struct SomeStruct
{
   unsigned a : 5;
   unsigned b : 1;
   unsigned c : 7;
};

Битовые поля! Число после двоеточия - это количество битов, требуемых члену, с членами, упакованными в указанный тип, поэтому приведенное выше будет выглядеть следующим образом, если unsigned составляет 16 бит:

xxxc cccc ccba aaaa

Skizz

3
ответ дан 23 November 2019 в 23:06
поделиться

intptr_t для объявления переменных типа указатель. Специфичен для C99 и объявлен в stdint.h

2
ответ дан 23 November 2019 в 23:06
поделиться

Лямбды (например, анонимные функции) в GCC:

#define lambda(return_type, function_body) \
    ({ return_type fn function_body fn })

Это может использоваться как:

lambda (int, (int x, int y) { return x > y; })(1, 2)

Что раскрывается в:

({ int fn (int x, int y) { return x > y } fn; })(1, 2)
5
ответ дан 23 November 2019 в 23:06
поделиться

Мне нравятся __ LINE __ и __ FILE __ . См. Здесь: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

5
ответ дан 23 November 2019 в 23:06
поделиться