当前位置: 首页 > news >正文

【C++从入门到精通】第2篇:C++基础知识(中)

文章目录

  • 2.1 iostream介绍:cout、cin和endl
    • 2.1.1 输入/输出库
    • 2.1.2 std::cout
    • 2.1.3 std::endl
    • 2.1.4 std::cout是缓冲的
    • 2.1.5 std::endl与\n
    • 2.1.6 std::cin
    • 2.1.7 总结
    • 2.1.8 练习时间
  • 2.2 未初始化的变量和未定义的行为
    • 2.2.1 未初始化的变量
    • 2.2.2 未定义行为
    • 2.2.3 明确定义的行为和不明确定义的行为
    • 2.2.4 练习时间
  • 2.3 关键字和命名标识符
    • 2.3.1 关键字
    • 2.3.2 标识符命名规则
    • 2.3.3 标识符命名最佳实践
    • 2.3.4 练习时间
  • 2.4 空白和基本格式
    • 2.4.1 某些语言元素必须以空格分隔
    • 2.4.2 引用的文本按字面意思使用空格的数量
    • 2.4.3 使用空白来格式化代码
    • 2.4.4 基本格式
  • 2.5 本章答案


2.1 iostream介绍:cout、cin和endl

本节,我们将更多地讨论在“Hello, World!”程序中使用的std::cout语句,将文本“Hello, World!”输出到控制台。我们还将探讨如何从用户获取输入,我们将使用这些使我们的程序更具交互性。

2.1.1 输入/输出库

输入/输出库(io 库)是处理基本输入和输出的C++标准库的一部分。我们将使用此库中的功能从键盘获取输入并将数据输出到控制台。iostream的io部分代表输入/输出。

要使用iostream库中定义的功能,我们需要在使用iostream中定义的内容的任何代码文件的顶部包含iostream 标头,如下所示:

#include <iostream>// 此处使用iostream功能的其余代码

2.1.2 std::cout

iostream库包含一些预定义的变量供我们使用。最有用的之一是std::cout,它允许我们将数据发送到控制台以打印为文本。cout代表“字符输出”。

作为复习,这是我们的“Hello, World!”程序:

#include <iostream> // 为了使用 std::coutint main()
{std::cout << "Hello world!"; // 打印 Hello world! 到控制台return 0;
}

在这个程序中,我们包含了iostream,以便可以使用std::cout。在我们的主函数中,我们使用std::cout以及插入运算符(<<)来发送文本Hello world!到要打印的控制台。

std::cout不仅可以打印文本,还可以打印数字:

#include <iostream> // 为了使用 std::coutint main()
{std::cout << 4; // 打印 4 到控制台return 0;
}

这将产生结果:

4

它还可用于打印变量的值:

#include <iostream> // 为了使用 std::coutint main()
{int x{ 5 }; // 定义整形变量x,初始化为值5std::cout << x; // 将x的值(5)打印到控制台return 0;
}

这将产生结果:

5

要在同一行上打印多个内容,可以在单个语句中多次使用插入运算符(<<)来连接。例如:

#include <iostream> // 为了使用 std::coutint main()
{std::cout << "Hello" << " world!";return 0;
}

此程序打印:

Hello world!

下面是另一个示例,我们在同一语句中同时打印文本和变量的值:

#include <iostream>int main()
{int x{ 5 };std::cout << "x is equal to: " << x;return 0;
}

此程序打印:

x is equal to: 5

2.1.3 std::endl

您觉得此程序会打印什么?

#include <iostream> // for std::coutint main()
{std::cout << "Hi!";std::cout << "My name is Alex.";return 0;
}

您可能会对结果感到惊讶:

Hi!My name is Alex.

单独的输出语句不会导致控制台上的输出行单独(即换行)。

如果我们想将单独的输出行打印到控制台,我们需要告诉控制台何时将光标移动到下一行。

一种方法是使用std::endl。使用std::cout输出时,std::endl会将换行符打印到控制台(导致光标转到下一行的开头)。在这种情况下,endl代表“终点线”。

例如:

#include <iostream> // 为了使用 std::cout 和 std::endlint main()
{std::cout << "Hi!" << std::endl; // std::endl 将使光标移动到控制台的下一行std::cout << "My name is Alex." << std::endl;return 0;
}

这将打印:

Hi!
My name is Alex.

拓展:
在上面的程序中,第二个std::endl实际上不是必需的,因为程序会立即结束。但是,它有一些有用的目的。

  • 首先,它有助于指示输出行是一个“完整的思想”(而不是在代码后面的某个地方完成的部分输出)。从这个意义上说,它的功能类似于在标准英语中使用句点。
  • 其次,它将光标定位在下一行,这样如果我们稍后添加额外的输出行(例如,让程序说“Bye!”),这些行将出现在我们期望的位置(而不是附加到前一行输出)。
  • 第三,从命令行运行可执行文件后,某些操作系统在再次显示命令提示符之前不会输出新行。如果我们的程序没有以新行上的光标结尾,则命令提示符可能会附加到前一行输出,而不是用户期望的新行的开头。

最佳实践
每当一行输出完成时,就输出换行符。

2.1.4 std::cout是缓冲的

考虑您最喜欢在游乐园乘坐过山车。乘客出现(以某种可变的速度)并排队。火车定期到达并登上乘客(直到火车的最大容量)。当火车满员或经过足够的时间后,火车带着一批乘客出发,然后开始乘坐。任何无法登上当前列车的乘客都等待下一班列车。

这个类比类似于发送到std::cout的输出通常在C++中处理的方式。程序中的语句请求将输出发送到控制台。但是,该输出通常不会立即发送到控制台。相反,请求的输出“排队”,并存储在为收集此类请求而预留的内存区域中(称为缓冲区)。缓冲区会定期刷新,这意味着缓冲区中收集的所有数据都将传输到其目标(在本例中为控制台)。

这也意味着,如果程序在刷新缓冲区之前崩溃、中止或暂停(例如出于调试目的),则不会显示缓冲区中仍在等待的任何输出。

关键点
缓冲输出的反面是无缓冲输出。对于无缓冲输出,每个单独的输出请求都直接发送到输出设备。
将数据写入缓冲区通常很快,而将数据批处理传输到输出设备相对较慢。缓冲可以最大程度地减少在存在多个输出请求时需要执行的慢速传输数,从而显著提高性能。

2.1.5 std::endl与\n

使用std::endl可能有点低效,因为它实际上执行两项工作:将光标移动到控制台的下一行,并刷新缓冲区。将文本写入控制台时,我们通常不需要刷新每行末尾的缓冲区。让系统定期自行冲洗会更有效(它被设计为这样有效地做)。

因此,通常首选使用“\n”字符。“\n”字符将光标移动到控制台的下一行,但不请求刷新,因此它通常会执行得更好。“\n”字符也更简洁,因为它既短又可以嵌入到现有文本中。

下面是以两种不同方式使用“\n”的示例:

#include <iostream>int main()
{int x{ 5 };std::cout << "x is equal to: " << x << '\n'; // 独立使用'\n'std::cout << "And that's all, folks!\n"; // 使用嵌入到双引号文本中的'\n'(注意:这样使用时没有单引号)return 0;
}

这将打印:

x is equal to: 5
And that's all, folks!

当’\n’单独用于将光标移动到控制台的下一行时,它应该用单引号引起来。当嵌入到已用双引号引号的文本中时,不需要其他引号。

最佳实践
将文本输出到控制台时,选择“\n”而不是std::endl

警告
'\n’使用反斜杠(与C++中的所有特殊字符一样),而不是正斜杠。改用正斜杠(例如“/n”)可能会导致意外错误。

2.1.6 std::cin

std::cin是iostream库中定义的另一个预定义变量。而使用插入运算符(<<)将数据打印到控制台,(代表“字符输入”)使用提取运算符(>>)从键盘读取输入。std::cin输入必须存储在要使用的变量中。

#include <iostream>  // 为了使用 std::cout 和 std::cinint main()
{std::cout << "Enter a number: "; // 向用户询问一个数int x{ }; // 定义整形变量x来保存用户输入(将其初始化为0)std::cin >> x; // 从键盘上获取数字并存储在变量x中std::cout << "You entered " << x << '\n';return 0;
}

尝试编译此程序并自己运行它。运行程序时,第5行将打印“Enter a number: ”。当代码到达第8行时,您的程序将等待您输入输入。输入数字(然后按回车键)后,您输入的数字将被分配给变量x。最后,在第10行,程序将打印“You entered”,后跟您刚刚输入的数字。

例如(我输入了 4):

Enter a number: 4
You entered 4

这是一种从用户那里获取键盘输入的简单方法,我们将在以后的许多示例中使用它。请注意,接受输入时不需要使用“\n”,因为用户需要按下Enter键才能接受其输入,这会将光标移动到控制台的下一行。

就像可以在一行中输出多位文本一样,也可以在一行中输入多个值:

#include <iostream>int main()
{std::cout << "Enter two numbers separated by a space: ";int x{ }; // 定义变量x来保存用户输入(将其初始化为零)int y{ }; // 定义变量y来保存用户输入(将其初始化为零)std::cin >> x >> y; // 得到两个数字,分别存储在变量x和y中std::cout << "You entered " << x << " and " << y << '\n';return 0;
}

这将产生输出:

Enter two numbers separated by a space: 5 6
You entered 5 and 6

2.1.7 总结

新程序员经常混淆std::cinstd::cout、插入运算符(<<)和提取运算符(>>)。这里有一个简单的记忆方法:

  • std::cinstd::cout 始终位于语句的左侧。
  • std::cout 用于输出值(cout = 字符输出)
  • std::cin 用于获取输入值(cin = 字符输入)
  • <<std::cout 一起使用,并显示数据的移动方向(如果 std::cout 表示控制台,则输出数据从变量移动到控制台)。
    std::cout << 4 将值 4 移动到控制台
  • >>std::cin 一起使用,并显示数据的移动方向(如果 std::cin 表示键盘,则输入数据从键盘移动到变量)。
    std::cin >> x 将用户从键盘输入的值移动到 x 中

2.1.8 练习时间

练习1
考虑我们上面使用的以下程序:

#include <iostream>int main()
{std::cout << "Enter a number: ";int x{};std::cin >> x;std::cout << "You entered " << x << '\n';return 0;
}

程序希望您输入一个整数值,因为用户输入的变量x是一个整数变量。

多次运行此程序,并描述输入以下类型的输入时会发生什么情况:

  1. 一个字母,例如h
  2. 带有小数分量的数字。尝试小数部分小于0.5和大于0.5的数字(例如3.2和3.7)。
  3. 一个小的负整数,如-3
  4. 一个词,例如Hello
  5. 一个非常大的数字(至少30亿)
  6. 一个小数字后跟一些字母,例如123abc

2.2 未初始化的变量和未定义的行为

2.2.1 未初始化的变量

与某些编程语言不同,C/C++不会自动将大多数变量初始化为给定值(如0)。因此,当一个变量被赋予一个用于存储数据的内存地址时,该变量的默认值恰好已经存在于该内存地址中的那个(垃圾)值!没有被赋予已知值(通常通过初始化或赋值)的变量称为未初始化变量

拓展
许多读者期望术语“初始化”和“未初始化”是严格的对立,但事实并非如此!初始化意味着在定义时为对象提供了初始值。未初始化意味着对象尚未被赋予已知值(通过任何方式,包括赋值)。因此,一个没有初始化但后来被赋值的对象不再是未初始化的(因为它已经被赋予了一个已知的值)。
回顾一下:

  • 初始化 --> 对象在定义时被赋予一个已知值。
  • 赋值 --> 对象被赋予一个超出定义点的已知值。
  • 未初始化 --> 对象尚未被赋予已知值。

拓展
缺乏初始化是从C继承来的性能优化,当时计算机速度很慢。想象一下,你要从一个文件中读入100000个值。在这种情况下,您可以创建100000个变量,然后用文件中的数据填充它们。
如果C++在创建时用默认值初始化所有这些变量,这将导致100000次初始化(很慢),并且几乎没有好处(因为无论如何都要覆盖这些值)。
现在,您应该始终初始化变量,因为这样做的成本与收益相比微不足道。一旦您对语言更加熟悉,可能会在某些情况下出于优化目的而省略初始化。但这应该始终是有选择性和有意识的。

使用未初始化变量的值可能会导致意外结果。考虑以下简短程序:

#include <iostream>int main()
{// 定义整形变量xint x; // 这个变量是未初始化的,因为我们没有给它赋予已知值// 将x的值打印到屏幕std::cout << x << '\n'; // 谁知道我们会获得什么呢!因为我们没有给它赋值return 0;
}

在这种情况下,计算机会将一些未使用的内存分配给x。然后,它将内存位置的值发送到std::cout,后者将打印该值(输出为整数)。但是它会打印什么值呢?答案是“谁知道呢!”,每次运行程序时,答案可能会(也可能不会)改变。当作者在Visual Studio中运行这个程序时,std::cout一次打印值7177728 ,下一次打印值5277592 。您可以自己编译并运行该程序(您的计算机不会爆炸)。

警告
当您使用调试生成配置时,某些编译器(如VisualStudio)会将内存内容初始化为某个预设值。使用发布构建配置时不会发生这种情况。因此,如果你想自己运行上面的程序,请确保你使用的是发布版本配置。例如,如果您在Visual Studio调试配置中运行上述程序,它将始终打印-858993460,因为这是Visual Studio在调试配置中初始化内存的值(输出为整数)。

大多数现代编译器将尝试检测是否在未给定值的情况下使用变量。如果他们能够检测到这一点,他们通常会发出编译时警告或错误。例如,在Visual Studio上编译上述程序会产生以下警告:

warning C4700: uninitialized local variable 'x' used

如果你的编译器不允许你编译和运行上面的程序(例如因为它将此问题视为错误),以下是解决此问题的可能解决方案:

#include <iostream>void doNothing(int&) // 现在不要担心&是什么,我们只是用它来欺骗编译器认为使用了变量x
{
}int main()
{// 定义整形变量xint x; // 变量是未初始化的doNothing(x); // 让编译器认为我们在给这个变量赋值// 将x的值打印到屏幕上(谁知道我们会得到什么,因为x没有初始化)std::cout << x << '\n';return 0;
}

使用未初始化的变量是新手程序员最常犯的错误之一,不幸的是,它也可能是调试最具挑战性的错误之一(因为如果未初始化的变量碰巧被分配到内存中具有合理值的位置,例如0,程序可能会运行得很好)。

这是最佳实践“始终初始化变量”的主要原因。

2.2.2 未定义行为

使用未初始化变量的值是我们第一个未定义行为的例子。未定义行为(Undefined behavior,通常缩写为UB)是执行C++语言未定义行为的代码的结果。在这种情况下,C++语言没有任何规则来确定如果使用尚未给定已知值的变量的值会发生什么。因此,如果你真的这样做,将导致未定义的行为。

实现未定义行为的代码可能会出现以下症状:

  • 程序每次运行都会产生不同的结果。
  • 你的程序总是产生同样的错误结果。
  • 你的程序行为不一致(有时产生正确的结果,有时不)。
  • 你的程序看起来像是在工作,但是在程序的后面产生了不正确的结果。
  • 你的程序崩溃了,要么立即崩溃,要么稍后崩溃。
  • 你的程序可以在某些编译器上工作,但在其他编译器上不行。
  • 你的程序可以正常工作,直到你改变了其他一些看似无关的代码。

或者,您的代码实际上可能会产生正确的行为。

C++包含许多情况,如果你不小心,可能会导致未定义的行为。注意这些情况,并确保避免它们。

规则
注意避免所有导致未定义行为的情况,例如使用未初始化的变量。

2.2.3 明确定义的行为和不明确定义的行为

明确定义的行为意味着一些语法的行为留给实现(编译器)来定义。这样的行为必须是一致的并且有文档记录的,但是不同的编译器可能会产生不同的结果。

让我们看一个实现定义的行为的简单示例:

#include <iostream>int main()
{std::cout << sizeof(int); // 打印一个int变量占用多少字节的内存return 0;
}

在大多数编译器上,这将产生4 ,但在其他编译器上,它可能会产生2 。

未明确定义的行为与明确定义的行为几乎相同,因为行为由实现决定,但不要求实现记录行为。

我们通常希望避免实现定义的和未指定的行为,因为这意味着如果在不同的编译器上编译,我们的程序可能无法按预期工作(甚至如果我们更改影响实现行为的项目设置,则在同一编译器上编译!)

最佳实践
尽可能避免实现定义的和未指定的行为,因为它们可能导致程序在其他实现上出现故障。

2.2.4 练习时间

练习2
什么是未初始化的变量?为什么要避免使用它们?

练习3
什么是未定义的行为?如果你做了一些表现出未定义行为的事情,会发生什么?


2.3 关键字和命名标识符

2.3.1 关键字

C++保留了92个关键字(从C++23开始)供己使用。这些词称为关键字(或保留字),每个关键字在C++语言中都有特殊的含义。

以下是所有C++关键字的列表(到C++23):

-1234
1alignasalignofandand_eq
2asmautobitandbitor
3boolbreakcasecatch
4charchar8_t (C++20起)char16_tchar32_t
5classcomplconcept (C++20起)const
6consteval (C++20起)constexprconstinit (C++20起)const_cast
7continueco_await (C++20起)co_return (C++20起)co_yield (C++20起)
8decltypedefaultdeletedo
9doubledynamic_castelseenum
10explicitexportexternfalse
11floatforfriendgoto
12ifinlineintlong
13mutablenamespacenewnoexcept
14notnot_eqnullptroperator
15oror_eqprivateprotected
16publicregisterreinterpret_castrequires (C++20起)
17shortsignedsizeofstatic
18static_caststructswitchtemplate
19thread_localthrowtruetry
20typeidtypenameunionunsigned
21virtualvoidvolatilewchar_t
22whilexorxor_eq

标记了“C++20起”的关键字是在在C++20中添加的。如果您的编译器不兼容C++20(或者具有C++20功能,但默认情况下关闭),则这些关键字可能不起作用。

C++还定义了特殊标识符:overridefinalimportmodule。这些在某些上下文中使用时具有特定含义,但在其他情况下不保留。

您已经遇到了其中的一些关键字,包括intreturn。沿着一组运算符,这些关键字和特殊标识符定义了整个C++语言(预处理器命令除外)。由于关键字和特殊标识符具有特殊的含义,IDE可能会更改这些单词的文本颜色,使它们从其他标识符中脱颖而出。

2.3.2 标识符命名规则

变量(或函数,类型或其他类型的项)的名称称为标识符。C++有着很大的灵活性,可以根据你的意愿命名标识符。但是,在命名标识符时必须遵循以下几条规则:

  1. 标识符不能是关键字。
  2. 标识符只能由字母(小写或大写)、数字和下划线字符组成。这意味着标识符不能包含符号(下划线除外)或空格(空格或制表符)。
  3. 标识符必须以字母(小写或大写)或下划线开始。不能以数字开头。
  4. C++是区分大小写的。 value 不同于 Value 不同于 VALUE 。

2.3.3 标识符命名最佳实践

现在你已经知道了如何命名一个变量,让我们来谈谈你应该怎样命名一个变量(或函数)。

首先,C++中的一个约定是变量名应该以小写字母开始。如果变量名是一个单词,则整个内容都应该用小写字母书写。

int value; // 正规的int Value; // 非正规的(应该以小写字母开头)
int VALUE; // 非正规的(应该以小写字母开头)
int VaLuE; // 非正规的(去看心理医生吧)

大多数情况下,函数名也以小写字母开头(尽管在这一点上存在一些分歧)。我们将遵循这个约定,因为函数main(所有程序都必须有)以小写字母开头,就像C++标准库中的所有函数一样。

以大写字母开头的标识符名称通常用于用户定义的类型(例如结构、类和枚举,我们将在后面介绍所有这些)。

如果变量或函数名是多字的,则有两种常见约定:用下划线分隔的单词(有时称为snake_case),或用大写字母分隔的单词(有时称为camelCase,因为大写字母像骆驼的驼峰一样突出)。

int my_variable_name;   // 正规的(由下划线分隔)
int my_function_name(); // 正规的(由下划线分隔)int myVariableName;   // 正规的(由大写字母分隔)
int myFunctionName(); // 正规的(由大写字母分隔)int my variable name;   // 无效的(不允许空格)
int my function name(); // 无效的(不允许空格)int MyVariableName;   // 非常规(应该以小写字母开头)
int MyFunctionName(); // 非常规(应该以小写字母开头)

在本教程中,我们通常使用第二种方法,因为它更容易阅读(在密集的代码块中很容易将下划线误认为空格)。但两者都很常见——C++标准库对变量和函数都使用下划线方法。有时你会看到两者的混合:下划线用于变量,大写字母用于函数。

值得注意的是,如果你正在使用别人的代码,通常认为与你正在使用的代码的风格相匹配比严格遵循上面列出的命名约定更好。

其次,应该避免以下划线开头命名标识符,因为这些名称通常是为操作系统、库和(或)编译器保留的。

第三,你的标识符应该明确它们所持有的值意味着什么(特别是如果单位不明显的话)。标识符的命名方式应该能够帮助那些不知道你的代码是什么的人尽快弄清楚。长时间后,当你再看你的程序时,你会感谢自己选择了有意义的变量名。

然而,给一个微不足道的变量起一个过于复杂的名字会妨碍对程序正在做什么的全面理解,就像给一个广泛使用的标识符一个不适当的名字一样。因此,一个好的经验法则是使标识符的长度与它的使用范围成正比。具有普通用途的标识符可以具有短名称(例如i)。更广泛使用的标识符(例如从程序中的许多不同位置调用的函数)应该具有更长且更具描述性的名称(例如使用openFileOnDisk而不是open)。

标识符好/坏原因
int ccount“count”前的“c”代表什么?
int customerCount清楚我们在计数什么
int i也许如果是不重要的,那么就是好的,否则就是坏的
int index也许如果索引知道是什么就是好的
int totalScore也许如果只有一个东西的分数,那就是好的,否则它太模糊
int _count不要使用下划线开头
int count也许如果清楚计数的是什么,那就是好的
int data什么数据?
int time秒,分,还是小时?
int minutesElapsed描述清楚
int value1, value2也许很难区分两者
int numApples描述清楚
int monstersKilled描述清楚
int x, y也许如果是不重要的,那么就是好的,否则就是坏的

在任何情况下,避免缩写(除非它们是明确的)。虽然它们减少了编写代码所需的时间,但它们使代码更难阅读。阅读代码的次数比编写代码的次数多,你在编写代码时节省的时间是每个读者,包括未来的你,在阅读代码时浪费的时间。如果您希望更快地编写代码,请使用编辑器的自动完成功能。

对于变量声明,使用注释来描述变量的用途或解释其他可能不明显的内容是很有用的。例如,假设我们声明了一个名为numberOfChars的变量,它应该存储一段文本中的字符数。文本“Hello World!”有10个、11个还是12个字符?这取决于我们是否包括空格或标点符号。与其命名为变量numberOfCharsIncludingWhitespaceAndPunctuation,这是相当冗长的,在声明行上或上方放置适当的注释应该有助于用户理解它:

// 一段文本中的字符数,包括空格和标点符号
int numberOfChars;

2.3.4 练习时间

练习4
根据如何命名一个变量,指出每个变量名是否正规或无效,以及为什么。

(1)

int sum {}; // 假设我们求的和是显而易见的

(2)

int _apples {};

(3)

int VALUE {};

(4)

int my variable name {};

(5)

int TotalCustomers {};

(6)

int void {};

(7)

int numFruit {};

(8)

int 3some {};

(9)

int meters_of_pipe {};

2.4 空白和基本格式

空白是一个术语,指用于格式化的字符。在C++中,这主要是指空格、制表符和换行符。C++中的空白通常用于3件事:分隔某些语言元素、在文本内部以及用于格式化代码。

2.4.1 某些语言元素必须以空格分隔

该语言的语法要求某些元素用空格分隔。当两个关键字或标识符必须连续放置以便编译器可以区分它们时,通常会出现这种情况。

例如,变量声明必须用空格分隔:

int x; // int 和 x 之间用空格分隔

如果我们输入intx ,编译器会将其解释为标识符,然后抱怨它不知道标识符intx是什么。

另一个例子,函数的返回类型和名称必须用空格分隔:

int main(); // int 和 main 之间用空格分隔

当需要空白作为分隔符时,编译器不关心使用了多少空白,只要存在一些空白即可。

以下变量定义均有效:

int x;
int                y;int
z;

在某些情况下,换行符用作分隔符。单行注释以换行符结束。

举个例子,做这样的事情会让你陷入麻烦:

std::cout << "Hello world!"; // 这是注释的一部分
这不是注释的一部分

预处理器指令(例如#include <iostream>)也必须放在单独的行上:

#include <iostream>
#include <string>

2.4.2 引用的文本按字面意思使用空格的数量

在引用的文本中,空格的数量是按字面意思计算的。

std::cout << "Hello world!";

不同于:

std::cout << "Hello          world!";

引用的文本中不允许有换行符:

std::cout << "Helloworld!"; // 不允许!

引用的文本由空白(空格、制表符或换行符)分隔,将被连接:

std::cout << "Hello ""world!"; // 打印"Hello world!"

2.4.3 使用空白来格式化代码

空白通常被忽略。这意味着我们可以在任何我们喜欢的地方使用空白来格式化代码,以使其更容易阅读。

例如,以下内容很难阅读:

#include <iostream>
int main(){std::cout<<"Hello world";return 0;}

以下是更好的(但仍然相当密集):

#include <iostream>
int main() {
std::cout << "Hello world";
return 0;
}

如果需要,可以将语句拆分为多行:

#include <iostream>int main()
{std::cout<< "Hello world"; // 正常工作return 0;
}

这对于特别长的语句很有用。

2.4.4 基本格式

与其他语言不同,C++不对程序员施加任何格式限制。因此,我们说C++是一个空白独立的语言。

这是喜忧参半的。一方面,有做任何你喜欢的事情的自由是很好的。另一方面,这些年来已经开发了许多不同的格式化C++程序的方法,你会发现(有时是重要的和分散注意力的)关于哪些是最好有很多分歧。我们的基本经验法则是,最好的样式是生成最可读的代码,并提供最大一致性的样式。

以下是我对基本格式的建议:

  1. 可以使用制表符或空格进行缩进(大多数IDE都有这个设置,可以将制表符按下转换为适当数量的空格)。
  2. 函数大括号有两种可接受的样式。

Google C++样式指南建议将左花括号放在与语句相同的行上:

int main() {
}

这样做的理由是,它减少了垂直空格的数量(除了开始的花括号之外,您不会将整行都用于其他内容),因此您可以在屏幕上容纳更多的代码。屏幕上的代码越多,程序就越容易理解。

但是,我更喜欢常见的替代方案,其中左大括号出现在自己的行上:

int main()
{
}

这增强了可读性,并且不容易出错,因为大括号对应该总是缩进在同一级别。如果由于大括号不匹配而出现编译器错误,很容易看出错误的位置。

  1. 花括号内的每个语句都应该从它所属函数的左括号开始一个制表符。举例来说:
int main()
{std::cout << "Hello world!\n"; // 一个制表符(4个空格)std::cout << "Nice to meet you.\n"; // 一个制表符(4个空格)
}
  1. 行不应该太长。通常,80个字符已经成为行的最大长度的事实标准。如果一行代码需要很长,它应该被分割(在一个合理的点)成多行。
int main()
{std::cout << "这是一个很长很长很长很长很长很长很长很长很长很长很长很长""很长的代码\n"; // 延续行的一个额外缩进std::cout << "这是另一一个很长很长很长很长很长很长很长很长很长很长很长很长""很长的代码\n"; // 文本与上一行对齐,作为延续行std::cout << "这一行比较短\n";
}

最佳实践
考虑将行的长度控制在80个字符或更少。

  1. 如果使用操作符(例如<<或+),则运算符应放在下一行的开头,而不是当前行的结尾。
std::cout << 3 + 4+ 5 + 6* 7 * 8;
  1. 使用空白可以通过对齐值或注释或在代码块之间添加间距来使代码更易于阅读。

更难阅读:

cost = 57;
pricePerItem = 24;
value = 5;
numberOfItems = 17;

更容易阅读:

cost          = 57;
pricePerItem  = 24;
value         = 5;
numberOfItems = 17;

更难阅读:

std::cout << "Hello world!\n"; // cout 在 iostream 库中
std::cout << "It is very nice to meet you!\n"; // 这些注释使程序难以阅读
std::cout << "Yeah!\n"; // 特别是不同行的时候

更容易阅读:

std::cout << "Hello world!\n";                  // cout 在 iostream 库中
std::cout << "It is very nice to meet you!\n";  // 这些注释使程序更容易阅读
std::cout << "Yeah!\n";                         // 尤其是当所有行都排队的时候

2.5 本章答案

练习1

  1. x为0。
  2. 小数部分被舍弃。
    由于列表初始化不允许收缩转换,新程序员有时会搞不清楚为什么这里允许删除分数。x的初始化发生在第6行,对收缩转换的限制只适用于这一行的列表初始化。用户的输入被处理并赋给第7行的x(此处此类限制不适用,因为此语句不是列表初始化)。
  3. 正常运行。
  4. x为0。
  5. 你最有可能得到的号码是2147483647。这是因为x只能容纳一定大小的数字。如果您输入的值大于x可以容纳的最大数值,它将被设置为x可以容纳的最大数值(可能是2147483647,但在您的系统上可能有所不同)。
  6. x获取数值(例如123)。

练习2
未初始化的变量是一个没有被程序赋予值的变量(通常通过初始化或赋值)。使用存储在未初始化变量中的值将导致未定义的行为。

练习3
未定义的行为是执行行为没有被语言很好定义的代码的结果。结果几乎可以是任何东西,包括行为正确的东西。

练习4
(1)正规。
(2)不正规。变量名不应该以下划线开头。
(3)不正规。变量名应该以小写字母开头。
(4)无效。变量名不能包含空格。
(5)非正规。变量名应该以小写字母开头。
(6)无效。void是关键字。
(7)正规。
(8)无效。变量名不能以数字开头。
(9)正规。

相关文章:

【C++从入门到精通】第2篇:C++基础知识(中)

文章目录 2.1 iostream介绍&#xff1a;cout、cin和endl2.1.1 输入/输出库2.1.2 std::cout2.1.3 std::endl2.1.4 std::cout是缓冲的2.1.5 std::endl与\n2.1.6 std::cin2.1.7 总结2.1.8 练习时间 2.2 未初始化的变量和未定义的行为2.2.1 未初始化的变量2.2.2 未定义行为2.2.3 明…...

【RuoYi移动端】uni-app中实现生成二维码功能(代码示例)

完整示例&#xff1a; <template><view><view class"titleBar">执法检查“通行码”信息</view><view class"twoCode"><canvas canvas-id"qrcode"></canvas></view></view> </templat…...

深度解剖数据在栈中的应用

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 望小伙伴们点赞&#x1f44d;收藏✨加关注哟&#x1f495;&#x1…...

Android10 SystemUI系列 需求定制(一)状态栏控制中心默认tile定制属性适配

一、前言 SystemUI 所包含的界面和模块比较多,这一节主要分享一下控制中心默认tile 列表的实现,通过配置可以实现 下拉状态栏,控制中心默认的tile显示 二、准备工作 按照惯例先找一下控制中心的代码,主要在下面这个路径下 frameworks/base/packages/SystemUI/src/com/andr…...

【微信小程序】文章设置

设置基本字体样式&#xff1a;行高、首行缩进 font-size: 32rpx;line-height: 1.6em;text-indent: 2em;padding: 20rpx 0;border-bottom: 1px dashed var(--themColor); 两端对齐 text-align: justify; css文字两行或者几行显示省略号 css文字两行或者几行显示省略号_css…...

程序员在线周刊(冒泡算法篇)

大家好&#xff0c;欢迎来到程序员在线周刊&#xff01;本期我们将深入探讨一种经典的排序算法——冒泡算法&#xff0c;并附上具体的代码实现。 目录 简介代码原理广告广告1广告2广告3 简介 冒泡算法是一种简单但效率较低的排序算法&#xff0c;它的原理非常直观&#xff1a…...

string

目录 六、STL简介 (一)什么是STL (二)STL的版本 (三)STL六大组件 七、string (一)标准库中的string 1、string类 2、string常用的接口 1)string类对象的常见构造 2)string类对象的容量操作 3)string类对象的访问及遍历操作 4)string类对象的修改操作 5)string类非成…...

html的日期选择插件

1.效果 2.文档 https://layui.gitee.io/v2/docs/ 3.引入 官网地址&#xff1a; https://layui.gitee.io/v2/ 引入&#xff08;在官网下载&#xff0c;&#xff09;jquery-1.7.2.min.js,layui/layui.js **<link href"js/layui/css/layui.css" rel"stylesh…...

OPPO哲库事件 “ 始末 ” ! 集体打哑谜?

1►OPPO哲库解散 2019 年&#xff0c;美国商务部以“科技网络安全”为由&#xff0c;将华为公司及其70家附属公司列入出口管制“实体名单”。与此同时&#xff0c;OPPO 创始人兼 CEO陈明永对外宣布&#xff0c;公司将为未来三年内投入 500 亿元用于前沿技术和深水区技术的探索…...

数据聚类分析

K均值 1.1 数据来源(随机生成) import matplotlib.pyplot as plt from sklearn.datasets import make_blobsX, y make_blobs(n_samples150,n_features2,centers3,cluster_std0.5,shuffleTrue,random_state0) # plt.scatter(X[:, 0], X[:, 1], cwhite, markero, edgecolorsbl…...

前 40 个 Microsoft Excel 面试问题答案

1&#xff09;什么是 Microsoft Excel&#xff1f; Microsoft Excel 是一个电子电子表格应用程序&#xff0c;使用户可以使用按行和列细分的电子表格系统&#xff0c;使用公式存储&#xff0c;组织&#xff0c;计算和处理数据。 它还提供了使用外部数据库进行分析&#xff0c;…...

ros2学习笔记:shell环境变量脚本setup.bash[-z][-n][-f]参数作用

-n作用 [ -n 字符串 ] or [ 字符串 ] 字符串的长度为非零&#xff08;有内容&#xff09;则为真。加-n与不加-n结果相同。 -z作用 [ -z 字符串 ] 字符串的长度为零则为真。 字符串为空即NULL时为真&#xff0c;与上面的-n相反。 -f作用 [ -f FILE ] 如果 FILE 存在且是一…...

xss渗透(跨站脚本攻击)

一、什么是XSS? XSS全称是Cross Site Scripting即跨站脚本&#xff0c;当目标网站目标用户浏览器渲染HTML文档的过程中&#xff0c;出现了不被预期的脚本指令并执行时&#xff0c;XSS就发生了。 这里我们主要注意四点&#xff1a; 1、目标网站目标用户&#xff1b; 2、浏览…...

9参数化重采样时频变换,基于MATLAB平台,程序已调通,可直接替换数据进行分析。

参数化重采样时频变换&#xff0c;基于MATLAB平台&#xff0c;程序已调通&#xff0c;可直接替换数据进行分析。 9matlab参数化重采样时频变换 (xiaohongshu.com)...

RK3568平台开发系列讲解(调试篇)系统运行相关频率设置

🚀返回专栏总目录 文章目录 一、CPU 频率设置二、DDR 频率设置三、NPU 频率设置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 CPU 默认是 interactive 状态,它会根据 CPU 使用率和目标负载来动态地调整 CPU 频率。为获得更高运行速度或者性能评估,我们需要手动固…...

嵌入式:驱动开发 Day2

作业&#xff1a;字符设备驱动&#xff0c;完成三盏LED灯的控制 驱动代码&#xff1a; mychrdev.c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include &q…...

RK3399平台开发系列讲解(入门篇)VIM的基础命令

🚀返回专栏总目录 文章目录 一、Vim 命令速查二、其他命令三、Vim模式沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将介绍Vim相关命令。 一、Vim 命令速查 简单说明一下,这张图上展示了一个键盘。图中的“•”表示,单个字母不是完整的命令,必须再有进一步…...

Rocky Linux 安装图解(替代centos)服务器+桌面

centos自从20年底转变为不稳定版本后&#xff0c;有很多替代方案 经过近3年的发展&#xff0c;rocky linux算是一个比较好的选择&#xff0c;一是依照red hat企业版来做&#xff0c;二是rocky的发起者也是centos的创始人 如果想安装debian&#xff0c;可以参考&#xff1a;deb…...

webpack 基础配置

常见配置 文件打包的出口和入口webpack如何开启一台服务webpack 如何打包图片&#xff0c;静态资源等。webpack 配置 loader配置 plugin配置sourceMap配置 babel 语法降级等 接下来 &#xff0c; 我们先从webpack的基本配置 开始吧&#xff01; 在准备 配置之前 , 搭建一个 …...

C语言和mfc按格式读取文件数据

fscanf()函数的功能是从文件中按格式读取一个或多个数据&#xff1b; 例如文件中有一行数据&#xff0c; 22 3.34 hello 则使用 fscanf(fp, "%d%f%s", &a, &f, str) 可一次读取整型、浮点、字符串三个数据&#xff1b; 此函数位于C标准库头文件<stdio…...

SQLyog 各版本下载与安装(目前最新版本为13.2.0)

文章目录 一、SQLyog Ultimate 各版本下载1. For Windows x642. For Windows x86 二、SQLyog Community 各版本下载1. For Windows x642. For Windows x863. For Linux x86_644. For Linux i386 三 、SQLyog 安装四、如何解决SQLyog试用期到期问题五、最后 数据库可视化工具&am…...

CopyOnWrite 容器

CopyOnWrite容器是Java并发包中提供的一种特殊类型的集合,它的特点是在进行修改操作时不会修改原始容器,而是创建一个新的容器副本进行修改,这样可以避免并发修改异常(ConcurrentModificationException)。 主要的CopyOnWrite容器包括: CopyOnWriteArrayList:这是一个基…...

云服务部署:AWS、Azure和GCP比较

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

Linux安装Ansible管理工具

条件情况说明 准备4台机器&#xff0c;是单master集群安装 192.168.186.128 ansible 192.168.186.129 node1 192.168.186.130 node2 192.168.186.131 node3 #永久修改主机名 hostnamectl set-hostname ansible && bash #在ansible上操作 hostnamectl set-hostname n…...

七天学会C语言-第二天(数据结构)

1. If 语句&#xff1a; If 语句是一种条件语句&#xff0c;用于根据条件的真假执行不同的代码块。它的基本形式如下&#xff1a; if (条件) {// 条件为真时执行的代码 } else {// 条件为假时执行的代码 }写一个基础的If语句 #include<stdio.h> int main(){int x 10;…...

高级功能的PID控制器在电离规等真空计线性化处理中的应用

摘要&#xff1a;针对高真空度用皮拉尼计和电离规信号的非线性和线性两种输出规格&#xff0c;为改进高真空度的测量和控制精度&#xff0c;本文提出了线性化处理的解决方案。解决方案的关键是采用多功能超高精度的真空压力控制器&#xff0c;具体内容一是采用控制器自带的最小…...

元素全排列问题的新思路(DFS,递归,计数器)

目录 前言 1&#xff0c;普通DFS实现1~n的元素全排列 2&#xff0c;计数器DFS实现重复元素全排列 总结 前言 我们之前看到的全排列问题的解法都是通过交换法达到的&#xff0c;去重的效果也是通过判断当前元素前是否有相同元素来实现&#xff0c;今天我们带来一个全新的思路…...

机器学习 day35(决策树)

决策树 上图的数据集是一个特征值X采用分类值&#xff0c;即只取几个离散值&#xff0c;同时也是一个二元分类任务&#xff0c;即标签Y只有两个值 上图为之前数据集对应的决策树&#xff0c;最顶层的节点称为根节点&#xff0c;椭圆形节点称为决策节点&#xff0c;矩形节点称…...

小程序引入vant-Weapp保姆级教程及安装过程的问题解决

小知识&#xff0c;大挑战&#xff01;本文正在参与“程序员必备小知识”创作活动。 本文同时参与 「掘力星计划」&#xff0c;赢取创作大礼包&#xff0c;挑战创作激励金 当你想在小程序里引入vant时&#xff0c;第一步&#xff1a;打开官方文档&#xff0c;第二步&#xff…...

LeetCode 周赛上分之旅 #45 精妙的 O(lgn) 扫描算法与树上 DP 问题

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…...

网站建设流程 文档/seo培训学什么

Bootstrap是最广泛采用的开源前端框架之一。 将Bootstrap包含在您的项目中&#xff0c;您将能够立即生成响应式网页。 如果您尝试将Masonry与Bootstrap Tabs小部件&#xff08;Bootstrap必须提供的众多JavaScript组件之一&#xff09;一起使用&#xff0c;那么您可能会偶然发现…...

做一个在线支付网站/建立一个网站需要多少钱?

一&#xff0c;现象&#xff1a;1. 1 远程连接数据库mariadb时&#xff0c;报错二&#xff0c;定位&#xff1a;2. 1 首先本地连接上数据库&#xff0c;然后操作权限表数据&#xff0c;然后远程再次连接依然连接不上&#xff1b;2. 2 搜索mariadb的配置文件&#xff0c;一一…...

wordpress 修改语言包/广州优化疫情防控举措

Bootstrap框架的表单控件的禁用状态和普通的表单禁用状态实现方法是一样的&#xff0c;在相应的表单控件上添加属性“disabled”。 在使用了“form-control”的表单控件中&#xff0c;样式设置了禁用表单背景色为灰色&#xff0c;而且手型变成了不准输入的形状。如果控件中不使…...

单招网站开发/企业网站建设公司

eclipse插件的安装方法大体有以下三种:...

直接IP做网站/企业网站seo

前言&#xff1a;对于动态路由协议的***&#xff0c;向来是***关注的目标&#xff0c;OSPF协议作为园区网络中较为常见的路由协议&#xff0c;其配置的安全性和重要性自然不言而喻。如何有效防御OSPF的安全隐患&#xff0c;更多的网管往往将目标投向了路由器自身安全&#xff0…...

优秀网站欣赏/如何免费制作网站

三层架构(3-tier architecture) 1、用户界面层&#xff08;User Interface layer&#xff09;2、业务逻辑层&#xff08;Business Logic Layer&#xff09;3、数据访问层&#xff08;Data access layer&#xff09;思想&#xff1a;高内聚低耦合面向接口设计的思想“抽屉”式架…...