Programing

너가 가장 좋아하는 C프로그래밍 트릭은 뭐니?

c10106 2022. 4. 24. 09:54
반응형

너가 가장 좋아하는 C프로그래밍 트릭은 뭐니?

예를 들어, 나는 최근에 리눅스 커널에서 다음과 같은 사실을 알게 되었다.

/* 조건이 참일 경우 컴파일 오류 강제 */#define BUG_BUG_ON(조건) ((void)sizeof(char[1 - 2**!!!!(조건))))))

코드에서 크기가 8바이트의 배수여야 하는 구조를 가지고 있다면 하드웨어 제약 때문에 다음을 수행할 수 있다.

BULD_BUG_ON(구조물 신비화) % 8!= 0);

그리고 구조체 신비구조의 크기가 8의 배수여야 컴파일할 수 있고, 8의 배수일 경우 런타임 코드는 전혀 생성되지 않는다.

내가 아는 또 다른 요령은 "그래픽 젬스"라는 책에서 나온 것인데, 이 요령은 헤더 파일 하나가 한 모듈에서 변수를 선언하고 초기화하는 동시에 다른 모듈에서는 변수를 외부로 선언할 수 있게 한다.

#ifdefine_MYHEADER_GLOBALS#글로벌 정의#정의 INIT(x, y) (x) = (y)#else#GLOBAL 외부 정의#정의 INIT(x, y)#endif
GLOBAL INIT(x, 0);GLOBAL int in some func(a, int b);

이를 통해 x와 some func를 정의하는 코드는 다음과 같이 한다.

#define Define_MYHEADER_GLOBALS#property "the_ward_file.h"

x와 funcuries만 사용하는 코드는 다음과 같다.

#property "the_ward_file.h"

따라서 글로발과 기능 프로토타입의 인스턴스(instance)를 필요한 위치에 선언하는 헤더 파일 한 개와 해당 외부 선언문(external)을 얻을 수 있다.

그렇다면, 그 대사들을 따라 당신이 가장 좋아하는 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;
    }
}

내가 생각해보지 못한 다른 멋진 기술들이 많을 거야.

퀘이크 2 소스 코드를 읽는 동안 나는 다음과 같은 것을 생각해냈다.

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

(약간, 나는 지금 그것을 확인할 수 있는 코드를 가지고 있지 않다.)

그때부터 전처기의 창조적 이용의 새로운 세계가 눈앞에 펼쳐졌다.더 이상 머리글만 포함하지 않고 때때로 코드 전체 청크(재사용성을 많이 향상) :-p

고마워 존 카맥! xD

나는 사용하는 것을 좋아한다.= {0};Memset을 호출할 필요 없이 구조물을 초기화한다.

struct something X = {0};

그러면 구조체(또는 배열)의 모든 구성원이 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

나는 역동적인 크기의 물체를 가진 "구조 해킹"을 좋아한다.이 사이트에서도 잘 설명하지만(구조체의 마지막 멤버로 "str[]"를 쓸 수 있는 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 유형의 구조를 int(렌용) 크기의 힙에 "헬로 월드"의 길이와 1(str1이 (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비트 정수)까지만 정의된다.

더 높은 교대조 값도 적용해야 하는 계산된 교대조정을 원하면 어떻게 하시겠습니까?테오라 비디오 코덱은 다음과 같이 처리한다.

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]

나는 그것을 사용해 본 적이 없기 때문에 그것을 별로 좋아하는 속임수라고 하지는 않겠지만, 더프스 디바이스에 대한 언급은 C에서 코루틴을 구현하는 것에 관한 이 기사를 떠올리게 했다.그것은 항상 나를 쿡쿡 웃게 하지만 언젠가 유용하게 쓰일 수 있을 거라고.

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

한편(0)은 프로그램에 아무런 영향을 미치지 않지만, 컴파일러는 "이것은 아무것도 하지 않는다"는 경고를 할 것이다. 이것은 내가 불쾌감을 주는 선을 보고 나서 내가 주의를 환기시키고자 했던 진짜 이유를 보게 하기에 충분하다.

나는 해킹을 좋아한다.

세 번째 온도 포인터를 사용하지 않고 두 개의 포인터를 스왑하십시오.

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"));

매크로의 내용이 복수의 문장으로 확장되는 경우에 대비하는 데 도움이 된다.

이 문장은 컴파일러용 '-D RELEASE' 플래그를 사용하지 않는 경우에만 인쇄된다.

그런 다음, 당신의 makefile 등에 깃발을 전달할 수 있다.

이 기능이 창에서 어떻게 작동하는지 확실하지 않지만 *nix에서는 잘 작동함

러스티는 실제로 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;
}

실제 헤더에는 다른 많은 유용한 매크로가 있는데, 이 매크로들은 제자리에 놓이기 쉽다.

나는 대부분의 인라인 기능을 고수함으로써 어두운 면(그리고 전처리기 남용)의 당김에 저항하기 위해 전력을 다해 노력하지만, 나는 당신이 설명한 것과 같이 영리하고 유용한 매크로를 즐긴다.

이런 종류의 것들을 위한 두 가지 좋은 출처 책은 "프로그래밍과 쓰기 솔리드 코드 연습"이다.그들 중 한 명(어느 것이 기억나지 않는다)은 다음과 같이 말한다: 열거형이 컴파일러에 의해 확인되기 때문에 가능한 곳에서 #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의 숨겨진 특징" 질문을 참조하십시오.

나는 리스트에서 예를 들어 사용되는 개념을 좋아한다.기본적으로 지정하지 않아도 됨next그리고last목록에 포함될 각 구조물의 필드대신 목록 구조 헤더를 실제 연결된 항목에 추가하십시오.

에 대해 보다include/linux/list.h실례를 들어

나는 사용자 데이터 포인터의 사용이 꽤 깔끔하다고 생각한다.요즘 유행에 뒤떨어진다.그것은 그렇게 많은 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
}

디버그 모드에서 메모리 누출을 추적할 수 있다.난 항상 이게 멋지다고 생각했어.

매크로로 재미:

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

다음은 C 코드가 앱 실행에 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;
}
if(---------)  
printf("hello");  
else   
printf("hi");

출력에서 hello나 hi가 나타나지 않도록 빈칸을 채우십시오.
ans:fclose(stdout)

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

반응형