Extensions and Enhancements of Standard C Language by GNU C on Linux

Extensions and Enhancements of Standard C Language by GNU C on Linux

The C compiler available on Linux is the GNU C compiler, which is based on the programming license of the Free Software Foundation, allowing for free distribution. GNU C extends the standard C language with a series of enhancements to improve its functionality.

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

The declaration char data[0] simply means that the data[index] member of the var_data structure instance can access the indexth address after len, and it does not allocate memory for the data[] array, thus 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 following code can be used to traverse this data:

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

GNU C also allows the definition of arrays with a variable length, such as the definition of double x[n] in the following code:

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

2. Case Ranges

GNU C supports syntax like case x...y, where numbers in the range [x, y] will satisfy this case condition. 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 case '0'...'9' in the code is equivalent to:

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

3. Statement Expressions

GNU C treats a compound statement enclosed in parentheses as an expression, known as a statement expression, which can appear in any place where an expression is allowed. We can use loops, local variables, etc., which are normally only allowed in compound statements, within statement expressions, for example:

#define min_t(type,x,y) \ 
( { type _x =(x);type _y = (y); _x < _y ? _x : _y; }) 
int ia, ib, mini; 
float fa, fb, minf; 
mini = min_t(int, ia, ib); 
minf = min_t(float, fa, fb);

Since the local variables _x and _y are redefined, the macro defined in this way will not have side effects. In standard C, the corresponding macro would produce side effects:

#define min(x,y) ((x) < (y) ? (x) : (y))

The code min(++ia,++ib) would expand to ((++ia) < (++ib) ? (++ia) : (++ib)), causing the arguments passed to the macro to be incremented twice.

4. typeof Keyword

The typeof(x) statement can obtain the type of x, thus allowing us to redefine the min macro using typeof:

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

We do not need to pass type as in the min_t(type,x,y) macro, because we can obtain the type through typeof(x) and typeof(y). The line (void) (&_x == &_y) checks whether the types of _x and _y are consistent.

5. Variadic Macros

Standard C supports variadic functions, meaning that the number of function parameters is 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 be zero or more, and these parameters along with the commas between them form the value of arg. During macro expansion, arg is replaced as follows:

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

will expand to:

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

Using ## is to handle the case where arg does not represent any parameters, in which case the preceding comma becomes redundant. After using ##, the GNU C preprocessor will discard the preceding comma, so that the following code:

pr_debug("success!\n")

will correctly expand to:

printk("success!\n")

instead of:

printk("success!\n",)

6. Designated Initializers

Standard C requires that the initialization values for arrays or structures must appear in a fixed order. In GNU C, by specifying the index or structure member name, initialization values can appear in any order.

The method of specifying array indices is to add [INDEX]= before the initialization value, and it can also be specified in the form [FIRST...LAST]= to indicate a range. For example, the following code defines an array and initializes all its elements to 0:

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

The following code initializes a structure using 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 preferably adopt the standard C way:

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, 
    .ioctl      = 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 store the name of the current function, __FUNCTION__ stores the name of the function in the source code, and __PRETTY_FUNCTION__ stores the name with language-specific characteristics. In C functions, these two names are the same.

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

The __FUNCTION__ in the code means the string “example”. C99 has supported the __func__ macro, so it is recommended to no longer use __FUNCTION__ in Linux programming, but to use __func__ instead:

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

8. Special Attribute Declarations

GNU C allows the declaration of special attributes for functions, variables, and types to manually optimize code and customize code checking methods. To specify an attribute for a declaration, simply add __attribute__((ATTRIBUTE)) after the declaration. ATTRIBUTE is the attribute specification, and if there are multiple attributes, they are separated by commas. GNU C supports more than a dozen attributes such as noreturn, format, section, aligned, packed, etc.

The noreturn attribute applies to functions, indicating that the function never returns. This allows the compiler to optimize the code and eliminate unnecessary warning messages. For example:

# define ATTRIB_NORET __attribute__((noreturn)) .... 
asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;

The format attribute is also used for functions, indicating that the function uses parameters in the style of printf, scanf, or strftime. Specifying the format attribute allows the compiler to check parameter types based on the format string. For example:

asmlinkage int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));

The first parameter in the above code is the format string, and from the second parameter onwards, all will be checked according to the rules of the printf() function’s format string.

The unused attribute applies to functions and variables, indicating that the function or variable may not be used, which can prevent the compiler from generating warning messages.

The aligned attribute is used for variables, structures, or unions, specifying the alignment of the variable, structure, or union in bytes. For example:

struct example_struct { 
    char a; 
    int b; 
    long c; 
} __attribute__((aligned(4)));

This indicates that the variable of this structure type is aligned to 4 bytes.

The packed attribute applies to variables and types, indicating the use of the minimum possible alignment for variables or structure members, and for enumeration, structure, or union types, indicating that the type uses the minimum memory. For example:

struct example_struct { 
    char a; 
    int b; 
    long c __attribute__((packed)); 
};

The compiler’s purpose for aligning structure members and variables is to access the memory occupied by structure members and variables more quickly. For example, for a 32-bit integer variable, if stored in a 4-byte manner (i.e., the lower two address bits are 00), the CPU can read 32 bits in one bus cycle; otherwise, the CPU needs two bus cycles to read 32 bits.

9. Built-in Functions

GNU C provides a large number of built-in functions, most of which are built-in versions of standard C library functions, such as memcpy(), which have the same functionality as the corresponding standard C library functions.

Other built-in functions that do not belong to library functions are usually named with a __builtin prefix, as shown below.

The built-in function __builtin_return_address(LEVEL) returns the return address of the current function or its caller, with the parameter LEVEL specifying the level of the call stack, where 0 indicates the return address of the current function, and 1 indicates the return address of the current function’s caller.

The built-in function __builtin_constant_p(EXP) is used to determine whether a value is a compile-time constant. If the value of the parameter EXP is a constant, the function returns 1; otherwise, it returns 0. For example, the following code can detect whether the first parameter is a compile-time constant to determine whether to use the parameter version or the non-parameter version:

#define test_bit(nr,addr) \ 
(__builtin_constant_p(nr) \ 
constant_test_bit((nr),(addr)) : \ 
variable_test_bit((nr),(addr)))

The built-in function __builtin_expect(EXP,C) is used to provide branch prediction information to the compiler, returning the value of the integer expression EXP, where C must be a compile-time constant.

Commonly used in Linux kernel programming, the likely() and unlikely() macros are based on __builtin_expect(EXP,C) implementation:

#define likely_notrace(x) __builtin_expect(!!(x), 1) 
#define unlikely_notrace(x) __builtin_expect(!!(x), 0)

If there are branches in the code, it may interrupt the pipeline, and we can use likely() and unlikely() to hint whether the branch is likely to occur or not, for example:

if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev)))
    if (ipv4_is_loopback(saddr)) 
    goto e_inval;

When using the gcc compiler for C programs, if the -ansi -pedantic compilation option is used, it tells the compiler not to use GNU extension syntax. For example, for the following C program test.c:

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

It can be compiled directly as:

gcc -c test.c

If the -ansi -pedantic compilation option is used, the compilation will issue a warning:

gcc -ansi -pedantic -c test.c 
test.c:3: warning: ISO C forbids zero-size array 'data'

Original source: Author: Aaron Lee;

https://www.cnblogs.com/lialong1st/p/8876527.html

Copyright belongs to the original author or platform, for learning reference and academic research only. If there is any infringement, please contact for deletion~ Thank you

The author has collected some embedded learning materials. Reply in the public account with 1024 to find the download link!

Recommended good articles  Click the blue text to jump
☞ Collection | Comprehensive Programming of Linux Applications
☞ Collection | Learn Some Networking Knowledge
☞ Collection | Handwritten C Language

☞ Collection | Handwritten C++ Language
☞ Collection | Experience Sharing
☞ Collection | From Microcontrollers to Linux
☞ Collection | Power Control Technology
☞ Collection | Essential Mathematics for Embedded Systems
☞  MCU Advanced Collection
☞  Embedded C Language Advanced Collection

Leave a Comment