好きなCプログラミングトリックは何ですか?[閉まっている]


134

たとえば、私は最近、これをLinuxカーネルで見つけました。

/ *条件が真の場合、コンパイルエラーを強制します* /
#define BUILD_BUG_ON(condition)((void)sizeof(char [1-2 * !!(condition)]))

したがって、コードで、たとえばいくつかのハードウェア制約のために、サイズが8バイトの倍数でなければならない構造がある場合は、次のようにすることができます。

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

また、構造体mystructのサイズが8の倍数でない限り、コンパイルされません。8の倍数の場合、ランタイムコードはまったく生成されません。

私が知っているもう1つのトリックは、1つのヘッダーファイルで1つのモジュールの変数を宣言および初期化しながら、そのモジュールを使用する他のモジュールで変数を宣言し、初期化するだけで、それらをexternとして宣言することです。

#ifdef DEFINE_MYHEADER_GLOBALS
#define GLOBAL
#define INIT(x、y)(x)=(y)
#そうしないと
#define GLOBAL extern
#define INIT(x、y)
#endif

GLOBAL int INIT(x、0);
グローバルint somefunc(int a、int b);

これで、xとsomefuncを定義するコードは次のようになります。

#define DEFINE_MYHEADER_GLOBALS
#include "the_above_header_file.h"

一方、単にxとsomefunc()を使用しているコードは、次のことを行います。

#include "the_above_header_file.h"

したがって、グローバルのインスタンスと関数プロトタイプの両方が必要な場所で宣言されている1つのヘッダーファイルと、対応するextern宣言を取得します。

それで、それらの線に沿ってあなたの好きなCプログラミングトリックは何ですか?


9
これは、Cプリプロセッサのトリックに似ています。
jmucchiello 2009年

BUILD_BUG_ONマクロについては、#errorinsideと#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、2011年

2
@RoryHarvey:調べてみたところ、純粋に経験的だったようです。一部の研究(どこで見たかは覚えていません)は、最適に近いが完全に最適ではないことを示しました。同様に、64ビットの場合、計算ではなく値が発見されたようです。
Matthieu M.

50

= {0};memsetを呼び出さなくても、構造体を初期化するのが好きです。

struct something X = {0};

これにより、構造体(または配列)のすべてのメンバーがゼロに初期化されます(ただし、パディングバイトはありません-ゼロにする必要がある場合はmemsetを使用してください)。

ただし、動的に割り当てられる大規模な構造には、これに関するいくつかの問題あることに注意してください。


ちなみに、グローバル変数には必要ありません。
ストレッジャー2009年

5
静的変数には必要ありません。グローバル変数はゼロになる場合がありますが、これは必須ではありません。
ジェイミー、

4
私は時々これを次のように拡張します。const struct something zero_something = { 0 };そして、変数をその場で、struct something X = zero_something;またはルーチンの途中でリセットできます。「X = zero_something;」を使用できます。唯一考えられる問題は、どこかからデータを読み取ることです。最近では、 'memset()'の方が速いかもしれませんが、割り当ての明確さを気に入っています。また、初期化子でゼロ以外の値を使用することもできます(memset()の後に個々のメンバーを微調整します)単純なコピーよりも遅い場合があります)。
ジョナサンレフラー、

45

私たちがcトリックについて話している場合、私のお気に入りはループの展開のためのダフのデバイスでなければなりません!私は実際に怒ってそれを使用するために一緒に来る適切な機会を待っています...


4
私はこれを1回使用して測定可能なパフォーマンスの向上を実現しましたが、最近では多くのハードウェアでは役に立ちません。常にプロフィール!
ダン・オルソン

6
ええ、ダフのデバイスのコンテキストを理解していないような人たちが作成されました。「コードの読みやすさ」は、コードが十分に高速でないと役に立たないものです。おそらく、あなたに反対票を投じた人は、ハードリアルタイムのためにコーディングする必要がなかったでしょう。
Rob K

1
+1、私は実際にDuffのデバイスを数回使用する必要がありました。初めてのループは、基本的にはものをコピーし、途中で小さな変換を行いました。そのアーキテクチャでは、単純なmemcpy()よりもはるかに高速でした。
マキス

3
怒りはあなたの後にあなたのコードを維持しなければならないあなたの同僚と後継者からのものです。
ジョナサンレフラー、

1
私が言ったように、私はまだ適切な機会を待っています-しかし、誰もまだ私を十分に悩ませていません。私はCを約25年間書いていますが、90年代初頭に最初にDuffのデバイスに出会ったので、まだ使用する必要がなかったと思います。他の人がコメントしたように、コンパイラーがこの種の最適化でより良くなるので、この種のトリックはますます役に立たなくなります。
ジャクソン

42

使用する __FILE__と、__LINE__デバッグのための

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

6
一部のコンパイラでは、FUNCTIONも使用できます。
JBRウィルキンソン2009年

11
__FUNCTION__は単なるのエイリアスで__func____func__c99にあります。かなり便利です。__PRETTY_FUNCTION__C(GCC)はのもう1つのエイリアスです__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!
ストレッジャー2009年

それがあまり好きではない-私の頭に浮かぶ最初のことは、いくつかのバグのためにDoSomeStackCheckStuffがメモリをめちゃくちゃにしていて、コードを読んでいる人は誰でもreturnの再定義を認識しておらず、/ hell /が何が起こっているのか疑問に思っているということです。
ギリガン

8
@stragerしかし、それは基本的にそれを役に立たなくするでしょう。重要なのは、すべての関数呼び出しにトレースを追加することです。それ以外の場合はDoSomeStackCheckStuff、トレースする関数の呼び出しを追加するだけです。
2010年

1
@gilliganこれは常に有効にしておくタイプのものではないと思います。ワンショットのデバッグ作業には非常に便利です。
sunetos

それは本当にうまくいくのでしょうか?:)私は書いた#define return if((DoSomeStackCheckStuff) && 0) ; else returnと思います...狂ったように私は推測します!
Paolo Bonzini、2011年

22

動的にサイズ変更されるオブジェクトを持つ「構造体ハック」が好きです。このサイトでもかなりよく説明されています(ただし、構造体の最後のメンバーとして "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を加えたタイプXの構造体をヒープに割り当てました(str 1はsizeof(X)に含まれているため)。

同じブロック内のいくつかの可変長データの直前に「ヘッダー」を置きたい場合に、これは一般に役立ちます。


個人的には、malloc()とrealloc()を自分で行い、長さを見つける必要があるときはいつでもstrlen()を使用する方が簡単だと思いますが、文字列の長さを知らないプログラムが必要で、それを多く見つける必要がある場合時々、これはおそらくより良い道です。
Chris Lutz、

4
"..." str [] "を記述できるC99バージョン" str [0]のようなコンテキストで、サイズがゼロの配列を見たことがあります。かなり頻繁に。C99だと思います。古いコンパイラは、サイズがゼロの配列について不満を言うのは知っていますが。
smcameron 2009年

3
私もこれが好きですが、malloc(offsetof(X、str)+ numbytes)のようなものを使用する必要があります。そうしないと、パディングと位置合わせの問題のために問題が発生します。たとえば、sizeof(struct X)は5ではなく8になる可能性があります
Fozi

3
@Fozi:実際にそれが問題になるとは思いません。このバージョンにはstr[1](ないstr[])があるため、strの1バイトがに含まれていsizeof(struct X)ます。これには含まれて間のパディングlenとをstr
エヴァンテラン2010

2
@Rusky:それはどのような悪影響を及ぼしますか?の後に「パディング」があるとしますstr。OK、割り当てた場合、sizeof(struct X) + 10これはstr効果的に10 - sizeof(int)(またはパディングがあると言ったので、さらに)大きくなります。このオーバーレイ strとその後のパディング。それが何らかの違いをもたらす唯一の方法は、メンバーがいて、strそれからとにかく全体を壊す場合、フレキシブルメンバーが最後でなければならないということです。最後のパディングは、割り当てが多すぎる可能性があります。実際に問題が発生する具体的な例を教えてください。
エヴァン・テラン

17

クラスをエミュレートすることによる、Cによるオブジェクト指向コード。

構造体と、その構造体へのポインタを最初のパラメータとして受け取る関数のセットを作成するだけです。


2
cfrontが以前のようにC ++をCに変換する何かがまだありますか?
MarkJ 2009年

11
これはほとんどオブジェクト指向ではありません。継承のあるOOの場合、オブジェクトの構造体にある種の仮想関数テーブルを追加する必要があります。これは、「サブクラス」によってオーバーロードできます。この目的のために、中途半端な「C with classes」スタイルのフレームワークがたくさんありますが、それを避けることをお勧めします。
exDM69 2011年

それは言われる必要がありました。+1。
アミット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のp。6.2.7.2およびp。6.7.3.5を参照)。
Ales Hakl、2011年

8

ビットシフトは、(32ビット整数で)31のシフト量までのみ定義されます。

より高いシフト値でも機能する必要がある計算されたシフトが必要な場合はどうしますか?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
私のマシンでは、gmov-4.3.2がcmov命令(条件付き移動)を使用して2番目のブランチを削除します
Adam Rosenfield

3
「分岐を使用するよりもかなり高速」:分岐は、のすべての値vに対して正しいことですが、halfshiftトリックは、32ビットアーキテクチャでは63に、64ビットアーキテクチャでは127に許容範囲を2倍にするだけです。
Pascal Cuoq、2011年

8

有限状態機械を実装するための関数へのポインタの配列を宣言します。

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

最も楽しい利点は、各刺激/状態にすべてのコードパスをチェックさせるのが簡単なことです。

組み込みシステムでは、そのようなテーブルを指すようにISRをマップし、必要に応じて(ISRの外で)再ベクトル化します。


私がこれで好きな1つのテクニックは、初期化が必要な関数がある場合、初期化ルーチンの呼び出しでポインターを初期化することです。それが実行されると、最後に、ポインターを実際の関数へのポインターに置き換え、その関数を呼び出します。このようにして、初期化子は関数が最初に呼び出されたときに自動的に呼び出され、実際の関数はそれ以降に呼び出されます。
TMN

7

もう1つの優れたプリプロセッサ「トリック」は、「#」文字を使用してデバッグ式を出力することです。例えば:

#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で名前空間を汚染し、2番目の使用法が取得されるため、2回使用することはできません。エラー:typedefの再定義 '__compile_time_assert'
smcameron 2009年

実際に試してみましたか?「typedef foo;」を実行できます。あなたが好きなだけ何度でも。このようにして、事前宣言を行います。私はこれを2.5年間、gcc、VC、および組み込み環境用のコンパイラーの両方のいくつかのコンパイラーで使用しており、問題に遭遇したことはありません。
Gilad Naor

私はCプリプロセッサが嫌いです... :(
hasen

1
はい、試しました。コンパイラからのエラーメッセージ、gccをカットアンドペーストしました。
smcameron 2009年

1
@Gilad:C ++では冗長なtypedefを持つことは合法ですが、Cでは不可です。
エヴァン・テラン

6

私はそれを使ったことがないので、私は実際にそれをお気に入りのトリックとは言いませんが、Duff's Deviceについての言及は、Cにコルーチンを実装することについてのこの記事を思い出させました。しばらくの間役に立ちます。


実際にこの手法を使用して、依存する非同期I / Oのシーケンスを駆動するコードを漠然と人間が読めるようにしました。主な違いは、コルーチンの状態をstatic変数に格納せず、代わりに構造体を動的に割り当てて、それへのポインターをコルーチン関数に渡すことです。マクロの束はこれをより口当たりの良いものにします。それは良いことではありませんが、あちこちにジャンプする非同期/コールバックバージョンよりも優れています。swapcontext()できればグリーンスレッド(* nixes 経由)を使用します。
pmdj '14年

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

while(0); プログラムには影響しませんが、コンパイラーは「これは何もしない」という警告を出します。これは、問題のある行を調べて、注意を喚起したい本当の理由を確認するのに十分です。


9
代わりに#warningを使用できませんでしたか?
Stefano Borini、

どうやら、私はできました。それは完全に標準ではありませんが、私が使用するコンパイラで機能しました。興味深いことに、組み込みコンパイラは#defineを翻訳しましたが、gccは翻訳しませんでした。
gbarry 2009年

6

私はxorハックのファンです。

3番目の一時ポインターなしで2つのポインターをスワップします。

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

または、ポインターが1つだけの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'フラグが使用されていない場合にのみ出力されます。

次にできます。フラグをメイクファイルなどに渡します。

これがWindowsでどのように機能するかはわかりませんが、* nixではうまく機能します


RELEASEが定義されている場合は、D(x)を{}に展開して、ifステートメントでうまく機能するようにすることができます。それ以外の場合は「if(a)D(x);」RELEASEを定義すると、「if(a)」だけに展開されます。それはあなたにリリース版にいくつかの素晴らしいバグを与えるでしょう
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ではなく2番目のifと一致します。
アダムローゼンフィールド

正直に言うと、私は主にこのマクロを印刷ステートメントのテストに使用します。または、条件付きステートメントがある場合、すべてを囲みます。D(if(a)foo(););
Simon Walker、

1
@AdamRosenfield:#define D(x) do { } while(0)代わりに使用すると、そのケースが処理されます(x一貫性のために挿入するブランチにも適用できます)
rpetrich

3

Rustyは実際に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;
}

実際のヘッダーには、他の多くの役立つマクロがあり、簡単にドロップできます。

主にインライン関数に固執することで、ダークサイド(およびプリプロセッサーの乱用)の引きずりに全力で抵抗しようとしますが、私はあなたが説明したような便利で便利なマクロを楽しんでいます。


ええ、私は最近ccanに出会い、コードの寄稿を検討していましたが、まだ「ccanの方法」に頭を抱えていません。リンクのおかげで、ccanを調べるモチベーションが高まりました。
smcameron 2009年

まあ、私は「ccan way」がより確立されるまであまり心配しません...現在、ccan-lintがGSOCプロジェクトとして提案されています。その小さくてやさしいグループ..そしてスニペットをダンプするのに最適な場所:)
Tim Post

ちなみに、RustyのBuILD_ASSERTはLinuxカーネルのマクロに似ている(驚くべきことではない)が、「nots」(またはbang、または!)の1つが欠けていることに気付き、私が投稿したマクロの使用例は正しくありません。"BUILD_BUG_ON((sizeof(struct mystruct)%8))"
smcameron 2009年

3

この種のもののための2つの優れたソースブックは、プログラミングの実践確実なコードの記述です。それらの1つ(私はどちらを覚えていないか)は次のように言っています:enumはコンパイラーによってチェックされるため、可能な場合は#defineを優先してください


1
AFAIK、C89 / 90では、列挙型の型チェックはありません。列挙型は、何とか便利な#definesです。
cschol 2009年

39ページの最後、第2版ED K&R。少なくとも確認の機会があります。
ジョナサンワトモフ11/10/14

3

Cに固有ではありませんが、XOR演算子は常に好きでした。それができるクールなことの1つは、「一時的な値なしでスワップする」ことです。

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; 同じ結果が得られる
グラムボット

これにより、aとbも入れ替わります。a ^ = b ^ = a ^ = b;
vikhyat

@TheCapn:ただし、加算はオーバーフローする可能性があります。
Michael Foukarakis、2011年


2

container_ofたとえばリストで使用されるという概念が好きです。基本的に、あなたは指定する必要はありませんnextし、lastリストに表示される各構造のためのフィールド。代わりに、リスト構造のヘッダーを実際のリンクされたアイテムに追加します。

見てください include/linux/list.h実生活の例のために。


1

userdataポインタの使用はかなりきちんとしていると思います。流行を失った今日のファッション。Cの機能ではありませんが、Cでの使用はかなり簡単です。


1
ここであなたが何を意味するのか理解できたらいいのに 詳しく説明していただけますか?userdataポインターとは何ですか?
Zan Lynx

1
Plzはこちらをご覧stackoverflow.com/questions/602826/...
epatel

それは主にコールバック用です。これは、コールバックが発生するたびに返されるデータです。C ++のこのポインターをコールバックに渡す場合に特に役立ちます。これにより、オブジェクトをイベントに関連付けることができます。
Evan Teran

ああ、そうです。ありがとう。私はこれを頻繁に使用しますが、そのように呼ばれたことはありません。
Zan Lynxは

1

私が使う Xマクロして、プリコンパイラにコードを生成させています。これらは、エラー値と関連するエラー文字列を1か所で定義するのに特に役立ちますが、それをはるかに超えることができます。


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

アプリを実行するためにハードウェアで実際に使用されているものを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、

最後に中断を生成するsom HWインターフェイスを使用してテストプログラムを作成する必要がある場合の例として。次に、このモジュールを設定して、通常のスコープ外の関数をシグナル/割り込みハンドラーとして実行できます。
eaanon01 2009年

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

空白を埋めて、helloもhiも出力に表示されないようにします。
ans:fclose(stdout)


{}ツールバーのボタンでコードをフォーマットできます(私はあなたのためにそれを行いました)。[引用]ボタンは空白を保持せず、構文の強調表示を適用しません。
アルバロゴンサレス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.