0%

Linux下C语言开发技巧

比较的宏以及typeof()类型判断宏`

#define max(a,b) ((a)>(b)?(a):(b))
// 上述无法处理包含a++等的情况
  • 使用typeof()类转换宏处理
    #define max(a, b) ({        \
    typeof(a) _a = (a); \
    typeof(b) _b = (b); \
    (void) (&_a == &_b); \
    _a > _b ? _a : _b; })
  • typeof(a) _a = (a)定义一个a类型的变量,值等于a
  • (void) (&_a == &_b)判断二者类型是否相同,不同的话会出现警告
    typeof(int *) a,b;//等价于:int *a,*b;

    零长数组(变长数组)

  • 满足需要变长度的结构体,因此有时也习惯性地称为变长数组。
  • 在一个结构体的最后, 申明一个长度为0的数组, 就可以使得这个结构体是可变长的
    struct line {
    int length;
    char contents[0];
    };

    struct line *thisline = malloc(sizeof(struct line) + this_length);
    thisline->length = this_length;
  • 上述结构体本身的大小只有一个length的大小,不包括content
  • 创建的时候人为分配空间给contents即可

switch case的条件指定范围

case low ... high:
case 'A' ...'Z':
  • 还可以用整形数来表示范围,但是这里需要注意在“…”两边有空格

struct的指定成员初始化

#include <stdio.h>

// 三维点的结构体
struct Point3D {
int x;
int y;
int z;
};

// 包含点操作的结构体
struct PointOperations {
// 函数指针,用于计算两个点的距离
double (*calculateDistance)(const struct Point3D* p1, const struct Point3D* p2);

// 函数指针,用于打印点的坐标
void (*printCoordinates)(const struct Point3D* point);
};

// 计算两个点的距离的具体实现
double calculateDistance(const struct Point3D* p1, const struct Point3D* p2) {
int dx = p1->x - p2->x;
int dy = p1->y - p2->y;
int dz = p1->z - p2->z;
return sqrt(dx*dx + dy*dy + dz*dz);
}

// 打印点的坐标的具体实现
void printCoordinates(const struct Point3D* point) {
printf("(%d, %d, %d)\n", point->x, point->y, point->z);
}

int main() {
// 定义一个三维点
struct Point3D pointA = {1, 2, 3};

// 实例化操作的结构体并初始化函数指针
struct PointOperations pointOps = {
.calculateDistance = calculateDistance,
.printCoordinates = printCoordinates
};

// 使用结构体中的函数指针计算两个点的距离
struct Point3D pointB = {4, 5, 6};
double distance = pointOps.calculateDistance(&pointA, &pointB);
printf("Distance between points: %f\n", distance);

// 使用结构体中的函数指针打印点的坐标
pointOps.printCoordinates(&pointA);

return 0;
}
  • 上文中的指定名称初始化是
    // 定义点操作的结构体并初始化函数指针
    struct PointOperations pointOps = {
    .calculateDistance = calculateDistance,
    .printCoordinates = printCoordinates
    };
  • 这里可以手动指定需要初始化的结构体成员的名字

可变参数宏

#include <linux/printk.h>

#define pr_debug(fmt, ...) \
dynamic_pr_debug(fmt, ##__VA_ARGS__)
  • dynamic_pr_debug 是 Linux 内核中用于动态调试打印的宏,而 fmt 和 __VA_ARGS__ 则是格式化字符串和可变参数列表。

  • fmt:格式化字符串,类似于 printf 中的格式化字符串,包含要打印的文本和格式说明符。

  • __VA_ARGS__:表示可变参数的宏,用于将额外的参数传递给 fmt 中的格式说明符。

  • ##__VA_ARGS__ 是一个预处理器技巧,用于处理当可变参数列表为空时的情况,确保宏在没有额外参数时也能正确展开

函数属性 __attribute__

  • __attribute__ ((attribute-list))

  • attribute-list的定义有很多,如noreturnformat以及const等。此外,还可以定义一些和处理器体系结构相关的函数属性

    void __attribute__((noreturn)) die(void);
  • 其他属性

    #define __pure           __attribute__((pure))
    #define __aligned(x) __attribute__((aligned(x)))
    #define __printf(a, b) __attribute__((format(printf, a, b)))
    #define __scanf(a, b) __attribute__((format(scanf, a, b)))
    #define noinline __attribute__((noinline))
    #define __attribute_const__ __attribute__((__const__))
    #define __maybe_unused __attribute__((unused))
    #define __always_unused __attribute__((unused))

    变量属性和类属性

  • 变量属性可以对变量或结构体成员进行属性设置。类型属性常见的属性有alignmentpackedsections等。

  • alignment属性规定变量或者结构体成员的最小对齐格式,以字节为单位

    struct qib_user_info {
    __u32 spu_userversion;
    __u64 spu_base_info;
    } __aligned(8);
  • 上述例子中结构体存储会以八字节对齐

    struct test{
    char a;
    int x[2] __attribute__ ((packed));
    };
  • x成员使用了packed属性,它会存储在变量a后面,所以这个结构体一共占用9字节

    内建函数

  • 内建函数以“_builtin_”作为函数名前缀。下面介绍Linux内核常用的一些内建函数

  • __builtin_constant_p(x):判断x是否在编译时就可以被确定为常量。如果x为常量,该函数返回1,否则返回0

  • __builtin_expect(exp, c):这里的意思是exp==c的概率很大,用来引导GCC编译器进行条件分支预测

    #define __swab16(x)        \
    (__builtin_constant_p((__u16)(x)) ? \
    ___constant_swab16(x) : \
    __fswab16(x))__builtin_expect(exp, c)
  • __builtin_prefetch(const void *addr, int rw, int locality):主动进行数据预取,在使用地址addr的值之前就把其值加载到cache中减少读取的延迟,从而提高性能。

    • 该函数可以接受3个参数:

      • 第一个参数addr表示要预取数据的地址;
      • 第二个参数rw表示读写属性,1表示可写,0表示只读;
      • 第三个参数locality表示数据在cache中的时间局部性,其中0表示读取完addr的之后不用保留在cache中,而1~3表示时间局部性逐渐增强

asmlinkage

  • 在标准C语言中,函数的形参在实际传入参数时会涉及参数存放问题
  • 对于x86架构,函数参数和局部变量被一起分配到函数的局部堆栈里
    <arch/x86/include/asm/linkage.h>
    #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
  • 告诉编译器一个声明了asmlinkage的函数不需要通过任何寄存器来传递参数,只通过堆栈来传递
  • 用法
    asmlinkage void my_assembly_function(int arg1, int arg2) 
    {
    // 汇编函数的实现
    // ...
    }
  • asmlinkage 用于标识 my_assembly_function 是一个汇编语言编写的函数,并且该函数使用堆栈而不是寄存器来传递参数
  • 对于ARM来说,函数参数的传递有一套ATPCS标准,即通过寄存器来传递。ARM中的R0~R4寄存器存放传入参数,当参数超过5个时,多余的参数被存放在局部堆栈中
    • ARM平台没有定义asmlinkage

switch case和枚举类型配合使用

#define XX(name) \
case LogLevel::name: \
return #name; \
break;

switch (level1) {
XX(DEBUG);
XX(INFO);
XX(WARN);
XX(ERROR);
XX(FATAL);
#undef XX
default:
return "UNKNOWN";
}
  • 此前包含一个枚举类型的定义包括DEBUG
  • 这个可以将枚举类型的数字逆向映射为字符串(通过#name转换为字符串)

std::enable_shared_from_this

  • 安全的获取一个对象的this指针
    • 防止重复析构等情况
  • 使用shared_from_this()函数返回当前函数的共享指针
  • 使用例
    #include <iostream>
    #include <memory>

    class Widget : public std::enable_shared_from_this<Widget>{
    public:
    Widget(){
    std::cout << "Widget constructor run" << std::endl;
    }
    ~Widget(){
    std::cout << "Widget destructor run" << std::endl;
    }

    std::shared_ptr<Widget> GetSharedObject(){
    return shared_from_this();
    }
    };

    int main()
    {
    std::shared_ptr<Widget> p(new Widget());
    std::shared_ptr<Widget> q = p->GetSharedObject();

    std::cout << p.use_count() << std::endl;
    std::cout << q.use_count() << std::endl;

    return 0;
    }

thread_local关键字

  • thread_local 是 C++11 标准引入的关键字,用于声明线程局部存储(Thread-local storage,TLS)变量。线程局部存储意味着每个线程都有自己独立的变量副本,这样可以避免线程间的竞争条件
  • 使得每个线程都有自己独立的某个这个变量的副本而不是与其他线程共享
    #include <iostream>
    #include <thread>

    thread_local int threadSpecificValue = 0;

    void threadFunction() {
    // 每个线程都有独立的 threadSpecificValue
    threadSpecificValue += 1;
    std::cout << "Thread Specific Value: " << threadSpecificValue << std::endl;
    }

    int main() {
    // 创建两个线程,并在每个线程中调用 threadFunction
    std::thread t1(threadFunction);
    std::thread t2(threadFunction);

    // 等待线程执行完成
    t1.join();
    t2.join();

    return 0;
    }