19.预处理-宏定义

1.预处理

在C语言程序源码中,凡是以井号(#)开头的语句被称为预处理语句,这些语句严格意义上并不属于C语言语法的范畴,它们在编译的阶段统一由所谓预处理器(cc1)来处理。所谓预处理,顾名思义,指的是真正的C程序编译之前预先进行的一些处理步骤,这些预处理指令包括:

  1. 头文件:#include
  2. 定义宏:#define
  3. 取消宏:#undef
  4. 条件编译:#if、#ifdef、#ifndef、#else、#elif、#endif
  5. 显示错误:#error
  6. 修改当前文件名和行号:#line
  7. 向编译器传送特定指令:#progma
  • 基本语法
    • 一个逻辑行只能出现一条预处理指令,多个物理行需要用反斜杠连接成一个逻辑行
    • 预处理是整个编译全过程的第一步:预处理 - 编译 - 汇编 - 链接
    • 可以通过如下编译选项来指定来限定编译器只进行预处理操作:
gcc example.c -o example.i -E

2.宏的概念

宏(macro)实际上就是一段特定的字串,在源码中用以替换为指定的表达式。例如:

#define PI 3.14

此处,PI 就是宏(宏一般习惯用大写字母表达,以区分于变量和函数,但这并不是语法规定,只是一种习惯),是一段特定的字串,这个字串在源码中出现时,将被替换为3.14。例如:

int main()
{
    printf("圆周率: %f\n", PI); 
    // 此语句将被替换为:printf("圆周率: %f\n", 3.14);
}

  • 宏的作用:
    • 使得程序更具可读性:字串单词一般比纯数字更容易让人理解其含义。
    • 使得程序修改更易行:修改宏定义,即修改了所有该宏替换的表达式。
    • 提高程序的运行效率:程序的执行不再需要函数切换开销,而是就地展开。

3.无参宏

无参宏意味着使用宏的时候,无需指定任何参数,比如:

#define PI          3.14
#define SCREEN_SIZE 800*480*4 
int main()
{
    // 在代码中,可以随时使用以上无参宏,来替代其所代表的表达式:
    printf("圆周率: %f\n", PI); 
    mmap(NULL, SCREEN_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, ...);
}

注意到,上述代码中,除了有自定义的宏,还有系统预定义的宏:

// 自定义宏:
#define PI          3.14
#define SCREEN_SIZE 800*480*4 

// 系统预定义宏
#define NULL ((void *)0)
#define PROT_READ    0x1    /* Page can be read.  */
#define PROT_WRITE    0x2    /* Page can be written.  */
#define MAP_SHARED    0x01    /* Share changes.  */

宏的最基本特征是进行直接文本替换,以上代码被替换之后的结果是:

int main()
{
    printf("圆周率: %f\n", 3.14); 
    mmap(((void *)0), 800*480*4, 0x1|0x2, 0x01, ...);
}

4.带参宏

带参宏意味着宏定义可以携带“参数”,从形式上看跟函数很像,例如:

#define MAX(a, b)   a>b ? a : b
#define MIN(a, b)   a<b ? a : b

以上的MAX(a,b) 和 MIN(a,b) 都是带参宏,不管是否带参,宏都遵循最初的规则,即宏是一段待替换的文本,例如在以下代码中,宏在预处理阶段都将被替换掉:

int main()
{
    int x = 100, y = 200;
    printf("最大值:%d\n", MAX(x, y));
    printf("最小值:%d\n", MIN(x, y));
    // 以上代码等价于:
    // printf("最大值:%d\n", x>y ? x : y);
    // printf("最小值:%d\n", x<y ? x : y);
}

  • 带参宏的特点:
    1. 直接文本替换,不做任何语法判断,更不做任何中间运算。
    2. 宏在编译的第一个阶段就被替换掉,运行中不存在宏。
    3. 宏将在所有出现它的地方展开,这一方面浪费了内存空间,另一方面有节约了切换时间。

5.带参宏的副作用

由于宏仅仅做文本替换,中间不涉及任何语法检查、类型匹配、数值运算,因此用起来相对函数要麻烦很多。例如:

#define MAX(a, b) a>b ? a : b

int main()
{
    int x = 100, y = 200;
    printf("最大值:%d\n", MAX(x, y==200?888:999));
}

直观上看,无论 y 的取值是多少,表达式 y==200?888:999 的值一定比 x 要大,但由于宏定义仅仅是文本替换,中间不涉及任何运算,因此等价于:

printf("最大值:%d\n", x>y==200?888:999 ? x : y==200?888:999);

可见,带参宏的参数不能像函数参数那样视为一个整体,整个宏定义也不能视为一个单一的数据,事实上,不管是宏参数还是宏本身,都应被视为一个字串,或者一个表达式,或者一段文本,因此最基本的原则是:

  • 将宏定义中所有能用括号括起来的部分,都括起来,比如:
#define MAX(a, b) ((a)>(b) ? (a) : (b))

6.宏定义中的符号粘贴

有些时候,宏参数中的符号并非用来传递数据,而是用来形成多种不同的字串,例如在某些系统函数中,系统本身规范了函数接口的部分标准,形如:

void __zinitcall_service_1(void)
{
    ...
}

void __zinitcall_service_2(void)
{
    ...
}

void __zinitcall_feature_1(void)
{
    ...
}

void __zinitcall_feature_2(void)
{
    ...
}

此时,若需要向用户提供一个方便整合字串的宏定义,可以这么写:

#define LAYER_INITCALL(num, layer)  __zinitcall_##layer##_##num

用户的调用如下:

LAYER_INITCALL(service, 1);
LAYER_INITCALL(service, 2);
LAYER_INITCALL(feature, 1);
LAYER_INITCALL(feature, 2);

注意:

在书写非字符串的字串时(如上述例子),使用两边双井号来粘贴字串,并且要注意如果字串出现在最末尾,则最后的双井号必须去除,例如上述代码不可写成:

#define LAYER_INITCALL(num, layer)  __zinitcall_##layer##_##num##

但如果粘贴的字串并非出现在最末尾,则前后都必须加上双井号:

#define LAYER_INITCALL(num, layer)  __zinitcall_##layer##_##num##end

注意:

另外,如果字串本身拼接为字符串,那么只需要使用一个井号即可,比如:

#define domainName(a, b) "www." #a "." #b ".com"

int main()
{
    printf("%s\n", domainName(yueqian, lab));
}

执行打印如下:

gec@ubuntu:~$ ./a.out
www.yueqian.lab.com
gec@ubuntu:~$

<think>嗯,用户遇到了Arduino编译错误,提示是expected ')' before numeric constant,发生在void loop()函数里。首先,我需要分析这个错误的原因。根据引用[2]中的信息,类似的错误通常是因为变量名与宏定义同名导致的重复定义问题。比如,如果有一个宏定义叫做Size,而用户又定义了一个名为Size的变量,编译器就会报错。所以,我需要建议用户检查代码中的变量名是否与已有的宏定义冲突。 接下来,常见的括号不匹配也可能导致这个错误。比如函数调用时少了一个右括号,或者结构体、数组初始化时括号不闭合。例如,如果用户写了一个类似digitalWrite(13, HIGH;,漏掉了右括号,编译器可能误认为后面的数字常量前面缺少括号,从而报错。这时候需要仔细检查函数调用和语法结构。 另外,检查数字常量的使用是否正确也很重要。比如在需要表达式的地方直接写了数字而没有正确的运算符,或者数组索引使用了非整型常量。例如,int arr[5.5];这样的声明会导致错误,因为数组大小必须是整数。 用户提到的错误发生在void loop()里,所以需要具体查看该函数内的代码。可能的情况包括:函数调用参数不完整、条件语句缺少括号、结构体或数组初始化错误等。建议用户定位到报错的行号,检查附近的代码是否有明显的语法问题,比如括号不匹配或者变量名冲突。 此外,预处理指令如#define可能无意中修改了常量或函数名,导致语法错误。比如,如果用户定义了一个宏#define A 5,然后在代码中使用了变量名A,这会引起冲突。需要检查所有宏定义是否与变量名、函数名重复。 最后,如果用户无法自行解决,建议提供具体的代码片段,以便更准确地诊断问题。例如,用户可能在某个函数调用中漏掉了括号,或者在变量声明时与宏定义冲突。通过逐步排除这些可能性,应该能够解决这个编译错误。</think>关于Arduino编译错误`expected ')' before numeric constant`的解决方法如下,分步骤说明: ### 1. **检查变量与宏定义冲突** 这是最常见的原因[^2]。例如: ```arduino #define MAX 100 // 宏定义 int MAX = 200; // 变量名与宏同名(报错源) ``` **解决方法**:检查代码中所有`#define`定义的宏名称是否与变量名重复,建议用全大写命名宏(行业惯例)。 --- ### 2. **验证括号闭合** 在`void loop()`中特别注意函数调用: ```arduino digitalWrite(13, HIGH; // 缺少右括号(正确应为HIGH) ``` **解决方法**:用IDE的括号匹配功能(快捷键Ctrl+Shift+P)检查括号闭合。 --- ### 3. **检查数字常量使用** 在需要表达式的位置直接使用数字: ```arduino int value = (5 + 3 * 2; // 缺少右括号(正确应为(5+3)*2) ``` **解决方法**:数学表达式需保证运算符和括号成对出现。 --- ### 4. **检查数组/结构体初始化** ```arduino int arr[] = {1, 2, 3; // 错误的分号(正确应为逗号) struct { int size; } obj = {5}; // 正确 ``` --- ### 5. **定位具体行号** IDE会提示类似错误信息: ``` sketch.ino: In function 'void loop()': sketch.ino:15:25: error: expected ')' before numeric constant ``` **关键操作**:跳转到第15行检查附近代码。 --- ### 示例修正 错误代码: ```arduino #define TIMEOUT 100 void loop() { int TIMEOUT = 50; // 与宏定义冲突 digitalWrite(13, HIGH; } ``` 修正后: ```arduino #define TIMEOUT 100 void loop() { int timeout = 50; // 更改变量名 digitalWrite(13, HIGH); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玄月初二丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值