【C/C++系列】#define中#和##的作用

本文介绍了C/C++预处理器宏中的#和##操作符的作用。#将后面的操作数转化为字符串字面量,##用于连接两个 token。文中提供示例说明了这两个操作符在宏定义中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Date:2017/9/29


     在学习的过程中发现,宏函数的作用还是很大的。通过使用宏函数可以有效减少函数的声明。通过连接符##可以实现这一点。下面就记录一下#define中#和##的作用。

     #:字符串化,宏定义中的#是把跟在后面的参数转成一个字符串;

   ##:连接字符串

例子:

 

#include <iostream>
using namespace std;
#define to_string(s) #s
#define test_macro  "hello"##"world"
int main(int argc, char* argv[])
{
	cout << to_string(Hello World!) << endl;
	cout << test_macro << endl;
	return 0;
}

 

输出分别为:Hello World!

                      helloworld

参考:

     http://blog.youkuaiyun.com/qq_15457239/article/details/56842450

     http://www.cnblogs.com/little-ant/p/3463080.html

/** * Copyright (c) 2019 - 2021, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef PCA10100_H #define PCA10100_H #ifdef __cplusplus extern "C" { #endif #include "nrf_gpio.h" // LEDs definitions for PCA10056 #define LEDS_NUMBER 4 #define LED_1 NRF_GPIO_PIN_MAP(0,13) #define LED_2 NRF_GPIO_PIN_MAP(0,14) #define LED_3 NRF_GPIO_PIN_MAP(0,15) #define LED_4 NRF_GPIO_PIN_MAP(0,16) #define LED_START LED_1 #define LED_STOP LED_4 #define LEDS_ACTIVE_STATE 0 #define LEDS_LIST { LED_1, LED_2, LED_3, LED_4 } #define LEDS_INV_MASK LEDS_MASK #define BSP_LED_0 13 #define BSP_LED_1 14 #define BSP_LED_2 15 #define BSP_LED_3 16 #define BUTTONS_NUMBER 4 #define BUTTON_1 11 #define BUTTON_2 12 #define BUTTON_3 24 #define BUTTON_4 25 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP #define BUTTONS_ACTIVE_STATE 0 #define BUTTONS_LIST { BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 } #define BSP_BUTTON_0 BUTTON_1 #define BSP_BUTTON_1 BUTTON_2 #define BSP_BUTTON_2 BUTTON_3 #define BSP_BUTTON_3 BUTTON_4 #define RX_PIN_NUMBER 8 #define TX_PIN_NUMBER 6 #define CTS_PIN_NUMBER 7 #define RTS_PIN_NUMBER 5 #define HWFC true // serialization APPLICATION board - temp. setup for running serialized MEMU tests #define SER_APP_RX_PIN NRF_GPIO_PIN_MAP(0,21) // UART RX pin number. #define SER_APP_TX_PIN NRF_GPIO_PIN_MAP(0,22) // UART TX pin number. #define SER_APP_CTS_PIN NRF_GPIO_PIN_MAP(0,2) // UART Clear To Send pin number. #define SER_APP_RTS_PIN NRF_GPIO_PIN_MAP(0,23) // UART Request To Send pin number. #define SER_APP_SPIM0_SCK_PIN NRF_GPIO_PIN_MAP(0,27) // SPI clock GPIO pin number. #define SER_APP_SPIM0_MOSI_PIN NRF_GPIO_PIN_MAP(0,2) // SPI Master Out Slave In GPIO pin number #define SER_APP_SPIM0_MISO_PIN NRF_GPIO_PIN_MAP(0,26) // SPI Master In Slave Out GPIO pin number #define SER_APP_SPIM0_SS_PIN NRF_GPIO_PIN_MAP(0,21) // SPI Slave Select GPIO pin number #define SER_APP_SPIM0_RDY_PIN NRF_GPIO_PIN_MAP(0,23) // SPI READY GPIO pin number #define SER_APP_SPIM0_REQ_PIN NRF_GPIO_PIN_MAP(0,22) // SPI REQUEST GPIO pin number // serialization CONNECTIVITY board #define SER_CON_RX_PIN NRF_GPIO_PIN_MAP(0,22) // UART RX pin number. #define SER_CON_TX_PIN NRF_GPIO_PIN_MAP(0,21) // UART TX pin number. #define SER_CON_CTS_PIN NRF_GPIO_PIN_MAP(0,23) // UART Clear To Send pin number. Not used if HWFC is set to false. #define SER_CON_RTS_PIN NRF_GPIO_PIN_MAP(0,2) // UART Request To Send pin number. Not used if HWFC is set to false. #define SER_CON_SPIS_SCK_PIN NRF_GPIO_PIN_MAP(0,27) // SPI SCK signal. #define SER_CON_SPIS_MOSI_PIN NRF_GPIO_PIN_MAP(0,2) // SPI MOSI signal. #define SER_CON_SPIS_MISO_PIN NRF_GPIO_PIN_MAP(0,26) // SPI MISO signal. #define SER_CON_SPIS_CSN_PIN NRF_GPIO_PIN_MAP(0,21) // SPI CSN signal. #define SER_CON_SPIS_RDY_PIN NRF_GPIO_PIN_MAP(0,23) // SPI READY GPIO pin number. #define SER_CON_SPIS_REQ_PIN NRF_GPIO_PIN_MAP(0,22) // SPI REQUEST GPIO pin number. #define SER_CONN_CHIP_RESET_PIN NRF_GPIO_PIN_MAP(1,1) // Pin used to reset connectivity chip // Arduino board mappings #define ARDUINO_SCL_PIN 27 // SCL signal pin #define ARDUINO_SDA_PIN 26 // SDA signal pin #define ARDUINO_AREF_PIN 2 // Aref pin #define ARDUINO_13_PIN NRF_GPIO_PIN_MAP(0, 23) // Digital pin 13 #define ARDUINO_12_PIN NRF_GPIO_PIN_MAP(0, 22) // Digital pin 12 #define ARDUINO_11_PIN NRF_GPIO_PIN_MAP(0, 21) // Digital pin 11 #define ARDUINO_10_PIN NRF_GPIO_PIN_MAP(0, 20) // Digital pin 10 #define ARDUINO_9_PIN NRF_GPIO_PIN_MAP(0, 19) // Digital pin 9 #define ARDUINO_8_PIN NRF_GPIO_PIN_MAP(0, 17) // Digital pin 8 #define ARDUINO_7_PIN NRF_GPIO_PIN_MAP(1, 8) // Digital pin 7 #define ARDUINO_6_PIN NRF_GPIO_PIN_MAP(1, 7) // Digital pin 6 #define ARDUINO_5_PIN NRF_GPIO_PIN_MAP(1, 6) // Digital pin 5 #define ARDUINO_4_PIN NRF_GPIO_PIN_MAP(1, 5) // Digital pin 4 #define ARDUINO_3_PIN NRF_GPIO_PIN_MAP(1, 4) // Digital pin 3 #define ARDUINO_2_PIN NRF_GPIO_PIN_MAP(1, 3) // Digital pin 2 #define ARDUINO_1_PIN NRF_GPIO_PIN_MAP(1, 2) // Digital pin 1 #define ARDUINO_0_PIN NRF_GPIO_PIN_MAP(1, 1) // Digital pin 0 #define ARDUINO_A0_PIN 3 // Analog channel 0 #define ARDUINO_A1_PIN 4 // Analog channel 1 #define ARDUINO_A2_PIN 28 // Analog channel 2 #define ARDUINO_A3_PIN 29 // Analog channel 3 #define ARDUINO_A4_PIN 30 // Analog channel 4 #define ARDUINO_A5_PIN 31 // Analog channel 5 #ifdef __cplusplus } #endif #endif // PCA10100_H 详细介绍下这段代码
最新发布
08-09
<think>我们正在解决两个问题:1.如何强制释放所有未释放的JSValue以避免JS_FreeRuntime导致的闪退。2.解释QuickJS中几个调试宏(DUMP_FREE,DUMP_CLOSURE,DUMP_BYTECODE,DUMP_GC)的作用。首先,关于强制释放未释放的JSValue:在QuickJS中,释放运行时(JS_FreeRuntime)之前,必须确保所有JSValue都已被释放,并且所有上下文(JSContext)也已被释放。如果还有未释放的JSValue,释放运行时会导致崩溃。解决方案:我们可以通过以下步骤来强制释放所有未释放的JSValue:步骤1:遍历并释放全局对象上的所有属性。步骤2:手动释放所有通过JS_NewContext创建的上下文(在释放运行时之前先释放上下文)。步骤3:在释放运行时之前,运行一次垃圾回收(JS_RunGC)以清理循环引用的对象。但是,请注意:QuickJS要求程序员显式释放每一个JSValue(通过JS_FreeValue)。因此,强制释放只能作为最后的手段,并且可能无法覆盖所有情况(例如,非全局对象可能无法通过全局对象遍历到)。下面提供一个函数,用于强制释放一个上下文(JSContext)中全局对象上的所有属性:```cppvoidForceFreeGlobalProperties(JSContext*ctx){//获取全局对象JSValueglobalObj=JS_GetGlobalObject(ctx);//获取全局对象的自有属性名(字符串属性)JSPropertyEnum*props=nullptr;uint32_tpropCount=0;if(JS_GetOwnPropertyNames(ctx,&props,&propCount,globalObj,JS_GPN_STRING_MASK|JS_GPN_ENUM_ONLY)<0){//错误处理JS_FreeValue(ctx,globalObj);return;}//遍历并释放每个属性值for(uint32_ti=0;i<propCount;i++){JSAtomatom=props[i].atom;JSValuepropVal=JS_GetProperty(ctx,globalObj,atom);if(!JS_IsUndefined(propVal)){JS_FreeValue(ctx,propVal);}JS_FreeAtom(ctx,atom);}//释放属性名数组js_free(ctx,props);//释放全局对象JS_FreeValue(ctx,globalObj);}```注意:这个方法只能释放全局对象上的属性,并不能释放所有可能存在的JSValue(例如,局部变量、闭包中的变量等)。因此,最好的做法是确保在代码中为每一个创建的JSValue调用JS_FreeValue。另外,在释放运行时之前,确保所有上下文都被释放:```cpp//创建运行时JSRuntime*rt=JS_NewRuntime();//创建上下文JSContext*ctx=JS_NewContext(rt);//...使用ctx执行操作...//强制释放全局属性(作为最后的手段)ForceFreeGlobalProperties(ctx);//释放上下文JS_FreeContext(ctx);//运行垃圾回收(可选,但有助于发现循环引用的问题)JS_RunGC(rt);//最后释放运行时JS_FreeRuntime(rt);```关于调试宏:QuickJS提供了一系列调试宏,可以在编译时启用,用于输出内部状态信息,帮助调试。这些宏通常在`quickjs.h`或`quickjs.c`中定义,需要在编译前设置。1.`DUMP_BYTECODE`:用于打印字节码。可以设置不同的位来控制打印的详细程度:-位0(1):在编译的第三阶段(最终阶段)打印字节码。-位1(2):在第二阶段打印字节码。-位2(4):在第一阶段打印字节码。-位3(8):打印标准库函数的字节码。-位4(16):以十六进制形式打印字节码。-位5(32):打印行号表。例如,设置`#defineDUMP_BYTECODE(1|2|4)`会打印所有阶段的字节码。2.`DUMP_FREE`:用于跟踪JSValue的释放。当定义这个宏时,每次调用`JS_FreeValue`都会打印被释放的值的信息(如地址、类型等)。这有助于发现双重释放或未释放的问题。3.`DUMP_CLOSURE`:当定义这个宏时,会在创建闭包时打印闭包的信息。闭包是JavaScript中的一个重要概念,这个宏可以帮助理解闭包的创建过程。4.`DUMP_GC`:用于打印垃圾回收的详细信息。当定义这个宏时,每次进行垃圾回收都会输出相关信息,如回收的对象数量、释放的内存等。5.`DUMP_LEAKS`:这个宏在之前的回答中提到过,用于在程序结束时检测并输出内存泄漏情况(即未释放的JSValue)。启用这个宏后,在释放运行时时会打印所有未被释放的对象。启用这些宏的方法是在编译QuickJS之前,在`quickjs.h`或通过编译命令行定义这些宏。例如:```bashgcc-DDUMP_LEAKS-DDUMP_FREE...-cquickjs.c```或者,在`quickjs.h`中添加:```c#defineDUMP_LEAKS#defineDUMP_FREE```注意:这些宏会输出大量信息,建议在调试时使用。总结:为了避免JS_FreeRuntime导致的闪退,必须确保:-所有JSValue都被正确释放(使用JS_FreeValue)。-所有JSContext都被释放(使用JS_FreeContext)。-在释放运行时前,可以运行一次垃圾回收(JS_RunGC)并启用调试宏(如DUMP_LEAKS)来检测泄漏。强制释放全局属性可以作为辅助手段,但不能替代正确的资源管理。调试宏的作用是输出QuickJS内部的调试信息,帮助开发者理解引擎的行为诊断问题。---相关问题:1.如何在QuickJS中正确管理C++对象的生命周期以避免内存泄漏?2.如何利用QuickJS的调试宏来诊断内存泄漏问题?3.QuickJS的垃圾回收机制如何工作?它与JS_FreeValue有什么关系?4.在多线程环境下使用QuickJS需要注意哪些内存管理问题?5.如何为QuickJS编写安全的C++绑定,确保JSValueC++对象的双向释放?</think>### 解决 QuickJS 中 `JS_FreeRuntime` 闪退问题及调试宏详解 #### 一、强制释放未释放 JSValue 的解决方案 **核心原则**: QuickJS 要求严格遵循资源释放顺序: $$ \text{JSValue} \rightarrow \text{JSContext} \rightarrow \text{JSRuntime} $$ **强制释放全局对象方法**: ```cpp void ForceFreeAllJSValues(JSContext* ctx) { // 获取全局对象 JSValue global = JS_GetGlobalObject(ctx); // 获取所有属性名 JSPropertyEnum* props; uint32_t count; JS_GetOwnPropertyNames(ctx, &props, &count, global, JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); // 遍历并释放所有属性值 for (uint32_t i = 0; i < count; i++) { JSValue val = JS_GetProperty(ctx, global, props[i].atom); if (!JS_IsUndefined(val)) { JS_FreeValue(ctx, val); // 强制释放值 } JS_FreeAtom(ctx, props[i].atom); // 释放原子 } // 释放资源 js_free(ctx, props); JS_FreeValue(ctx, global); // 释放全局对象 // 强制垃圾回收 JS_RunGC(JS_GetRuntime(ctx)); } ``` **使用示例**: ```cpp // 正确释放顺序 ForceFreeAllJSValues(ctx); // 强制释放残留值 JS_FreeContext(ctx); // 释放上下文 JS_RunGC(rt); // 最终垃圾回收 JS_FreeRuntime(rt); // 安全释放运行时 ``` #### 二、关键调试宏解析 QuickJS 的调试宏需在编译时启用,通过输出内部状态辅助诊断内存问题: | 宏名称 | 功能描述 | 启用方式 | |----------------|--------------------------------------------------------------------------|------------------------------| | **DUMP_BYTECODE** | 打印编译生成的字节码,通过位掩码控制输出内容:<br>- `1`: 最终字节码<br>- `2`: 阶段2字节码<br>- `4`: 阶段1字节码<br>- `8`: 标准库函数<br>- `16`: 十六进制格式<br>- `32`: 行号表 | `#define DUMP_BYTECODE 0x1F` | | **DUMP_FREE** | 跟踪每个 `JS_FreeValue` 调用,输出释放值的地址类型标签 | `#define DUMP_FREE` | | **DUMP_CLOSURE** | 打印闭包创建信息,包括作用域链变量捕获详情 | `#define DUMP_CLOSURE` | | **DUMP_GC** | 输出垃圾回收全过程:<br>- 标记阶段对象数量<br>- 扫描进度<br>- 释放内存统计 | `#define DUMP_GC` | | **DUMP_LEAKS** | 程序退出时检测未释放的 JSValue,输出详细泄漏报告 | `#define DUMP_LEAKS` | **编译启用方法**: ```bash # 启用所有调试功能 make clean make DUMP_LEAKS=1 DUMP_FREE=1 DUMP_GC=1 DUMP_BYTECODE=0x1F ``` #### 三、调试宏应用场景 1. **诊断未释放值**(DUMP_FREE + DUMP_LEAKS): ```log [DUMP_FREE] Freeing 0x7ffde8a9b2d0 (tag:1) // 对象 [DUMP_LEAKS] LEAK: string 0x7ffde8a9c110 "unfreed_var" // 泄漏的字符串 ``` 2. **分析字节码生成**(DUMP_BYTECODE): ```log [BYTECODE] func:0x561a34d2a7a0, name:main 0000: GET_LOCAL 0 0003: PUSH_STRING "test" // 字符串操作 0006: CALL 1 ``` 3. **监控垃圾回收**(DUMP_GC): ```log [GC] Mark phase: 542 objects [GC] Free phase: released 384KB // 释放内存统计 ``` #### 四、内存安全最佳实践 1. **防御性释放协议**: ```cpp void SafeRelease(JSRuntime* rt) { JSContext* ctx = JS_GetGlobalContext(rt); if (ctx) { ForceFreeAllJSValues(ctx); JS_FreeContext(ctx); } JS_RunGC(rt); // 清理循环引用 JS_FreeRuntime(rt); } ``` 2. **引用计数验证**: ```cpp #if DEBUG #define SAFE_FREE(v) do { \ printf("[SAFE_FREE] %s:%d ", __FILE__, __LINE__); \ if (JS_VALUE_HAS_REF_COUNT(v)) \ printf("ref_count=%d ", JS_VALUE_GET_REF_COUNT(v)); \ JS_FreeValue(ctx, v); \ } while(0) #else #define SAFE_FREE(v) JS_FreeValue(ctx, v) #endif ``` 3. **原子释放检测**: ```cpp void CheckAtomLeaks(JSContext* ctx) { JSRuntime* rt = JS_GetRuntime(ctx); printf("Active atoms: %d\n", rt->atom_count); // 检测未释放原子 } ``` #### 五、典型问题修复案例 **错误代码**: ```cpp JSValue obj = JS_NewObject(ctx); // 未释放的对象 JS_FreeRuntime(rt); // 导致闪退 ``` **修复后**: ```cpp JSValue obj = JS_NewObject(ctx); /* 使用对象 */ SAFE_FREE(obj); // 调试模式下输出释放信息 SafeRelease(rt); // 安全释放协议 ``` --- ### 相关问题 1. 如何利用 DUMP_LEAKS 精准定位 QuickJS 内存泄漏源? 2. QuickJS 字节码结构解析及优化方法有哪些? 3. 在多线程环境中如何安全使用 QuickJS 的垃圾回收机制? 4. 如何为 QuickJS 对象实现自定义的 C++ 析构函数? 5. DUMP_GC 输出日志的分析方法性能优化建议?[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

飞翔的鲲

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

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

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

打赏作者

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

抵扣说明:

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

余额充值