私はすべてのCコンパイラ実装の背後に標準があることを知っているので、隠された機能があってはなりません。それにもかかわらず、私はすべてのC開発者が彼らがいつも使用する隠された/秘密のトリックを持っていると確信しています。
56 に答える
GCCコンパイラのトリックですが、コンパイラに分岐指示のヒントを与えることができます(Linuxカーネルでは一般的です)
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
参照: http://kerneltrap.org/node/4705
これについて私が気に入っているのは、一部の機能に表現力を追加することです。
void foo(int arg)
{
if (unlikely(arg == 0)) {
do_this();
return;
}
do_that();
...
}
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t
これらは標準のオプション項目ですが、人々が絶えず再定義しているため、隠された機能でなければなりません。私が取り組んできた (そして今もなお) あるコード ベースには、複数の再定義があり、すべてが異なる識別子を持っています。ほとんどの場合、プリプロセッサ マクロを使用します。
#define INT16 short
#define INT32 long
等々。髪を抜きたくなる。 おかしな標準の整数型定義を使用するだけです!
コンマ演算子は広く使用されていません。確かに悪用される可能性はありますが、非常に便利な場合もあります。この使用法は最も一般的なものです:
for (int i=0; i<10; i++, doSomethingElse())
{
/* whatever */
}
ただし、この演算子はどこでも使用できます。観察:
int j = (printf("Assigning variable j\n"), getValueFromSomewhere());
各ステートメントが評価されますが、式の値は最後に評価されたステートメントの値になります。
構造体をゼロに初期化しています
struct mystruct a = {0};
これにより、すべての構造要素がゼロになります。
関数ポインタ。関数ポインターのテーブルを使用して、たとえば高速間接スレッド コード インタープリター (FORTH) やバイトコード ディスパッチャーを実装したり、オブジェクト指向のような仮想メソッドをシミュレートしたりできます。
次に、qsort()、bsearch()、strpbrk()、strcspn() [後者の 2 つは strtok() の置換を実装するのに役立ちます] など、標準ライブラリに隠された宝石があります。
C の欠点は、符号付き算術オーバーフローが未定義の動作 (UB) であることです。そのため、x+y などの式が両方とも符号付き int である場合は常に、オーバーフローして UB が発生する可能性があります。
複数文字の定数:
int x = 'ABCD';
これは(または、アーキテクチャによっては ) に設定x
されます。0x41424344
0x44434241
編集:特にintをシリアル化する場合、この手法は移植できません。ただし、自己文書化列挙型を作成すると非常に便利です。例えば
enum state {
stopped = 'STOP',
running = 'RUN!',
waiting = 'WAIT',
};
これにより、未加工のメモリ ダンプを見ていて、列挙型の値を調べる必要がない場合に、はるかに簡単になります。
私はビットフィールドを使用したことはありませんが、超低レベルのものにはクールに聞こえます.
struct cat {
unsigned int legs:3; // 3 bits for legs (0-4 fit in 3 bits)
unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
// ...
};
cat make_cat()
{
cat kitty;
kitty.legs = 4;
kitty.lives = 9;
return kitty;
}
これは、sizeof(cat)
が と同じくらい小さいことを意味しますsizeof(char)
。
Duff's Device のようなインターレース構造:
strncpy(to, from, count)
char *to, *from;
int count;
{
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}
C には標準がありますが、すべての C コンパイラが完全に準拠しているわけではありません (完全に準拠している C99 コンパイラはまだ見たことがありません!)。
そうは言っても、私が好むトリックは、C セマンティックに依存しているため、自明ではなく、プラットフォーム間で移植可能なものです。それらは通常、マクロまたはビット演算に関するものです。
例: 一時変数を使用せずに 2 つの符号なし整数を交換します。
...
a ^= b ; b ^= a; a ^=b;
...
または、次のような有限状態マシンを表す「拡張 C」:
FSM {
STATE(x) {
...
NEXTSTATE(y);
}
STATE(y) {
...
if (x == 0)
NEXTSTATE(y);
else
NEXTSTATE(x);
}
}
これは、次のマクロで実現できます。
#define FSM
#define STATE(x) s_##x :
#define NEXTSTATE(x) goto s_##x
ただし、一般的には、賢いがコードを読むのが不必要に複雑になる (swap の例のように) トリックは好きではなく、コードをより明確にし、意図を直接伝えるもの (FSM の例のように) が好きです。 .
私は、C99 で追加された指定イニシャライザが非常に好きです (そして gcc で長い間サポートされています):
#define FOO 16
#define BAR 3
myStructType_t myStuff[] = {
[FOO] = { foo1, foo2, foo3 },
[BAR] = { bar1, bar2, bar3 },
...
配列の初期化は位置に依存しなくなりました。FOO または BAR の値を変更すると、配列の初期化は自動的に新しい値に対応します。
C99 には、任意の順序で構造を初期化する素晴らしい機能があります。
struct foo{
int x;
int y;
char* name;
};
void main(){
struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}
匿名の構造と配列は私のお気に入りです。(http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.htmlを参照)
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
また
void myFunction(type* values) {
while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});
リンクリストをインスタンス化するために使用することもできます...
ええと... C言語の長所の1つはその移植性と標準性だと思います.可能な限り標準的で移植可能な C コード。
最初に見たときに「ショックを受けた」(隠された) 機能は、printf に関するものです。この機能を使用すると、変数を使用してフォーマット指定子自体をフォーマットできます。コードを探すと、よくわかります。
#include <stdio.h>
int main() {
int a = 3;
float b = 6.412355;
printf("%.*f\n",a,b);
return 0;
}
* 文字はこの効果を実現します。
gcc には、私が気に入っている C 言語の拡張機能がいくつかあります。私のお気に入りのいくつかはfunction attributes です。非常に役立つ例の 1 つは、format 属性です。これは、printf フォーマット文字列を取るカスタム関数を定義する場合に使用できます。この関数属性を有効にすると、gcc は引数をチェックして、フォーマット文字列と引数が一致することを確認し、必要に応じて警告またはエラーを生成します。
int my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
コンパイル時のアサーションについては、こちらで既に説明しています。
//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
typedef struct { \
char static_assertion[condition ? 1 : -1]; \
} static_assertion_t
//--- ensure structure fits in
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);
定数文字列連結
私が知っているすべてのコンパイラがサポートしているため、回答にまだ表示されていないことに非常に驚きましたが、多くのプログラマーはそれを無視しているようです。マクロを書くときだけでなく、本当に便利な場合もあります。
現在のコードの使用例:#define PATH "/some/path/"
構成ファイルに があります (実際には、makefile によって設定されます)。ここで、リソースを開くためのファイル名を含む完全なパスを作成したいと考えています。それはただ行く:
fd = open(PATH "/file", flags);
恐ろしい、しかし非常に一般的なものの代わりに:
char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);
一般的な恐ろしい解決策は次のとおりです。
- 3倍の長さ
- はるかに読みにくい
- はるかに遅い
- 任意のバッファサイズ制限に設定すると、それほど強力ではありません(ただし、定数文字列の連結なしでそれを回避するには、さらに長いコードを使用する必要があります)。
- より多くのスタック スペースを使用する
まあ、私はそれを使ったことがなく、誰かにそれをお勧めするかどうかはわかりませんが、サイモン・タタムのコルーチンのトリックについて言及しなければ、この質問は不完全だと思います。
配列または列挙型を初期化するときは、初期化子リストの最後の項目の後にコンマを置くことができます。例えば:
int x[] = { 1, 2, 3, };
enum foo { bar, baz, boom, };
これは、コードを自動的に生成する場合に、最後のコンマを削除することを心配する必要がないようにするために行われました。
構造体の割り当てはクールです。多くの人は、構造体も値であり、代入できることを認識していないようですmemcpy()
。単純な代入でうまくいく場合は、 を使用する必要はありません。
たとえば、架空の 2D グラフィックス ライブラリを考えてみましょう。(整数の) 画面座標を表す型を定義する場合があります。
typedef struct {
int x;
int y;
} Point;
ここで、関数の引数から初期化されたポイントを作成し、それを返す関数を作成するなど、「間違っている」ように見えることを行います。
Point point_new(int x, int y)
{
Point p;
p.x = x;
p.y = y;
return p;
}
これは (もちろん) 戻り値が構造体代入を使用して値ごとにコピーされる限り安全です。
Point origin;
origin = point_new(0, 0);
このようにして、非常にクリーンでオブジェクト指向的なコードを、すべてプレーンな標準 C で書くことができます。
奇妙なベクトル インデックス:
int v[100]; int index = 10;
/* v[index] it's the same thing as index[v] */
C コンパイラは、いくつかの標準の 1 つを実装しています。ただし、標準があるからといって、言語のすべての側面が定義されているわけではありません。 たとえば、ダフのデバイスはお気に入りの「隠れた」機能であり、非常に人気が高まっているため、最新のコンパイラには、この頻繁に使用されるパターンの望ましい効果が最適化手法によって損なわれないようにするための特別な目的の認識コードがあります。
一般に、コンパイラが使用する C 標準のカミソリエッジで実行しているため、隠された機能や言語のトリックは推奨されません。多くのそのようなトリックは、あるコンパイラから別のコンパイラでは機能しません。多くの場合、これらの種類の機能は、特定のメーカーによるコンパイラ スイートの 1 つのバージョンから別のバージョンでは機能しません。
Cコードを壊したさまざまなトリックには、次のものがあります。
- コンパイラが構造体をメモリに配置する方法に依存します。
- 整数/浮動小数点数のエンディアンに関する仮定。
- 関数 ABI に関する仮定。
- スタック フレームが成長する方向に関する仮定。
- ステートメント内の実行順序に関する仮定。
- 関数の引数内のステートメントの実行順序に関する仮定。
- short、int、long、float、および double 型のビット サイズまたは精度に関する仮定。
プログラマーがほとんどの C 標準で「コンパイラ依存」の動作として指定されている実行モデルについて仮定を行うたびに発生するその他の問題や問題。
sscanf を使用する場合、%n を使用して、読み続けるべき場所を見つけることができます。
sscanf ( string, "%d%n", &number, &length );
string += length;
どうやら、別の回答を追加することはできないので、ここに 2 番目の回答を含めます。「&&」と「||」を使用できます。条件として:
#include <stdio.h>
#include <stdlib.h>
int main()
{
1 || puts("Hello\n");
0 || puts("Hi\n");
1 && puts("ROFL\n");
0 && puts("LOL\n");
exit( 0 );
}
このコードは次を出力します。
やあ ROFL
Cの私のお気に入りの「隠された」機能は、スタックに書き戻すためにprintfで%nを使用することです。通常、printfはフォーマット文字列に基づいてスタックからパラメータ値をポップしますが、%nはそれらを書き戻すことができます。
ここでセクション3.4.2をチェックしてください。多くの厄介な脆弱性につながる可能性があります。
INT(3)を使用してコードにブレークポイントを設定することは、私の一番のお気に入りです。
列挙型を使用したコンパイル時の仮定チェック: ばかげた例ですが、コンパイル時に構成可能な定数を持つライブラリには非常に役立ちます。
#define D 1
#define DD 2
enum CompileTimeCheck
{
MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
MAKE_SURE_DD_IS_POW2 = 1/((((DD) - 1) & (DD)) == 0)
};
Gcc (c) には、ネストされた関数宣言や、a が false でない場合に a を返す ?: 演算子の a?:b 形式など、有効にできる楽しい機能がいくつかあります。
最近、ビットフィールドが 0 であることを発見しました。
struct {
int a:3;
int b:2;
int :0;
int c:4;
int d:3;
};
のレイアウトを提供します
000aaabb 0ccccddd
:0; なしの代わりに
0000aaab bccccddd
0 幅フィールドは、次のアトミック エンティティで次のビットフィールドを設定する必要があることを示します ( char
)
C99 スタイルの可変引数マクロ、別名
#define ERR(name, fmt, ...) fprintf(stderr, "ERROR " #name ": " fmt "\n", \
__VAR_ARGS__)
これは次のように使用されます
ERR(errCantOpen, "File %s cannot be opened", filename);
ここでは、文字列化演算子と文字列定数の連結など、私が本当に気に入っている他の機能も使用しています。
Variable size automatic variables are also useful in some cases. These were added i nC99 and have been supported in gcc for a long time.
void foo(uint32_t extraPadding) {
uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];
You end up with a buffer on the stack with room for the fixed-size protocol header plus variable size data. You can get the same effect with alloca(), but this syntax is more compact.
You have to make sure extraPadding is a reasonable value before calling this routine, or you end up blowing the stack. You'd have to sanity check the arguments before calling malloc or any other memory allocation technique, so this isn't really unusual.
GCC のラムダ (匿名関数など):
#define lambda(return_type, function_body) \
({ return_type fn function_body fn })
これは次のように使用できます。
lambda (int, (int x, int y) { return x > y; })(1, 2)
これは次のように展開されます。
({ int fn (int x, int y) { return x > y } fn; })(1, 2)
__LINE__
とが好き__FILE__
です。ここを参照してください: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
私はあなたが作ることができる可変サイズの構造が好きでした:
typedef struct {
unsigned int size;
char buffer[1];
} tSizedBuffer;
tSizedBuffer *buff = (tSizedBuffer*)(malloc(sizeof(tSizedBuffer) + 99));
// can now refer to buff->buffer[0..99].
また、現在ANSI Cになっているoffsetofマクロですが、初めて見たときは魔法のようでした。基本的にはヌルポインタを構造体変数としてリキャストするためのアドレス演算子(&)を使用します。
入力バッファをクリアするには、使用できませんfflush(stdin)
。正しい方法は次のとおりです。scanf("%*[^\n]%*c")
これにより、入力バッファーからすべてが破棄されます。
gcc の初期のバージョンは、ソース コードで「#pragma」が検出されるたびにゲームを実行しようとしました。こちらもご覧ください。
Conversion of types by using unusual typecasts. Though not hidden feature, its quite tricky.
Example:
If you needed to know how compiler stores float, just try this:
uint32_t Int;
float flt = 10.5; // say
Int = *(uint32_t *)&flt;
printf ("Float 10.5 is stored internally as %8X\n", Int);
or
float flt = 10.5; // say
printf ("Float 10.5 is stored internally as %8X\n", *(uint32_t *)&flt);
Note the clever use of typecasts. Converting address of variable (here &flt) to desired type (here (uint32_t * )) and extracting its content (applying '*').
This works other side of expression as well:
*(float *)&Int = flt;
This could also be accomplished using union:
typedef union
{
uint32_t Int;
float flt;
} FloatInt_type;
私はこれを15年以上のCプログラミングの後で初めて発見しました:
struct SomeStruct
{
unsigned a : 5;
unsigned b : 1;
unsigned c : 7;
};
ビットフィールド!コロンの後の数字はメンバーが必要とするビット数であり、メンバーは指定された型にパックされているため、unsigned が 16 ビットの場合、上記は次のようになります。
xxxc cccc ccba aaaa
スキズ
私はこれを少しのコードで一度見せてもらい、それが何をしたのか尋ねました:
hexDigit = "0123456789abcdef"[someNybble];
別のお気に入りは次のとおりです。
unsigned char bar[100];
unsigned char *foo = bar;
unsigned char blah = 42[foo];
実際には隠された機能ではありませんが、私が初めてこのようなものを見たとき、それはブードゥーのように見えました:
void callback(const char *msg, void *data)
{
// do something with msg, e.g.
printf("%s\n", msg);
return;
data = NULL;
}
この構造の理由は、これを-Wextraを使用して、「data = NULL;」行を指定せずにコンパイルすると、gccが未使用のパラメーターに関する警告を出力するためです。しかし、この役に立たない行を使用すると、警告は表示されません。
編集:私はそれらの警告を防ぐ他の(より良い)方法があることを知っています。これを初めて見たとき、それは私には奇妙に見えました。
変数をリテラルと比較するときは、リテラルを演算子の左側に置くこと==
をお勧めします。これにより、代わりに代入演算子を誤って使用したときにコンパイラがエラーを出すようになります。
if (0 == count) {
...
}
一見奇妙に見えるかもしれませんが、頭を悩ませることはありません (たまたま入力if (count = 0)
を間違えた場合など)。
ポインター型の変数を宣言するための intptr_t。C99 固有であり、stdint.h で宣言されています
Steve Webb__LINE__
はおよび__FILE__
マクロを指摘しました。以前の仕事で、メモリ内ロギングを行うためにそれらをハッキングした方法を思い出します。
デバイスからデバッグに使用されている PC にログ情報を渡すために使用できるポートがないデバイスで作業していました。ブレークポイントを使用して停止し、デバッガーを使用してプログラムの状態を知ることができましたが、システム トレースに関する情報はありませんでした。
デバッグ ログへのすべての呼び出しは実質的に 1 つのグローバル マクロであったため、そのマクロを変更して、ファイル名と行番号をグローバル配列にダンプするようにしました。この配列には、どのデバッグ呼び出しが呼び出されたかを示す一連のファイル名と行番号が含まれており、(実際のログ メッセージではありませんが) 実行トレースのかなりのアイデアが得られます。デバッガーで実行を一時停止し、これらのバイトをローカル ファイルにダンプし、スクリプトを使用してこの情報をコード ベースにマップすることができます。これが可能になったのは、厳密なコーディング ガイドラインがあったためです。そのため、1 つのファイルでロギング メカニズムを変更する必要がありました。
これがgccの3つの素晴らしいものです:
__FILE__
__FUNCTION__
__LINE__
抜粋:
このページでは、興味深い C プログラミングの質問/パズルのリストを見つけることができます。リストされているプログラムは、友人から電子メール転送として受け取ったもの、いくつかの本で読んだもの、インターネットからのものです。 Cでのコーディング経験からのいくつか。
レジスター変数
register
速度を上げるために、キーワードを使用していくつかの変数を宣言していました。これは、C コンパイラに、CPU レジスタをローカル ストレージとして使用するためのヒントを与えます。最近の C コンパイラはこれを自動的に行うため、これはおそらく必要なくなりました。
関数ポインタのサイズは標準ではありません。少なくともK&Rの本にはありません。他のタイプのポインターのサイズについて話していますが、(私が思うに)sizeof
関数ポインターは未定義の動作です。
また、コンパイル時の演算子でもあります。オンライン フォーラムで関数なのか演算子なのかsizeof
を尋ねる人がたくさんいます。sizeof
私が見た 1 つのエラーは次のとおりです (簡略化された例)。
int j;
int i;
j = sizeof(i++)
コンパイル時に評価されるように、インクリメントはi
実行されません。sizeof
プログラマーは、インクリメントi
と計算の両方の操作sizeof
を 1 つのステートメントでハックすることを意図していました。
C の演算子の優先順位は、評価の順序ではなく、関連付けの順序を管理します。たとえば、3 つの関数f
,がありg
、h
それぞれが を返し、int
それらが次のような式である場合:
f() + g() * h()
C 標準では、これらの関数の評価順序に関する規則はありません。との結果はg
、の結果をh
加算する前に乗算されf
ます。関数が状態を共有し、計算がこれらの関数の評価の順序に依存する場合、これはエラーにつながる可能性があります。これにより、移植性の問題が発生する可能性があります。
次のようにmallocとreallocをラップします。
#ifdef _DEBUG
#define mmalloc(bytes) malloc(bytes);printf("malloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define mrealloc(pointer, bytes) realloc(pointer, bytes);printf("realloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#else //_DEBUG
#define mmalloc(bytes) malloc(bytes)
#define mrealloc(pointer, bytes) realloc(pointer, bytes)
実際、これが私の完全なアルセノールです(BailIfNotはOO c用です):
#ifdef _DEBUG
#define mmalloc(bytes) malloc(bytes);printf("malloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define mrealloc(pointer, bytes) realloc(pointer, bytes);printf("realloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define BAILIFNOT(Node, Check) if(Node->type != Check) return 0;
#define NULLCHECK(var) if(var == NULL) setError(__FILE__, __LINE__, "Null exception", " var ", FATAL);
#define ASSERT(n) if( ! ( n ) ) { printf("<ASSERT FAILURE@%s:%d>", __FILE__, __LINE__); fflush(0); __asm("int $0x3"); }
#define TRACE(n) printf("trace: %s <%s@%d>\n", n, __FILE__, __LINE__);fflush(0);
#else //_DEBUG
#define mmalloc(bytes) malloc(bytes)
#define mrealloc(pointer, bytes) realloc(pointer, bytes)
#define BAILIFNOT(Node, Check) {}
#define NULLCHECK(var) {}
#define ASSERT(n) {}
#define TRACE(n) {}
#endif //_DEBUG
出力例を次に示します。
malloc: 12 <hash.c@298>
trace: nodeCreate <hash.c@302>
malloc: 5 <hash.c@308>
malloc: 16 <hash.c@316>
malloc: 256 <hash.c@320>
trace: dataLoadHead <hash.c@441>
malloc: 270 <hash.c@463>
malloc: 262144 <hash.c@467>
trace: dataLoadRecursive <hash.c@404>
この記事を読んだばかりです。それはいくつかのCと他のいくつかの言語の「隠された機能」を持っています。
typeof()演算子が好きです。コンパイル時に解決されるという点でsizeof()のように機能します。バイト数を返す代わりに、タイプを返します。これは、変数がどのタイプであっても、他の変数と同じタイプであると宣言する必要がある場合に役立ちます。
typeof(foo) copy_of_foo; //declare bar to be a variable of the same type as foo
copy_of_foo = foo; //now copy_of_foo has a backup of foo, for any type
これは単なるgcc拡張子かもしれませんが、よくわかりません。
他の場所の中で一般的なリゾルバーライブラリに見られる可変サイズの構造体。
struct foo {{ int a; int b; char b [1]; //[0]の使用は正しくなくなりました //最後に来る必要があります }; char * str = "abcdef"; int len = strlen(str); struct foo * bar = malloc(sizeof(foo)+ len); strcpy(bar.b、str); //私を止めてみてください!
スイッチ内でwhile(0)を使用して、breakなどのcontinueステートメントを使用できるようにするのはどうですか:-)
void sw(int s)
{
switch (s) while (0) {
case 0:
printf("zero\n");
continue;
case 1:
printf("one\n");
continue;
default:
printf("something else\n");
continue;
}
}
連鎖計算/エラーリターンにはNaNを使用します:
// #include <stdint.h>
static uint64_t iNaN = 0xFFF8000000000000;
const double NaN = *(double *)&iNaN; //静かなNaN
内部関数はエラーフラグとしてNaNを返すことができます。これはどの計算でも安全に使用でき、結果は常にNaNになります。
注:NaN!= NaN ... isnan(x)を使用するか、独自にロールするため、NaNのテストには注意が必要です。
xがNaNの場合、x!= xは数学的に正しいですが、一部のコンパイラによって最適化される傾向があります
フォーマット文字列で忘れられがちな%n
指定子は、非常に実用的な場合があります。printf
%nは、printfが出力をフォーマットするときに使用される仮想カーソルの現在の位置を返します。
int pos1, pos2;
char *string_of_unknown_length = "we don't care about the length of this";
printf("Write text of unknown %n(%s)%n text\n", &pos1, string_of_unknown_length, &pos2);
printf("%*s\\%*s/\n", pos1, " ", pos2-pos1-2, " ");
printf("%*s", pos1+1, " ");
for(int i=pos1+1; i<pos2-1; i++)
putc('-', stdout);
putc('\n', stdout);
次の出力があります
Write text of unknown (we don't care about the length of this) text
\ /
--------------------------------------
少し工夫が凝らされていますが、きれいなレポートを作成するときにいくつかの用途があります。
オブジェクト指向の C マクロ: コンストラクタ (init)、デストラクタ (dispose)、equal (equal)、コピー (copy)、およびインスタンス化のためのプロトタイプ (prototype) が必要です。
宣言では、コピーして派生する定数プロトタイプを宣言する必要があります。その後、行うことができますC_OO_NEW
。必要に応じて、さらに例を投稿できます。LibPurple は、コールバック システムを備えた大規模なオブジェクト指向の C コード ベースです (使用中のシステムを確認したい場合)。
#define C_copy(to, from) to->copy(to, from)
#define true 1
#define false 0
#define C_OO_PROTOTYPE(type)\
void type##_init (struct type##_struct *my);\
void type##_dispose (struct type##_struct *my);\
char type##_equal (struct type##_struct *my, struct type##_struct *yours); \
struct type##_struct * type##_copy (struct type##_struct *my, struct type##_struct *from); \
const type type##__prototype = {type##_init, type##_dispose, type##_equal, type##_copy
#define C_OO_OVERHEAD(type)\
void (*init) (struct type##_struct *my);\
void (*dispose) (struct type##_struct *my);\
char (*equal) (struct type##_struct *my, struct type##_struct *yours); \
struct type##_struct *(*copy) (struct type##_struct *my, struct type##_struct *from);
#define C_OO_IN(ret, type, function, ...) ret (* function ) (struct type##_struct *my, __VA_ARGS__);
#define C_OO_OUT(ret, type, function, ...) ret type##_##function (struct type##_struct *my, __VA_ARGS__);
#define C_OO_PNEW(type, instance)\
instance = ( type *) malloc(sizeof( type ));\
memcpy(instance, & type##__prototype, sizeof( type ));
#define C_OO_NEW(type, instance)\
type instance;\
memcpy(&instance, & type ## __prototype, sizeof(type));
#define C_OO_DELETE(instance)\
instance->dispose(instance);\
free(instance);
#define C_OO_INIT(type) void type##_init (struct type##_struct *my){return;}
#define C_OO_DISPOSE(type) void type##_dispose (struct type##_struct *my){return;}
#define C_OO_EQUAL(type) char type##_equal (struct type##_struct *my, struct type##_struct *yours){return 0;}
#define C_OO_COPY(type) struct type##_struct * type##_copy (struct type##_struct *my, struct type##_struct *from){return 0;}
In Visual Studio, it is possible for you to highlight your own defined types.
To do that, create a file called "usertype.dat" in the folder "Commom7/IDE". The contents of that file shall be the types you want to highlight. For example:
//content of usertype.dat
int8_t
int16_t
int32_t
int64_t
uint8_t
uint16_t
uint32_t
uint64_t
float32_t
float64_t
char_t