ホームページ > 記事 > システムチュートリアル > Linux GNU C と ANSI C の違いは何ですか?
Linux で使用できる C コンパイラは GNU C コンパイラです。これは Free Software Foundation のプログラミング ライセンスに基づいて構築されているため、自由に配布して使用できます。 GNU C は、標準 C の機能を強化するために、標準 C に一連の拡張を作成します。
GNU C では長さ 0 の配列を使用できます。これは、可変長オブジェクトのヘッダー構造を定義するときに非常に便利です。例えば:### リーリー
char data[0] は、プログラムが var_data 構造体インスタンスの data[index] メンバーを介して len の後のインデックス アドレスにアクセスできることのみを意味します。data[] 配列にはメモリが割り当てられないため、sizeof(struct var_data )=sizeof(int)。struct var_data のデータ フィールドが struct var_data の直後のメモリ領域に格納されていると仮定すると、データは次のコードを通じて走査できます。
リーリー
GNU C では、次のコードで定義されている「double x[n]」など、変数を使用して配列を定義することもできます。 リーリー 2.ケーススコープコード内のケース '0'...'9' は、標準 C のものと同等です。
リーリー 3. ステートメント式2 つのローカル変数 __xx と __y が再定義されるため、上記の方法で定義されたマクロには副作用はありません。標準 C では、対応するマクロによって副作用が発生します:
リーリーコード min(ia, ib) は ((ia) に展開されます
4.キーワードの種類型は typeof (x) と typeof (y) を通じて取得できるため、min_t (type, x, y) マクロのように型を渡す必要はありません。コード行 (void) (&_x==&_y) の機能は、_x と _y の型が一致しているかどうかをチェックすることです。
5. 可変パラメータマクロリーリー
ここで、arg は残りのパラメータを表し、0 個以上のパラメータを持つことができます。これらのパラメータとパラメータ間のカンマが arg の値を構成します。次のコードに示すように、arg はマクロ展開中に置き換えられます:リーリー
は次のように展開されます:リーリー
arg がパラメータを表さない状況を処理するには、"##" を使用します。このとき、前のカンマは冗長になります。 「##」を使用した後、GNU C プリプロセッサは前のコンマを破棄するため、次のコード:リーリー
は正しく展開されます:リーリー ### の代わりに: ### リーリー
6. ラベル要素
標準 C では、配列または構造体の初期化値が固定順序で出現する必要がありますが、GNU C では、インデックスまたは構造体のメンバー名を指定することにより、初期化値を任意の順序で出現させることができます。
GNU C は、現在の関数の名前を保存する 2 つの識別子を事前定義します。__FUNCTION__ は関数の名前をソース コードに保存し、__PRETTY_FUNCTION__ は名前を言語特性とともに保存します。 C 関数では、これら 2 つの名前は同じです。
リーリーコード内の __FUNCTION__ は、文字列「example」を意味します。 C99 はすでに __func__ マクロをサポートしているため、Linux プログラミングでは __FUNCTION__ を使用せず、代わりに __func__ を使用することをお勧めします。
void example(void) { printf("This is function:%s", __func__); }
GNU C允许声明函数、变量和类型的特殊属性,以便手动优化代码和定制代码检查的方法。要指定一个声明的 属性,只需要在声明后添加__attribute__((ATTRIBUTE))。其中ATTRIBUTE为属性说明,如果存在多个属 性,则以逗号分隔。GNU C支持noreturn、format、section、aligned、packed等十多个属性。
noreturn属性作用于函数,表示该函数从不返回。这会让编译器优化代码,并消除不必要的警告信息。例如:
# define ATTRIB_NORET __attribute__((noreturn)) .... asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;
format属性也用于函数,表示该函数使用printf、scanf或strftime风格的参数,指定format属性可以让编译器根据格 式串检查参数类型。例如:
asmlinkage int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));
上述代码中的第1个参数是格式串,从第2个参数开始都会根据printf()函数的格式串规则检查参数。
unused属性作用于函数和变量,表示该函数或变量可能不会用到,这个属性可以避免编译器产生警告信息。
aligned属性用于变量、结构体或联合体,指定变量、结构体或联合体的对齐方式,以字节为单位,例如:
struct example_struct { char a; int b; long c; } __attribute__((aligned(4)));
表示该结构类型的变量以4字节对齐。
packed属性作用于变量和类型,用于变量或结构体成员时表示使用最小可能的对齐,用于枚举、结构体或联合体类型时表示该类型使用最小的内存。例如:
struct example_struct { char a; int b; long c __attribute__((packed)); };
编译器对结构体成员及变量对齐的目的是为了更快地访问结构体成员及变量占据的内存。例如,对 于一个32位的整型变量,若以4字节方式存放(即低两位地址为00),则CPU在一个总线周期内就可以读取32 位;否则,CPU需要两个总线周期才能读取32位。
GNU C提供了大量内建函数,其中大部分是标准C库函数的GNU C编译器内建版本,例如memcpy()等,它们与对应的标准C库函数功能相同。
不属于库函数的其他内建函数的命名通常以__builtin开始,如下所示。
内建函数__builtin_return_address(LEVEL)返回当前函数或其调用者的返回地址,参数LEVEL指定调用栈的级数,如0表示当前函数的返回地址,1表示当前函数的调用者的返回地址。
内建函数__builtin_constant_p(EXP)用于判断一个值是否为编译时常数,如果参数EXP的值是常数,函数返回1,否则返回0。例如,下面的代码可检测第1个参数是否为编译时常数以确定采用参数版本还是非参数版本:
#define test_bit(nr,addr) \ (__builtin_constant_p(nr) \ constant_test_bit((nr),(addr)) : \ variable_test_bit((nr),(addr)))
内建函数__builtin_expect(EXP,C)用于为编译器提供分支预测信息,其返回值是整数表达式EXP的值,C的 值必须是编译时常数。
Linux内核编程时常用的likely()和unlikely()底层调用的likely_notrace()、unlikely_notrace()就是基于 __builtin_expect(EXP,C)实现的。
#define likely_notrace(x) __builtin_expect(!!(x), 1) #define unlikely_notrace(x) __builtin_expect(!!(x), 0)
若代码中出现分支,则即可能中断流水线,我们可以通过likely()和unlikely()暗示分支容易成立还是不容易 成立,例如:
if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) if (ipv4_is_loopback(saddr)) goto e_inval;
在使用gcc编译C程序的时候,如果使用“-ansi–pedantic”编译选项,则会告诉编译器不使用GNU扩展语法。例如对 于如下C程序test.c:
struct var_data { int len; char data[0]; }; struct var_data a;
直接编译可以通过:
gcc -c test.c
如果使用“-ansi–pedantic”编译选项,编译会报警:
gcc -ansi -pedantic -c test.c test.c:3: warning: ISO C forbids zero-size array 'data'
以上がLinux GNU C と ANSI C の違いは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。