IT

가장 좋아하는 C 프로그래밍 트릭은 무엇입니까?

lottoking 2020. 6. 26. 07:49
반응형

가장 좋아하는 C 프로그래밍 트릭은 무엇입니까? [닫은]


예를 들어, 나는 최근 리눅스 커널에서 이것을 발견했다.

/ * 조건이 true 인 경우 컴파일 오류를 발생시킵니다 * /
#define BUILD_BUG_ON (condition) ((void) sizeof (char [1-2 * !! (condition)]))

따라서 코드에서 8 바이트 크기의 배수와 같은 일부 구조가있는 경우 하드웨어 제약으로 인해 다음을 수행 할 수 있습니다.

BUILD_BUG_ON (((sizeof (struct mystruct) % 8)! = 0);

struct mystruct의 크기가 8의 배수가 아닌 한 컴파일되지 않으며 8의 배수이면 런타임 코드가 전혀 생성되지 않습니다.

내가 아는 또 다른 트릭은 단일 헤더 파일이 한 모듈에서 변수를 선언하고 초기화하는 동안 해당 모듈을 사용하는 다른 모듈에서는 externs로 선언 할 수있는 "Graphics Gems"책에서 나온 것입니다.

#ifdef DEFINE_MYHEADER_GLOBALS
#define GLOBAL
INIT (x, y) 정의 (x) = (y)
#그밖에
#define GLOBAL extern
INIT 정의 (x, y)
#endif

GLOBAL int INIT (x, 0);
GLOBAL int somefunc (int a, int b);

이를 통해 x와 somefunc를 정의하는 코드는 다음을 수행합니다.

#DEFINE_MYHEADER_GLOBALS 정의
#include "the_above_header_file.h"

x와 somefunc ()를 사용하는 코드는 다음과 같습니다.

#include "the_above_header_file.h"

따라서 필요한 전역 및 함수 프로토 타입 인스턴스와 해당 extern 선언을 모두 선언하는 하나의 헤더 파일을 얻습니다.

그렇다면 그 라인에서 가장 좋아하는 C 프로그래밍 트릭은 무엇입니까?


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;
    }
}

내가 생각하지 못한 다른 멋진 기술이 많이 있다고 확신합니다.


Quake 2 소스 코드를 읽는 동안 나는 다음과 같은 것을 생각해 냈습니다.

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

(더 많거나 적은, 지금 확인하기 편리한 코드가 없습니다).

그 이후로, 전 처리기의 창조적 인 사용의 새로운 세계가 내 눈앞에서 열렸다. 더 이상 헤더 만 포함하지 않지만 이제는 전체 코드 덩어리를 재사용 할 수 있습니다.

존 카맥 감사합니다! xD


= {0};memset을 호출하지 않고도 구조를 초기화 하는 것을 좋아 합니다.

struct something X = {0};

이것은 구조체 (또는 배열)의 모든 멤버를 0으로 초기화합니다 (그러나 패딩 바이트는 아님)-0을 초기화 해야하는 경우 memset을 사용하십시오).

그러나 동적으로 할당 된 대규모 구조에는 이와 관련하여 몇 가지 문제 가 있음을 알고 있어야합니다 .


우리가 C 트릭에 대해 이야기하고 있다면, 내가 가장 좋아하는 것은 루프 언 롤링을위한 더프의 장치 여야합니다 ! 나는 실제로 분노에서 그것을 사용할 수있는 올바른 기회를 기다리고 있습니다 ...


사용 __FILE____LINE__디버깅

#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);

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};

일단 내 친구와 나는 다시 정의하기가 까다로운 스택 손상 버그를 발견했습니다.

다음과 같은 것 :

#define return DoSomeStackCheckStuff, return

나는 동적 크기의 객체를 갖는 "struct hack"을 좋아한다. 이 사이트에서도 설명 잘되어 있습니다 (스트럭처의 마지막 멤버로 "str []"를 쓸 수있는 C99 버전을 참조하십시오). 다음과 같이 문자열 "object"를 만들 수 있습니다.

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;

여기에서는 int (len의 크기)와 "hello world"의 길이, 1을 더한 힙에 X 유형의 구조를 할당했습니다 ( 1str 의 크기가 sizeof (X)에 포함되므로).

일반적으로 동일한 블록에서 일부 가변 길이 데이터 바로 앞에 "헤더"를 사용하려는 경우에 유용합니다.


클래스를 에뮬레이트하여 C를 사용한 객체 지향 코드

구조체와 해당 구조체에 대한 포인터를 첫 번째 매개 변수로 사용하는 함수 집합을 만들기 만하면됩니다.


대신에

printf("counter=%d\n",counter);

사용하다

#define print_dec(var)  printf("%s=%d\n",#var,var);
print_dec(counter);

어리석은 매크로 트릭을 사용하여 레코드 정의를보다 쉽게 ​​관리 할 수 ​​있습니다.

#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;

선언 된 것을 제외한 모든 모듈에서 읽기 전용 인 변수를 작성하려면 다음을 수행하십시오.

// 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

비트 시프트는 31의 시프트 량 (32 비트 정수)까지만 정의됩니다.

더 높은 쉬프트 값으로 작동해야하는 계산 된 쉬프트를 원한다면 어떻게해야합니까? Theora vide-codec의 작동 방식은 다음과 같습니다.

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;
}

유한 상태 머신을 구현하기위한 함수에 대한 포인터의 배열 선언.

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

가장 좋은 장점은 각 자극 / 상태가 모든 코드 경로를 확인하도록하는 것이 간단하다는 것입니다.

임베디드 시스템에서는 종종 ISR을 매핑하여 그러한 테이블을 가리키고 필요에 따라 ISR 외부로 벡터를 다시 만듭니다.


또 다른 좋은 전 처리기 "트릭"은 "#"문자를 사용하여 디버깅 표현식을 인쇄하는 것입니다. 예를 들면 다음과 같습니다.

#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]

나는 그것을 사용하지 않았기 때문에 실제로 그것을 좋아하는 트릭이라고 부르지 않을 것이지만, Duff 's Device에 대한 언급은 C에서 Coroutines를 구현하는 것에 대한 이 기사를 상기시켜주었습니다 . 그것은 항상 저를 방해합니다. 시간이 좀있어


#if TESTMODE == 1    
    debug=1;
    while(0);     // Get attention
#endif

while (0); 프로그램에는 영향을 미치지 않지만 컴파일러는 "이 작업은 수행하지 않습니다"라는 경고를 표시합니다. 문제가되는 행을보고주의를 기울여야 할 실제 이유를 확인할 수 있습니다.


나는 xor 핵의 팬입니다.

세 번째 임시 포인터없이 2 개의 포인터를 교체합니다.

int * a;
int * b;
a ^= b;
b ^= a;
a ^= b;

또는 하나의 포인터로 xor 연결 목록을 정말 좋아합니다. (http://en.wikipedia.org/wiki/XOR_linked_list)

링크 된 목록의 각 노드는 이전 노드의 Xor와 다음 노드입니다. 앞으로 이동하기 위해 노드의 주소는 다음과 같은 방식으로 발견됩니다.

LLNode * first = head;
LLNode * second = first.linked_nodes;
LLNode * third = second.linked_nodes ^ first;
LLNode * fourth = third.linked_nodes ^ second;

기타

또는 뒤로 이동하려면

LLNode * last = tail;
LLNode * second_to_last = last.linked_nodes;
LLNode * third_to_last = second_to_last.linked_nodes ^ last;
LLNode * fourth_to_last = third_to_last.linked_nodes ^ second_to_last;

기타

별로 유용하지는 않지만 (임의의 노드에서 순회를 시작할 수 없음) 매우 멋집니다.


이것은 '발로 몸을 쏠 수있는 충분한 밧줄'책에서 나온 것입니다.

헤더에서 선언

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

코드 장소 테스트 문장에서 예를 들면 다음과 같습니다.

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

do / while은 매크로의 내용이 여러 명령문으로 확장되는 경우 도움이됩니다.

컴파일러에 대한 '-D RELEASE'플래그가 사용되지 않은 경우에만 명령문이 인쇄됩니다.

그런 다음 예를 들어. 플래그를 makefile 등에 전달하십시오.

이것이 Windows에서 어떻게 작동하는지 확실하지 않지만 * nix에서는 잘 작동합니다.


Rusty는 실제로 ccan 에서 전체 빌드 조건 세트를 생성 했습니다. 빌드 assert 모듈을 확인하십시오.

#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;
}

실제 헤더에는 다른 유용한 매크로가 많이 있으며 제자리에 놓기가 쉽습니다.

나는 대부분 인라인 함수를 사용하여 어두운면 (및 전 처리기 남용)의 견인에 저항하기 위해 최선을 다하지만, 나는 당신이 묘사 한 것과 같은 영리하고 유용한 매크로를 즐깁니다.


이런 종류의 것들에 대한 두 가지 좋은 소스 북은 The Practice of Programming and Writing Solid Code 입니다. 그들 중 하나 (어떤 것을 기억하지 못합니까)는 말합니다 : enum은 컴파일러가 검사하기 때문에 가능한 곳을 #define보다 선호합니다.


C에만 국한된 것은 아니지만 항상 XOR 연산자를 좋아했습니다. 그것이 할 수있는 멋진 일은 "임시 값없이 교체"입니다.

int a = 1;
int b = 2;

printf("a = %d, b = %d\n", a, b);

a ^= b;
b ^= a;
a ^= b;

printf("a = %d, b = %d\n", a, b);

결과:

a = 1, b = 2

a = 2, b = 1


"C의 숨겨진 기능" 질문을 참조하십시오 .


container_of예를 들어 목록에서 사용되는 개념이 마음 에 듭니다. 기본적으로 목록에 포함될 각 구조에 대해 nextlast필드 를 지정할 필요는 없습니다 . 대신 목록 구조 헤더를 실제 연결된 항목에 추가합니다.

include/linux/list.h실제 사례를 살펴보십시오 .


userdata 포인터를 사용하는 것이 매우 깔끔 하다고 생각합니다 . 요즘 유행하는 패션. C 기능은 아니지만 C에서 사용하기 쉽습니다.


사전 컴파일러가 코드를 생성 하도록 X-Macros사용 합니다. 한 곳에서 오류 값과 관련 오류 문자열을 정의하는 데 특히 유용하지만 그 이상을 넘어 설 수 있습니다.


우리의 코드베이스는

#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
}

which allows for the tracking of memory leaks in debug mode. I always thought this was cool.


Fun with macros:

#define SOME_ENUMS(F) \
    F(ZERO, zero) \
    F(ONE, one) \
    F(TWO, two)

/* Now define the constant values.  See how succinct this is. */

enum Constants {
#define DEFINE_ENUM(A, B) A,
    SOME_ENUMS(DEFINE_ENUMS)
#undef DEFINE_ENUM
};

/* Now a function to return the name of an enum: */

const char *ToString(int c) {
    switch (c) {
    default: return NULL; /* Or whatever. */
#define CASE_MACRO(A, B) case A: return #b;
     SOME_ENUMS(CASE_MACRO)
#undef CASE_MACRO
     }
}

Here is an example how to make C code completly unaware about what is actually used of HW for running the app. The main.c does the setup and then the free layer can be implemented on any compiler/arch. I think it is quite neat for abstracting C code a bit, so it does not get to be to spesific.

Adding a complete compilable example here.

/* 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;
}

if(---------)  
printf("hello");  
else   
printf("hi");

Fill in the blanks so that neither hello nor hi would appear in output.
ans: fclose(stdout)

참고URL : https://stackoverflow.com/questions/599365/what-is-your-favorite-c-programming-trick

반응형