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
解释
在这个例子中,常量用大写字母加下划线,变量用camelCase,而函数用PascalCase。此外,在循环中使用的变量通常用简短的形式来写。这些约定并不是严格要求的,但它们在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.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相似,但这个指针是在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++中使用调用约定有几个潜在的缺点。
复杂性: 使用不同的调用约定会增加你的代码的复杂性,特别是当你在一个有许多不同函数的大项目上工作时。这可能会使理解和维护你的代码更加困难。
兼容性问题: 如果你使用的调用约定不被特定的平台或编译器支持,你可能不得不修改你的代码或使用不同的调用约定。这可能很耗时,而且可能需要额外的测试来确保你的代码仍然是正确的。
性能开销: 一些调用约定可能会引入少量的性能开销,特别是如果它们需要额外的指令或寄存器使用。这对于大多数应用程序来说可能并不重要,但对于高性能代码来说,这可能是一个考虑因素。
可移植性: 如果你使用的是特定平台或编译器的调用约定,你的代码可能无法移植到其他平台或编译器。如果你想在不同的平台上重复使用你的代码,或者你在混合环境中工作,这可能是一个问题。
总的来说,与优点相比,使用调用约定的缺点通常是次要的,但在决定为你的代码使用哪种调用约定时,必须考虑这些缺点。