GCC的扩展语法

本文介绍了GCC的多种扩展特性,包括对齐操作符`__alignof__`,匿名联合,变长数组,零长度数组,属性关键字`__attribute__`,具有返回值的复合语句,条件操作数省略,枚举不完全类型,函数参数构造,函数内嵌,函数返回地址,标识符,整数类型,更换关键字,局部标识声明,左值表达式,可变参数的宏,字符串处理,指针运算,switch-case语法,typedef用法,typeof关键字,以及联合体强制类型转换等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.对齐
__alignof__操作符返回数据类型或指定数据项的分界对齐(boundary alignment).
如: __alignof__(long long);
 
2.匿名联合
在结构中,可以声明某个联合而不是指出名字,这样可以直接使用联合的成员,就好像它们
是结构中的成员一样。
例如:
struct {
    char code;
    union {
        chid[4];
        int unmid;
    };
    char* name;
} morx;
可以使用morx.code, morx.chid, morx.numid和morx.name访问;
 
3.变长数组
数组的大小可以为动态值,在运行时指定尺寸.
例如:
void add_str(const char* str1, const char* str2)
{
    char out_str[strlen(str1)+strlen(str2)+2];
    
    strcpy(out_str, str1);
    strcat(out_str, " ");
    strcat(out_str, str2);
    printf("%s/n", out_str);
}
 
变长数组也可作为函数参数传递。
如:
void fill_array(int len, char str[len])
{
 
...
 
}
 
4.零长度数组
允许创建长度为零的数组,用于创建变长结构.只有当零长度数组是结构体的最后一个成员
的时候,才有意义.
例如:
typedef struct {
    int size;
    char str[0];
}vlen;
 
printf("sizeof(vlen)=%d/n", sizeof(vlen)), 结果是sizeof(vlen)=4 .
 
也可以将数组定义成未完成类型也能实现同样功能.
例如:
typedef struct {
    int size;
    char str[];
}vlen;
 
vlen initvlen = {4, {'a', 'b', 'c', 'd'}};
 
printf("sizeof(initvlen)=%d/n", sizeof(initvlen));
 
5.属性关键字__attribute__
__attribute__可以为函数或数据声明赋属性值.给函数分配属性值主要是为了执行优化处理.
 
例如:
void fatal_error() __attribute__ ((noreturn)); //告诉编译器该函数不会返回到调用者.
int get_lim() __attribute__ ((pure, noline)); //确保函数不会修改全局变量,
                                              //而且函数不会被扩展为内嵌函数
struct mong {
    char id;
    int code __attribute__ ((align(4));
};
 
声明函数可用的属性:
alias, always_inine, const, constructor, deprecated, destructor, format,format_arg,
malloc, no_instrument, _function, noinline, noreturn, pure, section, used, weak
声明变量可用的属性:
aligned, deprecated, mode, nocommon, packed, section, unused, vector_size, weak
声明数据类型可用的属性:
aligned, deprecated, packed, transparent_union, unused
 
6.具有返回值的复合语句
复合语句是大括号包围的语句块, 其返回值是复合语句中最后一个表达式的类型和值.
例如:
ret = ({
    int a = 5;
    int b;
    b = a+3;
    });
返回值ret的值是8.
 
7.条件操作数省略
例如:
 x = y? y:z;
 如果y为表达式,则会被计算两次,GCC支持省略y的第二次计算
 x = y? : z;
以消除这种副作用.
 
8.枚举不完全类型
可以无需明确指定枚举中的每个值, 声明方式和结构一样, 只要声明名字, 无需指定内容.
例如:
    enum color_list;
    .....
    enum color_list {BLACK, WHITE, BLUE};
 
9.函数参数构造
void* __builtin_apply_args(void);
void* __builtin_apply(void (*func)(), void* arguments, int size);
__builtin_return(void* result);
 
10.函数内嵌
通过关键字inline将函数声明为内嵌函数, ISO C中的相应关键字是__inline__
 
11.函数名
内嵌宏__FUNCTION__保存了函数的名字, ISO C99中相应的宏是__func__
 
12.函数嵌套
支持函数内嵌定义, 内部函数只能被父函数调用.    
 
13.函数原型
新的函数原型可以覆盖旧风格参数列表表明的函数定义, 只要能够和旧风格参数的升级相匹配
既可.
例如:
下面可用的函数原型, 在调用是short参数可以自动升级为int
    int trigzed(int zvalue);
    ......
    int trized(zvalue)
    short zvalue;
    {
        return (zvalue==0);
    }
 
14.函数返回地址和堆栈帧
void* __builtin_return_address(unsigned int level);
void* __builtin_frame_address(unsigned int level);
 
15.标识符
标识符可以包含美元符号$.
 
16.整数
long long int a; //Singed 64-bit integer  
unsigned long long int b; //Unsigned 64-bit integer
a = 8686LL;
b = 8686ULL;
 
17.更换关键字
命令行选项-std和-ansi会使关键字asm,typeof和inline失效,但是在这里可以使用他们的
替代形式__asm__, __typeof__和__inline__
 
18.标识地址
可以使用标识来标记地址,将它保存到指针中,再用goto语句跳转到标记处.可通过&&操作符
返回地址.而对表达式得出的所有空指针,使用goto语句可进行跳转.
例如:
    #include <stdio.h>
    #include <time.h>
    int main(void)
    {
        void* target;
        time_t noe;
        
        now = time((time_t*)NULL);
        if (now & 0x1)
            target = &&oddtag;
        else
            target = &&eventag;
 
        goto *target;
enentag:
    printf("The time value %ld is even/n", now);
    return 0;
 
oddtag:
    printf("The time value %ld is odd/n", now);
    return 0;
}    
 
19.局部标识声明
使用关键字__label__说明标识为局部标识,然后在该范围内使用.
    {
        __label__ restart, finished; //声明两个局部标识
        ...
        goto restart;
    }
 
20.左值表达式
赋值操作符的左边可以使用复合表达式.
例如:
    (fn(), b) = 10; //访问复合表达式的最后一个成员变量的地址
    和fn(), (b = 10);相同
    
    ptr = &(fn(), b); //取复合表达式的最后一个成员的地址, ptr指向b
    
    ((a>5)?b:c) = 100; //条件表达式也可作为左值, 如果a大于5,则b的值是100,否则c的值是100
 
21.可变参数的宏
    ISO C99创建宏的变参宏如下:
    #define errout(fmt,...) fprintf(stderr, fmt, __VA_ARGS__)
    
    GCC支持上面形式,同时支持下面形式:
    #define errout(fmt,args...) fprintf(stderr, fmt, args)
 
22.字符串
一行新的字符可以被嵌入到字符串中,而不需要使用转义符/n.可按照字面意思将他们包含在源码中.
例如:
    char* str1 = "A string on/ntwo lines";
    char* str1 = "A string on
    two lines";
    上面两个字符串等价.
    
字符串换行符/可以省略.
例如:
    char* str3 = "The string will /
    be joined into one line.";
    char* str4 = "The string will
    be joined into one line.";
    上面两个字符串等价.
 
23.指针运算
支持void和函数指针加减运算,进行运算的单位是1.
 
24.Switch和Case分支语句
例如:
    支持 case 8 ... 10:
 
25.typedef
关键字typedef可根据表达式的数据类型创建数据类型.
    typedef name = expression;
 
例如:
    typedef smallreal = 0.0f;
    typedef largereal = 0.0;
    smallreal real1;
    largereal real2;
    类型smallreal为单精度浮点类型, 而largereal为双精度浮点类型.
 
    #define swap(a,b) /
    ({ typedef _tp = a; /
       _tp temp = a; /
       a = b; /
       b = temp; })
 
26.typeof
关键字typeof可以声明类型表达式.用法与sizeof类型, 但是结果是类型而不是size.
例如:
    char* pchar; //A char pointer
    typeof (*pchar) ch; //A char
    typeof (pchar) pcarray[10]; //Ten char pointers
 
27.联合体强制类型转换
如果数据项和联合体中成员类型相同,可以将数据项强制转为联合.
例如:
    union dparts {
        unsigned char byte[8];
        double dbl;
    };
    
    double v = 3.1415;
    printf("%02X", ((union dparts)v).byte[0]);
 
但是联合强制类型转换的结果不能作为左值.
例如:
    (union dparts)v.dbl = 1.2; // Error
    
    
   
### GCC 链接器的语法和用法 GCC 的链接阶段是整个编译流程中的最后一步,负责将目标文件(`.o` 或 `.obj`)以及库文件组合成可执行程序或共享库。以下是关于 GCC 链接器的语法和用法的具体说明。 #### 1. 基本语法 GCC 使用 `-o` 参数来指定最终输出的二进制文件名。如果没有提供该参数,默认情况下会生成名为 `a.out` 的可执行文件。 基本命令结构如下: ```bash gcc [-L<library_path>] [-l<library_name>] input_files... -o output_file ``` - **input_files**: 可以为源代码文件、汇编文件或者目标文件。 - **output_file**: 输出的可执行文件名称。 - **-L<library_path>**: 添加额外的库路径供链接器查找动态/静态库。 - **-l<library_name>**: 指定要链接的库,通常省略前缀 `lib` 和扩展名 `.so` 或 `.a`。 例如: ```bash gcc main.o utils.o -o program ``` 这条命令表示将两个目标文件 `main.o` 和 `utils.o` 进行链接并生成名为 `program` 的可执行文件[^1]。 --- #### 2. 动态库与静态库的链接 GCC 支持两种类型的库:**静态库**(`.a` 文件)和**动态库**(Linux 下为 `.so` 文件)。通过 `-l` 参数可以指定需要链接的库。 ##### (1) 静态库链接 静态库会在链接时被嵌入到最终的可执行文件中。假设有一个静态库 `libmylib.a` 存在于 `/usr/local/lib` 路径下,则可以通过以下方式链接: ```bash gcc main.o -L/usr/local/lib -lmylib -o program ``` 这里 `-L/usr/local/lib` 表示告诉链接器在 `/usr/local/lib` 中寻找库文件;而 `-lmylib` 则指定了需要链接的库为 `libmylib.a`[^2]。 ##### (2) 动态库链接 对于动态库,链接器只需要知道它的存在位置即可,在运行时由操作系统加载实际的库文件。例如,如果动态库位于标准路径之外,需显式指定路径: ```bash gcc main.o -L/path/to/dynamic/libs -lmydynlib -o program ``` 此时生成的可执行文件依赖于动态库 `libmydynlib.so`,并且在运行时需要确保此库能够被找到(可通过设置环境变量 `LD_LIBRARY_PATH` 实现)[^3]。 --- #### 3. 自定义链接脚本 有时可能需要更精细地控制链接行为,比如调整内存布局或段分配规则。这可以通过自定义链接脚本来实现。使用 `-T` 参数传递链接脚本文件给链接器: ```bash gcc *.o -o program -T custom.ld ``` 其中 `custom.ld` 是一个 Linker Script 文件,用于描述如何安排输入节区(Sections)、地址空间分布等内容[^1]。 --- #### 4. 特殊选项 一些常用的特殊选项可以帮助调试或优化链接过程: - **--static**: 强制仅使用静态库进行链接。 - **-shared**: 创建共享对象(Shared Object),即动态库。 - **-nostdlib/-nodefaultlibs**: 不链接标准 C 库或其他默认库。 - **-Wl,<options>**: 将特定选项传递给底层链接器(通常是 GNU ld)。 例如创建一个简单的动态库: ```bash gcc -shared -fPIC myfunc.c -o libmyfunc.so ``` 这里的 `-fPIC` 表示生成位置无关代码(Position Independent Code),这是构建动态库所必需的[^3]。 --- ### 示例代码 下面是一个完整的例子,展示从编写源码到最后生成可执行文件的过程: ```c // hello.c #include <stdio.h> int main() { printf("Hello, World!\n"); return 0; } ``` 依次执行以下命令完成编译和链接: ```bash # 预处理 gcc -E hello.c -o hello.i # 汇编 gcc -S hello.i -o hello.s # 编译为目标文件 gcc -c hello.s -o hello.o # 链接生成可执行文件 gcc hello.o -o hello_program ``` --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值