VC++编程从入门到实践:现代Windows开发环境搭建与核心技能精讲

VC++编程从入门到实践:现代Windows开发环境搭建与核心技能精讲
1. 项目概述为什么VC在今天依然值得投入如果你在搜索引擎里敲下“VC编程从入门到实践”大概率会看到两种声音一种是“都什么年代了还学VC”另一种是“工业软件、游戏引擎、金融交易系统哪个离得开C和VC”。作为一个从VC 6.0时代一路走过来的老码农我想说后一种声音更接近现实。VC或者说Visual C从来不是一门“过时”的技术它更像是一把需要深厚内力才能驾驭的“重剑”。它的价值不在于追赶时髦的语法糖而在于对Windows平台底层无与伦比的掌控力、极致的运行时性能以及庞大的历史代码遗产。这个“从入门到实践”的项目目标就是帮你跨过那道看似很高的门槛。它不是一本罗列语法的教科书而是一份结合了“教程”与“上机指导”的实战地图。教程告诉你原理和规则而上机指导则带你亲手在Visual Studio这个“工坊”里把规则变成可运行、可调试、甚至可解决实际问题的代码。你会发现从第一个“Hello World”窗口程序到一个能处理文件、绘制图形、与硬件交互的实用工具每一步都有迹可循。当前网络上充斥着各种“三天速成”、“一键安装”的教程但往往忽略了环境配置的坑、调试技巧的磨练和工程思维的建立这正是本指南要重点填补的空白。2. 核心学习路径与工具链搭建2.1 现代VC开发环境全景解析很多人对VC的印象还停留在那个独立的“Visual C 6.0”绿色图标时代。实际上现代的VC是Visual Studio IDE中一个功能完备的组件集。你需要理解几个关键概念Visual Studio是集成开发环境IDEVC是其中的C编译器、链接器、标准库和一系列开发工具如MSBuild的集合而Windows SDK则提供了开发Windows应用程序所需的头文件、库和工具。对于初学者我强烈建议从Visual Studio Community 2022开始。它是完全免费的功能对于学习和个人开发绰绰有余。安装时在“工作负载”选择界面务必勾选“使用C的桌面开发”。这个选项会自动安装VC编译工具集、Windows 10/11 SDK以及用于旧项目兼容性的MSVC v143 - VS 2022 C x64/x86生成工具。这就是你需要的核心“武器库”。注意网络上常说的“VC运行库”如Microsoft Visual C 2015-2022 Redistributable是程序运行时所依赖的动态链接库DLL比如msvcp140.dll。开发时你的代码在编译链接阶段已经静态或动态地绑定了这些库。用户运行你的程序时如果系统没有对应的运行库则需要安装。作为开发者你通常不需要单独安装它们因为SDK和工具链已经包含。2.2 第一个项目超越控制台的“Hello Windows”大多数教程会让你从控制台应用开始。这没错但为了立刻感受到VC在Windows图形界面开发上的魅力我们可以稍微激进一点直接从创建一个基础的Win32桌面应用程序开始。这能让你直观理解消息循环、窗口过程这些核心概念。创建项目打开VS2022选择“创建新项目” - “Windows桌面向导” - 下一步。给项目起名比如FirstWindow。在接下来的配置页面选择“应用程序类型”为“桌面应用程序(.exe)”并勾选“空项目”。点击创建。添加源文件在“解决方案资源管理器”中右键点击“源文件”文件夹 - 添加 - 新建项。选择“C文件(.cpp)”命名为main.cpp。编写窗口代码在main.cpp中输入以下代码。先不要被吓到我会逐行解释。#include windows.h // 声明窗口过程函数 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 1. 设计窗口类 const wchar_t CLASS_NAME[] LMyFirstWindowClass; WNDCLASS wc {}; wc.lpfnWndProc WindowProc; // 指定处理窗口消息的函数 wc.hInstance hInstance; // 当前程序实例句柄 wc.lpszClassName CLASS_NAME; // 窗口类名 wc.hCursor LoadCursor(nullptr, IDC_ARROW); // 加载标准箭头光标 // 2. 注册窗口类 RegisterClass(wc); // 3. 创建窗口 HWND hwnd CreateWindowEx( 0, // 扩展窗口样式 CLASS_NAME, // 窗口类名 L我的第一个VC窗口, // 窗口标题 WS_OVERLAPPEDWINDOW, // 窗口样式有标题栏、边框、系统菜单等 CW_USEDEFAULT, CW_USEDEFAULT, 500, 400, // 位置和大小x, y, width, height nullptr, // 父窗口句柄 nullptr, // 菜单句柄 hInstance, // 程序实例句柄 nullptr // 附加数据 ); if (hwnd nullptr) { return 0; } // 4. 显示窗口 ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // 5. 消息循环这是Windows程序的心脏 MSG msg {}; while (GetMessage(msg, nullptr, 0, 0)) { TranslateMessage(msg); // 翻译键盘消息 DispatchMessage(msg); // 将消息分发给窗口过程函数 } return 0; } // 6. 窗口过程函数处理所有发送到这个窗口的消息 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: // 当窗口被销毁时比如用户点击了关闭按钮 PostQuitMessage(0); // 发送退出消息使上面的GetMessage返回0结束消息循环 return 0; case WM_PAINT: { // 当窗口需要绘制内容时 PAINTSTRUCT ps; HDC hdc BeginPaint(hwnd, ps); // 获取设备上下文可以理解为画布 // 在这里进行绘制例如用TextOut写文字 TextOut(hdc, 50, 50, LHello, Windows! 这是我的第一个窗口。, 33); EndPaint(hwnd, ps); } return 0; } // 对于其他未处理的消息交给Windows默认处理 return DefWindowProc(hwnd, uMsg, wParam, lParam); }生成与运行按下F5开始调试或CtrlF5开始执行不调试。你会看到一个标准的Windows窗口弹出标题是“我的第一个VC窗口”客户区内显示着“Hello, Windows! 这是我的第一个窗口。”。实操心得第一次成功运行这个程序的意义重大。你亲手完成了一个完整的Windows GUI程序的生命周期注册、创建、显示、消息循环、绘制、销毁。虽然代码看起来比cout Hello World;复杂得多但这正是VC桌面开发的基石。理解了这个流程后续学习MFC、ATL甚至一些现代UI框架如Qt在Windows下的底层都会轻松很多。消息循环Message Loop是理解Windows编程的关键你可以把它想象成一个永不停止的“事件监听器”用户的鼠标点击、键盘输入、系统指令都通过它传递给WindowProc函数处理。2.3 项目配置与调试入门避开第一个大坑创建项目后你会注意到VS顶部工具栏有“Debug”和“x64”或“x86”的选项。这是两个至关重要的配置。解决方案配置Debug/ReleaseDebug调试编译器不会进行深度优化会生成完整的调试符号.pdb文件便于你设置断点、单步执行、查看变量值。程序运行速度较慢体积较大。在开发阶段务必使用Debug配置。Release发布编译器会进行大量优化如内联函数、删除未使用代码不包含调试符号。生成的可执行文件更小、运行更快。用于最终交付给用户。解决方案平台x86/x64x8632位兼容性最好可以在32位和64位Windows上运行。但无法直接使用超过4GB的内存每个进程。x6464位只能运行在64位系统上可以访问巨大的内存空间性能通常更好。对于新项目除非有明确的兼容性要求如调用古老的32位第三方库否则建议直接使用x64平台。一个常见的“坑”是库的链接。如果你从网上下载了一个第三方库比如一个用于图像处理的libpng.lib你必须确保下载的库版本Debug/Release, x86/x64与你的项目配置完全匹配。用Debug配置去链接一个Release版的库十有八九会导致链接错误或运行时崩溃。调试技巧实录最常用的调试快捷键是F9在当前行设置/取消断点、F10逐过程执行不进入函数内部、F11逐语句执行会进入函数内部、F5继续运行到下一个断点。当程序停在断点时将鼠标悬停在变量上可以查看其当前值或者在“监视”窗口Debug - Windows - Watch中输入变量名或表达式进行持续观察。这是定位逻辑错误最强大的武器。3. C核心语法与VC特性深度结合3.1 从标准C到微软扩展VC编译器MSVC高度兼容ISO C标准如C11/14/17/20你应该优先使用标准语法。但MSVC也提供了一些微软特定扩展了解它们有助于阅读和维护遗留代码。__declspec这是一个关键字用于指定存储类属性。最常见的两个是__declspec(dllexport)/__declspec(dllimport)用于从DLL中导出或导入函数、类。虽然现代方式更推荐使用模块定义文件.def或extern C配合__declspec但在大量现有代码中仍很常见。__declspec(align(#))指定数据在内存中的对齐方式用于性能优化或与特定硬件交互。#pragma指令一种编译器指令。例如#pragma once这是最常用的放在头文件开头作用是防止同一个头文件被多次包含。它比传统的#ifndef ... #define ... #endif宏守卫更简洁且几乎被所有主流编译器支持。#pragma comment(lib, xxx.lib)在代码中直接指定需要链接的库文件相当于在项目属性“链接器-输入-附加依赖项”里添加xxx.lib。结构化异常处理SEH这是Windows操作系统提供的一套异常处理机制使用__try,__except,__finally关键字。它与标准C的try/catch不同能捕获到如内存访问违规、除零错误等硬件和系统级异常。在系统级编程或驱动开发中可能会用到但在普通应用层开发中应优先使用标准的C异常处理。注意事项在新项目中除非必要如必须与使用这些扩展的旧代码交互否则应尽量避免使用微软扩展以保证代码的可移植性。#pragma once是个例外因其便利性被广泛接受。3.2 内存管理从原始指针到智能指针的进化C的自由度伴随着内存管理的责任。在VC环境中除了标准的new/delete你还会频繁接触到Windows API的内存管理函数。Windows API内存函数GlobalAlloc/GlobalFree早期的全局堆内存分配现在较少使用。LocalAlloc/LocalFree类似GlobalAlloc用于局部堆。HeapAlloc/HeapFree从指定的堆上分配内存更灵活。VirtualAlloc/VirtualFree最底层的内存分配直接操作进程的虚拟地址空间可以分配大块内存或设置内存页属性如可执行、只读。在需要特殊内存操作的场景如实现JIT编译器时会用到。重要提示必须成对使用。用GlobalAlloc分配就必须用GlobalFree释放不能混用也不能用delete释放。这曾是导致内存泄漏和崩溃的重灾区。现代C智能指针强烈推荐 为了从根本上解决手动管理内存的难题C11引入了智能指针。在VC项目中你应该毫无保留地拥抱它们。std::unique_ptrT独占所有权的智能指针。一个对象在任何时刻只能被一个unique_ptr拥有。当unique_ptr离开作用域时它会自动删除其管理的对象。它非常轻量开销几乎等同于原始指针是替代new/delete的首选。#include memory void foo() { std::unique_ptrMyClass ptr(new MyClass()); // C14后更推荐 std::make_uniqueMyClass() ptr-DoSomething(); // 函数结束时ptr自动销毁并删除MyClass对象 }std::shared_ptrT共享所有权的智能指针。通过引用计数跟踪有多少个shared_ptr指向同一个对象。当最后一个shared_ptr被销毁时对象才会被删除。适用于需要多个部分共享对象所有权的场景。std::weak_ptrT弱引用指针指向由shared_ptr管理的对象但不增加引用计数。用于打破shared_ptr的循环引用这是导致内存泄漏的常见原因。实操心得我的经验法则是默认使用std::unique_ptr仅在需要共享所有权时使用std::shared_ptr并时刻警惕循环引用。对于Windows API返回的原始资源句柄如HANDLE,HWND,HDC虽然它们不是指针但同样需要管理生命周期。你可以利用unique_ptr的自定义删除器功能来安全地管理它们。struct HandleDeleter { void operator()(HANDLE h) const { if (h h ! INVALID_HANDLE_VALUE) CloseHandle(h); } }; using UniqueHandle std::unique_ptrstd::remove_pointerHANDLE::type, HandleDeleter; UniqueHandle fileHandle(CreateFile(...)); // 无需手动调用 CloseHandle fileHandle 析构时会自动调用。3.3 字符串处理ANSI、Unicode与TCHAR的迷宫Windows编程中字符串处理是一大难点主要是因为历史遗留的字符编码问题。字符集类型char/std::string用于ANSI/MBCS多字节字符集字符串每个字符1字节或中文字符2字节。函数后缀通常带A如MessageBoxA。wchar_t/std::wstring用于UnicodeUTF-16LE字符串在Windows上每个字符2字节对于BMP范围内的字符。函数后缀通常带W如MessageBoxW。TCHAR/std::basic_stringTCHAR这是一个宏根据项目是否定义了UNICODE或_UNICODE宏在编译时决定展开为char还是wchar_t。这是为了编写同时支持ANSI和Unicode的“通用”代码。项目字符集设置在VS项目属性 - “高级” - “字符集”中你可以选择“使用多字节字符集”MBCS或“使用Unicode字符集”。现代Windows应用Windows 2000以后应毫不犹豫地选择“使用Unicode字符集”。这会在项目预定义宏中添加UNICODE和_UNICODE使得TCHAR变为wchar_t相关的API如MessageBox也会自动链接到W版本。字符串字面量ANSI string窄字符串字面量类型是const char[]。LUnicode string宽字符串字面量类型是const wchar_t[]。_T(TCHAR string)或TEXT(...)通用字符串字面量根据字符集设置展开为前两者之一。最佳实践新项目统一使用Unicode设置项目为Unicode字符集代码中直接使用wchar_t和std::wstring字符串字面量加L前缀。这能避免绝大多数乱码问题并更好地支持国际化。减少使用TCHAR除非你在维护一个需要兼容古老Windows 95/98的代码库否则在新项目中可以不用TCHAR直接使用明确的宽字符类型代码更清晰。编码转换当需要与使用UTF-8的库如许多开源库或网络数据交互时会涉及编码转换。可以使用Windows APIWideCharToMultiByte和MultiByteToWideChar或者第三方库如iconv。从VS2015开始MSVC也提供了一些codecvt头文件中的转换工具但注意C17已弃用部分可考虑使用其他库如icu。4. Windows API编程与MFC框架初探4.1 常用Windows API核心模块实战Windows API又称Win32 API是一座宝库提供了操作系统几乎所有功能的接口。掌握几个核心模块你就能编写出功能强大的程序。文件系统File APICreateFile功能极其强大不仅能创建/打开普通文件还能打开设备、管道、邮槽等。参数众多需要仔细查阅文档。ReadFile/WriteFile读写文件或设备。GetFileSize/SetFilePointer操作文件大小和指针。FindFirstFile/FindNextFile/FindClose遍历目录。// 示例遍历指定目录下的所有.txt文件 WIN32_FIND_DATAW findData; HANDLE hFind FindFirstFileW(LC:\\MyFolder\\*.txt, findData); if (hFind ! INVALID_HANDLE_VALUE) { do { if (!(findData.dwFileAttributes FILE_ATTRIBUTE_DIRECTORY)) { std::wcout L找到文件: findData.cFileName std::endl; } } while (FindNextFileW(hFind, findData)); FindClose(hFind); }进程与线程Process Thread APICreateProcess创建一个新进程及其主线程。CreateThread在当前进程中创建一个新线程。但请注意在C程序中更推荐使用C标准库的thread因为它能更好地与RAII和异常安全协同工作。如果必须用API要记得用CloseHandle关闭线程句柄。WaitForSingleObject/WaitForMultipleObjects等待一个或多个内核对象如进程、线程、事件、互斥量变为有信号状态。这是实现同步的基础。CreateMutex/CreateEvent/CreateSemaphore创建同步对象用于协调多线程间的执行顺序和数据访问。图形绘制GDI/GDIGDIGraphics Device Interface传统的2D图形接口函数如TextOut,Rectangle,Ellipse,BitBlt。它直接、高效但功能相对基础。GDIGDI的增强版提供了更丰富的功能如抗锯齿、渐变填充、图像格式支持JPEG, PNG等、矩阵变换。使用前需要初始化GdiplusStartup用完后需要GdiplusShutdown。对于现代UI更多使用Direct2D/DirectWrite或第三方库但GDI在绘制简单图形和处理图像时仍然方便。常见问题排查API调用失败几乎所有的Windows API在失败时都会返回一个特定的值如NULL,INVALID_HANDLE_VALUE,FALSE。紧接着你应该立即调用GetLastError()函数来获取详细的错误代码。然后可以使用FormatMessage函数将这个错误代码转换成可读的文本信息这是调试的黄金法则。HANDLE hFile CreateFile(...); if (hFile INVALID_HANDLE_VALUE) { DWORD err GetLastError(); LPWSTR errMsg nullptr; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, 0, (LPWSTR)errMsg, 0, nullptr); std::wcerr L创建文件失败错误: errMsg std::endl; LocalFree(errMsg); }4.2 MFC框架快速构建传统桌面应用的利器MFCMicrosoft Foundation Classes是一个用C封装了大部分Windows API的应用程序框架。在Visual Studio中你可以通过“MFC应用程序”向导快速生成一个带有菜单、工具栏、状态栏、视图窗口的完整应用骨架。对于需要快速开发内部工具、维护传统项目或偏爱文档-视图架构的开发者MFC仍有其价值。核心概念应用程序类CWinApp代表应用程序本身管理主消息泵。主框架窗口类CFrameWnd/CMDIFrameWnd应用程序的主窗口。文档类CDocument管理应用程序的数据。视图类CView负责显示文档数据并处理用户交互。文档和视图是分离的这是MFC的核心设计模式之一。对话框类CDialog用于创建模态或非模态对话框。快速上手在VS中新建项目选择“MFC应用程序”。在应用程序类型中选择“单个文档”SDI或“多个文档”MDI。一路下一步你可以选择是否需要“文档/视图结构支持”、是否需要“数据库支持”等。点击完成VS会为你生成一个可以立即编译运行的MFC应用程序包含了基本的文件、编辑、视图、帮助菜单。消息映射Message Map这是MFC处理Windows消息的机制它避免了在WindowProc中用巨大的switch-case。你在类声明中使用DECLARE_MESSAGE_MAP()宏在实现文件中使用BEGIN_MESSAGE_MAP,ON_COMMAND,ON_BN_CLICKED等宏将消息或命令映射到类的成员函数上。// 在 .h 文件中 class CMyView : public CView { DECLARE_MESSAGE_MAP() public: afx_msg void OnLButtonDown(UINT nFlags, CPoint point); }; // 在 .cpp 文件中 BEGIN_MESSAGE_MAP(CMyView, CView) ON_WM_LBUTTONDOWN() // 映射WM_LBUTTONDOWN消息到OnLButtonDown函数 END_MESSAGE_MAP() void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { // 处理鼠标左键按下事件 MessageBox(_T(你点击了视图), _T(提示)); CView::OnLButtonDown(nFlags, point); }注意事项MFC是一个庞大的框架学习曲线较陡。它的设计带有浓厚的90年代早期C风格如大量使用宏。对于全新的、追求现代UI和开发体验的项目建议考虑其他框架如Qt、WinUI 3或甚至使用Web技术如Electron。但如果你身处需要维护或扩展大量现有MFC代码的环境深入理解它是必须的。5. 高级主题与工程化实践5.1 动态链接库DLL开发与使用DLLDynamic Link Library是Windows生态的基石它允许代码模块化、共享和动态加载。创建DLL项目在VS中新建项目选择“动态链接库(DLL)”。向导会生成一个包含dllmain.cpp的项目。DllMain是DLL的入口点类似于控制台程序的main。导出函数/类方式一使用__declspec(dllexport/dllimport)// 在DLL项目头文件中 #ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) #else #define MYDLL_API __declspec(dllimport) #endif // 导出函数 extern C MYDLL_API int Add(int a, int b); // 导出整个类注意这要求调用方编译器与DLL编译器完全兼容如C版本、结构对齐等 class MYDLL_API MyExportedClass { ... };在DLL项目的预处理器定义中添加MYDLL_EXPORTS这样编译DLL时MYDLL_API就是__declspec(dllexport)。在使用DLL的项目中包含同一个头文件但不定义MYDLL_EXPORTS则MYDLL_API就是__declspec(dllimport)。方式二使用模块定义文件.def创建一个.def文件在其中列出要导出的函数名。这种方式不依赖编译器扩展兼容性更好。LIBRARY MyDll EXPORTS Add 1 AnotherFunction 2使用DLL隐式链接最常用将DLL对应的.lib文件导入库添加到项目的附加依赖项中并将DLL.dll文件放在可执行文件同级目录或系统路径下。程序启动时操作系统会自动加载DLL。显式链接使用LoadLibrary加载DLLGetProcAddress获取函数地址然后通过函数指针调用。这种方式更灵活可以在运行时决定加载哪个DLL也便于处理加载失败。typedef int (*FnAdd)(int, int); HMODULE hDll LoadLibrary(LMyDll.dll); if (hDll) { FnAdd pAdd (FnAdd)GetProcAddress(hDll, Add); if (pAdd) { int result pAdd(5, 3); } FreeLibrary(hDll); }5.2 多线程编程与同步现代程序离不开多线程。VC中你可以使用Windows API线程但更推荐使用C11标准库的thread,mutex,condition_variable等它们更安全、可移植。C标准库线程示例#include iostream #include thread #include vector #include mutex std::mutex g_coutMutex; // 用于保护std::cout防止多线程输出混乱 void worker(int id) { { std::lock_guardstd::mutex lock(g_coutMutex); std::cout 线程 id 开始工作。 std::endl; } std::this_thread::sleep_for(std::chrono::seconds(1)); { std::lock_guardstd::mutex lock(g_coutMutex); std::cout 线程 id 结束工作。 std::endl; } } int main() { std::vectorstd::thread threads; for (int i 0; i 5; i) { threads.emplace_back(worker, i); // 创建并启动线程 } for (auto t : threads) { t.join(); // 等待所有线程结束 } return 0; }同步原语选择std::mutex互斥锁用于保护共享数据确保同一时间只有一个线程能访问。std::lock_guard/std::unique_lockRAII风格的锁管理器在构造时加锁析构时自动解锁避免忘记解锁。std::condition_variable条件变量用于线程间等待和通知常与std::unique_lock配合使用。std::atomic原子类型对于简单的标量数据如计数器使用原子操作无需加锁性能更高。避坑指南多线程编程的核心难题是数据竞争和死锁。务必遵循“用锁保护所有共享数据”的原则。死锁通常发生在多个线程以不同顺序请求多个锁时。可以使用std::lock函数一次性锁定多个互斥量或者始终按固定的全局顺序获取锁来避免。5.3 性能分析与优化基础写出能运行的程序和写出高效的程序是两回事。VC提供了强大的性能分析工具。性能探测器Performance Profiler在VS中点击“调试” - “性能探测器”。你可以进行“CPU使用率”或“内存使用量”分析。CPU采样它会定期中断程序记录当前正在执行的函数。分析结束后你会看到一份“热点路径”报告清楚地显示哪个函数占用了最多的CPU时间。这是优化性能的第一步找到瓶颈。.NET内存分配对于托管代码和内存使用量帮助发现内存泄漏或过度分配的问题。代码优化实践算法与数据结构优先最大的性能提升往往来自选择更优的算法如将O(n²)的算法换成O(n log n)和数据结构如用std::unordered_map替代std::map进行快速查找。避免不必要的拷贝使用const 传递大的对象使用移动语义std::move转移资源所有权。关注缓存友好性尽量让数据连续存储如使用std::vector而非std::list遍历时顺序访问提高CPU缓存命中率。使用Release模式测试性能Debug模式下的性能与Release模式相差巨大所有性能测试都应在Release配置下进行。谨慎使用内联对于短小、频繁调用的函数使用inline关键字或定义在类内部的函数可以消除函数调用开销。但过度内联会导致代码膨胀反而可能降低缓存效率。从“入门”到“实践”VC的学习是一场深刻的旅程。它要求你不仅理解C语言的精妙还要洞悉Windows操作系统的运行机制。这个过程充满挑战但每当你亲手打造的工具流畅运行或成功优化了一段关键代码时那种成就感是无可替代的。记住最好的学习方式就是动手从一个具体的小目标开始比如写一个文件批量重命名工具、一个简单的串口调试助手在实现的过程中你会遇到本指南提到的各种问题而解决它们的过程就是最有效的成长。