Maison > Article > Tutoriel système > Quelle est la différence entre Linux GNU C et ANSI C ?
Le compilateur C disponible sous Linux est le compilateur GNU C, qui est construit sur la licence de programmation de la Free Software Foundation et peut donc être librement distribué et utilisé. GNU C réalise une série d'extensions au standard C pour améliorer les fonctionnalités du standard C.
GNU C permet l'utilisation de tableaux de longueur nulle, ce qui est très utile lors de la définition de la structure d'en-tête d'objets de longueur variable. Par exemple :
struct var_data { int len; char data[0]; };
char data[0] signifie uniquement que le programme peut accéder à l'adresse d'index après len via le membre data[index] de l'instance de structure var_data. Il n'alloue pas de mémoire pour le tableau data[], donc sizeof (struct var_data) =. taille de (int).
En supposant que le champ de données de la struct var_data est stocké dans la zone mémoire immédiatement après la struct var_data, les données peuvent être parcourues via le code suivant :
struct var_data s; ... for (i = 0; i printf("%02x", s.data[i]);
Dans GNU C, vous pouvez également utiliser 1 variable pour définir un tableau, tel que "double x[n]" défini dans le code suivant :
int main (int argc, char *argv[]) { int i, n = argc; double x[n]; for (i = 0; i return 0; }
GNU C prend en charge la syntaxe du cas x...y. Les nombres dans l'intervalle [x, y] rempliront les conditions de ce cas. Veuillez consulter le code suivant :
switch (ch) { case '0'... '9': c -= '0'; break; case 'a'... 'f': c -= 'a' - 10; break; case 'A'... 'F': c -= 'A' - 10; break; }
la casse '0'...'9' dans le code est équivalente à celle du standard C :
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
GNU C traite l'instruction composée contenue entre parenthèses comme une expression, appelée expression d'instruction, qui peut apparaître partout où les expressions sont autorisées. Nous pouvons utiliser des boucles, des variables locales, etc. qui ne peuvent être utilisées que dans des instructions composées dans des expressions d'instruction, par exemple :
#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);
Étant donné que les deux variables locales __xx et __y sont redéfinies, les macros définies de la manière ci-dessus n'auront aucun effet secondaire. En standard C, les macros correspondantes ci-dessous produiront des effets secondaires :
#define min(x,y) ((x)
Le code min(++ia,++ib) sera étendu à ((++ia)
L'instruction typeof(x) peut obtenir le type de x. Par conséquent, la macro min peut être redéfinie à l'aide de typeof :
.#define min(x,y) ({ \ const typeof(x) _x = (x); \ const typeof(y) _y = (y); \ (void) (&_x == &_y); \ _x
Nous n'avons pas besoin de transmettre le type comme la macro min_t (type, x, y), car le type peut être obtenu via typeof (x), typeof (y). La fonction de la ligne de code (void) (&_x==&_y) est de vérifier si les types de _x et _y sont cohérents.
Le standard C prend en charge les fonctions à paramètres variables, ce qui signifie que les paramètres de la fonction ne sont pas fixes. Par exemple, le prototype de la fonction printf() est :
.int printf( const char *format [, argument]... );
Dans GNU C, les macros peuvent également accepter un nombre variable de paramètres, par exemple :
#define pr_debug(fmt,arg...) \ printk(fmt,##arg)
Ici, arg représente les paramètres restants, qui peuvent avoir zéro ou plusieurs paramètres. Ces paramètres et les virgules entre les paramètres constituent la valeur de arg arg est remplacé lors du développement de la macro, comme indiqué dans le code suivant :
.pr_debug("%s:%d",filename,line)
sera étendu à :
printk("%s:%d", filename, line)
Utilisez "##" pour gérer la situation où arg ne représente aucun paramètre. À ce stade, la virgule précédente devient redondante. Après avoir utilisé "##", le préprocesseur GNU C supprimera la virgule précédente, donc le code suivant :
pr_debug("success!\n")
sera correctement développé en :
printk("success!\n")
au lieu de :
printk("success!\n",)
La norme C exige que les valeurs d'initialisation d'un tableau ou d'une structure apparaissent dans un ordre fixe. Dans GNU C, les valeurs d'initialisation peuvent apparaître dans n'importe quel ordre en spécifiant des index ou des noms de membres de structure.
La façon de spécifier l'index du tableau est d'ajouter "[INDEX]=" avant la valeur d'initialisation. Bien sûr, vous pouvez également spécifier une plage sous la forme de "[FIRST...LAST]=". Par exemple, le code suivant définit un tableau et attribue à tous ses éléments la valeur 0 :
unsigned char data[MAX] = { [0 ... MAX-1] = 0 };
Le code suivant initialise la structure à l'aide des noms des membres de la structure :
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, };
Cependant, Linux 2.6 recommande que le code similaire soit autant que possible en standard C :
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 prédéfinit deux identifiants pour enregistrer le nom de la fonction actuelle, __FUNCTION__ enregistre le nom de la fonction dans le code source et __PRETTY_FUNCTION__ enregistre le nom avec les caractéristiques du langage. Dans la fonction C, ces deux noms sont identiques.
void example() { printf("This is function:%s", __FUNCTION__); }
__FUNCTION__ dans le code signifie la chaîne "exemple". C99 prend déjà en charge la macro __func__, il est donc recommandé de ne plus utiliser __FUNCTION__ dans la programmation Linux et d'utiliser __func__ à la place :
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'
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!