news 2026/10/16 9:14:18

Windows C/C++开发:MinGW-w64 GCC 13.1.0离线安装与配置详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Windows C/C++开发:MinGW-w64 GCC 13.1.0离线安装与配置详解

简介:这是一份面向64位Windows开发者的MinGW离线安装包,内置GCC 13.1.0编译器,采用POSIX线程模型、SEH异常处理及UCRT运行时,适合在没有稳定网络的C/C++开发环境中快速搭建原生64位编译工具链。压缩包共18725个文件,整体约68.89MB,以HTML文档、Python字节码与脚本、C/C++头文件、静态库及可执行程序等为主,其中include目录提供标准库头文件,lib目录存放链接所需的库文件,bin目录包含gcc/g++等编译驱动,可供离线安装和日常编译使用。目前已有1647人学习下载。利用这份打包完整的MinGW,开发者可避开在线下载的等待与失败风险,直接解压后配置环境变量即可获得一套可用的GCC 13.1.0工具链,尤其适合局域网受限、网络不稳定或需要复现特定编译环境的场景。

1. 拿到 MinGW 64 位离线包:先弄明白这份 13.1.0 能帮你省掉什么

做 Windows 下的 C/C++ 开发,最烦的其实不是写代码,而是配环境。点开官方下载页看到一堆看不懂的命名规则,从 SourceForge 拉一个在线安装器又常常卡在半路,装完才发现线程模型不对、异常处理用不了 std::thread,折腾几个小时项目还是编译不过。这份 x86-64-13.1.0-release-posix-seh-ucrt-rt-v11-r 离线安装包的定位非常明确:它是给 Windows 64 位用户准备的、开箱即用的 GCC 13.1.0 工具链,自带 posix 线程模型、SEH 异常处理、UCRT 运行时,一次解压就能进命令行干活,不需要网络、不需要交互式安装器、不怕装到一半失败要回滚。适合三类人:被在线安装器折腾过的 C/C++ 学习者、需要给 CI 机器批量配置交叉编译环境的运维、以及想在本地快速验证某个开源项目是否能在 Windows 上编译通过的技术评估者。

我拿到这个包之后大概花了一个小时把整个流程跑通,顺手踩了几个坑。下面从选型逻辑开始讲,然后再落到安装步骤、配置参数和编译验证,把这份离线包的每个细节都拆开,你照着做就能得到一个干净的、可控的 Windows GCC 环境。

2. 为什么选 posix-seh-ucrt:四个关键编译参数与离线安装的完整流程

2.1 线程模型:posix 和 win32 到底差在哪

MinGW-w64 的发布包命名里,release-posix-seh 这个位置有讲究。posix 和 win32 指的是线程模型,这会直接决定你能否在 Windows 上使用 std::thread、std::mutex 这类 C++11 标准线程库。win32 线程模型用的是 Windows API 做底层实现,性能好但只支持 win32 平台特性;posix 线程模型则通过 winpthreads 库提供了一套 POSIX 线程接口的映射,让 GCC 的 libstdc++ 能完整支持 C++11 的线程功能。

我在实际项目里碰到过这样的情况:某开发者下载了一个 win32 线程模型的分发包,写了一个简单的 std::thread 程序,结果链接阶段直接报 undefined reference 错误。原因就是 libstdc++ 里的线程相关符号在 win32 模型下没有完全实现,你用了标准库的线程 API 就等于白瞎。所以如果你是写跨平台代码,或者只是想在 Windows 上复现 Linux 下的编译行为,posix 模型是更稳妥的选择。这份包选的是 posix,意味着你用 std::thread 不会碰到链接错误,多线程 Demo 可以直接跑。

2.2 异常处理:seh 和 sjlj 的性能分水岭

异常处理这块,MinGW-w64 提供三种选项:dwarf(32 位专用)、sjlj(setjmp/longjmp)、seh(Structured Exception Handling)。dwarf 性能最好但只能用于 x86 32 位,sjlj 兼容性强但每次进入 try 块都会付出额外开销,seh 是 64 位 Windows 上的原生异常处理机制,由操作系统直接支持,性能接近原生 C++ 异常,而且不会像 sjlj 那样引入额外的栈展开成本。

这份 64 位离线包选的是 seh,这是 x86_64 架构下的正确选择。如果你在 64 位环境下用 sjlj,编译器生成的异常处理代码会明显膨胀,异常路径上的性能也差一截。需要注意的一个点是,seh 模型下如果程序抛异常但没有被捕获,Windows 会走系统级的异常处理流程,容易出现调试器才看得到的细节差异。这一点后面避坑章节会详细说。

2.3 运行时:ucrt 和 msvcrt 的选择逻辑

最后一段 ucrt-rt-v11-r 表示运行时链接的是 Universal C Runtime。老版本 MinGW 默认链接 msvcrt,那是 Visual C++ 6.0 时代的产物,C 标准支持残缺,比如 snprintf 行为不对、某些 POSIX 函数缺失。UCRT 是微软从 Visual Studio 2015 开始力推的通用 C 运行时,随 Windows 10 及后续版本系统内置,对 C99/C11 的支持完整得多。

选 UCRT 的好处是这样编译出来的程序在 Windows 10/11 上不需要额外打包运行时 DLL,系统里有现成的。缺点是你如果还想着兼容 Windows 7 老系统,UCRT 在 Win7 上默认没有,需要随程序带 ucrtbase.dll。现在做 Windows 开发基本默认 Win10/11,所以 ucrt 是更合理的现代选择。rt-v11-r 是 GCC 的修订版本号,同一版本下不同的 r 编号意味着 bug fix 级别的差异,不影响选型逻辑。

2.4 离线解压与安装目录规范

这份包是离线安装包,下载下来之后是一个压缩文件,不需要运行安装器。我一般习惯把工具链解压到 C:\ 根目录下,比如 C:\mingw64,路径短、层级少,后面配环境变量和写脚本都省事。解压完之后验证一下目录结构:

# 假设你把压缩包放在了 D:\downloads 目录下 cd /d D:\downloads tar -xf mingw64-13.1.0-release-posix-seh-ucrt-rt-v11-r.7z -C C:\ # 或者用 7-Zip GUI 解压到 C:\mingw64 dir C:\mingw64\bin

解压完成后确认 bin 目录下能看到 gcc.exe、g++.exe、mingw32-make.exe、gdb.exe 这些文件。这里有个容易踩的坑:用 Windows 自带的 tar 命令解压 .7z 格式会失败,因为微软的 tar 是 libarchive 实现的,不支持 7z 压缩算法。如果手头没有 7-Zip,也可以用 WinRAR,但最稳的方式是装一个 7-Zip 命令行工具来处理。

验证工具链是否可用,打开一个全新的 cmd 窗口(注意不是解压前开的旧窗口),输入:

gcc --version g++ --version

如果能看到 13.1.0 版本信息,说明这一步已经成了。为什么强调要新开窗口,因为环境变量只在窗口启动时读一次,你解压之前就开着的 cmd 不会识别新的 PATH 配置。

3. 让编译器先跑起来:环境变量配置、路径优先级与第一个编译实验

3.1 PATH、INCLUDE 和 LIB 环境变量的正确写法

解压只是第一步,真正让工具链好用是要把环境变量配对。核心是 PATH,其他两个是 C_INCLUDE_PATH、CPLUS_INCLUDE_PATH 或者 LIBRARY_PATH,但多数场景下其实不用手动配 include 和 lib,GCC 自己知道安装目录在哪。

PATH 要加的是 C:\mingw64\bin,这一个目录就够。注意不要把整个 C:\mingw64 加进去,因为 bin 目录下才是可执行文件。配置方法右键“此电脑”->属性->高级系统设置->环境变量,在“用户变量”或“系统变量”里找到 PATH 编辑,新增一行 C:\mingw64\bin。我一般加到系统变量里,这样所有终端窗口、所有用户都能直接调用。

配置完成后,开一个全新的 cmd 窗口,运行 gcc --version 确认版本是 13.1.0。如果出来的是旧版本,说明 PATH 里还残留着其他编译器,比如老版本的 MinGW 或者 Visual Studio 的 cl.exe,这种冲突排起来有些麻烦,在避坑章节会专门讲。

3.2 编译第一个带线程和异常处理的 C++ 程序

环境配好之后,我一般会写一个同时用上线程、异常和 STL 的程序来验证工具链完整性。这样可以一次把编译器三件套的问题暴露出来:头文件路径、链接库、以及运行时行为。

#include <iostream> #include <thread> #include <vector> #include <stdexcept> void worker(int id, int count) { for (int i = 0; i < count; ++i) { std::cout << "thread-" << id << " running: " << i << std::endl; } } int main() { try { std::vector<std::thread> pool; for (int i = 0; i < 4; ++i) { pool.emplace_back(worker, i, 3); } for (auto &t : pool) { if (t.joinable()) { t.join(); } } // 触发一个异常测试异常处理路径 throw std::runtime_error("expected error path"); } catch (const std::exception &e) { std::cout << "caught: " << e.what() << std::endl; } return 0; }

保存成 first_test.cpp,然后在命令行里编译:

g++ -std=c++17 -Wall -Wextra -O2 -o first_test.exe first_test.cpp -pthread

这里有几个参数要点:-std=c++17指定语言标准,GCC 13 默认也能编 C++20,这里用 17 保持保守;-pthread在 posix 线程模型下必须要有,它会让链接器正确链接 winpthreads 库;-Wall -Wextra把所有警告打开,这个习惯建议从第一天就养成;-O2是常规优化等级,能让程序跑得快,但不会像-O3那样激进。

跑一次编译出来的程序,如果看到 4 个线程分别输出、最后捕获到异常,说明整个工具链从编译到链接到运行时都正常。如果-pthread不加,某些情况下会报undefined reference to __winpthread_***,这就是线程模型和编译参数不匹配的典型症状。

3.3 mingw32-make 的使用:为什么不是 make

bin 目录下有一个 mingw32-make.exe,它和 Linux 上的 make 是同一个东西的 Windows 移植版。为什么要叫这个名字,因为很多 Windows 项目会用到微软的 nmake,两者语法不通用,为了不混淆,MinGW 官方就加了前缀。使用方式上并没有区别:

mingw32-make -f Makefile

如果你的 Makefile 是 Linux 上写的,大概率需要改一些细节,比如路径分隔符从 / 变成 \ 或者统一用相对路径,命令名从 rm 变成 del 或者干脆加一句RM = del /Q。这时候可以直接跑,如果报mkdir不存在这些错误,就要把 Makefile 里的 shell 命令替换成 cmd 支持的写法。我习惯的做法是给 mingw32-make 建一个名为 make.bat 的批处理快捷方式放到 bin 目录外,这样在 cmd 里输入 make 就能调用,省得每次打一长串名字。

4. 配好顺手工具链的避坑记录:五个高频翻车现场与排查对照

4.1 翻车一:gcc 版本对不上,PATH 里藏着多个编译器

现象是输入gcc --version返回的是 8.1.0 或者其他老版本,而不是刚装的 13.1.0。继续往下编译 C++17 代码就会报各种语法不支持的错误,比如std::filesystem不存在、if constexpr不认。

原因是 PATH 环境变量里同时存在多个编译器目录,比如某些软件自动安装了老版本 MinGW,或者 Python 的某发行版自带一个 gcc。命令行解析 gcc 时按 PATH 顺序从上到下找,第一个碰到的就是它用的编译器。

解决方法是打开 cmd 输入where gcc查看实际匹配到的路径,然后打开环境变量编辑器,把 C:\mingw64\bin 这一项尽量往上移,或者干脆删除其他编译器目录里的 gcc.exe。从这之后,每次装完工具链我都会新增一个专门的环境变量MY_GCC_ROOT=C:\mingw64,然后在 PATH 里引用%MY_GCC_ROOT%\bin,这样后期切换版本时只需要改一处。

4.2 翻车二:用 seh 编译的 exe 在调试器里崩溃,报错信息看不懂

现象是程序正常编译、正常链接,双击运行看起来没反应,用 gdb 一跑发现入口点没进去,直接抛出一个 0xC0000409 类型的系统异常。

原因可能是程序里用了 SEH 的__try/__except语法,或者链接了一个用了 sjlj 异常处理编译的静态库,导致异常处理栈帧不兼容。GCC 的 seh 和 sjlj 生成的异常展开信息在二进制层面不通用,混用时异常会漏出栈顶被系统接住。

解决方法是确保整个项目所有编译单元都用同一个异常模型,不要一个 .cpp 用默认参数、另一个 .cpp 手动指定了-fno-exceptions。如果你确实要链接第三方静态库,去确认它发布时的说明写了什么模型。血泪经验:静态库的异常模型不匹配,是 Windows 上最难排查的问题之一,光看报错信息根本联想不到是这里出了问题。

4.3 翻车三:程序在自己机器上正常,拷到别的 Windows 上提示缺少 DLL

现象是编译好的 exe 放到另一台 Windows 10 电脑上运行,弹窗提示找不到 libwinpthread-1.dll 或者 libstdc++-6.dll。

原因是你用了动态链接方式,GCC 默认生成的可执行文件会依赖这些运行时 DLL,而这台目标机器上没有安装 MinGW 环境。注意,即使系统里有 UCRT 也没用,因为 libstdc++ 和 winpthreads 是 GCC 自己的库。

解决方法是编译时加静态链接参数:

g++ -std=c++17 -O2 -static-libgcc -static-libstdc++ -o app.exe app.cpp -pthread

如果你项目里用了 winpthreads(std::thread),还需要在链接阶段加-Wl,-Bstatic -lpthread -Wl,-Bdynamic或者直接用-static把所有库都静态链进去。-static是终极方案,生成的 exe 会大一圈但完全不依赖外部 DLL,适合分发场景。我一般分发给别人用的工具类程序一律-static,自己调试时才用动态链接。

4.4 翻车四:include 头文件找不到,但系统里明明有

现象是编译时提示fatal error: stdio.h: No such file or directory或者其他标准头文件找不到,而你去 C:\mingw64\include 里看这个文件是存在的。

原因是头文件搜索路径没配好,或者编译命令里用了-I指定了一个不存在的目录,导致 GCC 的默认头文件搜索路径被提前覆盖了。还有一种更隐蔽的情况:你的项目里有一个同名头文件在本地 src 目录下,GCC 优先找到了它,但里面内容不对。

解决方法是先查看 GCC 实际搜索了哪些路径:

g++ -E -x c++ - -v < NUL

-v会打印出 include 搜索路径列表,一条条对照确认 C:\mingw64\include\c++\13.1.0 等目录是否在里面。如果不在,检查是不是把安装目录移动过位置;如果之前是解压到别的盘再复制过来的,GCC 内部记录的路径可能还是旧的,需要重新解压而不是复制粘贴。

4.5 翻车五:中文路径让人抓狂,编译命令看着对但就是失败

现象是代码文件放在D:\项目资料\test\main.cpp,执行g++ -o main.exe main.cpp报错说找不到文件,或者编译过程出现一堆和中文相关的乱码错误。

原因是 GCC 在 Windows 上对非 ASCII 路径的处理一直不够稳,cmd 默认的代码页是 GBK,而现代项目文件经常用 UTF-8 命名。两边编码对不上就会出问题。

解决方法是两个方向:一种是项目路径全部用英文目录,这个最省事但现实不一定允许;另一种是在代码文件内部用#include "路径/头文件.h"时保持相对路径、避免中文目录名出现在编译命令或者 Makefile 里。如果你非要硬刚中文路径,可以把系统区域设置里的“Beta 版:使用 Unicode UTF-8 提供全球语言支持”打开,但我不推荐这么干,因为会影响其他软件的行为。从那以后我每次新建项目一律把项目根目录命名为纯英文路径,这个习惯帮我避免了很多玄学问题。

5. 让工具链真正顺手:VSCode 集成、多版本共存与常用编译配置

5.1 VSCode 里配置 C/C++ 扩展指向这份工具链

命令行能用之后,接下来就是跟编辑器打通。VSCode 的 C/C++ 扩展默认会用系统 PATH 里的编译器,但为了精确定位,我习惯在项目根目录放一个.vscode/c_cpp_properties.json,把编译器路径显式写死:

{ "configurations": [ { "name": "MinGW64-13.1", "includePath": [ "${workspaceFolder}/**", "C:/mingw64/include/**", "C:/mingw64/include/c++/13.1.0/**" ], "defines": [], "compilerPath": "C:/mingw64/bin/g++.exe", "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "windows-gcc-x64" } ], "version": 4 }

这里的 compilerPath 必须是绝对路径而不是靠 PATH 解析,这样即使默认终端里 PATH 顺序被其他工具改过,VSCode 的代码提示和调试配置还是能准确指向这份工具链。intelliSenseMode要写成windows-gcc-x64,如果写成linux-gcc-x64,提示信息会出现大量误报。

5.2 tasks.json 和 launch.json 的编译调试闭环

按下 F5 想直接调起来,需要配置编译任务和调试器。先在.vscode/tasks.json里定义一个编译任务:

{ "version": "2.0.0", "tasks": [ { "label": "build-debug", "type": "shell", "command": "g++", "args": [ "-g", "-std=c++17", "-Wall", "-Wextra", "-o", "${fileDirname}/${fileBasenameNoExtension}.exe", "${file}" ], "group": { "kind": "build", "isDefault": true }, "problemMatcher": ["$gcc"] } ] }

-g是生成调试信息,没有它 gdb 断点打不上。, problemMatcher里的$gcc让 VSCode 能解析 GCC 风格的编译错误输出,把你跳转到报错行去。这一步如果跳过,编译出错时输出窗口里只有一大段文字,没有红叉定位,体验打五折。

接着配置 launch.json 指向 gdb:

{ "version": "0.2.0", "configurations": [ { "name": "Debug MinGW", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}.exe", "args": [], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "C:/mingw64/bin/gdb.exe", "setupCommands": [ { "description": "Enable pretty printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "build-debug" } ] }

重点看preLaunchTask,它保证你按下 F5 之前先把当前文件编译成 exe。miDebuggerPath指向 gdb 的绝对路径,别写成gdb让系统去找,不然杂七杂八的 PATH 会引入一套不兼容的 gdb。

5.3 多版本 GCC 共存与快速切换

实际工作中你会碰到某个老项目必须用 GCC 8 编译、另一个新项目要求 GCC 13 的情况。同时装两个版本的关键是目录隔离和 PATH 切换。我会把不同版本解压到独立目录:

C:\toolchains\mingw64-8.1.0\bin C:\toolchains\mingw64-13.1.0\bin

然后用一个批处理脚本切换当前会话的 PATH,而不是全局改动,这样开新窗口就是默认版本,切到特定项目时运行一下切换脚本:

@echo off set MY_GCC_ROOT=C:\toolchains\mingw64-13.1.0 set PATH=%MY_GCC_ROOT%\bin;%PATH%

这个脚本的最小可用版本,复杂一点可以加 setx 写持久化,但在平时开发会话里用 set 就够了。我一般是给每个项目准备一个env.bat,里面对齐这个项目需要的工具链版本和额外路径。这样做的好处是项目之间完全隔离,不会出现 A 项目用了 B 项目的工具链导致编译出诡异行为。

5.4 为代码和数据文件准备一个干净的外部资源目录

这个话题严格说来不是 GCC 本身的知识,但是我在折腾多项目时养成的习惯带来了良好的连锁效果:给项目配套一个 external 目录专门放资源文件,然后通过-D编译参数把它暴露给代码。这样写程序时不会出现硬编码路径,配合不同的编译配置就能切不同的数据脚本,整体架构也更清晰。这个思路在通用开发中比较受用,值得一试。

6. 一个救命习惯:给工具链建一份「家庭档案」,每次换版本都有后悔药

我的习惯是给每个版本的 MinGW 建一份档案文件,内容是这份工具链的关键参数、用途和已知问题,放在安装目录的上层或者项目 docs 目录下。格式用纯文本或者 Markdown 都行,重要的是坚持写,例如:

版本: GCC 13.1.0 (x86_64-posix-seh-ucrt-rt-v11-r) 安装路径: C:\toolchains\mingw64-13.1.0 依赖: UCRT (Windows 10+ 内置, 无需分发 DLL) 适用范围: C++17/C++20 项目, 启用 std::thread, 需要 SEH 异常, 目标 Win10/11 已知问题: 静态链接 libstdc++ 时会同时引入 winpthreads, 需要 -Wl,-Bstatic 切换方法: 在 cmd 中执行 set PATH=C:\toolchains\mingw64-13.1.0\bin;%PATH%

这份档案配上离线压缩包本身,效果就体现出来了:环境坏了不想查原因?直接删了目录重新解压一份。解压出来的工具链被某个项目污染了?重新解压一份到别的目录,对比两份 bin 下的 gcc.exe 大小或者哈希值,基本都能定位。从那以后我每次部署新的 Windows 编译环境,都强制自己走一遍完整流程:解压 -> 配 PATH -> gcc --version 验证 -> 写档案文件。整个流程不超过五分钟,但后面每次遇到环境问题都不用再花半小时从头排查,直接按档案里的路径和已知问题表去对号入座。希望这份 13.1.0 离线包的拆解和踩坑记录能帮到你,让你在 Windows 上配置 GCC 时少走一段弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 9:13:54

基于DDPG强化学习的滑模控制自适应调参优化与Simulink仿真

做滑模控制那段时间&#xff0c;我几乎每天都在跟三组参数较劲。切换增益调大了&#xff0c;系统高频抖振明显&#xff1b;调小了&#xff0c;来一个阶跃扰动就出好几度超调。边界层厚度也是同样尴尬&#xff0c;太厚稳态误差变大&#xff0c;太薄又跟没加边界层差不多。后来我…

作者头像 李华
网站建设 2026/10/16 9:12:20

AI应用动态编排:Skill与Dify工作流选型深度解析

过去三个月&#xff0c;我一直陷在同一个选择题里&#xff1a;团队里的新AI应用&#xff0c;业务逻辑到底交给Skill还是Dify工作流来编排&#xff1f;这个问题一开始看着只是技术选型&#xff0c;真做起来才发现&#xff0c;它牵涉到开发节奏、运维方式、甚至团队分工。我把同一…

作者头像 李华
网站建设 2026/10/16 9:05:32

VRChat世界创作实战:用UdonSharp给机器球做情绪动画和交互

在 VRChat 里做世界创作&#xff0c;最难的点往往不是建模&#xff0c;而是如何让一个普通物件拥有“让人记住的性格”。如果你玩过 Portal 2&#xff0c;大概率还记得那个安静坐在测试室里、从不说话也从不伤害你的 Companion Cube&#xff0c;社区常把它叫做伙伴方块&#xf…

作者头像 李华
网站建设 2026/10/16 9:03:55

从玩梗到生产力:AI助手一周实测进化指南

最近圈里都在聊一件事&#xff1a;某头部互联网公司给自己家的AI助手定了个新调性&#xff0c;从“只会插科打诨的欢乐豆”正式进阶成了“能干正事的搭班同事”。说实话&#xff0c;我一开始对这种宣传是持保留态度的&#xff0c;毕竟AI助手被贴上“搞笑”标签太常见了——讲段…

作者头像 李华
网站建设 2026/10/16 9:00:36

后端开发四轮面经复盘:技术基础、项目深挖与系统设计

说实话&#xff0c;面试这类事&#xff0c;结果好了叫“面经”&#xff0c;结果不好就成了“教训”。3月9号那天&#xff0c;我面完一家中大型互联网公司的后端开发岗&#xff0c;从早上十点进场到下午三点多出来&#xff0c;四轮连轴转&#xff0c;一面技术基础、二面项目深挖…

作者头像 李华
网站建设 2026/10/16 8:58:58

STM32 PWM精确输出脉冲数控制电机:硬件定时器方案与避坑指南

简介&#xff1a;这份资源面向STM32嵌入式开发者与电机控制初学者&#xff0c;聚焦用PWM实现精确脉冲个数输出&#xff0c;解决步进电机步进次数与舵机转角难以精准控制的问题。包内共196个文件&#xff0c;以h头文件、c源文件、o目标文件、axf可执行文件、hex烧录文件、uvproj…

作者头像 李华