Каков Ваш любимый C, программирующий прием? [закрытый]

134
задан 4 revs, 4 users 100% 25 September 2017 в 20:53
поделиться

26 ответов

C99 предлагает некоторый действительно интересный материал с помощью анонимных массивов:

Удаляющие бессмысленные переменные

{
    int yes=1;
    setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
}

становятся

setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));

Передача Переменной Суммы Аргументов

void func(type* values) {
    while(*values) {
        x = *values++;
        /* do whatever with x */
    }
}

func((type[]){val1,val2,val3,val4,0});

Статические связанные списки

int main() {
    struct llist { int a; struct llist* next;};
    #define cons(x,y) (struct llist[]){{x,y}}
    struct llist *list=cons(1, cons(2, cons(3, cons(4, NULL))));
    struct llist *p = list;
    while(p != 0) {
        printf("%d\n", p->a);
        p = p->next;
    }
}

Любой, который я уверен много других прохладных методов, о которых я не думал.

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

Я думаю использование userdata , указатели круты. Вид, теряющий позиции в наше время. Это не так функция C, но довольно просто в использовании в C.

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

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

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

Наша кодовая база имеет прием, подобный

#ifdef DEBUG

#define my_malloc(amt) my_malloc_debug(amt, __FILE__, __LINE__)
void * my_malloc_debug(int amt, char* file, int line)
#else
void * my_malloc(int amt)
#endif
{
    //remember file and line no. for this malloc in debug mode
}

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

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

Вот пример, как заставить C кодировать completly не зная о том, что на самом деле используется HW для того, чтобы запустить приложение. main.c делает установку, и затем бесплатный слой может быть реализован на любом компиляторе/дуге. Я думаю, что это довольно аккуратно для абстракции C, кодируют немного, таким образом, это не добирается, чтобы быть к определенному.

Добавление полного компилируемого примера здесь.

/* free.h */
#ifndef _FREE_H_
#define _FREE_H_
#include <stdio.h>
#include <string.h>
typedef unsigned char ubyte;

typedef void (*F_ParameterlessFunction)() ;
typedef void (*F_CommandFunction)(ubyte byte) ;

void F_SetupLowerLayer (
F_ParameterlessFunction initRequest,
F_CommandFunction sending_command,
F_CommandFunction *receiving_command);
#endif

/* free.c */
static F_ParameterlessFunction Init_Lower_Layer = NULL;
static F_CommandFunction Send_Command = NULL;
static ubyte init = 0;
void recieve_value(ubyte my_input)
{
    if(init == 0)
    {
        Init_Lower_Layer();
        init = 1;
    }
    printf("Receiving 0x%02x\n",my_input);
    Send_Command(++my_input);
}

void F_SetupLowerLayer (
    F_ParameterlessFunction initRequest,
    F_CommandFunction sending_command,
    F_CommandFunction *receiving_command)
{
    Init_Lower_Layer = initRequest;
    Send_Command = sending_command;
    *receiving_command = &recieve_value;
}

/* main.c */
int my_hw_do_init()
{
    printf("Doing HW init\n");
    return 0;
}
int my_hw_do_sending(ubyte send_this)
{
    printf("doing HW sending 0x%02x\n",send_this);
    return 0;
}
F_CommandFunction my_hw_send_to_read = NULL;

int main (void)
{
    ubyte rx = 0x40;
    F_SetupLowerLayer(my_hw_do_init,my_hw_do_sending,&my_hw_send_to_read);

    my_hw_send_to_read(rx);
    getchar();
    return 0;
}
0
ответ дан 23 November 2019 в 23:48
поделиться

Две хороших исходных книги для этого вида материала Практика Программирования и , Пишущего Твердый Код . Один из них (я не помню, который), говорит: Предпочтите перечисление #define, где Вы можете, потому что перечисление проверяется компилятор.

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

Ржавый на самом деле произвел полный набор условных выражений сборки в ccan, проверьте сборку, утверждают модуль:

#include <stddef.h>
#include <ccan/build_assert/build_assert.h>

struct foo {
        char string[5];
        int x;
};

char *foo_string(struct foo *foo)
{
        // This trick requires that the string be first in the structure
        BUILD_ASSERT(offsetof(struct foo, string) == 0);
        return (char *)foo;
}

существует много других полезных макросов в фактическом заголовке, которое легко заскочить в место.

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

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

Этот происходит из книги флаг 'Enough rope to shoot yourself in the foot ':

In the header declare

#ifndef RELEASE
#  define D(x) do { x; } while (0)
#else
#  define D(x)
#endif

In your code place testing statements eg:

D(printf("Test statement\n"));

The do/while helps in case the contents of the macro expand to multiple statements.

The statement will only be printed if '-d RELEASE' для компилятора, не используется.

Вы можете тогда, например, передать флаг своему make-файлу и т.д.

Не уверенный, как это работает в окнах, но в *отклоняют его работы хорошо

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

Если мы говорим о приемах c, мой фаворит должен быть Устройство Вареного пудинга для развертывания цикла! Я просто ожидаю правильной возможности прийти, чтобы я на самом деле использовал ее в гневе...

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

Я люблю использование = {0}; для инициализации структур, не будучи должен назвать memset.

struct something X = {0};

Это инициализирует всех членов структуры (или массив) для обнуления (но не любые дополнительные байты - используют memset, если необходимо обнулить их также).

, Но необходимо знать, что существует некоторые проблемы с этим для больших, динамично выделенных структур .

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

В C99

typedef struct{
    int value;
    int otherValue;
} s;

s test = {.value = 15, .otherValue = 16};

/* or */
int a[100] = {1,2,[50]=3,4,5,[23]=6,7};
31
ответ дан 23 November 2019 в 23:48
поделиться

использование __FILE__ и __LINE__ для отладки

#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
42
ответ дан 23 November 2019 в 23:48
поделиться

Объектно-ориентированный код с C, путем эмуляции классов.

Просто создают структуру и ряд функций, которые берут указатель на ту структуру как первый параметр.

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

Как только мой помощник и я переопределили возврат для нахождения хитрой ошибки повреждения стека.

Что-то как:

#define return DoSomeStackCheckStuff, return
28
ответ дан 23 November 2019 в 23:48
поделиться

Мне нравится "взлом структуры" за то, что он имел динамично размерный объект. Этот сайт объясняет это вполне прилично также (хотя они обращаются к версии C99, где можно записать "ул. []" как последний член структуры). Вы могли сделать строку "объектом" как это:

struct X {
    int len;
    char str[1];
};

int n = strlen("hello world");
struct X *string = malloc(sizeof(struct X) + n);
strcpy(string->str, "hello world");
string->len = n;

здесь, мы выделили структуру типа X на "куче", которая является размером интервала (для len), плюс длина "привет мира", плюс 1 (так как ул. 1 включена в sizeof (X).

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

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

Используя глупый макро-прием для создания рекордных определений легче поддержать.

#define COLUMNS(S,E) [(E) - (S) + 1]

typedef struct
{
    char studentNumber COLUMNS( 1,  9);
    char firstName     COLUMNS(10, 30);
    char lastName      COLUMNS(31, 51);

} StudentRecord;
14
ответ дан 23 November 2019 в 23:48
поделиться

Сдвиги разряда только определяются до shift-amount 31 (на целом числе на 32 бита)..

, Что Вы делаете, если Вы хотите иметь вычисленный сдвиг, который должен работать с более высоким shift-values также? Вот то, как смотри-кодек Theora делает это:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  return (a>>(v>>1))>>((v+1)>>1);
}

Или намного более читаемый:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  unsigned int halfshift = v>>1;
  unsigned int otherhalf = (v+1)>>1;

  return (a >> halfshift) >> otherhalf; 
}

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

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  if (v<=31)
    return a>>v;
  else
    return 0;
}
8
ответ дан 23 November 2019 в 23:48
поделиться

Для создания переменной, которая только для чтения во всех модулях кроме того, в котором оно объявляется:

// Header1.h:

#ifndef SOURCE1_C
   extern const int MyVar;
#endif
<час>
// Source1.c:

#define SOURCE1_C
#include Header1.h // MyVar isn't seen in the header

int MyVar; // Declared in this file, and is writeable
<час>
// Source2.c

#include Header1.h // MyVar is seen as a constant, declared elsewhere
11
ответ дан 23 November 2019 в 23:48
поделиться

Объявление массива указателя на функции для реализации конечных автоматов.

int (* fsm[])(void) = { ... }

самое приятное преимущество состоит в том, что просто вынудить каждый стимул/состояние проверить все пути выполнения кода.

Во встроенной системе, я буду часто отображать ISR, чтобы указать на такую таблицу и повторно векторизовать ее по мере необходимости (вне ISR).

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

Другой хороший препроцессор "прием" должен использовать "#" символ для печати выражений отладки. Например:

#define MY_ASSERT(cond) \
  do { \
    if( !(cond) ) { \
      printf("MY_ASSERT(%s) failed\n", #cond); \
      exit(-1); \
    } \
  } while( 0 )

редактирование: код ниже только работает над C++. Благодаря smcameron и Evan Teran.

Да, время компиляции утверждает, является всегда большим. Это может также быть записано как:

#define COMPILE_ASSERT(cond)\
     typedef char __compile_time_assert[ (cond) ? 0 : -1]
7
ответ дан 23 November 2019 в 23:48
поделиться

Я действительно не назвал бы это любимым приемом, так как я никогда не использовал его, но упоминание об Устройстве Вареного пудинга напомнило мне о эта статья о реализации Сопрограмм в C. Это всегда дает мне хихиканье, но я уверен, что это могло быть полезно некоторое время.

6
ответ дан 23 November 2019 в 23:48
поделиться
#if TESTMODE == 1    
    debug=1;
    while(0);     // Get attention
#endif

, в то время как (0); не имеет никакого эффекта на программу, но компилятор выйдет, предупреждение об "этом не делает ничего", которого является достаточно, чтобы заставить меня пойти посмотреть на незаконную строку и затем видеть настоящие основания, я хотел привлечь внимание к нему.

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

Мне нравится концепция container_of используется, например, в списках. По сути, вам не нужно указывать поля следующее и последнее для каждой структуры, которая будет в списке. Вместо этого вы добавляете заголовок структуры списка к фактическим связанным элементам.

Посмотрите на include / linux / list.h реальные примеры.

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

Читая исходный код Quake 2, я придумал что-то вроде этого:

double normals[][] = {
  #include "normals.txt"
};

(более или менее, у меня нет кода, чтобы проверить это сейчас

С тех пор перед моими глазами открылся новый мир творческого использования препроцессора. Я больше не включаю только заголовки, а время от времени включаю целые фрагменты кода (это значительно улучшает возможность повторного использования) :-p

Спасибо, Джон Кармак! xD

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

Я не знаю, является ли это прием. Но когда я был юниором в университете мой друг, и я завершал лабораторию в нашем вводном курсе C++. Мы должны были взять имя человека и использовать для своей выгоды его, отобразить его назад и затем дать им опцию отображения их имени "в последний раз, сначала". Для этой лаборатории нам мешали использовать нотацию массива.

Он показал мне этот код, я думал, что это была самая прохладная вещь, которую я видел в то время.

char * ptr = "first name";

//flies to the end of the array, regardless of length
while( *ptr++ );
-1
ответ дан 23 November 2019 в 23:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: