C++ 基础入门
# C++ 简介
C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。
C++ 被认为是一种中级语言,它综合了高级语言和低级语言的特点。
C++ 是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++进一步扩充和完善了C语言,最初命名为带类的C,后来在1983年更名为C++。
C++ 是C的一个超集,事实上,任何合法的C程序都是合法的C++ 程序。
注意
使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。
# 面向对象程序设计
C++完全支持面向对象的程序设计,包括面向对象开发的四大特性:
- 封装(Encapsulation):封装是将数据和方法组合在一起,对外部隐藏实现细节,只公开对外提供的接口,这样可以提高安全性、可靠性和灵活性。
- 继承(Inheritance):继承是从已有类中派生出新类,新类具有已有类的属性和方法,并且可以扩展或修改这些属性和方法,这样可以提高代码的复用性和可扩展性。
- 多态(Polymorphism):多态是指同一种操作作用于不同的对象,可以有不同的解释和实现。可以通过接口或继承实现,提高代码的灵活性和可读性。
- 抽象(Abstraction):抽象是从具体的实例中提取共同的特征,形成抽象类或接口,以便于代码的复用和扩展,抽象类和接口可以让开发者专注于高层次的设计和业务逻辑,而不必关注底层的实现细节。
# 标准库
标准的C++由三个重要部分组成:
- 核心语言,提供了所有构件块,包括变量、数据类型和变量,等等。
- C++标准库,提供了大量的函数,用于操作文件、字符串等。
- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。
# ANSI 标准
ANSI标准是为了确保C++的便携性--开发者所编写的代码在Mac、UNIX、Windows、Alpha计算机上都能通过编译。由于ANSI标准已稳定使用了很长的时间,所有主要的C++编译器的制造商都支持ANSI标准。
# 学习 C++
学习C++,关键是要理解概念,而不应过于深究语言的技术细节。C++支持多种编程风格,开发者可以使用Fortran、C、Smalltalk等任意一种语言的编程风格来编写代码,每种风格都能有效地保证运行时间效率和空间效率。
# C++ 的使用
C++语言在许多行业和领域都有广泛应用,包括:
- 游戏开发:C++是游戏开发领域中最常用的编程语言之一,因为它具有高效的性能和直接控制硬件的能力,许多主要的游戏引擎,如Unreal Engine和Unity,都使用C++编写。
- 嵌入式系统开发:C++可以在嵌入式系统中发挥重要作用,如智能手机、汽车、机器人和家电等领域,由于嵌入式系统通常具有严格的资源限制和实时要求,因此C++的高效性能和内存控制功能非常有用。
- 金融领域:C++在金融领域中被广泛应用,如高频交易、算法交易和风险管理等领域,由于这些应用程序需要高效的性能和对硬件的直接控制.
- 图形图像处理:C++可以用于开发图形和图像处理应用程序,如计算机视觉、计算机图形和人工智能领域,这些应用程序需要高效的计算能力和对硬件的控制。
- 科学计算和数值分析:C++可以用于开发科学计算和数值分析应用程序,如数值模拟和高性能计算领域。
# 标准化
发布时间 | 通称 | 备注 |
---|---|---|
2020 | C++20, C++2a | ISO/IEC 14882:2020 |
2017 | C++17 | 第五个C++标准 |
2017 | coroutines TS | 协程库扩展 |
2017 | ranges TS | 提供范围机制 |
2017 | library fundamentals TS | 标准库扩展 |
2016 | concurrency TS | 用于并发计算的扩展 |
2015 | concepts TS | 概念库,用于优化编译期信息 |
2015 | TM TS | 事务性内存操作 |
2015 | parallelism TS | 用于并行计算的扩展 |
2015 | filesystem TS | 文件系统 |
2014 | C++14 | 第四个C++标准 |
2011 | - | 十进制浮点数扩展 |
2011 | C++11 | 第三个C++标准 |
2010 | - | 数学函数扩展 |
2007 | C++TR1 | C++技术报告:库扩展 |
2006 | - | C++性能技术报告 |
2003 | C++03 | 第二个C++标准 |
1998 | C++98 | 第一个C++标准 |
# C++ 环境设置
# 本地环境设置
设置C++语言环境,需要确保电脑上有以下两款可用的软件,文本编辑器和C++编译器。
# 文本编辑器
用于输入程序代码,包括Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。通过编辑器创建的文件称为源文件,源文件包含程序源代码,C++程序的源文件通常使用扩展名.cpp
、.cp
、.c
。
# C++ 编译器
写在源文件中的代码是人类可读的,它需要编译,转为机器语言,这样CPU可以按给定指令执行程序。C++编译器用于把源代码编译成最终的可执行程序。大多数的C++编译器并不在乎源文件的扩展名,如果为指定扩展名,默认使用.cpp
。最常用的免费可用的编译器是GNU的C/C++编译器,这里同时提到C/C++,主要是因为GNU的gcc编译器适合于C和C++编程语言。
# 安装 GNU 的 C/C++ 编译器
- UNIX/Linux 上的安装
可以在命令行使用下面的命令来检查系统上是否安装了GCC:
g++ -v
如果未安装GCC,可以安装http://gcc.gnu.org/install上的详细说明来安装GCC。
- Max OS X 上的安装
如果使用的是Max OS X,最快捷的获取GCC的方法是从苹果网站上下载Xcode开发环境,并按照安装说明进行安装,一旦安装上Xcode,就能使用GNU编译器。
Xcode目前可从https://developer.apple.com/download上下载,需要使用apple ID登录。
- Windows 上的安装
为了在Windows上安装GCC,需要安装MinGW,为了安装MinGW,需访问MinGW的主页mingw-w64.org,进入MinGW下载页面,下载最新版本的MinGW安装程序,命名格式为MinGW-<version>.exe
。当安装MinGW时,至少需要安装gcc-core
、gcc-g++
、binutils
和MinGW Runtime
,但是一般情况下都会安装更多其他的项。添加安装的MinGW的bin子目录到PATH环境变量中,这样可以在命令行中通过简单的名称来指定这些工具。当完成安装时,可以从Windows命令行上运行gcc
、g++
、ar
、ranlib
、dlltool
和其他一些GNU工具。
# 使用 Visual Studio (Graphical Interface) 编译
- 下载及安装Visual Studio Community 2015。
- 打开Visual Studio Community。
- 点击File -> New -> Project
- 左侧列表选择Templates -> Visual C++ -> Win32 Console Application,并设置项目名为MyFirstProgram。
点击OK。
在以下窗口点击Next
- 在弹出的窗口中选择Empty project选项后,点击Finish按钮
- 右击文件夹Source File并点击Add -> New Item
- 选择C++ File然后设置文件名为
main.cpp
,然后点击Add
- 拷贝以下代码到
main.cpp
中
#include <iostream>
int main() {
std::count << "Hello World!\n";
return 0;
}
2
3
4
5
6
界面如下:
- 点击菜单上的Debug -> Start Without Debugging(或按下Ctrl + F5)
- 完成以上操作后,可以看到如下输出
# g++ 应用说明
程序g++是将gcc默认语言设为C++的一个特殊的版本,链接时它自动使用C++标准库而不用C标准库,通过遵循源码的命名规范并指定对应库的名字,用gcc来编译链接*C++*程序是可行的,如下所示:
gcc main.cpp -lstdc++ -o main
下面是一个保存在文件helloworld.cpp
中一个简单的*C++*程序代码:
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
2
3
4
5
6
7
最简单的编译方式:
g++ helloworld.cpp
由于命令行中未指定可执行程序的文件名,编译器采用默认a.out
。程序可以运行:
./a.out
Hello, world!
2
通常使用-o
选项指定可执行程序的文件名,以下实例生成一个helloworld
的可执行文件:
g++ helloworld.cpp -o helloworld
执行helloworld
:
./helloworld
Hello, world!
2
如果是多个*C++*代码文件,如runoob1.cpp
、runoob2.cpp
,编译命令如下:
g++ runoob1.cpp runoob2.cpp -o runoob
生成一个runoob
可执行文件。
g++
有些系统默认使用C++98,可以指定使用C++11来编译main.cpp
文件:
g++ -g -Wall -std=c++11 main.cpp
*g++*常用命令选项:
选项 | 解释 |
---|---|
-ansi | 只支持ANSI标准的C语法,这一选项将禁止GNU C的某些特色,例如asm或typeof关键词。 |
-c | 只编译并生成目标文件 |
-DMACRO | 以字符串"1"定义MACRO宏 |
-DMACRO=DEFN | 以字符串"DEFN定义MACRO*宏 |
-E | 只运行C预编译器 |
-g | 生成调试信息,GNU调试器可利用该信息 |
-IDIRECTORY | 指定额外的头文件搜索路径DIRECTORY |
-LDIRECTORY | 指定额外的函数库搜索路径DIRECTORY |
-ILIBRARY | 连接时搜索指定的函数库LIBRARY |
-m486 | 针对486进行代码优化 |
-o | FILE生成指定的输出文件,用在生成可执行文件时。 |
-O0 | 不进行优化处理 |
-O | 或-O1 优化生成代码 |
-O2 | 进一步优化 |
-O3 | 比-O2 更进一步优化,包括inline 函数 |
-shared | 生成共享目标文件,通常用在建立共享库时 |
-static | 禁止使用共享连接 |
-UMACRO | 取消对MACRO宏的定义 |
-w | 不生成任何警告信息 |
-Wall | 生成所有警告信息 |
# C++ 基本语法
C++程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互:
- 对象:对象具有状态和行为,如:一只狗的状态-颜色、名称、品种,行为-摇动、叫唤、吃。对象是类的实例。
- 类:类可以定义为描述对象行为/状态的模板/蓝图。
- 方法:从基本上说,一个方法表示一种行为。一个类可以包含多个方法,可以在方法中写入逻辑、操作数据以及执行所有的动作。
- 即时变量:每个对象都有其独特的即时变量,对象的状态是由这些即时变量的值创建的。
# C++ 程序结构
#include <iostream>
using namespace std;
// main() 是程序开始执行的地方
int main() {
cout << "Hello World"; // 输出Hello World
return 0;
}
2
3
4
5
6
7
8
- C++语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息,上面的程序中,包含了头文件
<iostream>
。 using namespace std;
告诉编译器使用std
命名空间,命名空间是C++中一个相对新的概念。// main()是程序开始执行的地方
是一个单行注释,单行注释以//
开头,在行末结束。int main()
是主函数,程序从这里开始执行。cout << "Hello World;"
会在屏幕上显示消息"Hello World".return 0;
终止main()
函数,并向调用进程返回值0
。
# 编译 & 执行 C++ 程序
编译和执行程序的步骤:
- 打开文本编辑器,添加上述代码
- 保存文件为
hello.cpp
- 打开命令提示符,进入到保存文件所在的目录
- 在命令行输入
g++ hello.cpp
,输入回车,编译代码,如果代码没有错误,命令提示符会跳到下一行,并生成a.out
可执行文件 - 然后在命令行输入
a.out
,运行程序 - 在屏幕上即可显示
Hello World
g++ hello.cpp
./a.out
Hello World
2
3
# C++ 中的分号 & 语句块
在C++中,分号是语句结束符,也就是,每个语句必须以分号结束,它表明一个逻辑实体的结束。
x = y;
y = y + 1;
add(x, y);
2
3
等同于
x = y; y = y + 1; add(x, y);
# C++ 标识符
C++标识符是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。一个标识符以字母A-Z
或a-z
或下划线_
开始,后跟零个或多个字母、下划线和数字(0-9)。
C++标识符内不允许出现标点符号,比如@
、&
和%
。C++是区分大小写的编程语言,因此,在C++中,Manpower
和manpower
是两个不同的标识符。
# C++ 关键字
下表列出了C++中的保留字,这些保留字不能作为常量名、变量名或其他标识符名称。
asm | else | new | this |
auto | enum | operator | throw |
bool | explicit | private | true |
break | export | protected | try |
case | extern | public | typedef |
catch | false | register | typeid |
char | float | reinterpret_cast | typename |
class | for | return | union |
const | friend | short | unsigned |
const_cast | goto | signed | using |
continue | if | sizeof | virtual |
default | inline | static | void |
delete | int | static_cast | volatile |
do | long | struct | wchar_t |
double | mutable | switch | while |
dynamic_cast | namespace | template |
# 三字符组
三字符组就是用于表示另一个字符的三个字符序列,又称为三字符序列,三字符序列总是以两个问号开头。三字符序列不太常见,但C++标准允许把某些字符指定为三字符序列,以前为了表示键盘上没有的字符,这是必不可少的一种方法。三字符序列可以出现在任何地方,包括字符串、字符序列、注释和预处理指令。
下表列出了常用的三字符序列:
三字符组 | 替换 |
---|---|
??= | # |
??/ | \ |
??' | ^ |
??( | [ |
??) | ] |
??! | | |
??< | { |
??> | } |
??- | ~ |
如果希望在程序中有两个连续的问号,且不希望被预处理器替换,这种情况出现在字符串常量、字符串字面值或者是程序注释中,可选办法是用字符串的自动连接:
"..?""?..."
或者转义序列:"...?\?..."
。从Microsoft Visual C++ 2010版开始,编译器默认不再自动替换三字符组,如果需要使用三字符组替换(为了兼容古老的软件代码),需要设置编译器命令行选项/Zc:trigraphs
,g++仍默认支持三字符组,但会给出编译警告。
# C++ 中的空格
只包含空格的行,被称为空白行,可能带有注释,C++编译器会完全忽略它。在C++中,空格用于描述空白符、制表符、换行符和注释。空格分割语句的各个部分,让编译器能识别语句中的某个元素(比如int
)在哪里结束,下一个元素在哪里开始。因此,在下面的语句中:
int age;
在这里,int
和age
之间必须至少有一个空格字符(通常是一个空白符),这样编译器才能够区分它们。另一方面,在下面的语句中:
fruit = apples + oranges; // 获取水果的总数
fruit
和=
,或者=
和apples
之间的空格字符不是必需的,但为了增强可读性,可以根据需要适当增加一些空格。
# C++ 注释
程序的注释是解释性语句,可以在C++代码中包含注释,提高源码的可读性。所有的编程语言都允许某种形式的注释。C++支持单行注释和多行注释,注释中的所有字符会被C++编译器忽略。
C++注释一般有两种:
//
:一般用于单行注释/* ... */
:一般用于多行注释
#include <iostream>
using namespace std;
int main() {
// 这是一个注释
cout << "Hello World!";
return 0;
}
2
3
4
5
6
7
8
#include <iostream>
using namespace std;
int main() {
cout << "Hello World!"; // 输出 Hello World!
return 0;
}
2
3
4
5
6
7
#include <iostream>
using namespace std;
int main() {
/* 这是注释 */
/* C++ 注释也可以
* 跨行
*/
cout << "Hello World!";
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
# C++ 数据类型
使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当创建一个变量时,就会在内存中保留一些空间。
开发者可能需要存储各种数据类型(比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等)的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留的内存中存储什么。
# 基本的内置类型
C++为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型,下表列出了七种基本的C++数据类型:
类型 | 关键字 |
---|---|
布尔型 | bool |
字符型 | char |
整型 | int |
浮点型 | float |
双浮点型 | double |
无类型 | void |
宽字符型 | wchar_t |
其实wchar_t
是这样来的:
typedef short int wchar_t;
所以wchar_t
实际上的空间是和short int
一样。一些基本类型可以使用一个或多个类型修饰符进行修饰:
signed
unsigned
short
long
下表显示了各种变量类型在内存中存储值时需要占用的内存,以及该类型的变量所能存储的最大值和最小值。
注意:不同系统会有所差异,一字节为8位。
注意:默认情况下,int
、short
、long
都是带符号的,即``。
注意:long int
8个字节,int
4个字节,早期的C编译器定义了long int
占用4个字节,int
占用2个字节,新版的C/C++标准兼容了早期的这一设定。
类型 | 位 | 范围 |
---|---|---|
char | 1个字节 | -128到127或者0到255 |
unsigned char | 1个字节 | 0到255 |
signed char | 1个字节 | -128到127 |
int | 4个字节 | -2147483648到2147483647 |
unsigned int | 4个字节 | 0到4294967295 |
signed int | 4个字节 | -2147483648到2147483647 |
short int | 2个字节 | -32768到32767 |
unsigned short int | 2个字节 | 0到65535 |
signed short int | 2个字节 | -32768到32767 |
long int | 8个字节 | -9223372036854775808到9223372036854775807 |
signed long int | 8个字节 | -9223372036854775808到9223372036854775807 |
unsigned long int | 8个字节 | 0到18446744073709551615 |
float | 4个字节 | 精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字) |
double | 8个字节 | 双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字) |
long long | 8个字节 | 双精度型占8 个字节(64位)内存空间,表示 -9,223,372,036,854,775,807 到 9,223,372,036,854,775,807 的范围 |
long double | 16个字节 | 长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。 |
wchar_t | 2或4个字节 | 1个宽字符 |
注意
各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。以下列出了32位系统与64位系统的存储大小的差别:
下面代码会输出电脑上各种数据类型的大小:
#include <iostream>
#include <limits>
using namespace std;
int main() {
cout << "type: \t\t" << "************size**************" << endl;
cout << "bool: \t\t" << "所占字节数:" << sizeof(bool);
cout << "\t最大值:" << (numeric_limits<bool>::max)();
cout << "\t\t最小值:" << (numeric_limits<bool>::min)() << endl;
cout << "char: \t\t" << "所占字节数:" << sizeof(char);
cout << "\t最大值:" << (numeric_limits<char>::max)();
cout << "\t\t最小值:" << (numeric_limits<char>::min)() << endl;
cout << "signed char: \t\t" << "所占字节数:" << sizeof(signed char);
cout << "\t最大值:" << (numeric_limits<signed char>::max)();
cout << "\t\t最小值:" << (numeric_limits<signed char>::min)() << endl;
cout << "unsigned char: \t\t" << "所占字节数:" << sizeof(unsigned char);
cout << "\t最大值:" << (numeric_limits<unsigned char>::max)();
cout << "\t\t最小值:" << (numeric_limits<unsigned char>::min)() << endl;
cout << "wchar_t: \t\t" << "所占字节数:" << sizeof(wchar_t);
cout << "\t最大值:" << (numeric_limits<wchar_t>::max)();
cout << "\t\t最小值:" << (numeric_limits<wchar_t>::min)() << endl;
cout << "short: \t\t" << "所占字节数:" << sizeof(short);
cout << "\t最大值:" << (numeric_limits<short>::max)();
cout << "\t\t最小值:" << (numeric_limits<short>::min)() << endl;
cout << "int: \t\t" << "所占字节数:" << sizeof(int);
cout << "\t最大值:" << (numeric_limits<int>::max)();
cout << "\t\t最小值:" << (numeric_limits<int>::min)() << endl;
cout << "unsigned: \t\t" << "所占字节数:" << sizeof(unsigned);
cout << "\t最大值:" << (numeric_limits<unsigned>::max)();
cout << "\t\t最小值:" << (numeric_limits<unsigned>::min)() << endl;
cout << "long: \t\t" << "所占字节数:" << sizeof(long);
cout << "\t最大值:" << (numeric_limits<long>::max)();
cout << "\t\t最小值:" << (numeric_limits<long>::min)() << endl;
cout << "unsigned long: \t\t" << "所占字节数:" << sizeof(unsigned long);
cout << "\t最大值:" << (numeric_limits<unsigned long>::max)();
cout << "\t\t最小值:" << (numeric_limits<unsigned long>::min)() << endl;
cout << "double: \t\t" << "所占字节数:" << sizeof(double);
cout << "\t最大值:" << (numeric_limits<double>::max)();
cout << "\t\t最小值:" << (numeric_limits<double>::min)() << endl;
cout << "long double: \t\t" << "所占字节数:" << sizeof(long double);
cout << "\t最大值:" << (numeric_limits<long double>::max)();
cout << "\t\t最小值:" << (numeric_limits<long double>::min)() << endl;
cout << "float: \t\t" << "所占字节数:" << sizeof(float);
cout << "\t最大值:" << (numeric_limits<float>::max)();
cout << "\t\t最小值:" << (numeric_limits<float>::min)() << endl;
cout << "size_t: \t\t" << "所占字节数:" << sizeof(size_t);
cout << "\t最大值:" << (numeric_limits<size_t>::max)();
cout << "\t\t最小值:" << (numeric_limits<size_t>::min)() << endl;
cout << "string: \t\t" << "所占字节数:" << sizeof(string);
cout << "\t最大值:" << (numeric_limits<string>::max)();
cout << "\t\t最小值:" << (numeric_limits<string>::min)() << endl;
cout << "type: \t\t" << "************size**************" << endl;
return 0;
}
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
本实例使用了endl
,这将在每一行后插入一个换行符,<<
运算符用于向屏幕传多个值,sizeof()
运算符用来获取各种数据类型的大小。当上面的代码被编译和执行时,它会产生以下的结果,结果会根据所使用的计算机而有所不同:
# typedef 声明
可以使用typedef
为一个已有的类型取一个新的名字,下面是使用typedef
定义一个新类型的语法:
typedef type newname;
例如,下面的语句会告诉编译器,feet
是int
的另一个名称:
typedef int feet;
现在,下面的声明是完全合法的,它创建了一个整型变量distance
:
feet distance;
# 枚举类型
枚举类型(enumeration
)是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。如果一个变量只有几种可能的值,可以定义为枚举(enumeration
)类型。所谓枚举是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。创建枚举,需要使用关键字enum
,枚举类型的一般形式为:
enum 枚举名 {
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数]
} 枚举变量;
2
3
4
5
6
如果枚举没有初始化,即省掉=整型常数
时,则从第一个标识符开始。如,下面的代码定义了一个颜色枚举,变量c
的类型为color
,最后,c
被赋值为blue
。
enum color { red, green, blue } c;
c = blue;
2
默认情况下,第一个名称的值为0
,第二个名称的值为1
,第三个名称的值为2
,依次类推,但也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green
的值为5
。
enum color { red, green = 5, blue };
在这里,blue
的值为6
,因为默认情况下,每个名称都会比它前面的一个名称大1
,但red
的值依然为0
。
# 类型转换
类型转换是将一个数据类型的值转换为另一种数据类型的值。C++中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
- 静态转换(Static Cast)
静态转换是将一种数据类型的值强制转换为另一种数据类型的值,通常用于比较类型相似的对象之间的转换,例如将int
类型转换为float
类型,其不进行任何运行时类型检查,因此可能会导致运行时错误。
int i = 10;
float f = static_cast<float>(i); // 静态将int类型转换为float类型
2
- 动态转换(Dynamic Cast)
动态转换通常用于将一个基类指针或引用转换为派生类指针或引用,其在运行时进行类型检查,如果不能转换则返回空指针或引发异常。
class Base {};
class Derived : public Base {};
Base* ptr_base = new Derived;
Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); // 将基类指针转换为派生类指针
2
3
4
- 常量转换(Const Cast)
常量转换用于将const
类型的对象转换为非const
类型的对象,其只能用于转换掉const
属性,不能改变对象的类型。
const int i = 10;
int& r = const_cast<int&>(i); // 常量转换,将const int转换为int
2
- 重新解释转换(Reinterpret Cast)
重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换,其转换不进行任何类型检查,因此可能会导致未定义的行为。
int i = 10;
float f = reinterpret_cast<float&>(i); // 重新解释将int类型转换为float类型
2
# C++ 变量类型
变量其实只不过是程序可操作的存储区的名称,在C++中,有多种变量类型可用于存储不同种类的数据,C++中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上,变量的名称可以由字母、数字和下划线字符组成,且必须以字母或下划线开头。大写字母和小写字母是不同的,因为C++是大小写敏感的编程语言。
类型 | 描述 |
---|---|
bool | 布尔类型,存储值true 或false ,占用1个字节 |
char | 字符类型,用于存储ASCII字母,通常占用1个字节 |
int | 整数类型,通常用于存储普通整数,通常占用4个字节 |
float | 单精度浮点值,用于存储单精度浮点数,其格式为: 1位符号,8位指数,23位小数,通常占用4个字节。![]() |
double | 双精度浮点值,用于存储双精度浮点数,其格式为: 1位符号,11位指数,52位小数,通常占用8个字节。 |
void | 表示类型的缺失 |
wchar_t | 宽字符类型,用于存储更大范围的字符,通常占用2个或4个字节 |
C++也允许定义各种其他类型的变量,比如枚举、指针、数组、引用、数据结构、类等等。
- 整数类型(Integer Types)
int
: 用于表示整数,通常占用4个字节short
: 用于表示短整数,通常占用2个字节long
: 用于表示长整数,通常占用4个字节long long
: 用于表示更长的整数,通常占用8个字节
- 浮点类型(Floating-Point Types)
float
: 用于表示单精度浮点数,通常占用4个字节double
: 用于表示双精度浮点数,通常占用8个字节long double
: 用于表示更高精度的浮点数,占用字节数可以根据实现而变化
- 字符类型(Character Types)
char
: 用于表示字符,通常占用1个字节wchar_t
: 用于表示宽字符,通常占用2或4个字节char16_t
: 用于表示16位Unicode字符,占用2个字节char32_t
: 用于表示32位Unicode字符,占用4个字节
- 布尔类型(Boolean Type)
bool
: 用于表示布尔值,只能取true或false
- 枚举类型(Enumeration Type)
enum
: 用于定义一组命名的整数常量
- 指针类型(Pointer Types)
type*
: 用于表示指向类型为type的对象的指针
- 数组类型(Array Types)
type[]
或type[size]
: 用于表示具有相同类型的元素组成的数组
- 结构体类型(Structure Types)
struct
: 用于定义包含多个不同类型成员的结构
- 类类型(Class Types)
class
: 用于定义具有属性和方法的自定义类型
- 共用体类型(Union Types)
union
: 用于定义一种特殊的数据类型,它可以在相同的内存位置存储不同的数据类型
# C++ 中的变量定义
变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。
变量定义指定一个数据类型,并包含了该类型一个或多个变量的列表,如下:
type variable_list;
type
必须是一个有效的C++数据类型,可以是char
、wchar_t
、int
、float
、double
、bool
或任何用户自定义的对象,variable_list
可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。
extern int d = 3, f = 5; // d 和 f 的声明
int d = 3, f = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'
2
3
4
不带初始化的定义: 带有静态存储持续时间的变量会被隐式初始化为NULL
(所有字节的值都是0),其他所有变量的初始值是未定义的。
# C++ 中的变量声明
变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译,变量声明只在编译时有意义,在程序连接时编译器需要实际的变量声明。
当使用多个文件只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用,可以使用extern
关键字在任何地方声明一个变量,虽然可以在C++程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。
#include <iostream>
using namespace std;
// 变量声明
extern int a, b;
extern int c;
extern float f;
int main() {
// 变量定义
int a, b;
int c;
float f;
// 实际初始化
a = 10;
b = 20;
c = a + b;
cout << c << endl;
f = 70.0 / 3.0;
cout << f << endl;
return 0;
}
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
同样的,在函数声明时,提供一个函数名,而函数的实际定义则可以在任何地方进行,如:
// 函数声明
int func();
int main() {
// 函数调用
int i = func();
}
// 函数定义
int func() {
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
# C++ 中的左值(Lvalues)和右值(Rvalues)
C++ 中有两种类型的表达式:
- 左值(Lvalue): 指向内存位置的表达式被称为左值(Lvalue)表达式,左值可以出现在赋值号的左边或右边。
- 右值(Rvalue): 术语右值(Rvalue)指的是存储在内存中某些地址的数值,右值是不能对其进行赋值的表达式,也就是,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
变量是左值,因此可以出现在赋值号的左边,数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边,下面是一个有效的语句int g = 20;
但是下面的这个就不是一个有效的语句,会生成编译时的错误10 = 20;
。
# C++ 变量作用域
一般来说有三个地方可以定义变量:
- 在函数或一个代码块内部声明的变量,称为局部变量。
- 在函数参数的定义中声明的变量,称为形式参数。
- 在所有函数外部声明的变量,称为全局变量。
作用域是程序的一个区域,变量的作用域可以分为以下几种:
- 局部作用域: 在函数内部声明的变量具有局部作用域,它们只能在函数内部访问,局部变量在函数每次被调用时创建,在函数执行后被销毁。
- 全局作用域: 在所有函数和代码块之外声明的变量具有全局作用域,它们可以被程序中的任何函数访问,全局变量在程序开始时被创建,在程序结束时被销毁。
- 块作用域: 在代码块内部声明的变量具有块作用域,它们只能在代码块内部访问,块作用域变量在代码块每次被执行时被创建,在代码块执行后被销毁。
- 类作用域: 在类内部声明的变量具有类作用域,它们可以被类的所有成员函数访问,类作用域变量的声明周期与类的声明周期相同。
# 局部变量
在函数或一个代码块内部声明的变量,称为局部变量,它们智能被函数内部或者代码块内部的语句使用,如下:
#include <iostream>
using namespace std;
int main() {
// 局部变量声明
int a, b;
int c;
// 实际初始化
a = 10;
b = 20;
c = a + b;
cout << c;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 全局变量
在所有函数外部定义的变量(通常是在程序的头部),称为全局变量,全局变量的值在程序的整个声明周期内都是有效的。全局变量可以被任何函数访问,也就是,全局变量一旦声明,在整个程序中都是可用的,下面的实例使用了全局变量和局部变量:
#include <iostream>
using namespace std;
// 全局变量声明
int g;
int main() {
// 局部变量声明
int a, b;
// 实际初始化
a = 10;
b = 20;
g = a + b;
cout << g;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值,如下实例:
#include <iostream>
using namespace std;
// 全局变量声明
int g = 20;
int main() {
// 局部变量声明
int g = 10;
cout << g;
// 输出 10
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 初始化局部变量和全局变量
当局部变量被定义时,系统不会对其初始化,必须自行对其初始化,定义全局变量时,系统会自动初始化为下列值:
数据类型 | 初始化默认值 |
---|---|
int | 0 |
char | '\0' |
float | 0 |
double | 0 |
pointer | NULL |
正确地初始化变量是一个良好的编程习惯,否则有时候可能会产生意想不到的结果。块作用域指的是在代码块内部声明的变量:
#include <iostream>
int main() {
int a = 10;
{
int a = 20; // 块作用域变量
std::cout << "块变量: " << a << std::endl;
}
std::cout << "外部变量: " << a << std:endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
以上实例中,内部的代码块中声明了一个名为a
的变量,它与外部作用域中的变量a
同名,内部作用域中的变量a
将覆盖外部作用域中的变量a
,在内部作用域中访问a
时输出的是20,在外部作用域中访问a
时输出的是10。
# 类作用域
类作用域指的是在类内部声明的变量:
#include <iostream>
class MyClass {
public:
static int class_var; // 类作用域变量
};
int MyClass::class_var = 30;
int main() {
std::cout << "类变量: " << MyClass::class_var << std::endl; // 输出 类变量: 30
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
# C++ 常量
常量是固定值,在程序执行期间不会改变,又叫做字面量。常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。常量就像是常规的变量,只不过常量的值在定义后不能进行修改。
# 整数常量
整数常量可以是十进制、八进制或十六进制的常量,前缀指定基数:0x
或0X
表示十六进制,0
表示八进制,不带前缀则默认表示十进制。整数常量也可以带一个后缀,后缀是U
和L
的组合,U
表示无符号整数(unsigned
),L
表示长整数(long
),后缀可以是大写,也可以是小写,U
和L
的顺序任意。
下面列举几个整数常量的实例:
212 // 合法的
215u // 合法的
0xFeeL // 合法的
078 // 非法的:8不是八进制数字
032UU // 非法的:不能重复后缀
2
3
4
5
以下是各种类型的整数常量的实例:
85 // 十进制
0213 // 八进制
0x4b // 十六进制
30 // 整数
30u // 无符号整数
30l // 长整数
30ul // 无符号长整数
2
3
4
5
6
7
# 浮点常量
浮点常量由整数部分、小数点、小数部分和指数部分组成,可以使用小数形式或指数形式表示浮点常量。当使用小数形式表示时,必须包含整数部分、小数部分;当使用指数形式表示时,必须包含小数点、指数。带符号的指数是用e
或E
引入的。
下面列举几个浮点常量的实例:
3.14159 // 合法的
314159E-5L // 合法的
510E // 非法的:不完整的指数
210f // 非法的:没有小数或指数
.e55 // 非法的:缺少整数或分数
2
3
4
5
# 布尔常量
布尔常量共有两个,它们都是标准的C++关键字:
true
值代表真false
值代表假
# 字符常量
字符常量是括在单引号中,如果常量以L
(仅当大写时)开头,则表示它是一个宽字符常量(例如L'x'
),此时它必须存储在wchar_t
类型的变量中,否则,它就是一个窄字符常量(例如x
),此时可以存储在char
类型的简单变量中。字符常量可以是一个普通的字符(例如x
)、一个转义序列(例如\t
),或一个通用的字符(例如\u02C0
)。在C++中,有一些特定的字符,当它们前面有反斜杠时,就具有特殊的含义,被用来表示如换行符(\n
)或制表符(\t
)等,下表列出了一些这样的转义序列码:
转义序列 | 含义 |
---|---|
\\ | \ 字符 |
\' | ' 字符 |
\" | " 字符 |
\? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh... | 一个或多个十六进制数 |
下面的实例显示了一些转义序列字符:
#include <iostream>
using namespace std;
int main() {
cout << "Hello\tWorld\n\n";
return 0;
}
2
3
4
5
6
7
# 字符串常量
字符串字面值或常量是括在双引号""
中,一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符,可以使用\
做分隔符,把一个很长的字符串常量进行分行。如下实例:
#include <iostream>
#include <string>
using namespace std;
int main() {
string greeting = "hello, runoob";
cout << greeting;
cout << "\n"; // 换行符
string greeting2 = "hello, \
runoob";
cout << greeting2;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 定义常量
在C++中,有两种简单的定义常量的方式:
- 使用
#define
预处理器 - 使用
const
关键字
# #define
预处理器
下面是使用#define
预处理器定义常量的形式:
#define identifier value
#include <iostream>
using namespace std;
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main() {
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# const
关键字
可以使用const
声明指定类型的常量,如下:
const type = variable = value;
#include <iostream>
using namespace std;
int main() {
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# C++ 修饰符类型
C++允许在char
、int
和double
数据类型前放置修饰符。修饰符是用于改变变量类型行为的关键字,它更能满足各种情景的需求。下面列出了数据类型修饰符:
signed
: 表示变量可以存储负数,对于整型变量来说,signed
可以省略,因为整型变量默认为有符号类型unsigned
: 表示变量不能存储负数,对于整型变量来说,unsigned
可以将变量范围扩大一倍short
: 表示变量的范围比int
更小,short int
可以缩写为short
long
: 表示变量的范围比int
更大,long int
可以缩写为long
long long
:表示变量的范围比long
更大,C++11中新增的数据类型修饰符float
: 表示单精度浮点数double
: 表示双精度浮点数bool
: 表示布尔类型,只有true
和false
两个值char
: 表示字符类型wchar_t
: 表示宽字符类型,可以存储Unicode
字符
修饰符signed
、unsigned
、long
和short
可应用于整型,signed
和unsigned
可应用于字符型,long
可应用于双精度型,这些修饰符也可以组合使用,修饰符signed
和unsigned
也可以作为long
或short
修饰符的前缀,例如: unsigned long int
。C++允许使用速记符来声明无符号整数或无符号长整数,可以不写int
,只写单词unsigned
、short
或long
,int
是隐含的,例如:
signed int num1 = -10; // 定义有符号整型变量num1,初始值为 -10
unsigned int num2 = 20; // 定义无符号整型变量num2,初始值为 20
short int num1 = 10; // 定义短整型变量num1,初始值为 10
long int num2 = 100000; // 定义长整型变量num2,初始值为 100000
long long int num1 = 10000000000; // 定义长长整型变量num1,初始值为 10000000000
float num1 = 3.14f; // 定义单精度浮点数变量num1,初始值为 3.14
double num2 = 2.71828; // 定义双精度浮点数变量num2,初始值为 2.71828
bool flag = true; // 定义布尔类型变量flag,初始值为 true
char ch1 = 'a'; // 定义字符类型变量ch1,初始值为 'a'
wchar_t ch2 = L'你'; // 定义宽字符类型变量ch2,初始值为 '你'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
为了理解C++有符号整数和无符号整数修饰符之间的差别,看下面实例程序:
#include <iostream>
using namespace std;
/*
* 这个程序演示了有符号整数和无符号整数之间的差别
*/
int main() {
short int i; // 有符号短整数
short unsigned int j; // 无符号短整数
j = 50000;
i = j;
cout << i << " " << j; // -15536 50000
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上述结果中,无符号短整数50000的位模式被解释为有符号短整数-15536。
# C++ 中的类型限定符
类型限定符提供了变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字。
限定符 | 含义 |
---|---|
const | const 定义常量,表示该变量的值不能被修改 |
volatile | 修饰符volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程 |
restrict | 由restrict 修饰的指针是唯一一种访问它所指向的对象的方式,只有C99增加了新的类型限定符restrict |
mutable | 表示类中的成员变量可以在const 成员函数中被修改 |
static | 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问 |
register | 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率 |
const
实例
const int NUM = 10; // 定义常量NUM,其值不可修改
const int* ptr = &NUM; // 定义指向常量的指针,指针所指的值不可修改
int const* ptr2 = &NUM; // 和上面一行等价
2
3
volatile
实例
volatile int num = 20; // 定义变量num,其值可能会在未知的时间被改变
mutable
实例
class Example {
public:
int get_value() const {
return value_; // const 关键字表示该成员函数不会修改对象中的数据成员
}
void set_value(int value) const {
value_ = value; // mutable 关键字允许在const成员函数中修改成员变量
}
private:
mutable int value_;
}
2
3
4
5
6
7
8
9
10
11
static
实例
void example_function() {
static int count = 0; // static 关键字使变量 count 存储在程序声明周期内都存在
count++;
}
2
3
4
register
实例
void example_function(register int num) {
// register 关键字建议编译器将变量 num 存储在寄存器中
// 以提高程序执行速度
// 但是实际上是否会存储在寄存器中由编译器决定
}
2
3
4
5
# C++ 存储类
存储类定义C++程序中变量/函数的范围(可见性)和声明周期,这些说明符放置在它们所修饰的类型之前,下面列出C++程序中可用的存储类:
auto
register
static
extern
mutable
thread_local(C++11)
从C++17开始,auto
关键字不再是C++存储类说明符,且register
关键字被弃用。
# auto 存储类
自C++11以来,auto
关键字用于两种情况: 声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。C++98标准中auto
关键字用于自动变量的声明,但由于使用极少且多余,在C++17中已删除这一用法。根据初始化表达式自动推断被声明的变量的类型,如:
auto f = 3.14; // double
auto s("hello"); // const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3 = 'r'; // 错误,必须是初始化为同一类型
2
3
4
# register 存储类
register
存储类用于定义存储在寄存器中而不是RAM中的局部变量,这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的&
运算符(因为它没有内存位置)。
{
register int miles;
}
2
3
寄存器只用于需要快速访问的变量,比如计数器,还应注意的是,定义register
并不意味着变量被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
# static 存储类
static
存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁,因此,使用static
修饰局部变量可以在函数调用之间保持局部变量的值。static
也可以应用于全局变量,当static
修饰全局变量时,回使变量的作用域限制在声明它的文件内。在C++中,当static
用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。
#include <iostream>
// 函数声明
void func(void);
static int count = 10; // 全局变量
int main() {
while (count--) {
func();
}
return 0;
}
// 函数定义
void func(void) {
static int i = 5; // 局部静态变量
i++;
std::cout << "变量 i 为: " << i;
std::cout << ", 变量 count 为: " << count << std::endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# extern 存储类
extern
存储类用于提供一个全局变量引用,全局变量对所有的程序文件都是可见的,当使用extern
时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用extern
来得到已定义的变量或函数的引用,可以理解为,extern
是用来在另一个文件中声明一个全局变量或函数。extern
修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下:
- main.cpp
#include <iostream>
int count;
extern void write_extern();
int main() {
count = 5;
write_extern();
}
2
3
4
5
6
7
8
9
- support.cpp
#include <iostream>
extern int count;
void write_extern(void) {
std::cout << "Count is: " << count << std::endl;
}
2
3
4
5
6
7
在这里,第二个文件的extern
关键字用于声明已经在第一个文件main.cpp
中定义的count
。
# mutable 存储类
mutable
说明符仅使用于类的对象,它允许对象的成员替代常量,也就是说,mutable
成员可以通过const
成员函数修改。
# thread_local 存储类
使用thread_local
说明符声明的变量仅可在它在其上创建的线程上访问,变量在创建线程时创建,并在销毁线程时销毁,每个线程都有其自己的变量副本。thread_local
说明符可以与static
或extern
合并,可以将thread_local
仅应用于数据声明和定义,thread_local
不能用于函数声明或定义。以下演示了可以被声明为thread_local
的变量:
thread_local int x; // 命名空间下的全局变量
class X {
static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s; // X::s 是需要定义的
void foo() {
thread_local std::vector<int> v; // 本地变量
}
2
3
4
5
6
7
8
9
10
# C++ 运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号,C++内置了丰富的运算符,并提供了以下类型的运算符:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 杂项运算符
# 算术运算符
下表显示了C++支持的算术运算符,假设A的值为10,变量B的值为20,则:
|运算符|描述|实例|
|+
|把两个操作数相加|A + B
将得到30|
|-
|从第一个操作数中减去第二个操作数|A - B
将得到-10|
|*
|把两个操作数相乘|A * B
将得到200|
|/
|分子除以分母|B / A
将得到2|
|%
|取模运算符,整除后的余数|B % A
将得到0|
|++
|自增运算符,整数值增加1|A++
将得到11|
|--
|自减运算符,整数值减少1|A--
将得到9|
#include <iostream>
using namespace std;
int main() {
int a = 21;
int b = 10;
int c;
c = a + b;
cout << "Line 1 - c 的值是 " << c << endl;
c = a - b;
cout << "Line 2 - c 的值是 " << c << endl;
c = a * b;
cout << "Line 3 - c 的值是 " << c << endl;
c = a / b;
cout << "Line 4 - c 的值是 " << c << endl;
c = a % b;
cout << "Line 5 - c 的值是 " << c << endl;
int d = 10; // 测试自增、自减
c = d++;
cout << "Line 6 - c 的值是 " << c << endl;
d = 10; // 重新赋值
c = d--;
cout << "Line 7 - c 的值是 " << c << endl;
return 0;
}
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
# 关系运算符
下表显示C++支持的关系运算符,假设变量A的值为10,变量B的值为20,则:
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果相等则条件为真 | (A==B) 不为真 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真 | (A!=B) 为真 |
> | 检查左操作数是否大于右操作数的值,如果是则条件为真 | (A>B) 不为真 |
< | 检查左操作数是否小于右操作数的值,如果是则条件为真 | (A<B) 为真 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真 | (A>=B) 不为真 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真 | (A<=B) 为真 |
#include <iostream>
using namespace std;
int main() {
int a = 21;
int b = 10;
int c;
if (a == b) {
cout << "Line 1 - a 等于 b" << endl;
} else {
cout << "Line 1 - a 不等于 b" << endl;
}
if (a < b) {
cout << "Line 2 - a 小于 b" << endl;
} else {
cout << "Line 2 - a 不小于 b" << endl;
}
if (a > b) {
cout << "Line 3 - a 大于 b" << endl;
} else {
cout << "Line 3 - a 不大于 b" << endl;
}
/* 改变 a 和 b 的值 */
a = 5;
b = 20;
if (a <= b) {
cout << "Line 4 - a 小于或等于 b" << endl;
}
if (b >= a) {
cout << "Line 5 - b 大于或等于 a" << endl;
}
return 0;
}
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
# 逻辑运算符
下表显示了C++支持的关系逻辑运算符,假设变量A的值为1,变量B的值为0,则:
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符,如果两个操作数都true ,则条件为true | (A && B) 为false |
|| | 称为逻辑或运算符,如果两个操作数中有任意一个true ,则条件为true | (A || B) 为true |
! | 称为逻辑非运算符,用来逆转操作数的逻辑状态,如果条件为true 则逻辑非运算符将使其为false | !(A && B) 为true |
#include <iostream>
using namespace std;
int main() {
int a = 5;
int b = 20;
int c;
if (a && b) {
cout << "Line 1 - 条件为真" << endl;
}
if (a || b) {
cout << "Line 2 - 条件为真" << endl;
}
/* 改变 a 和 b 的值 */
a = 0;
b = 10;
if (a && b) {
cout << "Line 3 - 条件为真" << endl;
} else {
cout << "Line 4 - 条件不为真" << endl;
}
if (!(a && b)) {
cout << "Line 5 - 条件为真" << endl;
}
return 0;
}
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
# 位运算符
位运算符作用于位,并逐位执行操作,&
、|
和^
的真值表如下所示:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
假设如果 A = 60
,且B = 13
,现在以二进制格式表示,如下:
A = 0011 1100
B = 0000 1101
-------------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
2
3
4
5
6
7
下表显示了C++支持的位运算符,假设变量A的值为60,变量B的值为13,则:
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与操作,按二进制位进行与运算,运算规则: 0&0=0; 0&1=0; 1&0=0; 1&1=1; | (A & B) 将得到12,即为0000 1100 |
| | 按位或运算符,按二进制位进行或运算,运算规则: 0|0=0; 0|1=1; 1|0=1; 1|1=1; | (A | B) 将得到61,即为0011 1101 |
^ | 异或运算符,按二进制位进行异或运算,运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; | (A ^ B) 将得到49,即为0011 0001 |
~ | 取反运算符,按二进制位进行取反运算,运算规则: ~1=-2; ~0=-1; | (~A) 将得到-61,即为1100 0011 ,一个有符号二进制数的补码形式 |
<< | 二进制左移运算符,将一个运算对象的各二进制位全部左移若干位(左移的二进制位丢弃,右边补0) | A << 2 将得到240,即为1111 0000 |
>> | 二进制右移运算符,讲一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃 | A >> 2 将得到15,即为0000 1111 |
#include <iostream>
using namespace std;
int main() {
unsigned int a = 60; // 60 = 0011 1100
unsigned int b = 13; // 13 = 0000 1101
int c = 0;
c = a & b; // 12 = 0000 1100
cout << "Line 1 - c 的值是 " << c << endl;
c = a | b; // 61 = 0011 1101
cout << "Line 2 - c 的值是 " << c << endl;
c = a ^ b; // 49 = 0011 0001
cout << "Line 3 - c 的值是 " << c << endl;
c = ~a; //-61 = 1100 0011
cout << "Line 4 - c 的值是 " << c << endl;
c = a << 2; //240 = 1111 0000
cout << "Line 5 - c 的值是 " << c << endl;
c = a >> 2; // 15 = 0000 1111
cout << "Line 6 - c 的值是 " << c << endl;
return 0;
}
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
# 赋值运算符
下表列出了C++支持的赋值运算符:
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把A + B 的值赋给C |
+= | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于C = C + A |
-= | 减且赋值运算符,把左边减去右边操作数的结果赋值给左边操作数 | C -= A 相当于C = C - A |
*= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于C = C * A |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于C = C / A |
%= | 取模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于C = C % A |
<<= | 左移且赋值运算符 | C <<= 2 等同于C = C << 2 |
>>= | 右移且赋值运算符 | C >>= 2 等同于C = C >> 2 |
&= | 按位与且赋值运算符 | C &= 2 等同于C = C & 2 |
^= | 按位异或且赋值运算符 | C ^= 2 等同于C = C ^ 2 |
|= | 按位或且赋值运算符 | C |= 2 等同于C = C | 2 |
#include <iostream>
using namespace std;
int main() {
int a = 21;
int c;
c = a;
cout << "Line - 1 = 运算符实例,c的值 = : " << c << endl;
c += a;
cout << "Line - 2 += 运算符实例,c的值 = : " << c << endl;
c -= a;
cout << "Line - 3 -= 运算符实例,c的值 = : " << c << endl;
c *= a;
cout << "Line - 4 *= 运算符实例,c的值 = : " << c << endl;
c /= a;
cout << "Line - 5 /= 运算符实例,c的值 = : " << c << endl;
c = 200;
c %= a;
cout << "Line - 6 %= 运算符实例,c的值 = : " << c << endl;
c <<= 2;
cout << "Line - 7 <<= 运算符实例,c的值 = : " << c << endl;
c >>= 2;
cout << "Line - 8 >>= 运算符实例,c的值 = : " << c << endl;
c &= 2;
cout << "Line - 9 &= 运算符实例,c的值 = : " << c << endl;
c ^= 2;
cout << "Line - 10 ^= 运算符实例,c的值 = : " << c << endl;
c |= 2;
cout << "Line - 11 |= 运算符实例,c的值 = : " << c << endl;
return 0;
}
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
# 杂项运算符
下表列出了C++支持的其他一些重要的运算符
运算符 | 描述 |
---|---|
sizeof | sizeof 运算符返回变量的大小,例如,sizeof(a) 将返回4,其中a 是整数 |
Condition ? X : Y | 条件运算符 ,如果Condition 为真,则值为X ,否则值为Y |
, | 逗号运算符 会顺序执行一系列运算,整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值 |
.(点)和->(箭头) | 成员运算符 用于引用类、结构和共用体的成员 |
Cast | 强制转换运算符 把一种数据类型转换为另一种数据类型,例如,int(2.2000) 将返回2 |
& | 指针运算符& 返回变量的值,例如&a; 将给出变量的实际地址 |
* | 指针运算符* 指向一个变量,例如,*var; 将指向变量var |
# C++ 中的运算符优先级
运算符的优先级确定表达式中项的组合,这会影响到表达式如何计算,某些运算符比其他运算符有更高的优先级,如,乘除运算符具有比加减运算符更高的优先级,x = 7 + 3 * 2
在这里,x
被赋值为13,而不是20,因为运算符*
具有比+
更高的优先级,所以首先计算乘法3 * 2
,然后再加上7。
下表将按运算符优先级从高到底列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面,在表达式中,较高优先级的运算符会优先被计算。
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] -> . ++ -- | 从左到右 |
一元 | + - ! ~ ++ -- (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与AND | & | 从左到右 |
位异或XOR | ^ | 从左到右 |
位或OR | | | 从左到右 |
逻辑与AND | && | 从左到右 |
逻辑或OR | || | 从左到右 |
条件 | ? : | 从右到左 |
赋值 | = += -= *= /= %= >>= <<= &= ^= |= | 从右到左 |
逗号 | , | 从左到右 |
#include <iostream>
using namespace std;
int main() {
int a = 20;
int b = 10;
int c = 15;
int d = 5;
int e;
e = (a + b) * c / d; // (30 * 15) / 5
cout << "(a + b) * c / d 的值是 " << e << endl;
e = ((a + b) * c) /d; // (30 * 15) /5
cout << "((a + b) * c) / d 的值是 " << e << endl;
e = (a + b) * (c / d); // (30) * (15 / 5)
cout << "(a + b) * (c / d) 的值是 " << e << endl;
e = a + (b * c) / d; // 20 + (150 / 5)
cout << "a + (b * c) / d 的值是 " << e << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# C++ 循环
有的时候,可能需要多次执行同一块代码,一般情况下,语句是顺序执行的: 函数中的第一个语句先执行,接着是第二个语句,依次类推。编程语言提供了允许更为复杂的执行路径的多种控制结构,循环语句允许开发人员多次执行一个语句或语句组,下面是大多数编程语言中循环语句的一般形式。
# 循环类型
循环类型 | 描述 |
---|---|
while 循环 | 当给定条件为真时,重复语句或语句组,它会在执行循环主体之前测试条件 |
for 循环 | 多次执行一个语句序列,简化管理循环变量的代码 |
do ... while 循环 | 除了它是在循环主体结尾测试条件外,其他与while 语句类似 |
嵌套循环 | 可以在while 、for 或do ... while 循环内使用一个或多个循环 |
# C++ while 循环
只要给定的条件为真,while
循环语句会重复执行一个目标语句
- 语法
while(condition) {
statement(s);
}
2
3
在这里,statement(s)
可以是一个单独的语句,也可以是几个语句组成的代码块,condition
可以是任意的表达式,当为任意非零值时都为真,当条件为真时执行循环,当条件为假时,程序流将继续执行紧接着循环的下一条语句。
- 流程图
在这里,while
循环的关键点是循环可能一次都不会执行,当条件被测试且结果为假时,会跳过循环主体,直接执行紧接着while
循环的下一条语句。
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 10;
// while 循环执行
while (a < 20)
{
cout << "a 的值: " << a << endl;
a++;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# C++ for 循环
for
循环允许编写一个执行特定次数的循环的重复控制结构。
- 语法
for (init; condition; increment)
{
statement(s);
}
2
3
4
下面是for
循环的控制流:
init
会首先被执行,且只会执行一次,这一步允许声明并初始化任何循环控制变量,也可以不在这里写任何语句,只要有一个分号出现即可。- 接下来,会判断
condition
,如果为真,则执行循环主体,如果为假,则不执行循环主体,且控制流会跳转到紧接着for
循环的下一条语句 - 在执行完
for
循环主体后,控制流会跳回到上面的increment
语句,该语句允许更新循环控制变量,该语句可以留空,只要在条件后有一个分号出现即可。 - 条件再次被判断,如果为真,则执行循环,这个过程会不断重复。
- 流程图
#include <iostream>
using namespace std;
int main()
{
// for 循环执行
for (int a = 10; a < 20; a = a + 1)
{
cout << "a 的值: " << a << endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
- 基于范围的
for
循环(C++11)
int my_array[5] = {1, 2, 3, 4, 5};
// 每个数组元素乘于 2
for (int &x : my_array)
{
x *= 2;
cout << x << endl;
}
// auto 类型也是 C++11 新标准中的,用来自动获取变量的类型
for (auto &x : my_array)
{
x *= 2;
cout << x << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
上面for
语句的第一部分定义被用来做范围迭代的变量,就像被声明在一般for
循环的变量一样,其作用域仅只于循环的范围,而在:
之后的第二个区块,代表将被迭代的范围。
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string str("some string");
// range for 语句
for (auto &c : str)
{
c = toupper(c);
}
cout << str << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上面的程序使用Range for语句遍历一个字符串,并将所有字符全部转换为大写。
# C++ do ... while 循环
和for
、while
在循环头部测试循环条件不同,do ... while
循环是在循环的尾部检查它的条件。do ... while
循环与while
循环类似,但其会确保至少执行一次循环。
- 语法
do
{
statement(s);
} while (condition);
2
3
4
- 流程图
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 10;
// do 循环执行
do
{
cout << "a 的值: " << a << endl;
a = a + 1;
} while (a < 20);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# C++ 嵌套循环
一个循环内可以嵌套另一个循环,C++ 允许至少256个嵌套层次,关于嵌套循环有一点值得注意,可以在任何类型的循环内嵌套其他任何类型的循环,比如,一个for
循环可以嵌套在一个while
循环内,反之亦然。
# 循环控制语句
循环控制语句更改执行的正常序列,当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁,C++提供了下列控制语句:
控制语句 | 描述 |
---|---|
break 语句 | 终止loop 或switch 语句,程序流将继续执行紧接着loop 或switch 的下一条语句 |
continue 语句 | 引起循环跳过主体的剩余部分,立即重新开始测试条件 |
goto 语句 | 将控制转移到被标记的语句,但是不建议在程序中使用goto 语句 |
# C++ break 语句
C++ 中break
语句有以下两种用法:
- 当
break
语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。 - 它可用于终止
switch
语句中的一个case
如果您使用的是嵌套循环(即一个循环内嵌套另一个循环),break
语句会停止执行最内层的循环,然后开始执行该块之后的下一行代码。
- 语法
- 流程图
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 10;
// do 循环执行
do
{
cout << "a 的值: " << a << endl;
a = a + 1;
if (a > 15)
{
// 终止循环
break;
}
} while (a < 20);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# C++ continue 语句
C++中的continue
语句有点像break
语句,但它不是强迫终止,continue
会跳过当前循环中的代码,强迫开始下一次循环。对于for
循环,continue
语句会导致执行条件测试和循环增量部分,对于while
和do ... while
循环,continue
语句会导致程序控制回到条件测试上。
- 语法
- 流程图
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 10;
// do 循环执行
do
{
if (a == 15)
{
// 跳过迭代
a = a + 1;
continue;
}
cout << "a 的值: " << a << endl;
a = a + 1;
} while (a < 20);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# C++ goto 语句
goto
语句允许把控制无条件转移到同一函数内的被标记的语句。
注意
在任何编程语言中,都不建议使用goto
语句,因为它使得程序的控制流难以跟踪,使程序难以理解和难以修改,任何使用goto
语句的程序都可以改成不使用goto
语句的写法。
- 语法
goto label;
..
.
label: statement;
2
3
4
在这里,label
是识别被标记语句的标识符,可以是任何除C++关键字以外的纯文本,标记语句可以是任何语句,放置在标识符和冒号(:
)后面。
- 流程图
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 10;
// do 循环执行
LOOP:do
{
if (a == 15)
{
// 跳过迭代
a = a + 1;
goto LOOP;
}
cout << "a 的值: " << a << endl;
a = a + 1;
} while (a < 20);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
goto
语句一个很好的作用是退出深层嵌套例程,如:
for (...)
{
for (...)
{
while (...)
{
if (...) goto stop;
.
.
.
}
}
}
stop:
cout << "Error in program.\n";
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 无限循环
如果条件永远不为假,则循环将变成无限循环,for
循环在传统意义上可用于实现无限循环,由于构成循环的三个表达式中任何一个都不是必须的,可以将某些条件表达式留空来构成一个无限循环。
#include <iostream>
using namespace std;
int main()
{
for ( ; ; )
{
printf("This loop will run forever.\n");
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
当条件表达式不存在时,它被假设为真,当然也可以设置一个初始值和增量表达式,但是一般情况下,C++程序员偏向于使用for(;;)
结构来表示一个无限循环。
# C++ 判断
判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的),下面是大多数编程语言中典型的判断结构的一般形式:
# 判断语句
语句 | 描述 |
---|---|
if 语句 | 一个if 语句由一个布尔表达式后跟一个或多个语句组成 |
if ... else 语句 | 一个if 语句后可跟一个可选的else 语句,else 语句在布尔表达式为假时执行 |
嵌套if 语句 | 可以在一个if 或else if 语句内使用另一个if 或else if 语句 |
switch 语句 | 一个switch 语句允许测试一个变量等于多个值时的情况 |
嵌套switch 语句 | 可以在一个switch 语句内使用另一个switch 语句 |
# C++ if 语句
一个if
语句由一个布尔表达式后跟一个或多个语句组成。
- 语法
if (boolean_expression)
{
// 如果布尔表达式为真将执行的语句
}
2
3
4
如果布尔表达式为true
,则if
语句内的代码块将被执行,如果布尔表达式为false
,则if
语句结束后的第一组代码(闭括号后)将被执行。C语言把任何非零和非空的值假定为true
,把零或null假定为false
。
- 流程图
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 10;
// 使用if语句检查布尔条件
if (a < 20)
{
// 如果条件为真,则输出下面的语句
cout << "a 小于 20" << endl;
}
cout << "a 的值是 " << a << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# C++ if ... else 语句
一个if
语句后可跟一个可选的else
语句,else
语句在布尔表达式为假时执行
- 语法
if (boolean_expression)
{
// 如果布尔表达式为真将执行的语句
}
else
{
// 如果布尔表达式为假将执行的语句
}
2
3
4
5
6
7
8
- 流程图
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 100;
// 检查布尔条件
if (a < 20)
{
// 如果条件为真,则输出下面的语句
cout << "a 小于 20" << endl;
}
else
{
// 如果条件为假,则输出下面的语句
cout << "a 大于 20" << endl;
}
cout << "a 的值是 " << a << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
**if ... else if ... else
**语句: 一个if
语句后可跟一个可选的else if ... else
语句,可用于测试多种条件,不过需要注意:
一个
if
后可跟零个或一个else
,else
必须在所有else if
之后一个
if
后可跟零个或多个else if
,else if
必须在else
之前一旦某个
else if
匹配成功,其他的else if
或else
将不会被测试语法
if (boolean_expression 1)
{
// 当布尔表达式 1 为真时执行
}
else if (boolean_expression 2)
{
// 当布尔表达式 2 为真时执行
}
else if (boolean_expression 3)
{
// 当布尔表达式 3 为真时执行
}
else
{
// 当上面条件都不为真时执行
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
int main()
{
// 局部变量声明
int a = 100;
// 检查布尔条件
if (a == 10)
{
// 如果 if 条件为真,则输出下面的语句
cout << "a 的值是 10" << endl;
}
else if (a == 20)
{
// 如果 else if 条件为真,则输出下面的语句
cout << "a 的值是 20" << endl;
}
else if (a == 30)
{
// 如果 else if 条件为真,则输出下面的语句
cout << "a 的值是 30" << endl;
}
else
{
// 如果上面条件都不为真,则输出下面的语句
cout << "没有匹配的值" << endl;
}
cout << "a 的准确值是 " << a << endl;
return 0;
}
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
# C++ 嵌套 if 语句
在C++中,嵌套if-else
语句是合法的,这意味着可以在一个if
或else if
语句内使用另一个if
或else if
语句,嵌套if
语句是一种if
语句的变体,其中一个if
语句可以在另一个if
语句中嵌套,嵌套if
语句可以帮助精确地测试多个条件。
- 语法
if (boolean_expression 1)
{
// 当布尔表达式 1 为 true 时执行
if (boolean_expression 2)
{
// 当布尔表达式 2 为 true 时执行
}
}
2
3
4
5
6
7
8
if (condition1)
{
// 如果 condition1 为 true,则执行此处的代码
if (condition2)
{
// 如果 condition2 也为 true,则执行此处的代码
}
else
{
// 如果 condition2 为 false,则执行此处的代码
}
}
else
{
// 如果 condition1 为 false,则执行此处的代码块
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# C++ switch 语句
一个switch
语句允许测试一个变量等于多个值时的情况,每个值称为一个case
,且被测试的变量会对每个switch case
进行检查
- 语法
switch (expression)
{
case constant-expression :
statement(s);
break; // 可选的
case constant-expression :
statement(s);
break; // 可选的
// 可以有任意数量的 case 语句
default : // 可选的
statement(s);
}
2
3
4
5
6
7
8
9
10
11
12
switch
语句必须遵循下面的规则:
switch
语句中的expression
必须是一个整型或枚举类型,或者是一个class类型,其中class有一个单一的转换函数将其转换为整型或枚举类型在一个
switch
中可以有任意数量的case
语句,每个case
后跟一个要比较的值和一个冒号case
的constant-expression
必须与switch
中的变量具有相同的数据类型,且必须是一个常量或字面量当被测试的变量等于
case
中的常量时,case
后跟的语句将被执行,直到遇到break
语句为止当遇到
break
语句时,switch
终止,控制流将跳转到switch
语句后的下一行不是每一个
case
都需要包含break
,如果case
语句不包含break
,控制流将会继续后面的case
,直到遇到break
为止一个
switch
语句可以有一个可选的default case
,出现在switch
的结尾,default case
可用于在上面所有case
都不为真时执行一个任务,default case
中的break
语句不是必需的。流程图
#include <iostream>
using namespace std;
int main ()
{
// 局部变量声明
char grade = 'D';
switch(grade)
{
case 'A' :
cout << "很棒!" << endl;
break;
case 'B' :
case 'C' :
cout << "做得好" << endl;
break;
case 'D' :
cout << "您通过了" << endl;
break;
case 'F' :
cout << "最好再试一下" << endl;
break;
default :
cout << "无效的成绩" << endl;
}
cout << "您的成绩是 " << grade << endl;
return 0;
}
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
# C++ 嵌套 switch 语句
- 语法
switch(ch1) {
case 'A':
cout << "这个 A 是外部 switch 的一部分";
switch(ch2) {
case 'A':
cout << "这个 A 是内部 switch 的一部分";
break;
case 'B': // 内部 B case 代码
}
break;
case 'B': // 外部 B case 代码
}
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;
int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
switch(a) {
case 100:
cout << "这是外部 switch 的一部分" << endl;
switch(b) {
case 200:
cout << "这是内部 switch 的一部分" << endl;
}
}
cout << "a 的准确值是 " << a << endl;
cout << "b 的准确值是 " << b << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ? :
运算符
Exp1 ? Exp2 : Exp3;
其中,Exp1
、Exp2
和Exp3
是表达式,注意,冒号的使用和位置。?
表达式的值是由Exp1
决定的,如果Exp1
为真,则计算Exp2
的值,结果即为整个?
表达式的值,如果Exp1
为假,则计算Exp3
的值,结果即为整个?
表达式的值。
# C++ 函数
函数是一组一起执行一个任务的语句,每个C++程序都至少有一个函数,即主函数main()
,所有简单的程序都可以定义其他额外的函数,可以把代码划分到不同的函数中,如何划分到不同的函数中由开发者决定,但逻辑上,划分通常是根据每个函数执行一个特定的任务来进行。
函数声明告诉编译器函数的名称、返回类型和参数,函数定义提供了函数的实际主体。C++标准库提供了大量的程序可以调用的内置函数,如,函数strcat()
用来连接两个字符串,函数memcpy()
用来复制内存到另一个位置。函数还有很多叫法,比如方法、子例程或程序,等等。
# 定义函数
C++中的函数定义一般形式如下:
return_type function_name(parameter list)
{
body of the function
}
2
3
4
在C++中,函数由一个函数头和一个函数主体组成,下面列出一个函数的所有组成部分:
- 返回类型: 一个函数可以返回一个值,
return_type
是函数返回的值的数据类型,有些函数执行所需的操作而不返回值,在这种情况下,return_type
是关键字void
- 函数名称: 这是函数的实际名称,函数名和参数列表一起构成了函数签名
- 参数: 参数就像是占位符,当函数被调用时,开发者向参数传递一个值,这个值被称为实际参数,参数列表包括函数参数的类型、顺序、数量,参数是可选的,也就是,函数可能不包含参数
- 函数主体: 函数主体包含一组定义函数执行任务的语句
// 函数返回两个数中较大的那个数
int max(int num1, int num2)
{
// 局部变量声明
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
2
3
4
5
6
7
8
9
10
11
12
13
# 函数声明
函数声明会告诉编译器函数名称及如何调用函数,函数的实际主体可以单独定义。函数声明包括以下几个部分:
return_type function_name(parameter list);
针对上面定义的函数max()
,以下是函数声明:
int max(int num1, int num2);
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面的也是有效声明:
int max(int, int);
当在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的,这种情况,应在调用函数的文件顶部声明函数。
# 调用函数
创建C++函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务,当程序调用函数时,程序控制权会转移给调用的函数,被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值,如:
#include <iostream>
using namespace std;
// 函数声明
int max(int num1, int num2);
int main()
{
// 局部变量声明
int a = 100;
int b = 200;
int ret;
// 调用函数来获取最大值
ret = max(a, b);
cout << "Max value is: " << ret << endl;
return 0;
}
// 函数返回两个数中较大的那个数
int max(int num1, int num2)
{
// 局部变量声明
int result;
if (num1 > num2)
{
result = num1;
}
else
{
result = num2;
}
return result;
}
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
# 函数参数
如果函数要使用参数,则必须声明接受参数值的变量,这些变量称为函数的形式参数。形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。当调用函数时,有三种向函数传递参数的方式:
调用类型 | 描述 |
---|---|
传值调用 | 该方法把参数的实际值赋值给函数的形式参数,在这种情况下,修改函数内的形式参数对实际参数没有影响 |
指针调用 | 该方法把参数的地址赋值给形式参数,在函数内,该地址用于访问调用中要用到的实际参数,这意味着,修改形式参数会影响实际参数 |
引用调用 | 该方法把参数的引用赋值给形式参数,在函数内,该引用用于访问调用中要用到的实际参数,这意味这,修改形式参数会影响实际参数 |
默认情况下,C++使用传值调用来传递参数,一般来说,这意味着函数内的代码不能改变用于调用函数的参数,之前提到的实例,调用max()
函数时,使用了相同的方法。
# 参数的默认值
当定义一个函数,可以为参数列表中后边的每一个参数指定默认值,当调用函数时,如果实际参数的值留空,则使用这个默认值。这是通过在函数定义中使用赋值运算符来为参数赋值的,调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值,如下:
#include <iostream>
using namespace std;
int sum(int a, int b = 20)
{
int result;
result = a + b;
return (result);
}
int main()
{
// 局部变量声明
int a = 100;
int b = 200;
int result;
// 调用函数来添加值
result = sum(a, b);
cout << "Total value is : " << result << endl;
// 再次调用函数
result = sum(a);
cout << "Total value is : " << result << endl;
return 0;
}
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
# Lambda 函数与表达式
C++11提供了对匿名函数的支持,称为Lambda函数(也叫Lambda表达式)。Lambda表达式把函数看作对象,Lambda表达式可以向对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
Lambda表达式本质上与函数声明非常类似,Lambda表达式具体形式如下:
[capture](parameters)->return-type{body}
例如:
[](int x, int y){ return x < y; }
如果没有返回值可以表示为:
[capture](parameters){body}
例如:
[]{ ++global_x; }
在一个更为复杂的例子中,返回类型可以被明确指定:
[](int x, int y) -> int { int z = x + y; return z + x; }
本例中,一个临时的参数z
被创建用来存储中间的结果,如同一般的函数,z
的值不会保留到下一次该不具名函数再次被调用时,如果lambda函数没有传回值(例如void
),其返回类型可被完全忽略。在lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为,与Javascript闭包不同,C++传递有传值和传引用的区别,可以通过前面的[]
来指定:
[] // 没有定义任何变量,使用未定义变量会引发错误
[x, &y] // x 以传值方式传入(默认),y 以引用方式传入
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用
[&, x] // x 显式地以传值方式加以引用,其余变量以引用方式加以引用
[=, &z] // z 显式地以引用方式加以引用,其余变量以传值方式加以引用
2
3
4
5
6
另外有一点需要注意,对于[=]
或[&]
的形式,lambda表达式可以直接使用this
指针,但是,对于[]
的形式,如果要使用this
指针,必须显式传入:
[this]() { this -> someFunc(); }();
# C++ 数字
通常,当开发者需要用到数字时,会使用原始的数据类型,如int
、short
、long
、float
和double
等等,这些用于数字的数据类型,其可能的值和数值范围,前面已经讨论过。
# C++ 定义数字
#include <iostream>
using namespace std;
int main ()
{
// 数字定义
short s;
int i;
long l;
float f;
double d;
// 数字赋值
s = 10;
i = 1000;
l = 1000000;
f = 230.47;
d = 30949.374;
// 数字输出
cout << "short s :" << s << endl;
cout << "int i :" << i << endl;
cout << "long l :" << l << endl;
cout << "float f :" << f << endl;
cout << "double d :" << d << endl;
return 0;
}
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
# C++ 数字运算
在C++中,除了可以创建各种函数,还包含了各种有用的函数,这些函数写在标准C和C++库中,叫做内置函数。
序号 | 函数 | 描述 |
---|---|---|
1 | double cos(double); | 该函数返回弧度角的余弦 |
2 | double sin(double); | 该函数返回弧度角的正弦 |
3 | double tan(double); | 该函数返回弧度角的正切 |
4 | double log(double); | 该函数返回参数的自然对数 |
5 | double pow(double, double); | 假设第一个参数为x ,第二个参数为y ,则该函数返回x 的y 次方 |
6 | double hypot(double, double); | 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度 |
7 | double sqrt(double); | 该函数返回参数的平方根 |
8 | int abs(int); | 该函数返回参数的绝对值 |
9 | double fabs(double); | 该函数返回任意一个浮点数的绝对值 |
10 | double floor(double); | 该函数返回一个小于或等于传入参数的最大整数 |
#include <iostream>
#include <cmath>
using namespace std;
int main ()
{
// 数字定义
short s = 10;
int i = -1000;
long l = 100000;
float f = 230.47;
double d = 200.374;
// 数学运算
cout << "sin(d) :" << sin(d) << endl;
cout << "abs(i) :" << abs(i) << endl;
cout << "floor(d) :" << floor(d) << endl;
cout << "sqrt(f) :" << sqrt(f) << endl;
cout << "pow( d, 2) :" << pow(d, 2) << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# C++ 随机数
在许多情况下,需要生成随机数,关于随机数生成器,有两个相关的函数,一个是rand()
,该函数只返回一个伪随机数,生成随机数之前必须先调用srand()
函数。下面是一个关于生成随机数的简单实例,实例中使用了time()
函数来获取系统时间的秒数,通过调用rand()
函数来生成随机数:
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
int main ()
{
int i,j;
// 设置种子
srand( (unsigned)time( NULL ) );
/* 生成 10 个随机数 */
for( i = 0; i < 10; i++ )
{
// 生成实际的随机数
j= rand();
cout <<"随机数: " << j << endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# C++ 数组
C++支持数组数据结构,它可以存储一个固定大小相同类型元素的顺序集合,数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。数组的声明并不是声明一个个单独的变量,比如number0、number1、...、number99,而是声明一个数组变量,比如numbers,然后使用numbers[0]、numbers[1]、...、numbers[99]来代表一个个单独的变量,数组中的特定元素可以通过索引访问。
所有的数组都是有连续的内存位置组成,最低的地址对应第一个元素,最高的地址对应最后一个元素。
# 声明数组
在C++中要声明一个数组,需要指定元素的类型和元素的数量,如下:
type arrayNmae [ arraySize ];
这叫做一维数组,arraySize
必须是一个大于零的整数常量,type
可以是任意有效的C++数据类型,例如,要声明一个类型为double
的包含10个元素的数组balance
,语句如下:
double balance[10];
现在balance
是一个可用的数组,可以容纳10个类型为double
的数字。
# 初始化数组
在C++中,可以逐个初始化数组,也可以使用一个初始化语句,如下:
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
大括号{}
之间的值的数目不能大于我们在数组声明时在方括号[]
中指定的元素数目。如果省略掉了数组的大小,则为初始化时元素的个数,如:
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
上面的将创建一个数组,它与前一个实例中所创建的数组是完全相同的,下面是一个为数组中某个元素赋值的实例:
balance[4] = 50.0;
上述的语句把数组中第五个元素的值赋为50.0。所有的数组都以0作为第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去1,如下图:
# 访问数组元素
数组元素可以通过数组名称加索引进行访问,元素的索引是放在方括号内,跟在数组名称的后边,如:
double salary = balance[9];
上面的语句将把数组中第10个元素的值赋给salary
变量,下面的实例使用了上述的三个概念,即,声明数组、数组赋值、访问数组:
#include <iostream>
using namespace std;
#include <iomanip>
using std::setw;
int main()
{
int n[10]; // n 是一个包含 10 个整数的数组
// 初始化数组元素
for (int i = 0; i < 10; i++)
{
n[i] = i + 100; // 设置元素 i 为 i + 100
}
cout << "Element" << setw(13) << "Value" << endl;
// 输出数组中每个元素的值
for (int j = 0; j < 10; j++)
{
cout << setw(7) << j << setw(13) << n[j] << endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上面的程序使用了setw()
函数来格式化输出,结果如下:
# C++ 中数组详解
在C++中,数组是非常重要,下面列出了C++程序员必须清楚的一些与数组相关的重要概念:
概念 | 描述 |
---|---|
多维数组 | C++支持多维数组,多维数组最简单的形式是二维数组 |
指向数组的指针 | 可以通过指定不带索引的数组名称来生成一个指向数组中第一个元素的指针 |
传递数组给函数 | 可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针 |
从函数返回数组 | C++允许从函数返回数组 |
# C++ 多维数组
C++支持多维数组,多维数组声明的一般形式如:
type name[size1][size2]...[sizeN];
int threadim[5][10][4];
多维数组最简单的形式是二维数组,一个二维数组,本质上,是一个一维数组的列表,声明一个x行y列的二维整型数组,形式如:
type arrayName[x][y];
其中,type
可以是任意有效的C++数据类型,arrayName
是一个有效的C++标识符,一个二维数组可以被认为是一个带有x行y列的表格,下面是一个二维数组,包含3行和4列:
因此,数组中的每个元素是使用形式为a[i, j]
的元素名称来标识的,其中a
是数组名称,i
和j
是唯一标识a
中每个元素的下标。
多维数组可以通过在括号内为每行指定值来进行初始化,下面是一个带有3行4列的数组。
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
2
3
4
5
内部嵌套的括号是可选的,下面的初始化与上面是等同的
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的,如:
int val = a[2][3];
上面的语句将获取数组中第3行和第4个元素,可以使用嵌套循环来处理二维数组:
#include <iostream>
using namespace std;
int main ()
{
// 一个带有 5 行 2 列的数组
int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}};
// 输出数组中每个元素的值
for ( int i = 0; i < 5; i++ )
for ( int j = 0; j < 2; j++ )
{
cout << "a[" << i << "][" << j << "]: ";
cout << a[i][j]<< endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# C++ 指向数组的指针
数组名是指向数组中第一个元素的常量指针,因此,下面的声明: double runoobArray[50];
,runoobArray
是一个指向&runoobArray[0]
的指针,即数组runoobArray
的第一个元素的地址,因此下面的程序片段把p
赋值为runoobArray
的第一个元素的地址:
double *p;
double runoobArray[10];
p = runoobArray;
2
3
4
使用数组名作为常量指针是合法的,反之亦然,*(runoobArray + 4)
是一种访问runoobArray[4]
数据的合法方式,一旦把第一个元素的地址存储在p
中,就可以使用*p
、*(p + 1)
、*(p + 2)
等来访问数组元素。
#include <iostream>
using namespace std;
int main()
{
// 带有 5 个元素的双精度浮点型数组
double runoobArray[5] = {1000., 2.0, 3.4, 17.0, 50.0};
double *p;
p = runoobArray;
// 输出数组中每个元素的值
cout << "使用指针的数组值 " << endl;
for (int i = 0; i < 5; i++)
{
cout << "*(p + " << i << ") : ";
cout << *(p + i) << endl;
}
cout << "使用 runoobArray 作为地址的数组值 " << endl;
for (int i = 0; i < 5; i++)
{
cout << "*(runoobArray + " << i << ") : ";
cout << *(runoobArray + i) << endl;
}
return 0;
}
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
在上面的实例中,p
是一个指向double
型的指针,这意味着它可以存储一个double
类型的变量,一旦有了p
中的地址,*p
将给出存储在p
中响应地址的值。
# C++ 传递数组给函数
C++中可以通过指定不带索引的数组名来传递一个指向数组的指针,C++传数组给一个函数,数组类型自动转换为指针类型,因而传的实际是地址。如果想要在函数中传递一个一维数组作为参数,必须以下面三种方式来声明函数形式参数,这三种声明方式的结果是一样的,因为每种方式都会告诉编译器将要接收一个整型指针。
- 方式一
void myFunction(int *param)
{
...
}
2
3
4
- 方式二
void myFunction(int param[10])
{
...
}
2
3
4
- 方式三
void myFunction(int param[])
{
...
}
2
3
4
下面这个函数,把数组作为参数,同时传递另一个参数,根据所传的参数,会返回数组中各元素的平均值:
double getAverage(int arr[], int size)
{
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i)
{
sum += arr[i];
}
avg = double(sum) / size;
return avg;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
// 函数声明
double getAverage(int arr[], int size);
int main()
{
// 带有 5 个元素的整型数组
int balance[5] = {1000, 2, 3, 17, 50};
double avg;
// 传递一个指向数组的指针作为参数
avg = getAverage(balance, 5);
// 输出返回值
cout << "平均值: " << avg << endl;
return 0;
}
double getAverage(int arr[], int size)
{
int i, sum = 0;
double avg;
for (int i = 0; i < size; i++)
{
sum += arr[i];
}
avg = double(sum) / size;
return avg;
}
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
# C++ 从函数返回数组
C++不允许返回一个完整的数组作为函数的参数,但是,可以通过指定不带索引的数组名来返回一个指向数组的指针,如果想从函数返回一个一维数组,必须声明一个返回指针的函数,如下:
int* myFunction()
{
...
}
2
3
4
int* myFunction()
{
int myArray[3] = {1, 2, 3};
return myArray;
}
2
3
4
5
注意
不能简单第返回指向局部数组的指针,因为当函数结束时,局部数组将被销毁,指向它的指针将变得无效,C++不支持在函数外返回局部变量的地址,除非定义局部变量为static
变量,为了避免以上情况,可以使用静态数组或动态分配数组。使用静态数组需要在函数内创建一个静态数组,并将其地址返回,如:
int* myFunction()
{
static int myArray[3] = {1, 2, 3};
return myArray;
}
2
3
4
5
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
// 要生成和返回随机数的函数
int* getRandom()
{
static int r[10];
// 设置种子
srand((unsigned) time(NULL));
for (int i = 0; i < 10; i++)
{
r[i] = rand();
cout << r[i] << endl;
}
return r;
}
// 要调用上面定义函数的主函数
int main()
{
// 一个指向整数的指针
int *p;
p = getRandom();
for (int i = 0; i < 10; i++)
{
cout << "*(p + " << i << ") : ";
cout << *(p + i) << endl;
}
return 0;
}
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
使用动态分配数组需要在函数内部使用new
运算符来分配一个数组,并在函数结束时使用delete
运算符来释放该数组,如:
int* myFunction()
{
int* myArray = new int[3];
myArray[0] = 1;
myArray[1] = 2;
myArray[2] = 3;
return myArray;
}
int main()
{
int* result = myFunction();
// 使用 result
delete[] result;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
int* createArray(int size)
{
int* arr = new int[size];
for (int i = 0; i < size; i++)
{
arr[i] = i + 1;
}
return arr;
}
int main()
{
int* myArray = createArray(5);
for (int i = 0; i < 5; i++)
{
cout << myArray[i] << " ";
}
cout << endl;
delete[] myArray; // 释放内存
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在上面的例子中,声明了一个名为createArray
的函数,它接受一个整数参数size
,并返回一个由整数填充的整数数组,使用new
运算符在堆上动态分配了一个数组,并在函数内部填充了数组,最后,函数返回了指向数组的指针。在main
函数中,调用了createArray
函数,并将返回的数组指针存储在myArray
中,然后遍历了数组并打印了每个元素的值,最后,使用delete[]
运算符释放了myArray
所占用的内存,以避免内存泄露。
注意
当使用动态分配数组时,调用函数的代码负责释放返回的数组,这是因为在函数内部分配的数组在函数结束时不会自动释放。
# C++ 字符串
C++提供了以下两种类型的字符串表示形式:
- C 风格字符串
- C++ 引入的 string 类类型
# C 风格字符串
C风格的字符串起源于C语言,并在C++中继续得到支持,字符串实际上是使用null
字符\0
终止的一维字符数组,因此,一个以null
结尾的字符串,包含了组成字符串的字符。下面的声明和初始化创建了一个RUNOOB字符串,由于在数组的末尾存储了空字符,所以字符数组的大小比单词RUNOOB的字符数多一个。
char site[7] = {'R', 'U', "N', 'O', 'O', 'B', '\0'};
依据数组初始化规则,可以把上面的语句写成以下语句:
char site[] = "RUNOOB";
其实,并不需要把null
字符放在字符串常量的末尾,C++编译器会在初始化数组时,自动把\0
放在字符串的末尾:
#include <iostream>
using namespace std;
int main ()
{
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
cout << "菜鸟教程: ";
cout << site << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
C++中有大量的函数用来操作以null
结尾的字符串:
序号 | 函数 | 目的 |
---|---|---|
1 | strcpy(s1, s2); | 赋值字符串s2到字符串s1 |
2 | strcat(s1, s2); | 连接字符串s2到字符串s1的末尾,连接字符串也可以用+ 号,如: string str1 = "runoob"; string str2 = "google"; string str = str1 + str2; |
3 | strlen(s1); | 返回字符串s1的长度 |
4 | strcmp(s1, s2); | 如果s1和s2是相同的,则返回0;如果 s1 < s2 则返回值小于 0;如果 s1 > s2 则返回值大于 0 |
5 | strchr(s1, ch); | 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置 |
6 | strstr(s1, s2); | 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置 |
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str1[13] = "runoob";
char str2[13] = "google";
char str3[13];
int len;
// 复制 str1 到 str3
strcpy(str3, str1);
cout << "strcpy(str3, str1) : " << str3 << endl;
// 连接 str1 和 str2
strcat(str1, str2);
cout << "strcat(str1, str2) : " << str1 << endl;
// 连接后,str1 的总长度
len = strlen(str1);
cout << "strlen(str1) : " << len << endl;
return 0;
}
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
# C++ 中的 string 类
C++标准库提供了string
类类型,支持上述所有的操作,另外还增加了其他更多的功能:
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str1 = "runoob";
string str2 = "google";
string str3;
int len ;
// 复制 str1 到 str3
str3 = str1;
cout << "str3 : " << str3 << endl;
// 连接 str1 和 str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// 连接后,str3 的总长度
len = str3.size();
cout << "str3.size() : " << len << endl;
return 0;
}
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
# C++ 指针
通过指针,可以简化一些C++编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&
)运算符访问的地址,它表示了在内存中的一个地址。
#include <iostream>
using namespace std;
int main()
{
int var1;
char var2[10];
cout << "var1 变量的地址: ";
cout << &var1 << endl;
cout << "var2 变量的地址: ";
cout << &var2 << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 什么是指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址,就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明,指针变量声明的一般形式为:
type *var-name;
在这里,type
是指针的基类型,它必须是一个有效的C++数据类型,var-name
是指针变量的名称,用来声明指针的星号*
与乘法中使用的星号是相同的,但是,在这个语句中,星号是用来指定一个变量是指针,以下是有效的指针声明:
int *ip; // 一个整型的指针
double *dp; // 一个double型的指针
float *fp; // 一个浮点型的指针
char *ch; // 一个字符型的指针
2
3
4
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的十六机制数,不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
# C++ 中使用指针
使用指针时会频繁进行以下几个操作: 定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些通过使用一元运算符*
来返回位于操作数所指定地址的变量的值:
#include <iostream>
using namespace std;
int main()
{
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
ip = &var; // 在指针变量中存储 var 的地址
cout << "Value of var variable: ";
cout << var << endl;
// 输出在指针变量中存储的地址
cout << "Address stored in ip variable: ";
cout << ip << endl;
// 访问指针中地址的值
cout << "Value of *ip variable: ";
cout << *ip << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# C++ 指针详解
概念 | 描述 |
---|---|
C++ Null 指针 | C++支持空指针,NULL 指针是一个定义在标准库中的值为零的常量 |
C++ 指针的算术运算 | 可以对指针进行四种算术运算: ++ 、-- 、+ 、- |
C++ 指针 vs 数组 | 指针和数组之间有着密切的关系 |
C++ 指针数组 | 可以定义用来存储指针的数组 |
C++ 指向指针的指针 | C++允许指向指针的指针 |
C++ 传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变 |
C++ 从函数返回指针 | C++允许函数返回指针到局部变量、静态变量和动态内存分配 |
# C++ NULL 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL
值是一个良好习惯,赋为NULL
值的指针被称为空指针。
NULL
指针是一个定义在标准库中的值为零的常量,如下:
#include <iostream>
using namespace std;
int main() {
int *ptr = NULL;
cout << "ptr 的值是 " << ptr;
return 0;
}
2
3
4
5
6
7
8
9
结果: ptr 的值是 0
在大多数操作系统中,程序不允许访问地址为0
的内存,因为该内存是操作系统保留的,然而,内存地址0
有特别重要的意义,它表明该指针不指向一个可访问的内存地址,但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西,如果需要检查一个空指针,可以使用if
语句,如下:
if(ptr) /* 如果 ptr 非空, 则完成 */
if(!ptr) /* 如果 ptr 为空, 则完成 */
2
因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针,很多时候,未初始化的变量存在一些垃圾值,导致程序难以调试。
# C++ 指针的算术运算
指针是一个用数值表示的地址,因此可以对指针执行算术运算,可以对指针进行四种算术运算: ++
、--
、+
、-
。
假设ptr
是一个指向地址1000
的整型指针,是一个32位的整数,如果对该指针执行下列的算术运算:
ptr++
执行ptr++
后,指针ptr
会向前移动4个字节,指向下一个整型元素的地址,这是由于指针算术运算会根据指针的类型和大小来决定移动的距离,在这种情况下,由于是一个32位整数指针,每个整数占据4个字节,因此ptr++
会将指针ptr
向前移动4个字节,指向下一个整型元素的地址。
如果ptr
指向一个地址为1000
的字符,执行ptr++
指针ptr
的值会增加,指向下一个字符元素的地址,由于ptr
是一个字符指针,每个字符占据1个字节,因此ptr++
会将ptr的值增加1,执行后ptr
指向地址1001。
指针算术运算的详细解析:
- 加法运算: 可以对指针进行加法运算,当一个指针
p
加上一个整数n
时,结果是指针p
向前移动n
个元素的大小,如,如果p
是一个int
类型的指针,每个int
占4个字节,那么p+1
将指向p
所指向的下一个int
元素 - 减法运算: 可以对指针进行减法运算,当一个指针
p
减去一个整数n
时,结果是指针p
向后移动n
个元素的大小,如,如果p
是一个int
类型的指针,每个int
占4个字节,那么p-1
将指向p
所指向的前一个int
元素 - 指针与指针之间的减法运算: 可以计算两个指针之间的距离,当从一个指针
p
减去另一个指针q
时,结果是两个指针之间的元素个数,如,如果p
和q
是两个int
类型的指针,每个int
占4个字节,那么p - q
将得到两个指针之间的元素个数 - 指针与整数之间的比较运算: 可以将指针与整数进行比较运算,可以使用关系运算符(如
<
、>
、<=
、>=
)对指针和整数进行比较,这种比较通常用于判断指针是否指向某个有效的内存位置
- 递增一个指针
在程序中使用指针代替数组,变量指针可以递增,数组不能递增,因为数组是一个常量指针,下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
#include <iostream>
using namespace std;
const int MAX = 3;
int main() {
int var[MAX] = {10, 100, 200};
int *ptr;
// 指针中的数组地址
ptr = var;
for (int i = 0; i < MAX; i++) {
cout << "Address of var[" << i << "] = ";
cout << ptr << endl;
cout << "Value of var[" << i << "] = ";
cout << *ptr << endl;
// 移动到下一个位置
ptr++;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 递减一个指针
同样,对指针进行递减运算,即把值减去其数据类型的字节数,如下:
#include <iostream>
using namespace std;
const int MAX = 3;
int main() {
int var[MAX] = {10, 100, 200};
int *ptr;
// 指针中最后一个元素的地址
ptr = &var[MAX - 1];
for (int i = MAX; i > 0; i--) {
cout << "Address of var[" << i << "] = ";
cout << ptr << endl;
cout << "Value of var [" << i << "] = ";
cout << *ptr << endl;
// 移动到下一个位置
ptr--;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 指针的比较
指针可以用关系运算符进行比较,如==
、<
和>
,如果p1
和p2
指向两个相关的变量,比如同一个数组中的不同元素,则可对p1
和p2
进行大小比较,下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址&var[MAX - 1]
,则把变量指针进行递增:
#include <iostream>
using namespace std;
const int MAX = 3;
int main() {
int var[MAX] = {10, 100, 200};
int *ptr;
// 指针中第一个元素的地址
ptr = var;
int i = 0;
while (ptr <= &var[MAX - 1]) {
cout << "Address of var[" << i << "] = ";
cout << ptr << endl;
cout << "Value of var[" << i << "] = ";
cout << *ptr << endl;
// 指向上一个位置
ptr++;
i++;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# C++ 指针 vs 数组
指针和数组是密切相关的,事实上,指针和数组在很多情况下是可以互换的,如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组,如下程序:
#include <iostream>
using namespace std;
const int MAX = 3;
int main() {
int var[MAX] = {10, 100, 200};
int *ptr;
// 指针中的数组地址
ptr = var;
for (int i = 0; i < MAX; i++) {
cout << "Address of var[" << i << "] = ";
cout << ptr << endl;
cout << "Value of var[" << i << "] = ";
cout << *ptr << endl;
// 移动到下一个位置
ptr++;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
然而,指针和数组并不是完全互换的,如下:
#include <iostream>
using namespace std;
const int MAX = 3;
int main() {
int var[MAX] = {10, 100, 200};
for (int i = 0; i < MAX; i++) {
*var = i; // 这是正确的语法
var++; // 这是不正确的
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
把指针运算符*
应用到var
上是完全可以的,但修改var
的值是非法的,这是因为var
是一个指向数组开头的常量,不能作为左值,由于一个数组名对应一个指针常量,只要不改变数组的值,仍然可以用指针形式的表达式,例如,把var[2]
赋值为500: *(var + 2) = 500
# C++ 指针数组
#include <iostream>
using namespace std;
const int MAX = 3;
int main ()
{
int var[MAX] = {10, 100, 200};
for (int i = 0; i < MAX; i++)
{
cout << "Value of var[" << i << "] = ";
cout << var[i] << endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
不过,还可能有一种情况,想要让数组存储指向int
或char
或其他数据类型的指针,下面是一个指向整数的指针数组的声明: int *ptr[MAX];
在这里,把ptr
声明为一个数组,由MAX
个整数指针组成,因此ptr
中的每个元素,都是一个指向int
值的指针,如下:
#include <iostream>
using namespace std;
const int MAX = 3;
int main ()
{
int var[MAX] = {10, 100, 200};
int *ptr[MAX];
for (int i = 0; i < MAX; i++)
{
ptr[i] = &var[i]; // 赋值为整数的地址
}
for (int i = 0; i < MAX; i++)
{
cout << "Value of var[" << i << "] = ";
cout << *ptr[i] << endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
也可以用一个指向字符的指针数组来存储一个字符串列表,如下:
#include <iostream>
using namespace std;
const int MAX = 4;
int main ()
{
const char *names[MAX] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};
for (int i = 0; i < MAX; i++)
{
cout << "Value of names[" << i << "] = ";
cout << names[i] << endl;
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# C++ 指向指针的指针
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。指针的指针就是将指针的地址存放在另一个指针里面。通常,一个指针包含一个变量的地址,当定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号,如下,声明了一个指向int
类型指针的指针: int **var;
当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下:
#include <iostream>
using namespace std;
int main() {
int var;
int *ptr;
int **pptr;
var = 3000;
// 获取 var 的地址
ptr = &var;
// 使用运算符 & 获取 ptr 的地址
pptr = &ptr;
// 使用 pptr 获取值
cout << "var 值为: " << var << endl;
cout << "*ptr 值为: " << *ptr << endl;
cout << "**pptr 值为: " << **pptr << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# C++ 传递指针给函数
C++允许传递指针给函数,只需要简单地声明函数参数为指针类型即可,如下,可以传递一个无符号的long
型指针给函数,并在函数内改变这个值:
#include <iostream>
#include <ctime>
using namespace std;
// 在写函数时应习惯性的先声明函数,然后在定义函数
void getSeconds(unsigned long *par);
int main() {
unsigned long sec;
getSeconds(&sec);
// 输出实际值
cout << "Number of seconds: " << sec << endl;
return 0;
}
void getSeconds(unsigned long *par) {
// 获取当前的秒数
*par = time(NULL);
return;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
能接受指针作为参数的函数,也能接受数组作为参数,如下:
#include <iostream>
using namespace std;
// 函数声明
double getAverage(int *arr, int size);
int main() {
// 带有 5 个元素的整型数组
int balance[5] = {1000, 2, 3, 17, 50};
double avg;
// 传递一个指向数组的指针作为参数
avg = getAverage(balance, 5);
// 输出返回值
cout << "Average value is: " << avg << endl;
return 0;
}
double getAverage(int *arr, int size) {
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i) {
sum += arr[i];
}
avg = double(sum) / size;
return avg;
}
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
# C++ 从函数返回指针
在上一章中,已经了解了C++中如何从函数返回数组,类似,C++允许从函数返回指针,为了做到这点,必须声明一个返回指针的函数,如下:
int * myFunction() {
// ...
}
2
3
C++不支持在函数外返回局部变量的地址,除非定义局部变量为static
变量。
如下程序,会生成10个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下:
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
// 要生成和返回随机数的函数
int * getRandom() {
static int r[10];
// 设置种子
srand((unsigned) time(NULL));
for (int i = 0; i < 10; ++i) {
r[i] = rand();
cout << r[i] << endl;
}
return r;
}
// 要调用上面定义函数的主函数
int main() {
// 一个指向整数的指针
int *p;
p = getRandom();
for (int i = 0; i < 10; i++) {
cout << "*(p + " << i << ") : ";
cout << *(p + i) << endl;
}
return 0;
}
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
# C++ 引用
引用变量是一个别名,也就是,它是某个已存在变量的另一个名字,一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
# C++ 引用 vs 指针
引用很容易与指针混淆,它们之间有三个主要的不同:
- 不存在空引用,引用必须连接到一块合法的内存
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象,指针可以在任何时候指向到另一个对象
- 引用必须在创建时被初始化,指针可以在任何时间被初始化
# C++ 中创建引用
变量名称是变量附属在内存位置中的标签,可以把引用当成是变量附属在内存位置中的第二个标签,因此,可以通过原始变量名称或引用来访问变量的内容,如:
int i = 17;
可以为i
声明引用变量,如下:
int& r = i;
double& s = d;
2
在这些声明中,&
读作引用,因此,第一个声明可以读作r是一个初始化为i的整型引用,第二个声明可以读作s是一个初始化为d的double型引用,如下:
#include <iostream>
using namespace std;
int main() {
// 声明简单的变量
int i;
double d;
// 声明引用变量
int& r = i;
double& s = d;
i = 5;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
d = 11.7;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
引用通常用于函数参数列表和函数返回值,下面列出了C++程序员必须清楚的两个与C++引用相关的重要概念:
# 把引用作为参数
下面的实例使用了引用来实现引用调用函数:
#include <iostream>
using namespace std;
// 函数声明
void swap(int& x, int& y);
int main() {
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前, a的值: " << a << endl;
cout << "交换前, b的值: " << b << endl;
// 调用函数来交换值
swap(a, b);
cout << "交换后, a的值: " << a << endl;
cout << "交换后, b的值: " << b << endl;
return 0;
}
// 函数定义
void swap(int& x, int& y) {
int temp;
temp = x; // 保存地址 x 的值
x = y; // 把 y 赋值 给 x
y = temp; // 把 x 赋值给 y
return;
}
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
# 把引用作为返回值
通过使用引用来替代指针,会使C++程序更容易阅读和维护,C++函数可以返回一个引用,方式与返回一个指针类似,当函数返回一个引用时,则返回一个指向返回值的隐式指针,这样,函数就可以放在赋值语句的左边,如下:
#include <iostream>
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i) {
double& ref = vals[i];
return ref; // 返回第 i 个元素的引用,ref 是一个引用变量, ref 引用 vals[i]
}
// 要调用上面定义函数的主函数
int main() {
cout << "改变前的值" << endl;
for (int i = 0; i < 5; i++) {
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
cout << "改变后的值" << endl;
for (int i = 0; i < 5; i++) {
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
}
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
当返回一个引用时,要注意被引用的对象不能超出作用域,所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
int& func() {
int q;
// ! return q; // 在编译时发生错误
static int x;
return x; // 安全, x 在函数作用域外依然有效
}
2
3
4
5
6
# C++ 日期 & 时间
C++标准库没有提供所谓的日期类型,C++继承了C语言用于日期和时间操作的结构和函数,为了使用日期和时间相关的函数和结构,需要在C++程序中引用<ctime>
头文件。
有四个与时间相关的类型: clock_t
、time_t
、size_t
、tm
。类型clock_t
、size_t
和time_t
能够把系统时间和日期表示为某种整数,结构类型tm
把日期和时间以C
结构的形式保存,tm
结构的定义如下:
struct tm {
int tm_sec; // 秒, 正常范围从 0 到 59, 但允许至 61
int tm_min; // 分, 范围从 0 到 59
int tm_hour; // 小时, 范围从 0 到 23
int tm_mday; // 一月中的第几天, 范围从 1 到 31
int tm_mon; // 月, 范围从 0 到 11
int tm_year; // 自 1900 年起的年数
int tm_wday; // 一周中的第几天, 范围从 0 到 6, 从星期日算起
int tm_yday; // 一年中的第几天, 范围从 0 到 365, 从 1 月 1 日算起
int tm_isdst; // 夏令时
}
2
3
4
5
6
7
8
9
10
11
下面是C/C++中关于日期和时间的重要函数,所有这些函数都是C/C++标准库的组成部分:
序号 | 函数 | 描述 |
---|---|---|
1 | time_t time(time_t* time); | 该函数返回系统的当前日历时间,自1970年1月1日以来经过的秒数,如果系统没有时间,则返回-1。 |
2 | char* ctime(const time_t* time); | 该返回一个表示当地时间的字符串指针,字符串形式day month year hours:minutes:seconds year\n\0 |
3 | struct tm* localtime(const time_t* time); | 该函数返回一个指向表示本地时间的tm 结构的指针 |
4 | clock_t clock(void); | 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间,如果时间不可用,则返回-1 |
5 | char* asctime(const struct tm* time); | 该函数返回一个指向字符串的指针,字符串包含了time 所指向结构中存储的信息,返回形式为: day month date hours:minutes:seconds year\n\0 |
6 | struct tm* gmtime(const time_t* time); | 该函数返回一个指向time 的指针,time 为tm 结构,用协调世界时(UTC)也被称为格林尼治时间(GMT)表示 |
7 | time_t mktime(struct tm* time); | 该函数返回日历时间,相当于time 所指向结构中存储的时间 |
8 | double difftime(time_t time2, time_t time1); | 该函数返回time1 和time2 之间相差的秒数 |
9 | size_t strftime(); | 该函数可用于格式化日期和时间为指定的格式 |
# 当前日期和时间
下面程序获取当前系统的日期和时间,包括本地时间和协调世界时(UTC)。
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// 基于当前系统的当前日期/时间
time_t now = time(0);
// 把 now 转换为字符串形式
char* dt = ctime(&now);
cout << "本地日期和时间: " << dt << endl;
// 把 now 转换为 tm 结构
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "UTC 日期和时间: " << dt << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用结构 tm 格式化时间
tm
结构在C/C++中处理日期和时间相关的操作时,显得尤为重要,tm
结构以C
结构的形式保存日期和时间,大多数与时间相关的函数都使用了tm
结构,下面的实例使用了tm
结构和各种与日期和时间相关的函数,->
运算符来访问结构成员
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// 基于当前系统的当前日期/时间
time_t now = time(0);
cout << "1970 到目前经过秒数: " << now << endl;
tm *ltm = localtime(&now);
// 输出 tm 结构的各个组成部分
cout << "年: " << 1900 + ltm -> tm_year << endl;
cout << "月: " << 1 + ltm -> tm_mon << endl;
cout << "日: " << ltm -> tm_mday << endl;
cout << "时间: " << ltm -> tm_hour << ":";
cout << ltm -> tm_min << ":";
cout << ltm -> tm_sec << endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# C++ 基本的输入输出
C++ 标准库提供了一组丰富的输入/输出功能,C++ 的 I/O 发生在流中,流是字节序列,如果字节流是从设备(如键盘、磁盘驱动器、网络连接等)流向内存,这叫做输入操作。如果字节流是从内存流向设备(如显示屏、打印机、磁盘驱动器、网络连接等),这叫做输出操作。
# I/O 库头文件
下列的头文件在C++编程中很重要
头文件 | 函数和描述 |
---|---|
<iostream> | 该文件定义了cin 、cout 、cerr 和clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流 |
<iomanip> | 该文件通过所谓的参数化的流操纵器(比如setw 和setprecision ),来声明对执行标准化I/O有用的服务 |
fstream | 该文件为用户控制的文件处理声明服务 |
# 标准输出流(cout)
预定义的对象cout
是iostream
类的一个实例,cout
对象连接到标准输出设备,通常是显示屏。cout
是与流插入运算符<<
结合使用的,如下:
#include <iostream>
using namespace std;
int main() {
char str[] = "Hello C++";
cout << "Value of str is : " << str << endl;
}
2
3
4
5
6
7
8
C++ 编译器根据要输出变量的数据类型,选择合适的流插入运算符来显示值,<<
运算符被重载来输出内置类型(整型、浮点型、double
型、字符串和指针)的数据项。流插入运算符<<
在一个语句中可以多次使用。
# 标准输入流(cin)
预定义的对象cin
是iostream
类的一个实例,cin
对象附属到标准输入设备,通常是键盘,cin
是与流提取运算符>>
结合使用,如下:
#include <iostream>
using namespace std;
int main() {
char name[50];
cout << "请输入您的名称: ";
cin >> name;
cout << "您的名称是: " << name << endl;
}
2
3
4
5
6
7
8
9
10
11
C++ 编译器根据要输入值的数据类型,选择合适的流提取运算符来提取值,并把它存储在给定的变量中,流提取运算符>>
在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句:
cin >> name >> age;
相当于下面两个语句:
cin >> name;
cin >> age;
2
# 标准错误流(cerr)
预定义的对象cerr
是iostream
类的一个实例,cerr
对象附属到标准输出设备,通常也是显示屏,但是cerr
对象是非缓冲的,且每个流插入到cerr
都会立即输出。cerr
也是与流插入运算符<<
结合使用的,如下:
#include <iostream>
using namespace std;
int main() {
char str[] = "Unable to read ...";
cerr << "Error message: " << str << endl;
}
2
3
4
5
6
7
8
# 标准日志流(clog)
预定义的对象clog
是iostream
类的一个实例,clog
对象附属到标准输出设备,通常也是显示屏,但是clog
对象是缓冲的,这意味这每个流插入到clog
都会先存储在缓冲区,直到缓冲填满或者缓冲区刷新时才会输出,clog
也是与流插入运算符<<
结合使用的,如下:
#include <iostream>
using namespace std;
int main() {
char str[] = "unable to read ...";
clog << "Error message: " << str << endl;
}
2
3
4
5
6
7
8
# C++ 数据结构
C/C++ 数组允许定义可存储相同类型数据项的变量,但是结构是C++中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。
结构用于表示一条记录,比如想跟踪图书馆中书本的状态,可能就需要跟踪每本书的下列属性:
- Title: 标题
- Author: 作者
- Subject: 类目
- Book ID: 书的 ID
# 定义结构
为了定义结构,必须使用struct
语句,struct
语句定义了一个包含多个成员的新的数据类型,格式如下:
struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
...
} object_names;
2
3
4
5
6
type_name
是结构体类型的名称,member_type1 member_name1
是标准的变量定义,比如int i;
或者float f;
或者其他有效的变量定义。在结构定义的末尾,最后一个分号之前,可以指定一个或多个结构变量,这是可选的。下面是声明一个结构体类型Books
,变量为book
:
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
2
3
4
5
6
# 访问结构成员
为了访问结构的成员,使用成员访问运算符(.),成员访问运算符是结构变量名称和要访问的结构成员之间的一个句号,用法如下:
#include <iostream>
#include <cstring>
using namespace std;
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
Books Book1;
Books Book2;
strcpy(Book1.title, "C++ 教程");
strcpy(Book1.author, "Runoob");
strcpy(Book1.subject, "编程语言");
Book1.book_id = 12345;
strcpy(Book2.title, "CSS 教程");
strcpy(Book2.author, "Runoob");
strcpy(Book2.subject, "前端技术");
Book2.book_id = 12346;
cout << "第一本书标题: " << Book1.title << endl;
cout << "第一本书作者: " << Book1.author << endl;
cout << "第一本书类目: " << Book1.subject << endl;
cout << "第一本书 ID: " << Book1.book_id << endl;
cout << "第二本书标题: " << Book2.title << endl;
cout << "第二本书作者: " << Book2.author << endl;
cout << "第二本书类目: " << Book2.subject << endl;
cout << "第二本书 ID: " << Book2.book_id << endl;
return 0;
}
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
# 结构作为函数参数
可以把结构作为函数参数,传参方式与其他类型的变量或指针类似,可以使用上面实例中的方式来访问结构变量:
#include <iostream>
#include <cstring>
using namespace std;
void printBook(struct Books book);
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
Books Book1;
Books Book2;
strcpy(Book1.title, "C++ 教程");
strcpy(Book1.author, "Runoob");
strcpy(Book1.subject, "编程语言");
Book1.book_id = 12345;
strcpy(Book2.title, "CSS 教程");
strcpy(Book2.author, "Runoob");
strcpy(Book2.subject, "前端技术");
Book2.book_id = 12346;
printBook(Book1);
printBook(Book2);
return 0;
}
void printBook(struct Books book) {
cout << "书标题: " << book.title << endl;
cout << "书作者: " << book.author << endl;
cout << "书类目: " << book.subject << endl;
cout << "书 ID:" << book.book_id << endl;
}
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
# 指向结构的指针
可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下:
struct Books *struct_pointer;
可以在上述定义的指针变量中存储结构变量的地址,为了查找结构变量的地址,需把&
运算符放在结构名称的前面,如下:
struct_pointer = &Book1;
为了使用指向该结构的指针访问结构的成员,需使用->
运算符,如下:
struct_pointer->title;
#include <iostream>
#include <cstring>
using namespace std;
void printBook(struct Books *book);
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
Books Book1;
Books Book2;
strcpy(Book1.title, "C++ 教程");
strcpy(Book1.author, "Runoob");
strcpy(Book1.subject, "编程语言");
Book1.book_id = 12345;
strcpy(Book2.title, "CSS 教程");
strcpy(Book2.author, "Runoob");
strcpy(Book2.subject, "前端技术");
Book2.book_id = 12346;
printBook(&Book1);
printBook(&Book2);
return 0;
}
void printBook(struct Books *book) {
cout << "书标题: " << book->title << endl;
cout << "书作者: " << book->author << endl;
cout << "书类目: " << book->subject << endl;
cout << "书 ID:" << book->book_id << endl;
}
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
# typedef 关键字
下面是一种更简单的定义结构的方式,可以为创建的类型取一个别名,如:
typedef struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} Books;
2
3
4
5
6
现在,可以直接使用Books
来定义Books
类型的变量,而不再需要使用struct
关键字,如下:
Books Book1, Book2;
还可以使用typedef
关键字来定义非结构类型,如下:
typedef long int *pint32;
// x, y, z 都是指向长整型 long int 的指针
pint32 x, y, z;
2
3
4
# C++ vector 容器
C++重点 vector 是一种序列容器,它允许在运行时动态地插入和删除元素。vector 是基于数组的数据结构,但是它可以自动管理内存,这意味着不需要手动分配和释放内存。与C++数组相比,vector具有更多的灵活性和功能,这使其称为C++中常用的数据结构之一,vector是C++标准模板库(STL)的一部分,提供了灵活的接口和高效的操作。
基本特性:
- 动态大小: vector的大小可以根据需要自动增长和缩小。
- 连续存储: vector中的元素在内存中是连续存储的,这使得访问元素非常快速。
- 可迭代: vector可以被迭代,可以使用循环(如
for
)来访问元素。 - 元素类型: vector可以存储任何类型的元素,包括内置内省、对象、指针等。
使用场景:
- 当需要一个可以动态增长和缩小的数组时
- 当需要频繁地在序列的末尾添加或移除元素时
- 当需要一个可以高效随机访问元素的容器时
# 创建Vector
创建一个vector可以像创建其他变量一样简单:
std::vector<int> myVector; // 创建一个存储整数的空 vector
这将创建一个空的整数向量,也可以在创建时指定初始大小和初始值:
std::vector<int> myVector(5); // 创建一个包含 5 个整数的 vector,每个值都默认为 0
std::vector<int> myVector(5, 10); // 创建一个包含 5 个整数的 vector,每个值都为 10
2
或
std::vector<int> vec; // 默认初始化一个空的 vector
std::vector<int> vec2 = {1, 2, 3, 4}; // 初始化一个包含元素的 vector
2
# 添加元素
可以使用push_back
方法向vector中添加元素:
myVector.push_back(7); // 将整数 7 添加到 vector 的末尾
# 访问元素
可以使用下标操作符[]
或at()
方法访问vector中的元素:
int x = myVector[0]; // 获取第一个元素
int y = myVector.at(1); // 获取第二个元素
2
# 获取大小
可以使用size()
方法获取vector中元素的数量:
int size = myVector.size(); // 获取 vector 中的元素数量
# 迭代访问
可以使用迭代器遍历vector中的元素:
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
std::cout << *it << " ";
}
2
3
或者使用范围循环:
for (int element : myVector) {
std::cout << element << " ";
}
2
3
# 删除元素
可以使用erase()
方法删除vector中的元素:
myVector.erase(myVector.begin() + 2); // 删除第三个元素
# 清空 Vector
可以使用clear()
方法清空vector中的所有元素:
myVector.clear(); // 清空 vector
# 实例
以下是一个完整的使用实例,包括创建vector、添加元素、访问元素以及输出结果的代码:
#include <iostream>
#include <vector>
int main() {
// 创建一个空的整数向量
std::vector<int> myVector;
// 添加元素到向量中
myVector.push_back(3);
myVector.push_back(7);
myVector.push_back(11);
myVector.push_back(5);
// 访问向量中的元素并输出
std::cout << "Elements in the vector: ";
for (int element : myVector) {
std::cout << element << " ";
}
std::cout << std::endl;
// 访问向量中的第一个元素并输出
std::cout << "First element: " << myVector[0] << std::endl;
// 访问向量中的第二个元素并输出
std::cout << "Size of the vector: " << myVector.size() << std::endl;
// 删除向量中的第三个元素
myVector.erase(myVector.begin() + 2);
// 输出删除元素后的向量
std::cout << "Elements in the vector after erasing: ";
for (int element : myVector)
{
std::cout << element << " ";
}
std::cout << std::endl;
// 清空向量并输出
myVector.clear();
std::cout << "Size of the vector after clearing: " << myVector.size() << std::endl;
return 0;
}
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
- C++ 简介
- 面向对象程序设计
- 标准库
- ANSI 标准
- 学习 C++
- C++ 的使用
- 标准化
- C++ 环境设置
- 本地环境设置
- 文本编辑器
- C++ 编译器
- 安装 GNU 的 C/C++ 编译器
- 使用 Visual Studio (Graphical Interface) 编译
- g++ 应用说明
- C++ 基本语法
- C++ 程序结构
- 编译 & 执行 C++ 程序
- C++ 中的分号 & 语句块
- C++ 标识符
- C++ 关键字
- 三字符组
- C++ 中的空格
- C++ 注释
- C++ 数据类型
- 基本的内置类型
- typedef 声明
- 枚举类型
- 类型转换
- C++ 变量类型
- C++ 中的变量定义
- C++ 中的变量声明
- C++ 中的左值(Lvalues)和右值(Rvalues)
- C++ 变量作用域
- 局部变量
- 全局变量
- 初始化局部变量和全局变量
- 类作用域
- C++ 常量
- 整数常量
- 浮点常量
- 布尔常量
- 字符常量
- 字符串常量
- 定义常量
- C++ 修饰符类型
- C++ 中的类型限定符
- C++ 存储类
- auto 存储类
- register 存储类
- static 存储类
- extern 存储类
- mutable 存储类
- thread_local 存储类
- C++ 运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 杂项运算符
- C++ 中的运算符优先级
- C++ 循环
- 循环类型
- 循环控制语句
- 无限循环
- C++ 判断
- 判断语句
- ? :运算符
- C++ 函数
- 定义函数
- 函数声明
- 调用函数
- 函数参数
- 参数的默认值
- Lambda 函数与表达式
- C++ 数字
- C++ 定义数字
- C++ 数字运算
- C++ 随机数
- C++ 数组
- 声明数组
- 初始化数组
- 访问数组元素
- C++ 中数组详解
- C++ 字符串
- C 风格字符串
- C++ 中的 string 类
- C++ 指针
- 什么是指针
- C++ 中使用指针
- C++ 指针详解
- C++ 引用
- C++ 引用 vs 指针
- C++ 中创建引用
- C++ 日期 & 时间
- 当前日期和时间
- 使用结构 tm 格式化时间
- C++ 基本的输入输出
- I/O 库头文件
- 标准输出流(cout)
- 标准输入流(cin)
- 标准错误流(cerr)
- 标准日志流(clog)
- C++ 数据结构
- 定义结构
- 访问结构成员
- 结构作为函数参数
- 指向结构的指针
- typedef 关键字
- C++ vector 容器
- 创建Vector
- 添加元素
- 访问元素
- 获取大小
- 迭代访问
- 删除元素
- 清空 Vector
- 实例