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


134

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

/ * 조건이 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의 배수이면 런타임 코드가 전혀 생성되지 않습니다.

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

#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 프로그래밍 트릭은 무엇입니까?


9
이것은 C 전 처리기 트릭처럼 보입니다.
jmucchiello

BUILD_BUG_ON사용하여 매크로, what's 잘못된 #error내부를 #if?
Ricardo

답변:


80

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
나는 &(int){1}당신의 의도가 무엇인지 약간 더 명확하게하고 싶다면 첫 번째 예제를로도 쓸 수 있다고 생각합니다 .
릴리 발라드

67

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

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

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

그 이후로, 전 처리기의 창조적 인 사용의 새로운 세계가 내 눈앞에서 열렸다. 더 이상 헤더 만 포함하지 않지만 이제는 코드 전체를 사용하고 재사용 성을 크게 향상시킵니다. :-p

존 카맥 감사합니다! xD


13
지진 소스에 있던 빠른 역 sqrt를 언급하지 않고 최적화 스레드에서 carmack을 말할 수는 없습니다. en.wikipedia.org/wiki/Fast_inverse_square_root
pg1989

처음부터 0x5f3759df를 어디서 얻었습니까?
RSH1

2
@RoryHarvey : 그것을 찾을 때 찾을 수있는 것으로부터, 그것은 순전히 경험적인 것 같습니다. 일부 연구 (내가 본 곳을 기억하지 못함)는 그것이 최적에 가깝지만 완전히 최적은 아니라는 것을 보여주었습니다. 마찬가지로 64 비트의 경우 컴퓨팅이 아닌 가치가 발견 된 것으로 보입니다.
Matthieu M.

50

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

struct something X = {0};

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

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


그런데 전역 변수에는 필요하지 않습니다.
strager

5
정적 변수에는 필요하지 않습니다 . 전역 변수는 0이 될 수 있지만 필수 사항은 아닙니다.
Jamie

4
나는 때때로 이것을 확장하기 위해 : const struct something zero_something = { 0 };그리고 struct something X = zero_something;'X = zero_something;'을 사용할 수있는 루틴을 통해 또는 부분적으로 변수를 재설정 할 수 있습니다. 유일한 반대는 어딘가에서 데이터를 읽는 것입니다. 요즘 'memset ()'이 더 빠를 수도 있지만 할당의 명확성을 좋아합니다. 이니셜 라이저에서 0 이외의 값을 사용하는 것도 가능합니다 (memset () 다음에 개별 멤버에 대한 조정 간단한 사본보다 속도가 느릴 수 있습니다).
Jonathan Leffler

45

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


4
나는 그것을 측정 가능한 성능 향상을 위해 한 번 사용했지만 요즘에는 많은 하드웨어에서 유용하지 않습니다. 항상 프로필!
Dan Olson

6
네, 더프의 장치가 만들어진 문맥을 이해하지 못하는 사람들은 "코드 가독성"은 코드가 충분히 빠르지 않으면 쓸모가 없습니다. 아마도 당신을 억압 한 사람들 중 누구도 어려운 실시간 코딩을 한 적이 없었을 것입니다.
Rob K

1
+1, 실제로 Duff의 장치를 몇 번 사용해야했습니다. 처음에는 기본적으로 물건을 복사하고 도중에 약간의 변형을 한 루프였습니다. 해당 아키텍처의 간단한 memcpy ()보다 훨씬 빠릅니다.
Makis

3
분노는 귀하의 코드를 유지해야하는 동료 및 후임자에게 있습니다.
Jonathan Leffler

1
내가 말했듯이, 나는 여전히 올바른 기회를 기다리고 있지만 아직 아무도 나를 화나게하지 않았다. 나는 약 25 년 동안 C를 작성해 왔으며, 나는 90 년대 초반에 더프의 장치를 처음 접했으며 아직 그것을 사용할 필요가 없었습니다. 다른 사람들이 언급했듯이 컴파일러가 이러한 종류의 최적화에서 더 나아질수록 이러한 종류의 트릭은 점점 유용하지 않습니다.
Jackson

42

사용 __FILE____LINE__디버깅

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

6
일부 컴파일러에서는 FUNCTION 도 얻 습니다.
JBR 윌킨슨

11
__FUNCTION__의 별칭 __func__일 뿐이며 __func__c99에 있습니다. 아주 편리합니다. __PRETTY_FUNCTION__C (GCC)는에 대한 또 다른 별칭 __func__이지만 C ++에서는 전체 함수 서명을 얻을 수 있습니다.
sklnd

FILE 은 파일 이름의 전체 경로를 보여주기 때문에 basename ( FILE )을 사용합니다
Jeegar Patel

31

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

28

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

다음과 같은 것 :

#define return DoSomeStackCheckStuff, return

4
잘만되면 그것은 함수 본문에서 # define'd이고 마지막에 # undefine'd였습니다!
strager

내 마음에 오는 첫 번째 것은 DoSomeStackCheckStuff가 버그로 인해 메모리를 망쳐 놓고 코드를 읽는 사람은 반환의 재정의를 인식하지 못하고 / hell /이 무슨 일인지 궁금해한다는 것입니다.
Giligan

8
@strager 그러나 기본적으로는 쓸모가 없습니다. 요점은 모든 함수 호출에 약간의 추적을 추가하는 것 입니다. 그렇지 않으면 DoSomeStackCheckStuff추적하려는 함수를 호출하기 만하면 됩니다.
Clueless

1
@gilligan 나는 이것이 당신이 항상 활성화 된 것의 유형이라고 생각하지 않습니다. 원샷 디버깅 작업에는 매우 편리합니다.
sunetos

정말 작동합니까? :) 내가 쓴 것 같아 #define return if((DoSomeStackCheckStuff) && 0) ; else return... 미친 것 같아!
Paolo Bonzini

22

나는 동적 크기의 객체를 갖는 "struct hack"을 좋아한다. 이 사이트에서도 설명 잘되어 있습니다 (스트럭처의 마지막 멤버로 "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;

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

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


개인적으로 길이를 찾아야 할 때마다 malloc ()과 realloc ()을 직접 사용하고 strlen ()을 사용하는 것이 더 쉽다는 것을 알지만 문자열의 길이를 모르는 많은 프로그램이 필요할 경우 아마도 이것은 아마도 더 좋은 길일 것입니다.
Chris Lutz

4
"..."str [] ""를 쓸 수있는 C99 버전 저는 str [0]과 같은 컨텍스트에서 0 크기의 배열을 보았습니다. 상당히 자주. 나는 그것이 C99라고 생각합니다. 그래도 구형 컴파일러는 크기가 0 인 배열에 대해 불평합니다.
smcameron

3
나는 이것도 좋아하지만 malloc (offsetof (X, str) + numbytes) 과 같은 것을 사용해야합니다. 그렇지 않으면 패딩 및 정렬 문제로 인해 문제가 발생합니다. 예를 들어 sizeof (struct X)는 5가 아닌 8 일 수 있습니다.
Fozi

3
@ Fozi : 실제로 그것이 문제가 될 것이라고 생각하지 않습니다. 이 버전에는 str[1]( str[]1) str의 1 바이트가 포함되어 있기 때문에 sizeof(struct X). 와 사이의 모든 패딩 이 포함됩니다 . lenstr
Evan Teran

2
@Rusky : 그게 어떤 부정적인 영향을 미칠까요? 이후에 "패딩"이 있다고 가정합니다 str. 내가 할당하면 OK, sizeof(struct X) + 10그럼이 만들어 str효율적으로 10 - sizeof(int)큰 (우리는 패딩이 말했다 때문에, 더 많거나). 이것은 오버레이 str 와 그 이후의 패딩입니다. 차이 가있을 있는 유일한 방법은 str어쨌든 모든 일을 중단 한 멤버가 있다면 유연한 멤버가 마지막이어야합니다. 끝에있는 패딩은 너무 많이 할당 될 수 있습니다. 실제로 잘못 될 수있는 방법에 대한 구체적인 예를 제공해주십시오.
Evan Teran

17

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

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


2
cfront와 같은 C ++을 C로 변환하는 것이 여전히 있습니까?
MarkJ

11
이것은 거의 객체 지향이 아닙니다. 상속이있는 OO의 경우, "subclasses"에 의해 오버로드 될 수있는 일종의 가상 함수 테이블을 객체 구조체에 추가해야합니다. 이 목적을 위해 반 베이킹 된 "클래스가있는 C"스타일의 프레임 워크가 많이 있지만 그 자리를 벗어나는 것이 좋습니다.
exDM69

말할 필요가 있었다. +1입니다.
Amit S

3
@ exDM69에서 객체 지향은 코딩 패러다임처럼 문제에 대해 생각하는 방식입니다. 상속없이 성공적으로 할 수 있습니다. C ++로 전체 보어를 뛰어 넘기 전에 몇 가지 프로젝트 에서이 작업을 수행했습니다.
Mark Ransom

16

대신에

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

사용하다

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

14

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

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

11

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

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

위험 해 일치하지 않는 선언 및 정의입니다. 컴파일하는 동안 Source2.c컴파일러는 MyVar에 대한 함수 호출에서도 변경되지 않는다고 가정 할 수 있습니다 Source1.c. (이것은 실제 const 변수로서 const에 대한 포인터와 다릅니다. 후자의 경우, 지정된 객체는 여전히 다른 포인터를 통해 수정 될 수 있습니다.)
jilles

1
일부 컴파일 단위에서만 읽기 전용 인 변수는 생성하지 않습니다. 이는 정의되지 않은 동작을 생성합니다 (ISO 9899 6.2.7.2 페이지 및 6.7.3.5 페이지 참조).
Ales Hakl

8

비트 시프트는 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;
}

... 그리고 gcc는 실제로 그것을 인라인합니다 :) +1
Tim Post

2
내 컴퓨터에서 gcc-4.3.2는 cmov 명령을 사용하여 두 번째 분기에서 분기를 제거합니다 (조건부 이동)
Adam Rosenfield

3
"분기를 사용하는 것보다 훨씬 빠르다": 차이점은의 모든 값에 대해 분기가 올바른 v반면, halfshift트릭은 허용 범위를 32 비트 아키텍처에서 63으로, 64 비트 아키텍처에서 127을 두 배로 늘립니다.
Pascal Cuoq

8

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

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

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

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


내가 좋아하는 한 가지 기술은 초기화가 필요한 함수가있는 경우 초기화 루틴을 호출하여 포인터를 초기화하는 것입니다. 그것이 실행될 때, 마지막으로하는 일은 포인터를 실제 함수에 대한 포인터로 바꾸고 그 함수를 호출하는 것입니다. 이렇게하면 함수가 처음 호출 될 때 이니셜 라이저가 자동으로 호출되고 실제 함수는 매번 호출됩니다.
TMN

7

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

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

COMPILE_ASSERT 매크로는 typedef로 네임 스페이스를 오염시키고 두 번째 사용법으로 인해 두 번 사용할 수 없습니다. 오류 : typedef '__compile_time_assert'의 재정의
smcameron

실제로 이것을 시도 했습니까? 당신은 "typedef foo;" 당신이 원하는대로. 그것이 당신이 사전 선언을하는 방법입니다. 나는 2.5 년 동안 gcc, VC 및 임베디드 환경 용 컴파일러 모두에서 2.5 년 동안 사용해 왔으며 결코 어려움을 겪지 않았습니다.
Gilad Naor

나는 C 전처리 ... :( 싫어
하센

1
예, 시도했습니다. 컴파일러의 오류 메시지 인 gcc를 잘라 붙여 넣었습니다.
smcameron

1
@Gilad : c ++에서는 중복 typedef를 갖는 것이 합법적이지만 C ++에서는 합법적이지 않습니다.
Evan Teran

6

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


실제로 실제로이 기술을 사용하여 코드를 의존적 인 비동기 I / O 시퀀스를 모호하게 읽을 수있게 만들었습니다. 가장 큰 차이점은 static변수에 코 루틴 상태를 저장하지 않고 구조체를 동적으로 할당하고 그 포인터를 코 루틴 함수에 전달한다는 것입니다. 많은 매크로가 이것을 더 맛있게 만듭니다. 좋지는 않지만 비동기식 / 콜백 버전보다 뛰어납니다. swapcontext()그래도 가능하다면 (* nixes 를 통해) 녹색 스레드를 사용 합니다.
pmdj

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

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


9
#warning을 대신 사용할 수 없습니까?
Stefano Borini

분명히, 나는 할 수 있었다. 그것은 완전히 표준은 아니지만 내가 사용하는 컴파일러에서 작동했습니다. 흥미롭게도 임베디드 컴파일러는 #define을 번역했지만 gcc는 그렇지 않았습니다.
gbarry 2009

6

나는 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;

기타

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


5

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

헤더에서 선언

#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에서는 잘 작동합니다.


RELEASE가 정의 된 경우 D (x)를 {}으로 확장하여 if 문과 잘 어울릴 수 있습니다. 그렇지 않으면 "if (a) D (x);" RELEASE를 정의하면 "if (a)"로 확장됩니다. 그것은 당신에게 RELEASE 버전의 멋진 버그를 줄 것입니다
MarkJ

3
@MarkJ : 아니요. "if (a) D (x);" "if (a);"로 확장 완벽하게 괜찮습니다. D (x)가 {}로 확장 된 경우 "if (a) if (b) D (x); else foo ();" "if (a) if (b) {}; else foo ();"로 잘못 확장되어 "else foo ()"가 첫 번째 if 대신 if와 일치합니다.
Adam Rosenfield

솔직히 말해서 나는 주로 인쇄 문장을 테스트하기 위해이 매크로를 사용하거나 조건문이있는 경우 모든 것을 포함합니다. D (if (a) foo (););
Simon Walker

1
@AdamRosenfield : #define D(x) do { } while(0)대신 사용하면 해당 사례를 처리 할 수 ​​있으며 x일관성을 위해 삽입하는 브랜치에도 적용될 수 있습니다.
rpetrich

3

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

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

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


예, 최근에 ccan을 발견했고 코드를 기고하는 것을 고려하고 있었지만 아직 "ccan way"를 감추지 않았습니다. 링크에 감사드립니다 .ccan을 보려는 더 많은 동기 부여가 있기를 바랍니다.
smcameron

글쎄, 나는 ccan-lint가 GSOC 프로젝트로 제안 될 때까지 'ccan way'에 너무 걱정하지 않을 것입니다. 작고 친근한 그룹 .. 스 니펫을 버릴 수있는 좋은 장소 :)
Tim Post

BTW, Rusty의 BuILD_ASSERT는 리눅스 커널의 매크로와 비슷하지만 (놀랍지 않은) "nots"(또는 앞머리 또는!) 중 하나가없고 내가 게시 한 매크로의 사용 예는 다음과 같습니다. 정확하지 않습니다. "BUILD_BUG_ON ((sizeof (struct mystruct) % 8))"
smcameron

3

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


1
AFAIK, C89 / 90에는 열거 형에 대한 유형 검사가 없습니다. 열거 형은 다소 더 편리합니다.
cschol

39 페이지 하단, 2 차 ED K & R. 적어도 확인 기회가 있습니다.
Jonathan Watmough

3

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


a = 1; b = 2; a = a + b; b = ab; a = ab; 같은 결과를 제공합니다
Grambot

이것은 또한 a와 b를 교환 할 것이다 : a ^ = b ^ = a ^ = b;
vikhyat

@TheCapn : 추가가 넘칠 수 있습니다.
Michael Foukarakis


2

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

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


1

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


1
나는 당신이 여기서 무엇을 의미했는지 이해하기를 바랍니다. 더 설명해 주시겠습니까? 사용자 데이터 포인터 란 무엇입니까?
Zan Lynx

1
Plz 여기를 참조하십시오 stackoverflow.com/questions/602826/…
epatel

주로 콜백입니다. 콜백이 실행될 때마다 다시 제공하고자하는 일부 데이터입니다. C ++이 포인터를 콜백에 전달하여 객체를 이벤트에 묶을 때 특히 유용합니다.
Evan Teran

아 예. 감사. 나는 이것을 많이 사용하지만 결코 그것을 부르지 않았습니다.
Zan Lynx

1

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


1

우리의 코드베이스는

#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

매크로로 즐기는 재미 :

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

0

다음은 앱을 실행하기 위해 실제로 HW가 사용하는 것에 대해 C 코드를 완전히 인식하지 못하게하는 방법의 예입니다. 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;
}

4
정교하게 관리하고 실제 사용을 설명 할 수 있습니까?
Leonardo Herrera

결국 interupts를 생성하는 som HW 인터페이스를 사용하여 테스트 프로그램을 작성 해야하는 경우 wxample로. 그런 다음이 모듈을 설정하여 신호 / 인터럽트 핸들러로 일반 스코프 외부에서 기능을 실행할 수 있습니다.
eaanon01

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

출력에 hello 또는 hi가 나타나지 않도록 공백을 채우십시오.
답변 :fclose(stdout)


{}툴바 버튼 으로 코드를 포맷 할 수 있습니다 (나는 당신을 위해했습니다). "Quote"버튼은 공백을 유지하거나 구문 강조를 적용하지 않습니다.
Álvaro González
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.