Google C++ 编程规范 (C Language Part)
首先需要明确一点:Google 官方发布的是一份 C++ 编程规范,其中包含了专门针对 C 语言的章节(大约第 1-7 章),这份文档是 C 语言部分的权威来源。
官方文档链接: Google C++ Style Guide (C Language Part)
下面我将根据这份官方文档,为你梳理出核心要点,并辅以易于理解的解释和示例。
核心原则
在深入具体规则之前,理解 Google 规范背后的四大核心原则至关重要:
- 可读性优先:代码被读的次数远多于被写的次数,清晰的代码比聪明的代码更重要。
- 一致性:在同一个项目中,风格应保持一致,这能减少认知负荷,让开发者专注于逻辑而非格式。
- 自动化工具:尽可能使用工具(如
clang-format)来保证格式一致性,让开发者专注于代码的逻辑和结构。 - 前瞻性:代码不仅要为今天的你而写,更要为未来维护它的同事(包括未来的你)而写。
格式化与风格
这是最直观的部分,也是最容易统一的部分。
缩进与空格
-
使用空格,不使用 Tab:设置你的编辑器将 Tab 自动展开为 2 个或 4 个空格,Google 推荐 2 个空格 作为缩进。
-
行宽:每行代码最多 80 个字符,过长的代码难以阅读,尤其是在并排显示的屏幕上,如果超过,应进行合理的换行。
-
函数声明与定义:
- 返回类型与函数名在同一行。
- 参数列表如果过长,可以在逗号后换行,并保持对齐。
- 左大括号 与函数名在同一行。
- 右大括号 独占一行,与函数声明/定义的缩进级别相同。
// 好的示例 int ThisIsAVeryLongFunctionName(int argument_one, int argument_two, int argument_three) { // 函数体 return 0; } // 不好的示例 int ThisIsAVeryLongFunctionName(int argument_one, int argument_two, int argument_three) { // 左大括号换行,不推荐 // 函数体 return 0; }
注释
-
或 :两者皆可,但 更常用,因为它能明确注释的结束位置。
-
文件头注释:每个源文件都应有文件头注释,包含:
- 版权信息
- 作者
- 文件描述(文件的主要功能)
- 修订历史(可选,但推荐)
// Copyright 2025 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @file mymodule.c * @brief This module provides functions for handling my data. * @author Your Name */
-
函数注释:每个公共函数都应有注释,说明其功能、参数和返回值,推荐使用 Doxygen 风格。
/** * @brief Calculates the sum of two integers. * * This function adds two integers and returns the result. * * @param a The first integer. * @param b The second integer. * @return The sum of a and b. */ int Add(int a, int b);
-
实现内注释:用于解释复杂的代码逻辑、算法或临时的解决方案。
命名规范
命名是代码可读性的关键,Google 的命名规则非常清晰。
| 实体 | 命名规则 | 示例 |
|---|---|---|
| 文件名 | 全小写,可下划线分隔 | my_parser.c, http_server.c |
| 类型名 (Type) | PascalCase (首字母大写) |
MyStruct, NetworkConnection |
| 成员变量 | snake_case (全小写,下划线分隔) |
my_struct->member_variable |
| 变量名 | snake_case |
int counter; |
| 函数名 | snake_case |
void open_file(); |
| 命名空间 | (C语言不常用,但C++中为snake_case) |
namespace my_library {} |
| 宏/常量 | snake_case 或 Pascal_CASE |
MAX_BUFFER_SIZE, kDaysInAWeek |
| 枚举类型 | PascalCase |
enum Color { ... } |
| 枚举值 | PascalCase 或 kPascalCase |
enum Color { COLOR_RED, COLOR_BLUE } |
重要说明:
- 避免匈牙利命名法:Google 明确反对,现代编辑器和 IDE 已经能提供足够的信息(如类型提示),匈牙利命名法会增加代码的冗余性。
- 成员变量:C 语言没有内置的类成员概念,但通常在结构体中模拟,Google 推荐使用
snake_case来区分成员变量和普通变量。
语言特性
预处理器与宏
-
谨慎使用宏:宏容易导致意想不到的副作用,因为它只是简单的文本替换。
-
使用
static inline代替简单函数宏:对于简单的、单行的函数,优先使用static inline函数,这样更安全,有类型检查,并且调试时能正常显示。// 不推荐 (有副作用) #define MAX(a, b) ((a) > (b) ? (a) : (b)) int a = 5; int b = 10; int c = MAX(++a, b); // a 会被递增两次! // 推荐 static inline int Max(int a, int b) { return (a > b) ? a : b; } -
宏命名:宏名应全部大写,并用下划线分隔。
-
#include顺序:按照以下顺序排列#include:- 相关的头文件(
my_class.h包含my_class.c的实现细节) - C 标准库头文件(如
<stdio.h>,<stdlib.h>) - 其他库的头文件(如
<gtk/gtk.h>) - 项目内部的其他头文件 每个部分之间用一个空行隔开。
#include "my_class.h" // 相关头文件 #include <stdio.h> // C标准库 #include <stdlib.h> #include <gtk/gtk.h> // 其他库 #include "other_module.h" // 项目内部头文件
- 相关的头文件(
指针
-
指针与类型声明: 靠近类型名还是变量名?Google 规范推荐将 靠近类型名。
// 推荐 char *c; // c 是一个指向 char 的指针 char *a, *b; // a 和 b 都是指向 char 的指针 // 不推荐 (容易混淆) char *a, b; // 只有 a 是指针,b 是 char
-
指针与引用:C 语言没有引用,但 Google 规范指出,C++ 中的引用在某些情况下比指针更安全,因为它不能为空,在 C 语言中,要时刻警惕空指针。
变量
-
初始化:所有变量都应在声明时进行初始化,以使用未初始化变量。
-
作用域:变量的作用域应尽可能小,在需要时才声明,而不是在函数开头全部声明。
// 不推荐 (作用域过大) void MyFunction() { int i; for (i = 0; i < 10; ++i) { // ... } // i 在这里仍然可见,但可能不再需要 } // 推荐 (作用域最小化) void MyFunction() { for (int i = 0; i < 10; ++i) { // ... } // i 在这里已不可见 }
C++ 特性在 C 中的使用
Google 的 C 语言规范是在 C++ 规范的背景下制定的,因此它鼓励使用一些现代 C 编译器支持的、源自 C++ 的特性来提高代码质量和安全性。
- 注释:虽然是 C++ 风格,但现代 C 编译器(C99 及以后)完全支持。
bool类型:推荐使用<stdbool.h>中的bool,true,false,而不是int0 和 1。static inline函数:如前所述,用于替代简单宏。- 变长数组:在 C99 中引入,可以在栈上创建大小在运行时才确定的数组,但要谨慎使用,避免栈溢出。
其他重要规则
不要使用 goto
除非在极少数情况下(如从深层嵌套循环中跳出,或处理复杂的错误清理),否则应避免使用 goto,它会使代码流程难以追踪。
使用 enum 而不是 #define 定义常量
enum 提供了类型安全,并且调试时能看到有意义的名称。
// 推荐
enum Color {
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE
};
// 不推荐
#define RED 0
#define GREEN 1
#define BLUE 2
强制转换
- 避免不必要的 C 风格强制转换:
(type)expression,它太强大,可以强制转换任何类型。 - 优先使用 C++ 风格的强制转换(在 C 语言中同样可用且更安全):
static_cast:用于编译时可以确定的类型转换。reinterpret_cast:用于底层的、不安全的指针类型转换。const_cast:用于移除或添加const属性。
虽然这些是 C++ 的概念,但现代 C 编译器也支持它们,并且它们比 C 风格的强制转换更具描述性和安全性。
总结与最佳实践
遵循 Google C 语言编程规范,可以帮你写出:
- 清晰易读:统一的命名和格式让任何人都能快速理解代码。
- 健壮可靠:通过避免宏、强制初始化、使用
enum等实践,减少潜在的 bug。 - 易于维护:良好的注释和结构化的代码使得未来的修改和扩展变得简单。
如何开始实践?
- 阅读官方文档:将 Google C++ Style Guide 作为案头手册。
- 使用自动化工具:
clang-format:这是最强大的工具之一,可以根据配置文件自动格式化你的 C/C++ 代码,完美符合 Google 风格。clang-tidy:一个静态分析工具,可以帮你发现潜在的 bug 和代码异味。
- 在你的项目中建立规范:从第一天起就强制执行这些规则,让它们成为团队的开发习惯。
