开始使用C ++

Download c++ eBook

备注

“Hello World”程序是一个常见的示例,可以简单地用于检查编译器和库的存在。它使用C ++标准库,使用来自<iostream> std::cout ,并且只有一个文件可以编译,从而最大限度地减少了编译期间可能出现用户错误的可能性。


编译器和操作系统之间编译C ++程序的过程本质上是不同的。主题Compiling and Building包含有关如何在不同平台上为各种编译器编译C ++代码的详细信息。

版本

标准发布日期
C ++ 98 ISO / IEC 14882:1998 1998-09-01
C ++ 03 ISO / IEC 14882:2003 2003-10-16
C ++ 11 ISO / IEC 14882:2011 2011-09-01
C ++ 14 ISO / IEC 14882:2014 情节中字
C ++ 17 TBD 2017年1月1日
C ++ 20 TBD 2020年1月1日

你好,世界

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

#include <iostream>

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

在Coliru现场观看

分析

让我们详细研究一下这段代码的每一部分:

  • #include <iostream> 是一个预处理程序指令 ,包含标准C ++头文件iostream

    iostream标准库头文件 ,包含标准输入和输出流的定义。这些定义包含在std 命名空间中,如下所述。

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

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

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

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

    如果不存在return 语句,则main 函数(以及程序本身)默认返回0 。在这个例子中,我们不需要显式写入return 0;

    除返回void 类型的函数外,所有其他函数必须根据其返回类型显式返回值,否则根本不能返回。

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

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

      有许多名称空间。在这里,我们使用:: 来表示我们想要从std 命名空间中使用cout 。有关更多信息,请参阅范围解析操作员 - Microsoft文档

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

    • << 就是, 在这种情况下流插入操作 ,所谓的,因为它插入对象插入到对象。

      标准库定义<< 运算符,用于将某些数据类型的数据插入到输出流中。 stream << contentcontent 插入到流中并返回相同但更新的流。这允许链流插入: std::cout << "Foo" << " Bar"; 将“FooBar”打印到控制台。

    • "Hello World!"字符串文字 ,或“文字文字”。字符串文字的流插入运算符在文件iostream 定义。

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

      流操纵器std::endl 做了两件事:首先插入行尾字符,然后刷新流缓冲区以强制文本显示在控制台上。这可确保插入到流中的数据实际显示在控制台上。 (流数据通常存储在缓冲区中,然后批量“刷新”,除非您立即强制刷新。)

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

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

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

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

评论

注释是一种将任意文本放在源代码中的方法,而不需要C ++编译器将其解释为具有任何功能意义。注释用于深入了解程序的设计或方法。

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

单行评论

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

int main()
{
   // This is a single-line comment.
   int a;  // this also is a single-line comment
   int i;  // this is another single-line comment
}
 

C风格/块评论

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

int main()
{
   // This is a single-line comment.
   int a;  // this also is a single-line comment
   int i;  // this is another single-line comment
}
 

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

int main()
{
   // This is a single-line comment.
   int a;  // this also is a single-line comment
   int i;  // this is another single-line comment
}
 

上面的例子是有效的C ++(和C)代码。但是,在块注释中添加/* 可能会导致某些编译器发出警告。

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

int main()
{
   // This is a single-line comment.
   int a;  // this also is a single-line comment
   int i;  // this is another single-line comment
}
 

评论的重要性

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

  • 明确的代码文档,使其更易于阅读/维护
  • 解释代码的目的和功能
  • 有关代码背后的历史或推理的详细信息
  • 直接在源代码中放置版权/许可证,项目说明,特别感谢,贡献者积分等。

但是,评论也有其缺点:

  • 必须维护它们以反映代码中的任何更改
  • 过多的注释往往会降低代码的可读性

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

注释标记用于禁用代码

在开发过程中,注释还可用于快速禁用部分代码而不删除它。这通常对测试或调试有用,但对于临时编辑以外的任何其他方式都不是好的样式。这通常被称为“评论”。

类似地,将一段代码的旧版本保留在注释中用于参考目的是不受欢迎的,因为与通过版本控制系统探索代码的历史相比,它在提供很少价值的同时使文件变得混乱。

功能

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

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

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

函数的灵感来自过程和数学函数的概念。

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

功能通常用于执行特定任务。并且可以从程序的其他部分调用。必须先声明和定义函数,然后才能在程序中的其他位置调用它。

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

功能声明

函数声明声明存在一个函数及其名称和类型签名给编译器。语法如下:

int add2(int i); // The function is of the type (int) -> (int)
 

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

  • 返回类型int
  • 该函数的名称add2
  • 函数的参数个数为1:
    • 第一个参数是int 类型。
    • 第一个参数将在函数的内容中通过名称i 引用。

参数名称是可选的;该功能的声明也可以如下:

int add2(int i); // The function is of the type (int) -> (int)
 

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

int add2(int i); // The function is of the type (int) -> (int)
 

如果函数不返回任何内容,则其返回类型将写为void 。如果不带参数,则参数列表应为空。

int add2(int i); // The function is of the type (int) -> (int)
 

功能调用

声明后可以调用一个函数。例如,以下程序在main 函数中调用值为2 add2

int add2(int i); // The function is of the type (int) -> (int)
 

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

功能定义

函数定义 *类似于声明,除了它还包含在函数体内调用函数时执行的代码。

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

int add2(int i); // The function is of the type (int) -> (int)
 

功能重载

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

int add2(int i); // The function is of the type (int) -> (int)
 

这两个函数都使用相同的名称add2 调用,但调用的实际函数直接取决于调用中参数的数量和类型。在大多数情况下,C ++编译器可以计算要调用的函数。在某些情况下,必须明确说明类型。

默认参数

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

int add2(int i); // The function is of the type (int) -> (int)
 

在此示例中,可以使用一个或两个参数调用multiply() 。如果只给出一个参数,则b 默认值为7.默认参数必须放在函数的后一个参数中。例如:

int add2(int i); // The function is of the type (int) -> (int)
 

特殊功能调用 - 操作员

在C ++中存在特殊的函数调用,它们具有与name_of_function(value1, value2, value3) 不同的语法。最常见的例子是运营商。

某些特殊字符序列将被编译器简化为函数调用,例如!+-*%<< 以及更多。这些特殊字符通常与非编程用法相关联或用于美学(例如, + 字符通常被认为是C ++编程和初等数学中的加法符号)。

C ++使用特殊语法处理这些字符序列;但实际上,每次出现的运算符都会缩减为函数调用。例如,以下C ++表达式:

int add2(int i); // The function is of the type (int) -> (int)
 

相当于以下函数调用:

int add2(int i); // The function is of the type (int) -> (int)
 

所有操作员函数名称都以operator 开头。

虽然在C ++的前一个C中,通过提供具有不同类型签名的附加定义,操作符函数名称不能被赋予不同的含义,在C ++中,这是有效的。在一个唯一的函数名称下“隐藏”其他函数定义在C ++中称为运算符重载 ,并且是C ++中相对常见但不通用的约定。

预处理器

预处理器是编译器的重要组成部分

它编辑源代码,削减一些比特,改变其他部分,并添加其他东西。

在源文件中,我们可以包含预处理器指令。这些指令告诉预处理器执行特定操作。指令以新行上的#开头。例:

#define ZERO 0
 

您将遇到的第一个预处理器指令可能是

#define ZERO 0
 

指示。它的作用是需要的所有something ,并将其插入你的文件,其中的指令了。 hello world程序从该行开始

#define ZERO 0
 

此行添加了允许您使用标准输入和输出的函数和对象。

C语言也使用预处理器,没有与C ++语言一样多的头文件 ,但在C ++中,您可以使用所有C头文件。


下一个重要指令可能是

#define ZERO 0
 

指示。这告诉预处理器,当它沿着文件传递时,它应该用something 替换每一个出现的something_else 。它也可以使功能类似,但可能算作高级C ++。

不需要something_else ,但是如果你定义的something 没什么,那么在预处理器指令之外,所有出现的something 都会消失。

由于#if#else#ifdef 指令,这实际上很有用。这些格式如下:

#define ZERO 0
 

这些指令插入true位中的代码,并删除false位。这可以用于包含仅包含在某些操作系统上的代码,而无需重写整个代码。

标准的C ++编译过程

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

编译器是一种程序,它将代码从编程语言转换为另一种形式,这种形式(更多)可直接为计算机执行。使用编译器来转换代码称为编译。

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

  1. C ++预处理器将任何包含的头文件的内容复制到源代码文件中,生成宏代码,并将使用#define定义的符号常量替换为它们的值。
  2. 由C ++预处理器生成的扩展源代码文件被编译为适合该平台的汇编语言。
  3. 由编译器生成的汇编程序代码被组装成适合该平台的目标代码。
  4. 汇编程序生成的目标代码文件与用于生成可执行文件的任何库函数的目标代码文件链接在一起。
  • 注意:某些已编译的代码链接在一起,但不能创建最终程序。通常,这种“链接”代码也可以打包成其他程序可以使用的格式。这个“打包的,可用的代码包”是C ++程序员所称的库。

许多C ++编译器也可以合并或取消合并编译过程的某些部分,以方便或进行其他分析。许多C ++程序员将使用不同的工具,但是当他们参与程序的生成时,所有这些工具通常都会遵循这个通用的过程。

下面的链接扩展了这个讨论,并提供了一个很好的图形来帮助。 [1]: http//faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html

函数原型和声明的可见性

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

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

有两种方法可以解决这个问题:在main() 使用foo() 的定义或声明之前。这是一个例子:

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

但是,也可以通过在使用之前仅放置“原型”声明来“转发声明”该函数,然后再定义函数体:

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

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

将其集成到源文件组织中的一种常用方法是创建包含所有原型声明的头文件:

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

然后在别处提供完整的定义:

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

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

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

当函数原型调用存在时,会发生“未解析的外部符号”错误,但未定义函数 。由于编译器在最后的链接阶段之前不会报告错误,并且它不知道在代码中跳转到哪一行来显示错误,因此解决这些问题会更加棘手。

Stats

9370 Contributors: 111
Wednesday, August 2, 2017
许可下: CC-BY-SA

不隶属于 Stack Overflow
Rip Tutorial: info@zzzprojects.com

下载电子书