面试题答案
一键面试宏的特性方面
- 宏展开的不确定性:宏是在预处理阶段进行文本替换,这可能导致在复杂表达式中出现意外的优先级问题。为了避免这种情况,在宏定义时,将所有参数用括号括起来,确保其优先级正确。例如
#define SET_BIT(data, bit) (((data) |= (1UL << (bit))))
,#define CLEAR_BIT(data, bit) (((data) &= ~(1UL << (bit))))
。
编译器优化方面
- 内存屏障:现代编译器会对代码进行优化,可能会打乱位操作的顺序。使用编译器特定的内存屏障指令,如
__sync_synchronize()
(GCC编译器),来防止编译器对指令进行重排序。在宏操作前后添加内存屏障,确保位操作按预期顺序执行。例如:
#define SET_BIT(data, bit) do { \
__sync_synchronize(); \
(data) |= (1UL << (bit)); \
__sync_synchronize(); \
} while(0)
#define CLEAR_BIT(data, bit) do { \
__sync_synchronize(); \
(data) &= ~(1UL << (bit)); \
__sync_synchronize(); \
} while(0)
多线程同步方面
- 互斥锁(Mutex):使用互斥锁来保护对共享数据的位操作。在进入位操作前锁定互斥锁,操作完成后解锁。
#include <pthread.h>
pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
#define SET_BIT(data, bit) do { \
pthread_mutex_lock(&data_mutex); \
(data) |= (1UL << (bit)); \
pthread_mutex_unlock(&data_mutex); \
} while(0)
#define CLEAR_BIT(data, bit) do { \
pthread_mutex_lock(&data_mutex); \
(data) &= ~(1UL << (bit)); \
pthread_mutex_unlock(&data_mutex); \
} while(0)
- 原子操作:如果编译器支持原子操作(如C11标准中的
<stdatomic.h>
头文件),可以使用原子类型和原子操作函数代替普通的位操作宏。例如:
#include <stdatomic.h>
atomic_uint64_t data;
#define SET_BIT(bit) atomic_fetch_or(&data, 1UL << (bit))
#define CLEAR_BIT(bit) atomic_fetch_and(&data, ~(1UL << (bit)))
原子操作保证了操作的原子性,避免了数据竞争,同时也具有一定的内存屏障效果。