首页 > 其他分享 >d3d12龙书阅读----Direct3D的初始化

d3d12龙书阅读----Direct3D的初始化

时间:2024-03-12 20:13:21浏览次数:28  
标签:ThrowIfFailed mCommandList WRL ComPtr 龙书 ---- D3D12 缓冲区 d3d12

d3d12龙书阅读----Direct3D的初始化

使用d3d我们可以对gpu进行控制与编程,以硬件加速的方式来完成3d场景的渲染,d3d层与硬件驱动会将相应的代码转换成gpu可以执行的机器指令,与之前的版本相比,d3d12大大减少了cpu的开销,同时也改进了对多线程的支持,但是使用的api也更加复杂。
接下来,我们将先介绍在d3d初始化中一些重要的概念,之后通过具体的代码进行介绍。

组件对象模型(com)

COM 在 D3D 编程中提供了一种结构化和标准化的方式来处理对象和接口,有助于简化图形编程的复杂性,并提高代码的兼容性和可维护性
在使用com对象时,com对象会统计其引用次数,因此,在使用完com接口之后,我们需要使用它的release方法,当com对象的引用次数为0时,它将自己释放它所占的内存。
为了辅助管理com对象的生存周期,我们可以使用Microsoft::WRL::ComPtr类,我们可以把它当做是com对象的智能指针,当一个ComPtr超出了作用域的范围,它便会自动调用相应Com对象的release方法,免去了我们手动调用的麻烦。

定义举例:

//DXGI接口
Microsoft::WRL::ComPtr<IDXGIFactory4> mdxgiFactory;
Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;
//D3D接口
Microsoft::WRL::ComPtr<ID3D12Device> md3dDevice;

Microsoft::WRL::ComPtr<ID3D12Fence> mFence;
UINT64 mCurrentFence = 0;

Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc;
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mCommandList;

static const int SwapChainBufferCount = 2;
int mCurrBackBuffer = 0;
Microsoft::WRL::ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount];
Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;

Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;

comptr的常用方法:

1.Get方法:返回一个指向此底层com接口的指针 一般将原始的接口指针作为参数传递给函数
Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
ThrowIfFailed(mdxgiFactory->CreateSwapChain(
	mCommandQueue.Get(),
	&sd, 
	mSwapChain.GetAddressOf()));
2.GetAddress方法:返回指向此底层com接口指针的地址 凭借此方法可利用函数参数返回接口指针
Microsoft::WRL::ComPtr<IDXGISwapChain> mSwapChain;
ThrowIfFailed(mdxgiFactory->CreateSwapChain(
	mCommandQueue.Get(),
	&sd, 
	mSwapChain.GetAddressOf()));
3.Reset方法:将comptr设置为nullptr并且释放与之相关的所有引用
Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;
mDepthStencilBuffer.Reset();

纹理

在本书中,纹理涉及的范围较广,可以把它看成是由数据元素构成的矩阵(1D/2D/3D),可以存储贴图信息与缓冲区信息等等。

缓冲区包括前台缓冲区,后台缓冲区,深度缓冲区,模版缓冲区等等。
其中前台与后台缓冲区,前台缓冲区存储的是当前显示在屏幕上的图像数据,而下一帧的图像数据绘制在后台缓冲区中,当后台缓冲区绘制完成之后,两种缓冲区的角色互换,只需交换两个缓冲区的指针即可,如下图所示:
img

这种方法又被称为双缓冲,而还有一种方法被叫做三缓冲,是为了解决gpu渲染速度与显示器的刷新率之间的矛盾:
在三重缓冲中,有一个正在显示的缓冲区,一个等待显示的缓冲区,和一个正在由 GPU 渲染的缓冲区。当 GPU 完成渲染时,它会将渲染好的帧移到等待显示的缓冲区。当显示器准备好刷新时,它会显示等待中的帧,并将之前显示的帧移动到渲染队列。这样,GPU 可以继续渲染下一帧,而不必等待显示器的刷新。

对于纹理而言,其中存储的数据格式并不是固定的,而是受到一定的限制,常用的设置数据格式有:
img

描述符

描述符是d3d中的又一重要概念,在发出绘制命令之前,我们需要将本次draw call的相应资源绑定到渲染流水线上,但是这一过程并不是直接将资源绑定,而是通过描述符来完成。
通过中间层描述符,有几大好处:

  1. 不同的描述符可以指定不同的资源
  2. 通过描述符可以为GPU解释资源 将资源使用到渲染流水线的不同阶段 告知资源如何使用(我们可以为一个资源创建两个不同的描述符)
  3. 可以使用描述符来绑定资源的局部数据
  4. 资源在创建时采用了无类型格式,描述符可以为其指明具体的类型
    常用的描述符可分为以下几类:
    img
    描述符堆中存有一系列描述符,本质上是存放某种特定类型描述符的一块内存:
//描述符堆的描述符的定义
 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;
 Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;
 D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
 rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
 rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
 rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
 rtvHeapDesc.NodeMask = 0;
 ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
    &rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
//描述符的定义
 Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;
 D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
 dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
 dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
 dsvDesc.Format = mDepthStencilFormat;
 dsvDesc.Texture2D.MipSlice = 0;
 md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());

CPU与GPU的交互

命令列表,命令队列与命令分配器

每个gpu都会至少维护一个命令队列(本质上是一个ring buffer,环形缓冲区),cpu可利用命令列表将其中的命令提交给gpu执行,同时命令列表里面的命令存储于命令分配器上,命令队列是从命令分配器中来提取命令。
总结一下:

 在头文件中加入相应com接口的定义
 //命令队列
 Microsoft::WRL::ComPtr<ID3D12CommandQueue> mCommandQueue;
 //命令分配器
 Microsoft::WRL::ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc;
 //命令列表
 Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> mCommandList;
之后进行初始化
void D3DApp::CreateCommandObjects()
{
	//填写命令队列结构体
	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
	queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	//创建命令队列
	//IID_PPV_ARGS 将COM ID类型转换为void**类型 作为函数参数
	ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
    
	//创建命令分配器
	ThrowIfFailed(md3dDevice->CreateCommandAllocator(
		D3D12_COMMAND_LIST_TYPE_DIRECT,
		IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));
    
	//创建命令列表
	ThrowIfFailed(md3dDevice->CreateCommandList(
		0,
		D3D12_COMMAND_LIST_TYPE_DIRECT,
		mDirectCmdListAlloc.Get(), // 关联命令分配器
		nullptr,                   
		IID_PPV_ARGS(mCommandList.GetAddressOf())));

	//起始时让命令列表处于关闭状态 因为我们在使用命令列表前需要对其进行reset操作(安全地复用旧列表所占用的底层内存)而在reset之前需要关闭命令列表
	mCommandList->Close();
}
向命令列表中加入命令
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
	D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);
将命令列表提交给命令队列 然后执行
ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

CPU与GPU之间的同步

为了实现cpu与gpu之间的同步,我们需要强制cpu等待 直到gpu完成所有命令的处理,d3d通过围栏实现这一点:

//定义围栏com接口 以及相应的围栏点
 Microsoft::WRL::ComPtr<ID3D12Fence> mFence;
 UINT64 mCurrentFence = 0;
//创建围栏对象
ThrowIfFailed(md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE,
	IID_PPV_ARGS(&mFence)));
//使cpu与gpu同步
void D3DApp::FlushCommandQueue()
{
	//增加围栏点的值
    mCurrentFence++;

    //设置新的围栏点
    ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));

	// 直到gpu处理完围栏点之前的命令 围栏点的值才会增加 循环才会结束
    if(mFence->GetCompletedValue() < mCurrentFence)
	{
		HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
        ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));
		WaitForSingleObject(eventHandle, INFINITE);
        CloseHandle(eventHandle);
	}
}

资源转换

有时候我们可能需要对一个资源先进行写操作,然后再进行读操作进行显示,为了防止在进行写操作的时候读,d3d设置了一组资源状态属性,防止类似上述这种资源冒险的情况发生,例如:

mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
	D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
上述代码将资源从渲染目标状态转换为呈现状态

D3D的初始化

接下来的部分只是大致介绍一下流程 以及重要函数
至于每个函数 每个描述子结构体的参数的详细介绍 可自行查阅

创建设备

进行d3d初始化首先要创建d3d设备

启动调试层
#if defined(DEBUG) || defined(_DEBUG) 
{
	ComPtr<ID3D12Debug> debugController;
	ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));
	debugController->EnableDebugLayer();
}
#endif

	ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory)));

	尝试创建显示适配器(显卡)
	HRESULT hardwareResult = D3D12CreateDevice(
		nullptr,             // default adapter
		D3D_FEATURE_LEVEL_11_0,
		IID_PPV_ARGS(&md3dDevice));

	// 创建失败回退到warp设备
	if(FAILED(hardwareResult))
	{
		ComPtr<IDXGIAdapter> pWarpAdapter;
		ThrowIfFailed(mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));

		ThrowIfFailed(D3D12CreateDevice(
			pWarpAdapter.Get(),
			D3D_FEATURE_LEVEL_11_0,
			IID_PPV_ARGS(&md3dDevice)));
	}

创建围栏

这一点前面已经说明

检测对4x msaa的支持

    首先填写质量等级的结构体 设置为4x 然后使用checkfeaturesupport来检测硬件是否支持
	D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
	msQualityLevels.Format = mBackBufferFormat;
	msQualityLevels.SampleCount = 4;
	msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
	msQualityLevels.NumQualityLevels = 0;
	ThrowIfFailed(md3dDevice->CheckFeatureSupport(
		D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
		&msQualityLevels,
		sizeof(msQualityLevels)));

    m4xMsaaQuality = msQualityLevels.NumQualityLevels;
	assert(m4xMsaaQuality > 0 && "Unexpected MSAA quality level.");

创建命令队列与列表

描述并创建交换链

void D3DApp::CreateSwapChain()
{
    释放之前创建的交换链
    mSwapChain.Reset();
    填写对应的描述子
    DXGI_SWAP_CHAIN_DESC sd;
    sd.BufferDesc.Width = mClientWidth;
    sd.BufferDesc.Height = mClientHeight;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferDesc.Format = mBackBufferFormat;
    sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    sd.SampleDesc.Count = m4xMsaaState ? 4 : 1;
    sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.BufferCount = SwapChainBufferCount;
    sd.OutputWindow = mhMainWnd;
    sd.Windowed = true;
	sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

    ThrowIfFailed(mdxgiFactory->CreateSwapChain(
		mCommandQueue.Get(),
		&sd, 
		mSwapChain.GetAddressOf()));
}

创建描述符堆

我们需要创建描述符堆来存储相应的描述符

一个堆用于存储rtv 即交换链的两个缓冲区
 D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
 rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
 rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
 rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	rtvHeapDesc.NodeMask = 0;
 ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
     &rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));

一个腿用于存储dsv 即深度缓冲区
 D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
 dsvHeapDesc.NumDescriptors = 1;
 dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
 dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	dsvHeapDesc.NodeMask = 0;
 ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
     &dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));

创建渲染目标视图 rtv

在之前创建了交换链的缓冲区之后 我们还需要创建相应的描述子/视图 才能将其绑定到渲染流水线进行输出

CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for (UINT i = 0; i < SwapChainBufferCount; i++)
{
	ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
	md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
	rtvHeapHandle.Offset(1, mRtvDescriptorSize);
}

创建深度缓冲区及其视图 dsv

填写深度缓冲区描述符然后使用CreateCommittedResource创建
 D3D12_RESOURCE_DESC depthStencilDesc;
 depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
 depthStencilDesc.Alignment = 0;
 depthStencilDesc.Width = mClientWidth;
 depthStencilDesc.Height = mClientHeight;
 depthStencilDesc.DepthOrArraySize = 1;
 depthStencilDesc.MipLevels = 1;
 depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
 depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
 depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
 depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

 D3D12_CLEAR_VALUE optClear;
 optClear.Format = mDepthStencilFormat;
 optClear.DepthStencil.Depth = 1.0f;
 optClear.DepthStencil.Stencil = 0;
 ThrowIfFailed(md3dDevice->CreateCommittedResource(
     &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
		D3D12_HEAP_FLAG_NONE,
     &depthStencilDesc,
		D3D12_RESOURCE_STATE_COMMON,
     &optClear,
     IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));

 //创建深度视图 用于绑定资源
	D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
	dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
	dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
	dsvDesc.Format = mDepthStencilFormat;
	dsvDesc.Texture2D.MipSlice = 0;
 md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, DepthStencilView());

 // 将深度缓冲区资源设置为depth buffer 涉及到之前提到的资源的转换
	mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
		D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));

设置视口与裁剪矩形

可以先设置视口与裁剪矩形的范围:

mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width    = static_cast<float>(mClientWidth);
mScreenViewport.Height   = static_cast<float>(mClientHeight);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;

mScissorRect = { 0, 0, mClientWidth, mClientHeight };

之后我们可以在实际渲染过程中设置视口与裁剪矩形:

mCommandList->RSSetViewports(1, &mScreenViewport);
mCommandList->RSSetScissorRects(1, &mScissorRect);

本结示例代码

void InitDirect3DApp::Draw(const GameTimer& gt)
{
    //reset命令分配器 注意这里要保证里面的命令已经全部被gpu执行完毕
	ThrowIfFailed(mDirectCmdListAlloc->Reset());
   //reset命令列表
    ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));

	//将资源从呈现状态转换到渲染目标状态 读到写
	mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
		D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

    //reset视口与裁剪矩形 每次reset命令列表都要reset
    mCommandList->RSSetViewports(1, &mScreenViewport);
    mCommandList->RSSetScissorRects(1, &mScissorRect);

    //清空后台缓冲区与深度缓冲区
	mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
	mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
	
    // 指明我们要写入的缓冲区
	mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
	
    // 将后台缓冲区从渲染目标状态转换到呈现状态
	mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
		D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

    // 关闭命令列表
	ThrowIfFailed(mCommandList->Close());
 
    // 执行命令
	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
	
	// 交换前后缓冲区
	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

	// 等待gpu执行完所有命令 保证同步
	FlushCommandQueue();
}

标签:ThrowIfFailed,mCommandList,WRL,ComPtr,龙书,----,D3D12,缓冲区,d3d12
From: https://www.cnblogs.com/dyccyber/p/18069119

相关文章

  • 高dpi下,Vb.net调整控件位置的小经验
     高dpi下,Vb.net调整控件位置的小经验 boy8199/3vdo/club最近写了一个捕快TXT网文采集软件,结果发现在DPI不同的情况下,软件布局会变形.找了半天原因才发现是DPI的问题,默认系统的dpi是96(100%)现在显示器的屏幕比较大,所以好多人会把显示放大到125%或150%导致程序控件变形......
  • Vue3——集成mock 模拟数据生成器
    安装依赖[email protected]在vite.config.js文件中引入并配置vite-plugin-mock插件import{UserConfigExport,ConfigEnv}from'vite'import{viteMockServe}from'vite-plugin-mock'importvuefrom'@vitejs/plugin-......
  • 亚稳态知识复习
    亚稳态的概念和基本知识亚稳态是指触发器无法在某个规定时间段内达到一个可确认的姿态。当一个触发器进入亚稳态时,既无法预测该单元的输出电平,也无法预测何时输出才能稳定在某个正确的电平上。在这个期间,触发器输出一些中间级电平,或者可能处于振荡状态,并且这种无用的输......
  • SimpleUI [12/Mar/2024 19:32:11] "GET /admin/logout/ HTTP/1.1" 405 0 Method Not
    Django使用SimpleUI后,登出报错[12/Mar/202419:32:11]"GET/admin/logout/HTTP/1.1"4050MethodNotAllowed(GET):/admin/logout/MethodNotAllowed:/admin/logout/[12/Mar/202419:36:20]"GET/admin/logout/HTTP/1.1"4050原因升级到5.0后不......
  • 01Python基础
    Python基础按照约定俗成的惯例,应该始终坚持使用4个空格的缩进。Python程序是大小写敏感的,如果写错了大小写,程序会报错。数据类型和变量数据类型整数任意大小的整数,包括负整数,和数学上的写法一致。十六进制,用0x前缀和0-9,a-f表示对于很大的数,100000000,可以写成100_00......
  • Delphi TDictionary字典类
    TDictionary是一个开放哈希表,支持泛型,并提供了以下常用属性和方法:属性:Count:返回字典中元素的数量。Keys:返回字典中所有键所组成的动态数组。Values:返回字典中所有值所组成的动态数组。方法:Add:向字典中添加一个键和其对应的值。Clear:清空字典中的所有元素。ContainsKey:判断字......
  • sed第三天
    sed第三天利用sed取出ifconfIgens33命令中本机的IPv4地址可以百度扩展了解即可也可以用别的命令实现只要有结果也可以ifconfigens33|sed-n's/.*inet\([0-9\.]\+\).*/\1/p'ipa|grep-A2'ens33:'|tail-n1|awk'{print$2}'[root@master~]#ip......
  • find第二天
    find第二天1.找出/tmp目录下,属主不是root,且文件名不以f开头的文件find/tmp!-userroot-a!-name'f*'或者find/tmp!-userroot!-name'f*'这条命令find/tmp!-userroot!-name'f*'用于在/tmp目录下查找文件,并应用了两个条件来过滤结果。find/tmp:从......
  • 郑莉cpp习题7-11
    代码#include<iostream>usingnamespacestd;classBaseClass{public:voidfn1();voidfn2();};voidBaseClass::fn1(){cout<<"调用基类的函数fn1()"<<endl;}voidBaseClass::fn2(){cout<<"调用基类的函数fn......
  • docke安装与部署
    1.linux安装dockersudoyuminstall-yyum-utilssudoyum-config-manager--add-repohttps://download.docker.com/linux/centos/docker-ce.repoyuminstall-ydocker-cedocker-ce-clicontainerd.io启动dockersystemctlstartdockerdockerrunhello-world如果出......