C++ 调用约定
在C++中,约定是指编写代码时遵循的标准规则和指南。
这些约定涵盖了广泛的主题,包括:
1. 命名约定
这些是关于在代码中命名变量、函数和其他标识符的规则。例如,通常使用驼峰命名法( camelCase)命名变量,而使用大驼峰命名法( PascalCase)命名函数。
C++ 代码
#include
// constants are usually written in all uppercase with underscores
const int ARRAY_SIZE = 100;
// variables are usually written in camelCase
int array[ARRAY_SIZE];
int arrayIndex = 0;
// functions are usually written in PascalCase
void InitializeArray() {
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = 0;
}
}
int main() {
// variables used in a loop are often written in a shortened form
for (int i = 0; i < ARRAY_SIZE; i++) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
return 0;
}
输出:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
解释
在这个示例中,常量以大写字母加下划线的形式书写,变量以驼峰式书写,函数以帕斯卡命名法书写。此外,在循环中使用的变量通常以缩写形式书写。这些约定并不是严格要求的,但它们在C++社区中广泛遵循,可以帮助使您的代码更具可读性和易于理解。
2. 代码布局约定
这些是用于格式化和组织代码的规则。例如,许多程序员使用缩进来表示代码的块结构,并使用空格来分隔不同的元素。
C++代码
#include
// constants are usually written in all uppercase with underscores
const int ARRAY_SIZE = 100;
int main() {
// variables are usually written in camelCase
int array[ARRAY_SIZE];
int arrayIndex = 0;
// indentation is used to indicate the block structure of the code
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = i;
}
// whitespace is used to separate different elements
for (int i = 0; i < ARRAY_SIZE; i++) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
return 0;
}
输出:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
说明
在这个示例中,缩进用于表示代码的块结构,空格用于分隔不同的元素。这些约定有助于使代码更易读和理解。您还可以遵循许多其他代码布局约定,如使用空行分隔不同的代码部分,使用注释提供附加上下文和文档。通过遵循这些约定,您可以使代码更加一致和易于使用。
3. 注释约定
这些是用于编写代码注释的规则。注释可以帮助解释代码片段的目的或为函数提供文档。
C++ 代码
#include
// constants are usually written in all uppercase with underscores
const int ARRAY_SIZE = 100;
int main() {
// variables are usually written in camelCase
int array[ARRAY_SIZE];
// initialize all elements of the array to 0
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = 0;
}
// print out all elements of the array
for (int i = 0; i < ARRAY_SIZE; i++) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
return 0;
}
输出:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
解释
在这个示例中,使用注释来提供代码的额外背景和解释。例如,注释用于解释将数组初始化为0的循环的目的,另一个注释用于解释打印数组元素的循环的目的。
注释的约定可能有所不同,但通常在代码中包含注释是一个好的做法,可以帮助解释代码的目的并提供额外的背景信息。这可以使你的代码更容易理解和维护。
4. 文档约定
这些是为你的代码编写文档(如API文档或类文档)的规则。
C++代码
/**
* @file example.cpp
* @brief An example program that demonstrates documentation conventions.
*
* This program initializes an array of integers to 0 and then prints
* out all of the elements of the array.
*
* @author John Doe
* @date January 1, 2020
*/
#include
// constants are usually written in all uppercase with underscores
const int ARRAY_SIZE = 100;
/**
* The main function of the program.
*
* @return 0 if the program executes successfully, non-zero otherwise.
*/
int main() {
// variables are usually written in camelCase
int array[ARRAY_SIZE];
// initialize all elements of the array to 0
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = 0;
}
// print out all elements of the array
for (int i = 0; i < ARRAY_SIZE; i++) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
return 0;
}
输出:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
解释
在这个示例中,在文件的开头以及主函数上方提供了文档。文件的文档包括程序的目的、作者和日期的信息。函数的文档包括主函数的目的和返回值的信息。文档约定可以有所不同,但通常在代码中包含文档是一个很好的习惯,可以帮助解释代码的目的并提供额外的上下文。这可以使你的代码更容易理解和维护。
通过遵循约定,你可以让你的代码对其他程序员来说更容易阅读和理解。它还可以帮助确保你的代码是一致的,并遵循最佳实践。
5. 调用约定
在C++中,调用约定是一组规则,它规定了如何将函数参数传递给函数,以及如何将返回值传递给调用者。调用约定用于确保在不同的平台和编译器上以正确和一致的方式调用函数。
在C++中有几种常用的调用约定,包括:
__cdecl: 这是大多数平台上C++的默认调用约定。它是一种“干净”的调用约定,不使用任何特殊的指令或寄存器来传递函数参数或返回值。相反,它依靠堆栈来传递参数,EAX寄存器来返回值。
__stdcall: 这个调用约定用于通过函数名调用的函数。它类似于__cdecl,但调用者负责在函数返回后清理堆栈。
__fastcall: 这个调用约定用于通过寄存器(ECX和EDX)传递前两个参数来优化函数调用。它比__cdecl或__stdcall更快,但不太灵活,因为它只能在寄存器中传递有限数量的参数。
__thiscall: 这个调用约定用于C++类的成员函数。它类似于__fastcall,但this指针在ECX寄存器中传递。
你可以通过使用函数属性来为C++中的函数指定特定的调用约定。例如:
C++代码
#include
// specify the __cdecl calling convention
extern "C" void __cdecl cdeclFunction(int a, int b);
// specify the __stdcall calling convention
extern "C" void __stdcall stdcallFunction(int a, int b);
// specify the __fastcall calling convention
extern "C" void __fastcall fastcallFunction(int a, int b);
int main() {
// call the functions using the specified calling conventions
cdeclFunction(1, 2);
stdcallFunction(3, 4);
fastcallFunction(5, 6);
return 0;
}
解释
在这个示例中,使用不同的调用约定声明了三个函数:__cdecl,__stdcall和__fastcall。主函数使用指定的调用约定调用这些函数。
正确使用函数的调用约定非常重要,以确保正确调用函数并避免出现栈损坏或错误的返回值等问题。
你可以通过使用函数属性来为C++函数指定特定的调用约定。例如:
语法-1
void __stdcall foo(int a, int b);
或者,您可以使用#pragma指令为模块中的所有函数指定调用约定:
语法-2
#pragma argsused
在C++中使用调用约定的优势
在C++中使用调用约定有几个优势:
兼容性: 不同的平台和编译器对于函数参数的传递和返回值的返回可能有不同的要求。通过使用特定的调用约定,你可以确保你的代码与不同的平台和编译器兼容。
一致性: 使用一致的调用约定可以帮助确保你的代码易于阅读和理解,特别是在团队中工作或者代码将被他人使用时。
优化: 某些调用约定被设计得比其他调用约定更高效。例如,__fastcall调用约定可以比__cdecl或者__stdcall更快,因为它使用寄存器来传递函数参数。这对于频繁调用的函数尤其有用。
安全性: 使用一致的调用约定可以帮助防止堆栈破坏或者错误的返回值等错误。例如,__stdcall调用约定确保调用者在函数返回后负责清理堆栈,这可以帮助防止堆栈破坏。
总的来说,使用调用约定可以使你的代码更兼容、一致、高效和安全。
在C++中使用调用约定的缺点
在C++中使用调用约定有一些潜在的缺点:
复杂性: 使用不同的调用约定可能会给你的代码增加复杂性,特别是在一个具有许多不同函数的大型项目中工作时。这可能会使你的代码更难理解和维护。
兼容性问题: 如果你使用的调用约定不被特定的平台或者编译器支持,你可能需要修改你的代码或者使用不同的调用约定。这可能需要花费时间,并且可能需要额外的测试来确保你的代码仍然正确。
性能开销: 某些调用约定可能会引入一些性能开销,特别是如果它们需要额外的指令或者寄存器使用。这对于大多数应用程序来说可能不重要,但对于高性能代码来说可能是一个考虑因素。
可移植性: 如果你使用的调用约定是特定于特定平台或编译器的,你的代码可能无法在其他平台或编译器上移植。这可能是一个问题,如果你想在不同的平台上重用你的代码,或者在混合环境中工作。
总的来说,使用调用约定的缺点通常与优点相比较都比较小,但在决定使用哪种调用约定时,考虑这些缺点仍然很重要。