在C语言中,常量是指在程序运行期间其值不可更改的量。与变量不同,常量在编译时就被确定并直接嵌入到机器指令中。从底层实现来看,整型常量通常以立即数的形式存在于指令中,而字符串常量则会被存放在.rodata(只读数据段)。
注意:使用#define定义的宏常量在预处理阶段就被替换,不会占用运行时内存空间,这与const修饰的常量有本质区别。
当未指定后缀时,编译器根据数值大小自动推断类型:
c复制2147483647 // 典型int最大值(32位系统)
2147483648 // 超过int范围,自动升为long
123ULL // C99标准引入的unsigned long long类型
浮点常量默认为double类型(8字节),添加f后缀可指定为float(4字节)。在嵌入式开发中,合理选择精度可节省存储空间:
c复制3.1415926535f // float精度,节省50%内存
3.1415926535 // double精度,计算更精确
转义字符本质是ASCII码的特殊表示形式,在内存中存储为单个字节:
实际开发中,Windows换行符(\r\n)与Linux换行符(\n)的差异常导致文本文件跨平台显示异常,建议统一使用\n并在代码中做兼容处理。
字符串常量以'\0'结尾的特性决定了其内存占用比可见字符多1字节。例如"hello"实际占用6字节(5字符+1终止符)。在嵌入式系统中,大量使用字符串常量时会显著影响ROM占用,需特别注意。
c复制void func() {
static int count = 0; // 保持值的持久性
count++;
}
强制转换可能引发数据截断或精度损失,特别是在嵌入式硬件寄存器操作时:
c复制uint32_t reg = 0x12345678;
uint8_t byte = (uint8_t)(reg >> 16); // 安全提取高字节
float f = 1.234;
int i = (int)f; // 丢失小数部分,i=1
了解编译器自动转换规则可避免隐蔽bug:
调试技巧:使用-Wconversion编译选项可警告隐式类型转换
c复制uint32_t a = 4000000000;
uint32_t b = 3000000000;
uint32_t sum = a + b; // 发生溢出!
// 安全加法实现
if (UINT32_MAX - a < b) {
// 处理溢出情况
} else {
sum = a + b;
}
c复制float f1 = 0.1f;
float f2 = 0.2f;
if (f1 + f2 == 0.3f) { // 可能不成立!
// 应使用容差比较
}
嵌入式开发中常用位操作进行寄存器配置:
c复制#define LED_ON (1 << 5)
#define LED_OFF ~(1 << 5)
// 设置GPIO输出高电平
PORT |= LED_ON;
// 清除GPIO位
PORT &= LED_OFF;
sizeof是编译时运算符,不会产生运行时开销。特殊用法包括:
c复制// 获取数组元素个数
int arr[10];
size_t count = sizeof(arr)/sizeof(arr[0]);
// 结构体大小对齐检查
struct packet {
uint8_t cmd;
uint32_t data;
};
static_assert(sizeof(struct packet) == 8, "结构体对齐错误");
c复制typedef enum {
STATE_IDLE = 0,
STATE_RUNNING,
STATE_ERROR
} SystemState;
c复制#define GPIOA_BASE (0x40020000UL)
#define GPIOA_MODER (*(volatile uint32_t*)(GPIOA_BASE + 0x00))
c复制struct {
unsigned int flag1 : 1;
unsigned int flag2 : 1;
unsigned int value : 6;
} status;
c复制__attribute__((aligned(4))) uint8_t buffer[128];
c复制uint32_t x = y << 3; // 等价于y*8
uint32_t z = y >> 2; // 等价于y/4
c复制if (status & 0x80) { // 检测最高位
// 处理标志位
}
c复制#define MAX 100
void func() {
#define MAX 200 // 错误!重定义宏
}
c复制char *p = "constant";
p[0] = 'C'; // 运行时错误!尝试修改只读数据
c复制int sum; // 未初始化
for (int i=0; i<10; i++) sum += i; // 结果不确定
c复制void func() {
char buf[1024*1024]; // 大数组可能引发栈溢出
}
c复制if (flags & FLAG_A && FLAG_B) // 实际等价于(flags&FLAG_A)&&FLAG_B
// 正确应为:if ((flags & (FLAG_A|FLAG_B)) == (FLAG_A|FLAG_B))
c复制int i = 0;
int j = i++ + ++i; // 未定义行为!
在嵌入式开发实践中,我强烈建议: