一.编程规范
C语言编程规范是一套旨在提高代码质量、可读性和维护性的标准和建议。遵循良好的编程规范可以使得代码更加清晰、易于理解和管理,尤其是在团队协作的项目中。以下是一些常见的C语言编程规范和最佳实践:
1. 代码格式化
if (condition) {
// 代码块
} else {
// 另一个代码块
}
- 缩进:使用空格或用Tap键制表符(通常是4个空格)进行缩进,以保持代码结构的清晰。
- 大括号风格:对于函数、条件语句和循环,大括号的使用有多种风格(如K&R、Allman等)。选择一种风格并保持一致。
- 行长度:限制代码的行长度(通常不超过80或120个字符),过长的行可以适当换行。
- 例如
printf("这是一个非常非常非常非常非常非常非常非常非常非常长的字符串,可能会导致阅读和维护上的不便");
2. 命名约定
不推荐:
int n; // 不清楚变量用途
float prc; // 缩写可能导致混淆
推荐:
int itemCount;
float itemPrice;
- 变量和函数命名:使用有意义的名称,避免使用缩写。变量名使用小写字母,多个单词之间可以用下划线分隔;函数名可以使用驼峰命名法。
- 常量命名:常量名使用大写字母,单词之间用下划线分隔。
-
#define MAX_SIZE 100 const int MaxScore = 100;
- 枚举命名:枚举类型及其值的命名应体现枚举的意图和内容。
3. 注释和文档
- 函数注释:每个函数声明前应有注释,描述函数的目的、参数、返回值和任何副作用。
-
/** * 计算两个整数的最大值。 * @param a 第一个整数 * @param b 第二个整数 * @return 两个整数中的最大值 */ int max(int a, int b) { return a > b ? a : b; }
- 代码注释:对复杂的逻辑或代码段进行适当注释,解释其实现的目的和方式。
- 避免过多注释:代码本身应尽可能清晰自解释,避免不必要或显而易见的注释。
4. 编程实践
- 避免使用全局变量:全局变量会增加代码间的耦合,使得程序的流程难以跟踪和维护。
-
int gCounter; void increment() { gCounter++; }
- 这个时候我们可通过将变量作为参数传递,减少全局变量的使用。
int increment(int counter) { return counter + 1; }
- 使用常量和宏:对于不会改变的值,使用
#define
宏或const
关键字声明常量,以增强代码的可读性和可维护性。 - 指针安全:使用指针时,注意检查空指针并避免悬挂指针和内存泄露。
- 错误处理:适当检查函数返回值和错误码,进行错误处理。
FILE *file = fopen("example.txt", "r"); if (file == NULL) { perror("文件打开失败"); return -1; }
5. 代码重用和模块化
// 计算数组中所有元素的和
int sum(int arr[], int size) {
int total = 0;
for (int i = 0; i < size; i++) {
total += arr[i];
}
return total;
}
- 函数复用:避免重复代码,将重复逻辑抽象为函数进行复用。
- 模块化设计:将相关功能组织为模块或库,提高代码的模块化程度和重用性。
二.C语言中的关键字
我们在这例举了44个关键字,我们要知道C语言中,关键字是不允许作为标识符被出现在程序中的。
下面我们来看看C语言的关键字
-
auto
-
break
-
case
-
char
-
const
-
continue
-
default
-
do
-
double
-
else
-
enum
-
extern
-
float
-
for
-
goto
-
if
-
inline
(C99) -
int
-
long
-
register
-
restrict
(C99) -
return
-
short
-
signed
-
sizeof
-
static
-
struct
-
switch
-
typedef
-
union
-
unsigned
-
void
-
volatile
-
while
-
_Alignas
(C11) -
_Alignof
(C11) -
_Atomic
(C11) -
_Bool
(C99) -
_Complex
(C99) -
_Generic
(C11) -
_Imaginary
(C99) -
_Noreturn
(C11) -
_Static_assert
(C11) -
_Thread_local
(C11)
三.标识符
int mainCounter; // 正确:以字母开头
float _temperature; // 正确:以下划线开头
char *myString; // 正确:使用字母和下划线
int 2ndAttempt; // 错误:不能以数字开头
float float; // 错误:不能使用C语言的关键字
char a*b; // 错误:不能包含除字母、数字和下划线以外的字符
标识符的规则:
- 开头:标识符必须以字母(
A-Z
或a-z
)或下划线(_
)开头。 - 字符:标识符的其余部分可以包含字母、数字和下划线。
- 区分大小写:C语言中的标识符是区分大小写的,这意味着
Example
、example
和EXAMPLE
是三个不同的标识符。 - 关键字限制:标识符不能是C语言的关键字。
- 长度限制:尽管大多数现代C编译器支持的标识符长度非常长,但是前31个字符对于标识符的唯一性是有保证的,超过这个长度的部分可能会被编译器忽略。
标识符的命名最佳实践:
- 明确性:选择有意义的名称,使得标识符能够准确反映其代表的实体或用途。
- 简洁性:尽量简洁,同时保持明确和有意义。
- 规范性:遵守项目或团队的命名规范,如驼峰命名法(
myVariableName
)或下划线分隔法(my_variable_name
)。 - 避免使用下划线开头:尽管下划线开头的标识符在C语言中是合法的,但通常它们被保留用于特定的编译器或库的实现,因此在普通项目中使用时应谨慎。
四.数据类型
在C语言中,数据类型是一个非常重要的概念,它定义了变量可以存储的数据种类,以及对数据可以进行的操作。C语言提供了多种数据类型,主要可以分为以下几类:
1. 基本类型
- 整型(Integer Types):用于存储整数。包括
int
,short
,long
,long long
以及它们的无符号版本unsigned int
,unsigned short
,unsigned long
,unsigned long long
。 - 字符型(Character Type):用于存储单个字符。包括
char
以及unsigned char
和signed char
。 - 浮点型(Floating-point Types):用于存储带有小数部分的数。包括
float
,double
, 和long double
。 - 布尔型(Boolean Type):C99标准新增,用
_Bool
表示,通常用于表示真 (1
) 或假 (0
)。
2. 枚举类型
- 枚举(Enumeration Type):一种用户定义的类型,它允许程序员为整数值指定更具可读性的名字。通过关键字
enum
定义。
3. 派生类型
- 指针(Pointer Types):用于存储变量地址的类型。
- 数组(Array Types):用于存储固定大小和类型相同的元素序列。
- 结构体(Structure Types):允许将多个不同类型的数据项组合成一个单一的复合类型。
- 联合体(Union Types):允许在相同的内存位置存储不同的数据类型,但一次只能使用其中一个。
- 函数类型(Function Types):代表函数的类型,定义了函数的返回类型和参数类型。
4. void类型
- void:特殊的类型,表示无类型。常用于指定没有返回值的函数,或作为无类型指针
void*
使用。
示例
int main() {
int integer = 10; // 整型
char character = 'A'; // 字符型
float floatingPoint = 3.14f; // 浮点型
_Bool boolean = 0; // 布尔型 (C99)
// 枚举类型
enum color {RED, GREEN, BLUE} myColor;
myColor = RED;
// 指针类型
int* pointerToInt = &integer;
// 数组类型
int integerArray[5] = {0, 1, 2, 3, 4};
// 结构体类型
struct person {
char name[50];
int age;
};
struct person person1 = {"Alice", 30};
// 联合体类型
union data {
int integer;
float floatingPoint;
char string[20];
};
union data myData;
// 函数类型
// 定义一个返回整型,参数为两个整型的函数
int add(int a, int b) {
return a + b;
}
return 0;
}
5.数值型和字符型数据的字节数和储存范围
整型
char
:通常1字节,范围通常是-128到127(有符号char
),或0到255(无符号uchar
)。short
:通常2字节,范围通常是-32,768到32,767(有符号short
),或0到65,535(无符号ushort
)。int
:通常4字节,范围通常是-2,147,483,648到2,147,483,647(有符号int
),或0到4,294,967,295(无符号uint
)。long
:在32位系统上通常是4字节,64位系统上可能是4字节(LP64模型)或8字节(LLP64模型),范围至少是-2,147,483,648到2,147,483,647(有符号long
),或0到4,294,967,295(无符号ulong
)。在8字节的系统上,范围扩展到-9,223,372,036,854,775,808到9,223,372,036,854,775,807(有符号),或0到18,446,744,073,709,551,615(无符号)。long long
:通常8字节,范围是-9,223,372,036,854,775,808到9,223,372,036,854,775,807(有符号),或0到18,446,744,073,709,551,615(无符号)。
浮点型
float
:通常4字节,范围大约是±3.4e±38(7位有效数字)。double
:通常8字节,范围大约是±1.7e±308(15位有效数字)。long double
:大小和范围依编译器和平台不同,通常比double
拥有更高的精度和更大的范围,可能是8, 12, 或16字节。
字符型
char
:如上所述,即使char
主要用来表示字符,它在内存中的存储也是以数值形式,因此也遵循上述的字节数和范围。
五.变量的储存类别
1. auto
- 描述:
auto
是默认的存储类别,用于局部变量。实际上,局部变量默认就是auto
,因此很少显式使用这个关键字。 - 作用域:局部作用域。
- 生命周期:只在定义它们的代码块内有效。
void function() {
auto int localVar = 0;
}
2. register
- 描述:
register
存储类别用于声明寄存器变量。寄存器变量被建议存储在CPU的寄存器中,以便快速访问,但实际上这是编译器的优化决策。 - 作用域:局部作用域。
- 生命周期:只在定义它们的代码块内有效。
void function() {
register int loopCounter = 0;
}
3. static
- 描述:
static
存储类别用于声明静态变量。静态局部变量的值在函数调用之间保持持久,静态全局变量只在声明它们的文件内可见。 - 作用域:局部变量时为局部作用域,但在函数调用之间保持其值不变;全局变量时为文件作用域。
- 生命周期:整个程序执行期间。
void function() {
static int staticVar = 0; // 局部静态变量
}
static int fileScopeVar = 0; // 文件作用域的静态变量
4. extern
- 描述:
extern
存储类别用于提供一个全局变量的引用,该变量在其他文件中定义。它用于在一个文件中声明另一个文件中定义的全局变量或函数。 - 作用域:全局作用域。
- 生命周期:整个程序执行期间。
// 假设在file1.c中定义了globalVar
extern int globalVar; // 在file2.c中声明,可以使用file1.c中定义的globalVar
void function() {
globalVar = 5;
}
注意点
- 作用域和生命周期:作用域决定了代码中可以访问变量的区域。生命周期决定了变量在内存中存在的时间。
- 选择存储类别:正确选择变量的存储类别对于程序的正确性和效率至关重要。
- 编译器优化:对于
register
声明,现代编译器会进行自己的优化判断,可能会忽略register
建议。