Home  >  Article  >  System Tutorial  >  What is the difference between Linux GNU C and ANSI C?

What is the difference between Linux GNU C and ANSI C?

王林
王林forward
2024-02-05 16:30:131412browse

The C compiler available on Linux is the GNU C compiler, which is built on the Free Software Foundation's programming license and therefore can be freely distributed and used. GNU C makes a series of extensions to standard C to enhance the functionality of standard C.

Linux GNU C 与 ANSI C 有什么区别?

1. Zero-length and variable-length arrays

GNU C allows the use of zero-length arrays, which is very useful when defining the header structure of variable-length objects. For example:

struct var_data { 
    int len; 
    char data[0]; 
};

char data[0] only means that the program can access the index address after len through the data[index] member of the var_data structure instance. It does not allocate memory for the data[] array, so sizeof(struct var_data )=sizeof(int).

Assuming that the data field of struct var_data is stored in the memory area immediately following struct var_data, the data can be traversed through the following code:

struct var_data s; 
... 
for (i = 0; i printf("%02x", s.data[i]);

In GNU C, you can also use a variable to define an array, such as "double x[n]" defined in the following code:

int main (int argc, char *argv[]) 
{ 
    int i, n = argc; 
    double x[n]; 
    for (i = 0; i return 0; 
}

2.case scope

GNU C supports the syntax of case x...y. The numbers in the interval [x, y] will meet the conditions of this case. Please see the following code:

switch (ch) { 
case '0'... '9': c -= '0'; 
    break;
case 'a'... 'f': c -= 'a' - 10; 
    break; 
case 'A'... 'F': c -= 'A' - 10; 
    break; 
}

The case '0'...'9' in the code is equivalent to that in standard C:

case '0': case '1': case '2': case '3': case '4': 
case '5': case '6': case '7': case '8': case '9':

3. Statement expression

GNU C treats the compound statement contained in parentheses as an expression, called a statement expression, which can appear anywhere where expressions are allowed. We can use loops, local variables, etc. that can only be used in compound statements in statement expressions, for example:

#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);

Because the two local variables __xx and __y are redefined, macros defined in the above way will have no side effects. In standard C, the corresponding macros will produce side effects:

#define min(x,y) ((x) 

The code min(ia, ib) will be expanded to ((ia)

4.typeof keyword

The typeof(x) statement can obtain the type of x. Therefore, the min macro can be redefined with the help of typeof:

#define min(x,y) ({ \ 
const typeof(x) _x = (x); \ 
const typeof(y) _y = (y); \ 
(void) (&_x == &_y); \ 
_x 

We don’t need to pass in type like the min_t (type, x, y) macro, because type can be obtained through typeof (x) and typeof (y). The function of the code line (void) (&_x==&_y) is to check whether the types of _x and _y are consistent.

5. Variable parameter macro

Standard C supports variable parameter functions, which means that the parameters of the function are not fixed. For example, the prototype of the printf() function is:

int printf( const char *format [, argument]... );

In GNU C, macros can also accept a variable number of parameters, for example:

#define pr_debug(fmt,arg...) \ 
printk(fmt,##arg)

Here arg represents the remaining parameters, which can have zero or more parameters. These parameters and the commas between the parameters constitute the value of arg. arg is replaced during macro expansion, as shown in the following code:

pr_debug("%s:%d",filename,line)

will be expanded to:

printk("%s:%d", filename, line)

Use "##" to handle the situation where arg does not represent any parameter. At this time, the preceding comma becomes redundant. After using "##", the GNU C preprocessor will discard the preceding comma, so the following code:

pr_debug("success!\n")

will be correctly expanded to:

printk("success!\n")

instead of:

printk("success!\n",)

6. Label elements

Standard C requires that the initialization values ​​of an array or structure must appear in a fixed order. In GNU C, initialization values ​​are allowed to appear in any order by specifying the index or structure member name.

The way to specify the array index is to add "[INDEX]=" before the initialization value. Of course, you can also specify a range in the form of "[FIRST...LAST]=". For example, the following code defines an array and assigns all elements in it to 0:

unsigned char data[MAX] = { [0 ... MAX-1] = 0 };

The following code initializes the structure with the help of structure member names:

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, 
};

However, Linux 2.6 recommends that similar code should be as standard as possible:

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, 
};

7. Current function name

GNU C predefines two identifiers to save the name of the current function, __FUNCTION__ saves the name of the function in the source code, and __PRETTY_FUNCTION__ saves the name with language characteristics. In the C function, these two names are the same.

void example() 
{ 
    printf("This is function:%s", __FUNCTION__); 
}

The __FUNCTION__ in the code means the string "example". C99 already supports the __func__ macro, so it is recommended to no longer use __FUNCTION__ in Linux programming and use __func__ instead:

void example(void) 
{ 
    printf("This is function:%s", __func__); 
}

8.特殊属性声明

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位。

9.内建函数

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'

The above is the detailed content of What is the difference between Linux GNU C and ANSI C?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete