目录
最近依然在嵌入式遨游,以及加深对free rt os 源码以及内核的理解。以及为了解决学习工作中分布式设备的命名问题。于是就想到了 #和##这两个宏。
既然都是宏,则它们在编译的第一个阶段也就是预编译阶段被替换掉,在程序的运行过程中不存在宏。
试想我能不能在字符串中插入变量。答案就是#。它必须放在变量前,把变量字符串化。 #A 为 "A" (A必须在编译器被确定)
作用:
#define DEV(X) "DEV"X // 于是DEV(1) 就为 "DEV1"
- #define dev(x) "dev("#x")"
- #define mytest(x) "test"#x
- int main() {
-
-
-
-
- std::cout << dev(1) << "\r\n";
- std::cout << mytest(21) << "\r\n";
-
- }
结果
从结果看到确实能把参数字符串化并它们拼接在一起。
但是:#的单独使用并不能连接字符串。 正如上面所提到到,他必须要放在参数前,可以通过以下方式连接两个字符串.
#define test3(x) "hello"#x"world"
测试:std::cout<
而当我想用这样的方式两个两个以上的字符串的宏时却发现不行。
比如这样的形式会报错(编译不通过,因为不认识 xstr2)
#define str1 "hello"
#define str2 "world"
#define test3(x) str1#xstr2
这时就需要 宏 ##字符串出马了!
str1#x##str2 //把三个字符串拼接
- #define str1 "hello"
- #define str2 "world"
- #define test3(x) str1#x##str2
- int main() {
-
-
- std::cout << test3() << "\r\n";
-
- }
试想既然是在预编译就会被替换,哪#能不能变函数呢?
比如
- void fun1() {
-
- std::cout << "fun1\r\n";
-
- }
-
- #define finaltest(X) fun#X()
-
- int main() {
-
-
- finaltest(1);
-
- }
结果报错,原因是fun是未定义的标识符,而把宏改成"fun"#x"()",则更加不行,这从根本上就是字符串啊。。。至于我为什么要这么做,这看上有些笨拙,当这正是认识的过程,当然,这也为我引入后面的 宏 “##”
有了上面的认识,就知道 宏 ##可以连接任意的字符串,可以连接任意字符串的宏。同时它可以做标识符、
作用
比如连接字符串
- #define str1 "hhhhhh"
- #define mytest(x) "hello"#x##"world"##"my name is yzh"##str1
-
-
-
- void fun1() {
-
- std::cout << "fun1\r\n";
-
- }
-
-
- int main() {
- std::cout << mytest(2) << "\r\n";
- }
这个VS上提升找不到用户定义的文本运算符,当依然能够编译成功并运行。
比如做标识符
- #define finaltest(x) fun##x
-
- #define fun(x) var##x
-
-
-
-
- void fun1() {
-
-
- std::cout << "fan1\r\n";
- }
-
- int main() {
-
-
- int fun(1) = 21;
- var1 = 999;
- std::cout << fun(1) << "\r\n";
-
- finaltest(1)();
- }
运行
在不同的网络下,不同的设备连接不同的网络,订阅各自由自己的名字组成的主题,发布由自己的名字组成的主题。
比如 ,设备名是ESP1 ,订阅 dev/set/ESP1 ,发布 dev/date/ESP1 ,发布 callback/ESP1 。同时,能方便更改所连接的WIFI,所连接的服务器地址(ip)以及连接服务器所对应的端口。
WIFI账号和密码,服务器地址,端口这些用简单的宏定义就行了。
比如
#define WIFINAME "WIFI"
关键在于要把它们组成AT指令的报文,所以就有固 定的格式,对命令的每个部分都有明确的要求。这时候 宏 # 和 ##就派上用场了。
-
- #define WIFINAME "rookie" //WIFI用户名
- #define WIFIPWD "yy061457" //WIFI密码
- #define MQTTNAME "admin" //服务器用户名
- #define MQTTPWD "061457" //服务器密码
- #define MQTTPORT "1883" //服务器端口
- #define MQTTURL "192.168.1.13" //服务器地址
-
-
- #define DEVNAME(X) "ESP"#X //设备名
- #define REALNAME DEVNAME(1) //真正要用到的设备名
- #define PUBTOPICALL "callback/"##REALNAME //发布主题
- #define SUBTOPICCMD "dev/set/"##REALNAME //订阅主题
- #define PUBTOPICDATE "dev/date/"##REALNAME //订阅主题名
-
-
-
-
- extern __IO u8 mqttstate;
-
-
- //订阅主题格式
- #define SUBTOPIC "AT+MQTTSUB=0,\"%s\",0\r\n"
-
- //发布对应主题对应信息格式
- #define PUBTOPIC "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n"
-
- //连接对应WIFI 账号 密码
- #define CONNECTWIFI "AT+CWJAP=\
- \""##WIFINAME##"\",\
- \""##WIFIPWD##"\"\r\n"
-
-
- //清除MQTT连接信息
- #define CLEANMQTTINFO "AT+MQTTCLEAN=0\r\n"
-
-
-
- //连接EMQX服务器的本地登录账号和密码
- #define LOGINMQTT "AT+MQTTUSERCFG=0,1,\""##REALNAME##"\",\""##MQTTNAME##"\",\
- \""##MQTTPWD##"\",\
- 0,0,\"\"\r\n\
- "
-
- //连接本地EMQX 下的mqtt服务器地址 设置断开自动重连
- #define CONNECTMQTT "AT+MQTTCONN=0,\""##MQTTURL##"\","##MQTTPORT##",1\r\n"
另外,在free rt os ,以及stm32中的库里经常用到 # 和##来封装库函数。