龙空技术网

C++自学手册 01 注释和函数

启辰8 58

前言:

此刻同学们对“c语言连接器返回错误代码1是什么意思啊”大体比较着重,小伙伴们都需要了解一些“c语言连接器返回错误代码1是什么意思啊”的相关内容。那么小编同时在网上搜集了一些对于“c语言连接器返回错误代码1是什么意思啊””的相关知识,希望你们能喜欢,大家一起来学习一下吧!

这个程序将“Hello World!”打印到标准输出流:

#include <iostream>int main() {    std::cout << "Hello World!" << std::endl;}

分析

让我们详细检查代码的每个部分:

#include <iostream> 是一个预处理器指令,它包含了标准 C++ 头文件 iostream 的内容。

iostream 是一个标准库头文件,包含了标准输入和输出流的定义。这些定义包含在 std 命名空间中,下面会进行解释。

标准输入/输出(I/O)流为程序提供了从外部系统(通常是终端)获取输入和输出的方式。

int main() { ... } 定义了一个名为 main 的新函数。按照惯例,程序执行时会调用 main 函数。C++ 程序中必须只有一个 main 函数,并且它必须返回一个整数类型的值。

这里,int 是所谓的函数的返回类型。main 函数返回的值是一个退出代码。

按照惯例,程序退出代码为 0 或 EXIT_SUCCESS 被系统解释为成功。任何其他返回代码都与错误相关联。

如果未出现 return 语句,则 main 函数(因此,程序本身)将默认返回 0。在这个例子中,我们不需要显式地写 return 0;。

所有其他函数(返回 void 类型的函数除外)必须根据其返回类型显式返回一个值,或者根本不返回。

std::cout << "Hello World!" << std::endl; 将“Hello World!”打印到标准输出流:

std 是一个命名空间,:: 是作用域解析运算符,它允许在命名空间内按名称查找对象。

这里,我们使用 :: 来表明我们想要使用 std 命名空间中的 cout。

有关更多信息,请参阅 Microsoft 文档中的作用域解析运算符。

std::cout 是在 iostream 中定义的标准输出流对象,它打印到标准输出(stdout)。

<< 在这里的上下文中是流插入运算符,之所以被称为插入,是因为它将对象插入到流对象中。

标准库为某些数据类型定义了 << 运算符,以将数据插入到输出流中。

流 << 内容将内容插入流并返回相同的,但已更新的流。这允许流插入被链式操作:std::cout << "Foo" << " Bar"; 将“FooBar”打印到控制台。

"Hello World!" 是一个字符字符串字面量,或称为“文本字面量”。iostream 文件中为字符字符串字面量定义了流插入运算符。

std::endl 是一个特殊的 I/O 流操纵符对象,也在 iostream 文件中定义。将操纵符插入流会更改流的状态。

流操纵符 std::endl 执行两个操作:首先它插入换行符,然后它刷新流缓冲区,以强制文本显示在控制台上。这确保了插入到流中的数据实际上出现在您的控制台上。(流数据通常存储在缓冲区中,然后“刷新”成批,除非您立即强制刷新。)

避免刷新的另一种方法是:

std::cout << "Hello World!\n";

其中 \n 是换行符的字符转义序列。

分号 (;) 通知编译器一个语句已经结束。所有 C++ 语句和类定义都需要一个结束/终止分号。

1.2节:注释

注释是将任意文本放入源代码中的一种方式,使 C++ 编译器不对其进行任何功能上的解释。注释用于提供对程序的设计或方法的见解。

C++ 中有两种类型的注释:

单行注释

双正斜杠序列 // 将直到新行的所有文本标记为注释:

int main() {    // 这是单行注释。    int a; // 这也是单行注释。    int i; // 这是另一个单行注释。}

C 风格/块注释

使用序列 /* 来声明注释块的开始,使用序列 */ 来声明注释的结束。开始和结束序列之间的所有文本都被解释为注释,即使该文本是有效的 C++ 语法。这些有时被称为“C 风格”注释,因为这种注释语法是从 C++ 的前身语言 C 继承而来的:

int main() {    /* * 这是块注释。 */    int a;}

在任何块注释中,你可以写任何你想要的。当编译器遇到 */ 符号时,它会终止块注释:

int main() {    /* 一个包含符号 /* 的块注释。    * 注意,尽管编译器不受第二个 /* 的影响,但一旦到达结束块注释符号,注释就结束了。 */    int a;}

上述示例是有效的 C++(和 C)代码。然而,块注释中的额外 /* 可能会导致一些编译器发出警告。

块注释也可以在单行内开始和结束。例如:

void SomeFunction(/* 参数 1 */ int a, /* 参数 2 */ int b);

注释的重要性

与其他编程语言一样,注释提供了几个好处:

为代码提供明确的文档,使其更易于阅读/维护

解释代码的目的和功能

提供代码的历史或理由的详细信息

直接在源代码中放置版权/许可证、项目笔记、特别感谢、贡献者信用等

然而,注释也有其缺点:

它们必须维护以反映代码中的任何更改

过多的注释往往会降低代码的可读性

通过编写清晰、自解释的代码,可以减少注释的需要。一个简单的例子是为变量、函数和类型使用解释性名称。将逻辑相关的任务分解为离散的函数与此相辅相成。

注释标记用于禁用代码

在开发过程中,注释也可以用来快速禁用代码的部分而不必删除它。这通常对测试或调试很有用,但不是长期编辑的好风格。这通常被称为“注释掉”。同样,为了参考目的而将旧版本的代码保留在注释中是不推荐的,因为这会弄乱文件,与通过版本控制系统探索代码历史相比,提供的价值很小。

1.3节:标准 C++ 编译过程

可执行的 C++ 程序代码通常由编译器生成。

编译器是一个将编程语言代码转换为计算机(更)直接可执行形式的程序。使用编译器将代码转换称为编译。

C++ 继承了其“父”语言 C 的编译过程的形式。以下是 C++ 中编译的四个主要步骤的列表:

C++ 预处理器将任何包含的头文件的内容复制到源代码文件中,生成宏代码,并使用 #define 定义的符号常量替换它们的值。

C++ 预处理器生成的扩展源代码文件被编译为适合平台的汇编语言。

由编译器生成的汇编器代码被汇编成适合平台的适当目标代码。

由汇编器生成的目标代码文件与任何库函数使用的目标代码文件链接在一起,以产生可执行文件。

注意:一些编译代码被链接在一起,但不是创建最终程序。通常,这个“链接”的代码也可以被打包成其他程序可以使用的格式。这个“打包的可用代码”捆绑包是 C++ 程序员所指的库。

许多 C++ 编译器也可能为了便捷或额外的分析而合并或拆分编译过程的某些部分。许多 C++ 程序员会使用不同的工具,但所有这些工具在参与程序生产时通常会遵循这个一般化的过程。

1.4节:函数

函数是代表一系列语句的代码单元。

函数可以接收参数或值,并返回单个值(或不返回)。要使用函数,需要使用参数值进行函数调用,并将函数调用本身的使用替换为其返回值。

每个函数都有一个类型签名——它的参数类型和返回类型的类型。

函数受到过程和数学函数概念的启发。

注意:C++ 函数本质上是过程,并不遵循数学函数的确切定义或规则。

函数通常旨在执行特定任务,并且可以从程序的其他部分调用。在程序的其他位置调用函数之前,必须先声明并定义函数。

注意:流行的函数定义可能隐藏在其他包含的文件中(通常为了方便和跨多个文件重用)。这是头文件的常见用法。

函数声明

函数声明向编译器宣布一个函数的存在,包括其名称和类型签名。

语法如下:

int add2(int i); // 该函数的类型为 (int) -> (int)

在上面的示例中,int add2(int i) 函数向编译器声明以下内容:

返回类型为 int。

函数名为 add2。

函数的参数数量为 1:

第一个参数的类型为 int。

在函数的内容中,第一个参数将被称为 i。 参数名称是可选的;函数的声明也可以是以下形式:

int add2(int); // 允许省略函数参数的名称。

根据单一定义规则,具有某个类型签名的函数在整个 C++ 代码库中对 C++ 编译器可见的范围内只能声明或定义一次。换句话说,具有特定类型签名的函数不能被重新定义——它们必须只定义一次。因此,以下代码不是有效的 C++:

int add2(int i); // 编译器将注意到 add2 是一个 (int) -> int 函数int add2(int j); // 由于 add2 已经有 (int) -> int 的定义,编译器将认为这是一个错误。

如果函数不返回任何内容,其返回类型写为 void。如果它不接受任何参数,参数列表应该是空的。

void do_something(); // 该函数不接受参数,也不返回任何内容。// 注意,它仍然可以影响它有权访问的变量。

函数调用

在函数被声明后,可以被调用。例如,在 main 函数中使用值为 2 调用 add2:

#include <iostream>int add2(int i); // add2 的声明// 注意:add2 仍然缺少定义。// 即使它没有直接出现在代码中,// add2 的定义可能从另一个目标文件中链接进来。int main() {    std::cout << add2(2) << "\n"; // 在这一点上评估 add2(2),    // 并将结果打印出来。返回 0;}

这里,add2(2) 是函数调用的语法。

函数定义

函数定义与声明类似,只是它还包含在函数被调用时执行的代码。

add2 的函数定义示例可能是:

int add2(int i) { // 传递给 (int i) 的数据将被称为 i    int j = i + 2; // 定义变量 j 为 i+2 的值    return j; // 返回或本质上替换 j 为对 add2 的函数调用}

函数重载

可以创建多个具有相同名称但参数不同的函数。

int add2(int i) { // 当 add2() 用一个参数调用时,将评估这个定义中的代码    int j = i + 2;    return j;}int add2(int i, int j) { // 然而,当 add2() 用两个参数调用时,    // 初始声明的代码将被重载,    int k = i + j + 2;    return k;}

两个函数都被称为 add2,但实际调用的函数直接取决于调用中的参数数量和类型。在大多数情况下,C++ 编译器可以计算出要调用哪个函数。

在某些情况下,必须显式指定类型。

默认参数

函数参数的默认值只能在函数声明中指定。

int multiply(int a, int b = 7); // b 的默认值为 7int multiply(int a, int b) {    return a * b; // 如果 multiply() 用一个参数调用,    // 将与默认值 7 相乘。}

在这个示例中,multiply() 可以用一个或两个参数调用。如果只给定一个参数,b 将具有默认值 7。默认参数必须放在函数的后部参数中。例如:

int multiply(int a = 10, int b = 20); // 这是合法的int multiply(int a = 10, int b); // 这是非法的,因为 int a 在前者

特殊函数调用 - 运算符

C++ 中存在具有与 name_of_function(value1, value2, value3) 不同语法的特殊函数调用。最常见的示例是运算符。某些特殊字符序列将由编译器减少为函数调用,例如 !, +, -, *, %, << 和许多更多。这些特殊字符通常与非编程用途相关联,或用于审美(例如,+ 字符在 C++ 编程中和小学数学中都被认为是加法符号)。

C++ 用特殊语法处理这些字符序列;但本质上,每个运算符的出现都被减少为函数调用。例如,以下 C++ 表达式:

3 + 3

等同于以下函数调用:

operator+(3, 3)

所有运算符函数名称都以 operator 开始。

虽然在 C++ 的直接前身 C 中,运算符函数名称不能通过提供具有不同类型签名的额外定义来分配不同的意义,但在 C++ 中,这是有效的。在 C++ 中,将额外的函数定义“隐藏”在唯一的函数名称下被称为运算符重载,这是 C++ 中相对常见但不普遍的约定。

1.5节:函数原型和声明的可见性

在 C++ 中,代码必须在使用前声明或定义。例如,以下产生编译时错误:

int main() {    foo(2); // 错误:foo 被调用,但尚未声明}void foo(int x) { }

有两种方法可以解决这个问题:将 foo() 的定义或声明放在其在 main() 中的使用之前。

这是一个示例:

void foo(int x) {} // 首先声明 foo 函数和主体int main() {    foo(2); // OK:foo 在此之前已完全定义,所以可以在这里调用。}

然而,也可以通过在之前使用“原型”声明来“预先声明”该函数,然后稍后定义函数主体:

void foo(int); // foo 的原型声明,在 main 中可见// 必须指定返回类型、名称和参数列表类型int main() {    foo(2); // OK:foo 是已知的,尽管其主体尚未定义,但可以调用。}void foo(int x) { } // 必须与原型匹配

原型必须指定返回类型(void)、函数的名称(foo)和参数列表变量类型(int),但不需要参数名称。

将此集成到源文件组织的常见方式是制作一个包含所有原型声明的头文件:

// foo.hvoid foo(int); // 原型声明

然后在其他地方提供完整的定义:

// foo.cpp -> foo.o#include "foo.h" // foo 的原型声明在这里“隐藏”void foo(int x) { } // foo 的主体定义

然后,在编译的链接阶段,将相应的目标文件 foo.o 链接到使用它的编译目标文件中:

// main.cpp -> main.o#include "foo.h" // foo 的原型声明在这里“隐藏”int main() {    foo(2); // foo 是有效的调用,因为其原型声明是事先的。    // 通过目标文件将 foo 的原型和主体定义链接在一起}

如果存在函数原型和调用,但未定义函数主体,则会发生“未解决的外部符号”错误。这些错误可能更难解决,因为编译器直到最终链接阶段才报告错误,而且它不知道要跳转到代码中的哪一行以显示错误。

标签: #c语言连接器返回错误代码1是什么意思啊