用Visual Studio 2022的.map文件来查看C++变量在内存中的布局情况

先看几个实例

代码1

#include <iostream>
int data_arr[32768];
int main()
{
    data_arr[1] += 11;
    std::cout<<"data_arr[1]: " << data_arr[1] << std::endl;
    return data_arr[1];
}

上述代码在Win10 X64,MSVC Release模式下编译,编译得到的二进制文件大小为15KB左右。

代码2

#include <iostream>
int data_arr[32768] = {0,0,0,0,0};
int main()
{
    data_arr[1] += 11;
    std::cout<<"data_arr[1]: " << data_arr[1] << std::endl;
    return data_arr[1];
}

上述代码在Win10 X64,MSVC Release模式下编译,编译得到的二进制文件大小为15KB左右。

代码3

#include <iostream>
int data_arr[32768] = {2};
int main()
{
    data_arr[1] += 11;
    std::cout<<"data_arr[1]: " << data_arr[1] << std::endl;
    return data_arr[1];
}

上述代码在Win10 X64,MSVC Release模式下编译,编译得到的二进制文件大小为143KB左右。

代码4

#include <iostream>
int data_arr[32768] = {1,2,3,4,5,6};
int main()
{
    data_arr[1] += 11;
    std::cout<<"data_arr[1]: " << data_arr[1] << std::endl;
    return data_arr[1];
}

上述代码在Win10 X64,MSVC Release模式下编译,编译得到的二进制文件大小为143KB左右。

情况分析

        为何前两份代码后两份代码编译之后的二进制文件大小会差异这么大?

        原因就是全局变量data_arr 定义的方式不同。前两份代码中data_arr变量定义但是没有初始化或初始化为0,此变量运行时实际会存放在bss段(Block Started by Symbol Segment)中,只存符号(只有占位符),没有初始化的具体的值,自然也就不需要在二进制文件中保存这些值,因此文件很小。

        而后两份代码中data_arr变量定义并初始化为具体的值,此变量运行时实际会存放到data段中,又因为初始化了具体的值,这些值需要保存在二进制程序源文件中,所以文件就变大了。

确认data_arr变量在内存中的布局

        在Visual Studio 2022中用对应的.map文件,来确认data_arr变量在内存中的具体布局情况,看看它们运行时到底存放在哪个内存段中。

        生成.map和了解.map文件内容请见: Visual Studio(2022)生成链接过程的.map映射文件以及.map映射文件的内容说明_含影的博客-CSDN博客

        前两份代码对应的的.map文件摘录如下:

Preferred load address is 0000000140000000
 Start         Length     Name                   Class
 0001:000013d0 00000091H .text$x                 CODE
 0002:00000ec8 00000788H .idata$6                DATA
 0003:00000000 00000040H .data                   DATA
 0003:00000040 00020088H .bss                    DATA
 0004:00000000 00000240H .pdata                  DATA

  Address         Publics by Value              Rva+Base               Lib:Object

 0003:00000030       __scrt_ucrt_dll_is_in_use  0000000140005030     MSVCRT:ucrt_stubs.obj
 0003:00000040       ?data_arr@@3PAHA           0000000140005040     ccwindowsMain.obj

        从上面的.map文件内容,可以看到,data_arr变量,被分配到地址为0003:00000040这个内存空间中,而这个内存空间就是bss段(见于:0003:00000040 00020088H .bss)。

        后两份代码对应的的.map文件摘录如下:

 demo_ccwindows
 Preferred load address is 0000000140000000

 Start         Length     Name                   Class
 0001:00000000 00001390H .text$mn                CODE
 0002:00000ec8 00000788H .idata$6                DATA
 0003:00000000 00020040H .data                   DATA
 0003:00020040 00000088H .bss                    DATA
 0004:00000000 00000240H .pdata                  DATA

  Address         Publics by Value              Rva+Base               Lib:Object

 0002:00000c90       __NULL_IMPORT_DESCRIPTOR   0000000140003c90     msvcprt:MSVCP140.dll
 0003:00000000       ?data_arr@@3PAHA           0000000140005000     ccwindowsMain.obj

        从上面的.map文件内容,可以看到,data_arr变量,被分配到地址为0003:00000000这个内存空间中,而这个内存空间就是data段(见于:0003:00000000 00020040H .data)。

复杂一点的代码示例

#include <iostream>
#include <string>
int data_arr[32768] = {1, 2, 3, 4, 5, 6, 7, 8};
volatile const static int Major_version = 22;
volatile const float      Minor_Version = 17;
std::string               base_str_0      = "sssssAAAAA00000";
int                       parseSignal(int signal)
{
    static int  baseSignal = 1013;
    std::string base_str   = "sssssAAAAA11111";
    if (signal > 15)
    {
        base_str += "sssssAAAAA22222" + base_str_0;
        signal *= base_str.size();
    }
    return signal * baseSignal;
}
int main(int argc, char** argv)
{
    data_arr[1] += 11;
    std::cout << "data_arr[1]: " << data_arr[1] << std::endl;
    return data_arr[1] + (Major_version << argc) + Minor_Version * argc + parseSignal(argc >> 1);
}

对应的.map内容节选如下:

 Preferred load address is 0000000140000000

 Start         Length     Name                   Class
 0001:00000000 00000050H .text$di                CODE
 0001:00000050 000021a0H .text$mn                CODE
 0001:000021f0 00000040H .text$mn$00             CODE
 0001:00002230 000000c0H .text$x                 CODE
 0001:000022f0 00000064H .text$yd                CODE
 0002:00000000 00000278H .idata$5                DATA
 0002:00000278 00000038H .00cfg                  DATA
 0002:000002b0 00000008H .CRT$XCA                DATA
 0002:000002b8 00000008H .CRT$XCAA               DATA
 0002:000002c0 00000008H .CRT$XCU                DATA
 0002:000002c8 00000008H .CRT$XCZ                DATA
 0002:000002d0 00000008H .CRT$XIA                DATA
 0002:000002d8 00000008H .CRT$XIAA               DATA
 0002:000002e0 00000008H .CRT$XIAC               DATA
 0002:000002e8 00000008H .CRT$XIZ                DATA
 0002:000002f0 00000008H .CRT$XPA                DATA
 0002:000002f8 00000008H .CRT$XPZ                DATA
 0002:00000300 00000008H .CRT$XTA                DATA
 0002:00000308 00000008H .CRT$XTZ                DATA
 0002:00000310 00000000H .gehcont$y              DATA
 0002:00000310 00000000H .gfids$y                DATA
 0002:00000310 000002f0H .rdata                  DATA
 0002:00000600 00000080H .rdata$CastGuardVftablesA DATA
 0002:00000680 00000080H .rdata$CastGuardVftablesC DATA
 0002:00000700 000001f4H .rdata$r                DATA
 0002:000008f4 000000a8H .rdata$voltmd           DATA
 0002:0000099c 000003c4H .rdata$zzzdbg           DATA
 0002:00000d60 00000008H .rtc$IAA                DATA
 0002:00000d68 00000008H .rtc$IZZ                DATA
 0002:00000d70 00000008H .rtc$TAA                DATA
 0002:00000d78 00000008H .rtc$TZZ                DATA
 0002:00000d80 00000418H .xdata                  DATA
 0002:00001198 000000ecH .xdata$x                DATA
 0002:00001284 00000000H .edata                  DATA
 0002:00001284 000000b4H .idata$2                DATA
 0002:00001338 00000018H .idata$3                DATA
 0002:00001350 00000278H .idata$4                DATA
 0002:000015c8 00000868H .idata$6                DATA
 0003:00000000 00020078H .data                   DATA
 0003:00020078 00000080H .data$r                 DATA
 0003:000200f8 00000028H .data$rs                DATA
 0003:00020120 000005f0H .bss                    DATA
 0004:00000000 000003b4H .pdata                  DATA
 0005:00000000 00000060H .rsrc$01                DATA
 0005:00000060 00000180H .rsrc$02                DATA

  Address         Publics by Value              Rva+Base               Lib:Object
  
 0003:00000000       ?data_arr@@3PAHA           0000000140006000     ccwindowsMain.obj
 0002:000003a8       ?Major_version@@3HD        00000001400043a8     ccwindowsMain.obj
 0002:000003ac       ?Minor_Version@@3MD        00000001400043ac     ccwindowsMain.obj
 0003:00020020       ?baseSignal@?1??parseSignal@@YAHH@Z@4HA 0000000140026020     ccwindowsMain.obj
 0003:00020000       ?base_str_0@@3V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@A 0000000140026000     ccwindowsMain.obj
 0002:000002c0       ?base_str_0$initializer$@@3P6AXXZEA 00000001400042c0     ccwindowsMain.obj
 0002:00000310       ??_C@_0BA@DGGOEDOG@sssssAAAAA00000@ 0000000140004310     ccwindowsMain.obj
 0002:000003b0       ??_C@_0BA@GADHIFOA@sssssAAAAA11111@ 00000001400043b0     ccwindowsMain.obj
 0002:000003c0       ??_C@_0BA@JKNNMPOK@sssssAAAAA22222@ 00000001400043c0     ccwindowsMain.obj

由以上代码可以看到, Major_version和Minor_Version放在只读数据区(.rdata),baseSignal这个局部静态变量放在.data数据段,而字符串常量放在只读数据段.rdata。

注:这里用 volatile 是为了防止编译器优化。

可以在 Visual Studio 项目的 连接器 -> 系统 -> 堆栈保留大小 这里按照字节数设置当前程序的默认堆栈大小。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Visual Studio 实现三维图形的坐标系之间的变换和三维图形几何变换,可以使用 DirectX API。以下是修改示例图形为给定的三棱锥模型的代码示例: ```c++ #include <Windows.h> #include <d3d11.h> #include <d3dcompiler.h> #include <DirectXMath.h> #include <iostream> #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dcompiler.lib") using namespace DirectX; // 定义三棱锥顶点数据类型 struct Vertex { XMFLOAT3 position; XMFLOAT4 color; }; // 定义矩阵缓冲区数据类型 struct MatrixBuffer { XMMATRIX world; XMMATRIX view; XMMATRIX projection; }; // 定义全局变量 HWND hwnd = NULL; const int Width = 800; const int Height = 600; IDXGISwapChain* swapChain; ID3D11Device* d3d11Device; ID3D11DeviceContext* d3d11DeviceContext; ID3D11RenderTargetView* renderTargetView; ID3D11DepthStencilView* depthStencilView; ID3D11Texture2D* depthStencilBuffer; ID3D11InputLayout* inputLayout; ID3D11VertexShader* vertexShader; ID3D11PixelShader* pixelShader; ID3D11Buffer* vertexBuffer; ID3D11Buffer* indexBuffer; ID3D11Buffer* matrixBuffer; XMMATRIX worldMatrix; XMMATRIX viewMatrix; XMMATRIX projectionMatrix; // 创建交换链 bool CreateSwapChain() { DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; swapChainDesc.BufferCount = 1; swapChainDesc.BufferDesc.Width = Width; swapChainDesc.BufferDesc.Height = Height; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = hwnd; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Windowed = TRUE; D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &d3d11Device, NULL, &d3d11DeviceContext); if (FAILED(hr)) { std::cout << "Failed to create swap chain" << std::endl; return false; } return true; } // 创建渲染目标视图 bool CreateRenderTargetView() { ID3D11Texture2D* backBuffer; HRESULT hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer); if (FAILED(hr)) { std::cout << "Failed to get back buffer" << std::endl; return false; } hr = d3d11Device->CreateRenderTargetView(backBuffer, NULL, &renderTargetView); if (FAILED(hr)) { std::cout << "Failed to create render target view" << std::endl; return false; } backBuffer->Release(); return true; } // 创建深度模板缓冲区和深度模板视图 bool CreateDepthStencilBufferAndView() { D3D11_TEXTURE2D_DESC depthStencilDesc = {}; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; HRESULT hr = d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer); if (FAILED(hr)) { std::cout << "Failed to create depth stencil buffer" << std::endl; return false; } hr = d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); if (FAILED(hr)) { std::cout << "Failed to create depth stencil view" << std::endl; return false; } return true; } // 创建顶点着色器 bool CreateVertexShader() { ID3DBlob* vertexShaderBlob; HRESULT hr = D3DCompileFromFile(L"VertexShader.hlsl", NULL, NULL, "main", "vs_5_0", 0, 0, &vertexShaderBlob, NULL); if (FAILED(hr)) { std::cout << "Failed to compile vertex shader" << std::endl; return false; } hr = d3d11Device->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &vertexShader); if (FAILED(hr)) { std::cout << "Failed to create vertex shader" << std::endl; return false; } // 定义顶点输入布局 D3D11_INPUT_ELEMENT_DESC inputElementDesc[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0} }; hr = d3d11Device->CreateInputLayout(inputElementDesc, 2, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &inputLayout); if (FAILED(hr)) { std::cout << "Failed to create input layout" << std::endl; return false; } vertexShaderBlob->Release(); return true; } // 创建像素着色器 bool CreatePixelShader() { ID3DBlob* pixelShaderBlob; HRESULT hr = D3DCompileFromFile(L"PixelShader.hlsl", NULL, NULL, "main", "ps_5_0", 0, 0, &pixelShaderBlob, NULL); if (FAILED(hr)) { std::cout << "Failed to compile pixel shader" << std::endl; return false; } hr = d3d11Device->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &pixelShader); if (FAILED(hr)) { std::cout << "Failed to create pixel shader" << std::endl; return false; } pixelShaderBlob->Release(); return true; } // 创建顶点缓冲区 bool CreateVertexBuffer() { // 定义三棱锥的顶点数据 Vertex vertices[] = { {XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f)}, // A {XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f)}, // B {XMFLOAT3(1.0f, 0.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f)}, // C {XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f)}, // D {XMFLOAT3(0.5f, 1.0f, 0.5f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f)} // E }; D3D11_BUFFER_DESC vertexBufferDesc = {}; vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof(Vertex) * 5; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData = {}; vertexBufferData.pSysMem = vertices; HRESULT hr = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &vertexBuffer); if (FAILED(hr)) { std::cout << "Failed to create vertex buffer" << std::endl; return false; } return true; } // 创建索引缓冲区 bool CreateIndexBuffer() { // 定义三棱锥的索引数据 unsigned int indices[] = { 0, 1, 2, // 底面三角形 ABC 0, 2, 3, // 底面三角形 ACD 0, 1, 4, // 侧面三角形 ABE 1, 2, 4, // 侧面三角形 BCE 2, 3, 4, // 侧面三角形 CDE 3, 0, 4 // 侧面三角形 DAE }; D3D11_BUFFER_DESC indexBufferDesc = {}; indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(unsigned int) * 18; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA indexBufferData = {}; indexBufferData.pSysMem = indices; HRESULT hr = d3d11Device->CreateBuffer(&indexBufferDesc, &indexBufferData, &indexBuffer); if (FAILED(hr)) { std::cout << "Failed to create index buffer" << std::endl; return false; } return true; } // 创建矩阵缓冲区 bool CreateMatrixBuffer() { D3D11_BUFFER_DESC matrixBufferDesc = {}; matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; matrixBufferDesc.ByteWidth = sizeof(MatrixBuffer); matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; matrixBufferDesc.MiscFlags = 0; HRESULT hr = d3d11Device->CreateBuffer(&matrixBufferDesc, NULL, &matrixBuffer); if (FAILED(hr)) { std::cout << "Failed to create matrix buffer" << std::endl; return false; } return true; } // 初始化 DirectX bool InitDirectX() { if (!CreateSwapChain()) { return false; } if (!CreateRenderTargetView()) { return false; } if (!CreateDepthStencilBufferAndView()) { return false; } d3d11DeviceContext->OMSetRenderTargets(1, &renderTargetView, depthStencilView); D3D11_VIEWPORT viewport = {}; viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.Width = Width; viewport.Height = Height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; d3d11DeviceContext->RSSetViewports(1, &viewport); if (!CreateVertexShader()) { return false; } if (!CreatePixelShader()) { return false; } if (!CreateVertexBuffer()) { return false; } if (!CreateIndexBuffer()) { return false; } if (!CreateMatrixBuffer()) { return false; } return true; } // 渲染场景 void RenderScene() { // 设置顶点缓冲区和索引缓冲区 unsigned int stride = sizeof(Vertex); unsigned int offset = 0; d3d11DeviceContext->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset); d3d11DeviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R32_UINT, 0); // 设置输入布局 d3d11DeviceContext->IASetInputLayout(inputLayout); // 设置基本图元类型 d3d11DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // 更新矩阵缓冲区 MatrixBuffer matrixBufferData = {}; matrixBufferData.world = worldMatrix; matrixBufferData.view = viewMatrix; matrixBufferData.projection = projectionMatrix; D3D11_MAPPED_SUBRESOURCE mappedResource = {}; HRESULT hr = d3d11DeviceContext->Map(matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if (FAILED(hr)) { std::cout << "Failed to map matrix buffer" << std::endl; return; } memcpy(mappedResource.pData, &matrixBufferData, sizeof(MatrixBuffer)); d3d11DeviceContext->Unmap(matrixBuffer, 0); d3d11DeviceContext->VSSetConstantBuffers(0, 1, &matrixBuffer); // 设置顶点着色器和像素着色器 d3d11DeviceContext->VSSetShader(vertexShader, NULL, 0); d3d11DeviceContext->PSSetShader(pixelShader, NULL, 0); // 绘制图形 d3d11DeviceContext->DrawIndexed(18, 0, 0); // 切换缓冲区,显示渲染结果 swapChain->Present(0, 0); } // 释放资源 void Release() { if (matrixBuffer) { matrixBuffer->Release(); } if (indexBuffer) { indexBuffer->Release(); } if (vertexBuffer) { vertexBuffer->Release(); } if (pixelShader) { pixelShader->Release(); } if (vertexShader) { vertexShader->Release(); } if (inputLayout) { inputLayout->Release(); } if (depthStencilView) { depthStencilView->Release(); } if (depthStencilBuffer) { depthStencilBuffer->Release(); } if (renderTargetView) { renderTargetView->Release(); } if (swapChain) { swapChain->Release(); } if (d3d11DeviceContext) { d3d11DeviceContext->Release(); } if (d3d11Device) { d3d11Device->Release(); } } // 窗口过程函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam); } int main() { // 注册窗口类 HINSTANCE hInstance = GetModuleHandle(NULL); WNDCLASSEX wc = {}; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = L"DirectXWindowClass"; RegisterClassEx(&wc); // 创建窗口 hwnd = CreateWindowEx(0, L"DirectXWindowClass", L"DirectX Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, NULL, NULL, hInstance, NULL); if (hwnd == NULL) { std::cout << "Failed to create window" << std::endl; return 0; } ShowWindow(hwnd, SW_SHOW); // 初始化 DirectX if (!InitDirectX()) { Release(); return 0; } // 设置世界坐标系、物坐标系和屏幕坐标系的变换矩阵 worldMatrix = XMMatrixIdentity(); viewMatrix = XMMatrixLookAtLH(XMVectorSet(0.0f, 1.5f, -3.0f, 0.0f), XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)); projectionMatrix = XMMatrixPerspectiveFovLH(XM_PIDIV4,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值