Heim > Artikel > System-Tutorial > Was ist der Unterschied zwischen Linux GNU C und ANSI C?
Der unter Linux verfügbare C-Compiler ist der GNU C-Compiler, der auf der Programmierlizenz der Free Software Foundation basiert und daher frei verbreitet und verwendet werden kann. GNU C führt eine Reihe von Erweiterungen zu Standard C durch, um die Funktionalität von Standard C zu verbessern.
GNU C ermöglicht die Verwendung von Arrays mit der Länge Null, was beim Definieren der Header-Struktur von Objekten variabler Länge sehr nützlich ist. Zum Beispiel:
struct var_data { int len; char data[0]; };
char data[0] bedeutet nur, dass das Programm über das data[index]-Mitglied der var_data-Strukturinstanz auf die Indexadresse zugreifen kann. Es reserviert keinen Speicher für das data[]-Array, also sizeof (struct var_data) = sizeof(int).
Angenommen, das Datenfeld der Struktur var_data wird im Speicherbereich gespeichert, der unmittelbar auf die Struktur var_data folgt, können die Daten mit dem folgenden Code durchlaufen werden:
struct var_data s; ... for (i = 0; i printf("%02x", s.data[i]);
In GNU C können Sie auch eine Variable verwenden, um ein Array zu definieren, z. B. „double x[n]“, definiert im folgenden Code:
int main (int argc, char *argv[]) { int i, n = argc; double x[n]; for (i = 0; i return 0; }
GNU C unterstützt die Syntax von Fall x...y. Die Zahlen im Intervall [x, y] erfüllen die Bedingungen dieses Falls:
switch (ch) { case '0'... '9': c -= '0'; break; case 'a'... 'f': c -= 'a' - 10; break; case 'A'... 'F': c -= 'A' - 10; break; }
case '0'...'9' im Code entspricht dem in Standard-C:
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
GNU C behandelt die in Klammern enthaltene zusammengesetzte Anweisung als einen Ausdruck, der als Anweisungsausdruck bezeichnet wird und überall dort erscheinen kann, wo Ausdrücke zulässig sind. Wir können Schleifen, lokale Variablen usw. verwenden, die nur in zusammengesetzten Anweisungen in Anweisungsausdrücken verwendet werden können, zum Beispiel:
#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);
Da die beiden lokalen Variablen __xx und __y neu definiert werden, haben auf die oben beschriebene Weise definierte Makros keine Nebenwirkungen. In Standard C erzeugen die entsprechenden Makros unten Nebenwirkungen:
#define min(x,y) ((x)
Der Code min(++ia,++ib) wird erweitert zu ((++ia)
Die typeof(x)-Anweisung kann den Typ von x erhalten. Daher kann das Min-Makro mit Hilfe von typeof neu definiert werden:
#define min(x,y) ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x
Wir müssen keinen Typ wie beim Makro min_t (type, x, y) übergeben, da der Typ über typeof (x), typeof (y) erhalten werden kann. Die Funktion der Codezeile (void) (&_x==&_y) besteht darin, zu prüfen, ob die Typen von _x und _y konsistent sind.
Standard C unterstützt Funktionen mit variablen Parametern, was bedeutet, dass die Parameter der Funktion nicht festgelegt sind. Der Prototyp der printf()-Funktion lautet beispielsweise:
int printf( const char *format [, argument]... );
In GNU C können Makros auch eine variable Anzahl von Parametern akzeptieren, zum Beispiel:
#define pr_debug(fmt,arg...) \ printk(fmt,##arg)
Hier stellt arg die verbleibenden Parameter dar, die null oder mehr Parameter haben können, und die Kommas zwischen den Parametern bilden den Wert von arg, der während der Makroerweiterung ersetzt wird:
pr_debug("%s:%d",filename,line)
wird erweitert zu:
printk("%s:%d", filename, line)
Verwenden Sie „##“, um mit der Situation umzugehen, in der arg keinen Parameter darstellt. Zu diesem Zeitpunkt wird das vorangehende Komma überflüssig. Nach der Verwendung von „##“ verwirft der GNU C-Präprozessor das vorangehende Komma, sodass der folgende Code entsteht:
pr_debug("success!\n")
wird korrekt erweitert zu:
printk("success!\n")
statt:
printk("success!\n",)
Standard C verlangt, dass die Initialisierungswerte eines Arrays oder einer Struktur in einer festen Reihenfolge erscheinen müssen. In GNU C dürfen Initialisierungswerte durch Angabe von Indizes oder Strukturmitgliedsnamen in beliebiger Reihenfolge erscheinen.
Der Array-Index kann durch Hinzufügen von „[INDEX]=" vor dem Initialisierungswert angegeben werden. Natürlich können Sie auch einen Bereich in der Form „[FIRST...LAST]=" angeben. Der folgende Code definiert beispielsweise ein Array und weist allen darin enthaltenen Elementen den Wert 0 zu:
unsigned char data[MAX] = { [0 ... MAX-1] = 0 };
Der folgende Code initialisiert die Struktur mithilfe von Strukturelementnamen:
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, };
Linux 2.6 empfiehlt jedoch, dass ähnlicher Code so weit wie möglich in Standard-C vorliegen sollte:
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 definiert zwei Bezeichner vor, um den Namen der aktuellen Funktion zu speichern, __FUNCTION__ speichert den Namen der Funktion im Quellcode und __PRETTY_FUNCTION__ speichert den Namen mit Sprachmerkmalen. In der C-Funktion sind diese beiden Namen identisch.
void example() { printf("This is function:%s", __FUNCTION__); }
__FUNCTION__ im Code bedeutet die Zeichenfolge „Beispiel“. C99 unterstützt bereits das Makro __func__, daher wird empfohlen, __FUNCTION__ in der Linux-Programmierung nicht mehr zu verwenden und stattdessen __func__ zu verwenden:
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'
Das obige ist der detaillierte Inhalt vonWas ist der Unterschied zwischen Linux GNU C und ANSI C?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!