Pengkompil C yang tersedia di Linux ialah Pengkompil GNU C, yang dibina di atas lesen pengaturcaraan Yayasan Perisian Percuma dan oleh itu boleh diedarkan dan digunakan secara bebas. GNU C membuat satu siri sambungan kepada standard C untuk meningkatkan kefungsian standard C.
GNU C membenarkan penggunaan tatasusunan sifar panjang, yang sangat berguna apabila mentakrifkan struktur pengepala objek panjang berubah-ubah. Contohnya:
struct var_data { int len; char data[0]; };
data char[0] hanya bermakna program boleh mengakses alamat indeks selepas len melalui ahli data[index] contoh struktur var_data Ia tidak memperuntukkan memori untuk tatasusunan data[], jadi sizeof (struct var_data) = sizeof(int).
Dengan mengandaikan bahawa medan data struct var_data disimpan dalam kawasan memori sejurus selepas struct var_data, data boleh dilalui melalui kod berikut:
struct var_data s; ... for (i = 0; i printf("%02x", s.data[i]);
Dalam GNU C, anda juga boleh menggunakan 1 pembolehubah untuk menentukan tatasusunan, seperti "double x[n]" yang ditakrifkan dalam kod berikut:
int main (int argc, char *argv[]) { int i, n = argc; double x[n]; for (i = 0; i return 0; }
GNU C menyokong sintaks kes x...y Nombor dalam selang [x, y] akan memenuhi syarat kes ini. Sila lihat kod berikut:
switch (ch) { case '0'... '9': c -= '0'; break; case 'a'... 'f': c -= 'a' - 10; break; case 'A'... 'F': c -= 'A' - 10; break; }
huruf '0'...'9' dalam kod adalah bersamaan dengan dalam standard C:
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
GNU C menganggap pernyataan majmuk yang terkandung dalam kurungan sebagai ungkapan, dipanggil ungkapan pernyataan, yang boleh muncul di mana-mana sahaja di mana ungkapan dibenarkan. Kita boleh menggunakan gelung, pembolehubah tempatan, dsb. yang hanya boleh digunakan dalam pernyataan majmuk dalam ungkapan pernyataan, contohnya:
#define min_t(type,x,y) \ ( { type _ _x =(x);type _ _y = (y); _ _xfloat fa, fb, minf; mini = min_t(int, ia, ib); minf = min_t(float, fa, fb);
Oleh kerana dua pembolehubah tempatan __xx dan __y ditakrifkan semula, makro yang ditakrifkan dengan cara di atas tidak akan mempunyai kesan sampingan. Dalam standard C, makro yang sepadan akan menghasilkan kesan sampingan:
#define min(x,y) ((x)
Kod min(++ia,++ib) akan dikembangkan kepada ((++ia)
Pernyataan jenis(x) boleh mendapatkan jenis x Oleh itu, makro min boleh ditakrifkan semula dengan bantuan jenis:
#define min(x,y) ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x
Kita tidak perlu memasukkan jenis seperti min_t (jenis, x, y) makro, kerana jenis boleh diperolehi melalui jenis (x), jenis (y). Fungsi baris kod (kosong) (&_x==&_y) adalah untuk menyemak sama ada jenis _x dan _y adalah konsisten.
Standard C menyokong fungsi parameter berubah, yang bermaksud bahawa parameter fungsi tidak tetap Contohnya, prototaip fungsi printf() ialah:
int printf( const char *format [, argument]... );
Dalam GNU C, makro juga boleh menerima bilangan parameter yang berubah-ubah, contohnya:
#define pr_debug(fmt,arg...) \ printk(fmt,##arg)
Di sini arg mewakili parameter yang selebihnya, yang boleh mempunyai sifar atau lebih parameter ini dan koma antara parameter membentuk nilai arg digantikan semasa pengembangan makro, seperti yang ditunjukkan dalam kod berikut:
pr_debug("%s:%d",filename,line)
akan diperluaskan kepada:
printk("%s:%d", filename, line)
Gunakan "##" untuk mengendalikan situasi di mana arg tidak mewakili sebarang parameter Pada masa ini, koma sebelumnya menjadi berlebihan. Selepas menggunakan "##", prapemproses GNU C akan membuang koma sebelumnya, jadi kod berikut:
pr_debug("success!\n")
akan dikembangkan dengan betul kepada:
printk("success!\n")
bukannya:
printk("success!\n",)
Standard C memerlukan nilai permulaan tatasusunan atau struktur mesti muncul dalam susunan tetap Dalam GNU C, nilai permulaan dibenarkan muncul dalam sebarang susunan dengan menyatakan indeks atau nama ahli struktur.
Cara untuk menentukan indeks tatasusunan adalah dengan menambah "[INDEX]=" sebelum nilai permulaan Sudah tentu, anda juga boleh menentukan julat dalam bentuk "[PERTAMA...TERAKHIR]=". Sebagai contoh, kod berikut mentakrifkan tatasusunan dan memperuntukkan semua elemen di dalamnya kepada 0:
unsigned char data[MAX] = { [0 ... MAX-1] = 0 };
Kod berikut memulakan struktur dengan bantuan nama ahli struktur:
struct file_operations ext2_file_operations = { llseek: generic_file_llseek, read: generic_file_read, write: generic_file_write, ioctl: ext2_ioctl, mmap: generic_file_mmap, open: generic_file_open, release: ext2_release_file, fsync: ext2_sync_file, };
Walau bagaimanapun, Linux 2.6 mengesyorkan bahawa kod yang serupa hendaklah berada dalam standard C sebanyak mungkin:
struct file_operations ext2_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .write = generic_file_write, .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .ioct = ext2_ioctl, .mmap = generic_file_mmap, .open = generic_file_open, .release = ext2_release_file, .fsync = ext2_sync_file, .readv = generic_file_readv, .writev = generic_file_writev, .sendfile = generic_file_sendfile, };
GNU C mentakrifkan dua pengecam untuk menyimpan nama fungsi semasa, __FUNCTION__ menyimpan nama fungsi dalam kod sumber dan __PRETTY_FUNCTION__ menyimpan nama dengan ciri bahasa. Dalam fungsi C, kedua-dua nama ini adalah sama.
void example() { printf("This is function:%s", __FUNCTION__); }
__FUNCTION__ dalam kod bermaksud rentetan "contoh". C99 sudah pun menyokong makro __func__, jadi adalah disyorkan untuk tidak lagi menggunakan __FUNCTION__ dalam pengaturcaraan Linux dan sebaliknya menggunakan __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'
Atas ialah kandungan terperinci Apakah perbezaan antara Linux GNU C dan ANSI C?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!