C语言冷门知识

572 阅读3分钟

attribute

__attribute__可以设置函数属性,变量属性和类型属性

  • 函数属性:noreturn, noinline, always_inline, pure, const, nothrow, sentinel, format, format_arg, no_instrument_function, section, constructor, destructor, used, unused, deprecated, weak, malloc, alias, warn_unused_result, nonnull
  • 变量属性:aligned, packed
  • 类型属性:aligned, packed, transparent_union, unused, deprecated, may_alias

属性的书写格式为 __attribute__((xxx))

当然也可以在这些属性关键字前后添加下划线,来防止头文件中存在同名的宏导致的错误,如:__attribute__((__xxx__))

format

__attribute__((format(archetype, string-index, first-to-check)))

format属性可以给被声明的函数加上类似 printfscanf 的特征,使编译器检查函数声明和实际调用参数之间的格式化字符串是否匹配。

  • archetype:只能填入 printfscanf,指定按照哪种方式来检测参数;
  • string-index:说明格式化字符串是第几个参数;
  • first-to-check:指定第一个用于格式化可变参数是传入的第几个参数

如果函数是一个成员函数,那么他还会传入一个隐藏的 this 指针作为第一个参数。这时,需要注意指定的参数位置是否正确!

检查从函数 log2 参数开始的参数是否能依次格式化到第 1 个参数指定的字符串中
​
extern void log(const char *fmt, ...) __attribute__((__format__(printf, 1, 2)));
​
log("integer: %d\n", 0);
log("string: %s\n", "abc");

noreturn

告诉编译器,函数永远不会 return,而是退出程序。在使用该属性时,建议开启 -O2 优化,编译器会自动将该函数的 return 语句忽略,并且直接退出。该函数之后的所有代码都会被优化掉(忽略)

packed

让该变量、结构体或结构体字段不要对齐

struct __attribute__((__packed__)) packed_struct_t {
    char a;
    int b;
    char c;
};

宏定义

#运算符

#的功能是将其后面的宏参数进行字符串化操作

如果宏定义中某参数前有 # 运算符,则不展开对应参数,而是直接将该参数转为一个字符串

#include<stdio.h>
#define Str(s) #s
int main(){
    printf(Str(hello world));
    return 0;
}
#include <stdio.h>#define FOO(a, ...) #a#__VA_ARGS__
#define PRINT_SELF(...) #__VA_ARGS__int main() {
    printf("%s\n", FOO(1, 2, 3));   
    printf("%s\n", PRINT_SELF(1, 2, 3));    
    return 0;
}
​
12, 3
1, 2, 3

##连接符

#include<stdio.h>
#define Var(x) var##x
int main()
{
    int Var(1)=1,Var(2)=2,Var(3)=3;
    printf("var1=%d\n",var1);
    printf("var2=%d\n",var2);
    printf("var3=%d\n",var3);
    return 0;
}

预定义宏

C99 开始引入一个特殊标识符 __func__,该标识符是一个变量名而非宏定义,因此不属于宏定义的范畴,但是它的作用和 __FILE__, __LINE__ 类似。

#include <stdio.h>
​
void func() {
    printf("This function is %s\n", __func__);
    printf("This is line %d\n", __LINE__);
}
​
int main() {
    printf("This file is %s\n", __FILE__);
    printf("This date is %s\n", __DATE__);
    printf("This time is %s\n", __TIME__);
    printf("This function is %s\n", __func__);
    printf("This is line %d\n", __LINE__);
    func();
    return 0;
}

泛型

_Generic 是 C11 的关键字,第一项是一个表达式,后面每项都由一个类型、一个冒号和一个值组成,如 _Generic(x, int: 0, float: 1, double: 2, default3) 。当然,后面的值也可以是字符串

#include <stdio.h>#define HAHA(x) _Generic((x), \
    int: "data is int", \
    float: "data is float", \
    double: "data is double", \
    default: "other")int main() {
    printf("%s\n", HAHA(1));       // Output: data is int
    printf("%s\n", HAHA(1.0f));    // Output: data is float
    printf("%s\n", HAHA(1.0));     // Output: data is double
    return 0;
}