C++语法基础

C++环境安装和基础知识

1:C++环境安装与配置

我使用的编译器是Clion,解释器配置的是Dev C++自带的解释器,Clion安装见下文。

Clion的安装和配置(C/C++开发神器)_王菜鸟的博客-CSDN博客_clion

Dec C++安装好之后安装目录会有这个子目录

在这里插入图片描述

Clion配置好之后就可以生成运行C++项目了,运行见下帖。

(8条消息) 【Clion】Clion如何创建并运行C/C++文件_是空空吖的博客-CSDN博客_clion运行c++

2:C++基础语法

本人也是C++小白,但是有一丢丢其他语言类似于python和java的基础,所以C++基础语法也得过一下。为了快点我就直接看菜鸟教程了(C++网课太长了简直是折磨)

2.1:C++ 基本语法

(1)C++程序构成

C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。

  1. 对象:对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。
  2. 类:类可以定义为描述对象 行为/状态 的 模板/蓝图。
  3. 方法:从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。
  4. 即时变量:每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。

在 C++ 中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。

(2)C++程序结构
#include <iostream>
using namespace std;
// main() 是程序开始执行的地方
int main()
{
   cout << "Hello World"; // 输出 Hello World
   return 0;
}
  1. C++ 语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。上面这段程序中,包含了头文件
  2. 下一行 using namespace std; 告诉编译器使用 std 命名空间。命名空间是 C++ 中一个相对新的概念。
  3. 下一行 // main() 是程序开始执行的地方 是一个单行注释。单行注释以 // 开头,在行末结束。
  4. 下一行 int main() 是主函数,程序从这里开始执行。
  5. 下一行 cout << “Hello World”; 会在屏幕上显示消息 “Hello World”。
  6. 下一行 return 0; 终止 main( )函数,并向调用进程返回值 0。
(3)C++关键字

C++和其他语言一样,关键字不可做变量名,关键字很多,具体关键字及其对应的意思见下文。

C++ 的关键字(保留字)完整介绍 | 菜鸟教程 (runoob.com)

2.2:C++的注释

C++ 支持单行注释和多行注释。注释中的所有字符会被 C++ 编译器忽略。

C++ 注释一般有两种:

  1. // - 一般用于单行注释。
  2. /* … */ - 一般用于多行注释。

2.3:C++ 数据类型

使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。

您可能需要存储各种数据类型(比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等)的信息,操作系统会根据变量的数据类型,来分配内存和决定在保留内存中存储什么

(1)输出数据类型大小

sizeof() 运算符用来获取各种数据类型的大小。

(2)数据类型重命名

您可以使用 typedef 为一个已有的类型取一个新的名字。下面是使用 typedef 定义一个新类型的语法:

typedef type newname; 

例如,下面的语句会告诉编译器,feet 是 int 的另一个名称:

typedef int feet;

现在,下面的声明是完全合法的,它创建了一个整型变量 distance:

feet distance;

2.4:C++变量作用域

(1)变量分类

作用域是程序的一个区域,一般来说有三个地方可以定义变量:

  • 在函数或一个代码块内部声明的变量,称为局部变量
  • 在函数参数的定义中声明的变量,称为形式参数
  • 在所有函数外部声明的变量,称为全局变量
(2)初始化局部变量和全局变量

当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:

数据类型 初始化默认值
int 0
char ‘\0’
float 0
double 0
pointer NULL

2.5:C++ 常量

(1)整数常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

(2)字符串常量

字符串字面值或常量是括在双引号 “” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。

您可以使用 \ 做分隔符,把一个很长的字符串常量进行分行。

string greeting2 = "hello, \
					runoob";
// hello, runoob
(3)定义常量*

在 C++ 中,有两种简单的定义常量的方式:

  • 使用 #define 预处理器。
  • 使用 const 关键字。
(4)使用 #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;
   // 输出50然后换行
}

在这里插入图片描述

(5)const 关键字

您可以使用 const 前缀声明指定类型的常量(就是加了const,这个变量名就代表一个常量,且不可更改),如下所示:

在这里插入图片描述

2.6:C++修饰符

(1)C++修饰符

C++ 允许在 char、int 和 double 数据类型前放置修饰符。修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求。

下面列出了数据类型修饰符:

  1. signed:意思为有符号的,也就是第一个位代表正负,剩余的代表大小,例如:signed int 大小区间为-128-127
  2. unsigned:unsigned意思为无符号的,所有的位都为大小,没有负数,例如:unsigned int 大小区间为:0-255
  3. long
  4. short

修饰符 signed、unsigned、long 和 short 可应用于整型,signedunsigned 可应用于字符型,long 可应用于双精度型。

修饰符 signedunsigned 也可以作为 longshort 修饰符的前缀。例如:unsigned long int

(2)C++ 中的类型限定符

类型限定符提供了变量的额外信息。

限定符 含义
const const 类型的对象在程序执行期间不能被修改改变。
volatile 修饰符 volatile 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。
restrict restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。

2.7:C++存储类(***重要)

存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:

  • auto
  • register
  • static
  • extern
  • mutable
  • thread_local (C++11)
(1)auto 存储类
  1. 声明变量时根据初始化表达式自动推断该变量的类型,类似于javascript中的let,var之类的,简直就是声明变量时的赖子
  2. 声明函数时函数返回值的占位符。

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)register 存储类

register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。

register int  miles;

寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

(3)static 存储类

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。

(4)extern 存储类

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 ‘extern’ 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候

C++ 存储类 | 菜鸟教程 (runoob.com)

(5)mutable 存储类

mutable 说明符仅适用于类的对象,这将在本教程的最后进行讲解。它允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。

(6)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.8:C++ 运算符

(1)算术运算符
运算符 描述
+ 把两个操作数相加
- 从第一个操作数中减去第二个操作数
* 把两个操作数相乘
/ 分子除以分母
% 取模运算符,整除后的余数
++ 自增运算符,整数值增加 1
自减运算符,整数值减少 1
(2)关系运算符
运算符 描述
== 检查两个操作数的值是否相等,如果相等则条件为真。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。
(3)逻辑运算符
运算符 描述
&& 称为逻辑与运算符。如果两个操作数都 true,则条件为 true。
|| 称为逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态,如果条件为 true 则逻辑非运算符将使其为 false。
(4)位运算符

位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:

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
(5)赋值运算符
= 简单的赋值运算符,把右边操作数的值赋给左边操作数
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数
/= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数
<<= 左移且赋值运算符
>>= 右移且赋值运算符
&= 按位与且赋值运算符
^= 按位异或且赋值运算符
|= 按位或且赋值运算符
(6)杂项运算符*
运算符 描述
sizeof sizeof 运算符 返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
, 逗号运算符 会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.(点)和 ->(箭头) 成员运算符 用于引用类、结构和共用体的成员。
Cast 强制转换运算符 把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
& 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
* 指针运算符 *指向一个变量。例如,*var; 将指向变量 var。

2.9:C++ 循环

(1)循环类型
循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环 多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环 您可以在 while、for 或 do…while 循环内使用一个或多个循环。
(2)循环控制语句
控制语句 描述
break 语句 终止 loopswitch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。
continue 语句 引起循环跳过主体的剩余部分,立即重新开始测试条件。
(3)无限循环

如果条件永远不为假,则循环将变成无限循环。for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。

当条件表达式不存在时,它被假设为真。您也可以设置一个初始值和增量表达式,但是一般情况下,C++ 程序员偏向于使用 for( ; ; ) 结构来表示一个无限循环。

**注意:**您可以按 Ctrl + C 键终止一个无限循环。

2.10:C++ 判断

(1)判断语句
语句 描述
if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if…else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句 您可以在一个 ifelse if 语句内使用另一个 ifelse if 语句。
switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句。

举个栗子:

嵌套switch语句

#include <iostream>
using namespace std;

int main ()
{
    // 局部变量声明
    int a = 100;
    int b = 200;

    switch(a) {
        case 100:
            cout << "a = 100" << endl;
            switch(b) {
                case 100:
                    cout << "b = 100" << endl;
                    break;
                case 200:
                    cout << "b = 200" << endl;
                    break;
            }
        case 200:
            cout << "a = 200" << endl;
    }
    cout << "a 的准确值是 " << a << endl;
    cout << "b 的准确值是 " << b << endl;

    return 0;
}

在这里插入图片描述

多数出了一个a=200是因为前面的case语句块内部最后没加break。

(2)? : 运算符

条件运算符 **? :,**可以用来替代 if…else 语句。它的一般形式如下:

Exp1 ? True_Exp2 : False_Exp3;

其中,Exp1、True_Exp2和 False_Exp3是表达式。请注意,冒号的使用和位置。

? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 True_Exp2的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 False_Exp3的值,结果即为整个 ? 表达式的值。

2.11:C++ 函数基础

函数是一组一起执行一个任务的语句。每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。

(1)定义函数

C++ 中的函数定义的一般形式如下:

return_type function_name( parameter list ) {
    body of the function 
}

在 C++ 中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:

  • **返回类型:**一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void
  • **函数名称:**这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • **参数:**参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • **函数主体:**函数主体包含一组定义函数执行任务的语句。
(2)函数声明

函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

函数声明包括以下几个部分:

return_type function_name( parameter list );

比如以下是函数声明:

int max(int num1, int num2);

在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:

int max(int, int);

2.12:函数的调用!!!!

(1)调用函数

创建 C++ 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。

当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。

调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。例如:

#include <iostream>
using namespace std;

// 函数声明
int max(int, int);

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)函数参数!!!!

如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数

形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。(这个理解一下,形式参数在退出函数时被销毁,这个函数不会对参数进行任何改变的报错)

默认情况下,C++ 使用传值调用来传递参数。一般来说,这意味着函数内的代码不能改变用于调用函数的参数。之前提到的实例,调用 max() 函数时,使用了相同的方法。

函数参数有三种

  1. 传值调用
  2. 指针调用!!!!!!!!!!!!
  3. 引用调用!!!!!!!!!!!!

由于过于重要而且和java及python有较大区别,所以本文详细记录一下。

(3)传值调用

https://www.runoob.com/cplusplus/cpp-function-call-by-value.html

传值调用:该方法把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响

比如我们for 一个 exemple:

#include <iostream>
using namespace std;

// 函数声明
void swap(int, int);

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 a, int b)
{
    int temp;
    temp = a; /* 保存 x 的值 */
    a = b;    /* 把 y 赋值给 x */
    b = temp; /* 把 x 赋值给 y */
    cout << "交换后函数内部输出,a 的值:" << a << endl;
    cout << "交换后函数内部输出,b 的值:" << b << endl;
    return;
}

输出结果如下,你品,你细品。

在这里插入图片描述

(4)指针调用!!!

https://www.runoob.com/cplusplus/cpp-function-call-by-pointer.html

该方法把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

#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;
    /* 调用函数来交换值
     * &a 表示指向 a 的指针,即变量 a 的地址
     * &b 表示指向 b 的指针,即变量 b 的地址
     */
    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;
}

在这里插入图片描述

(5)引用调用!!!

https://www.runoob.com/cplusplus/cpp-function-call-by-reference.html

该方法把参数的引用赋值给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

按引用传递值,参数引用被传递给函数,就像传递其他值给函数一样。因此相应地,在下面的函数 swap() 中,您需要声明函数参数为引用类型,该函数用于交换参数所指向的两个整数变量的值。

实例测试:

#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;
}

在这里插入图片描述

(6)参数的默认值

当您定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。

这是通过在函数定义中使用赋值运算符来为参数赋值的。调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值

#include <iostream>
using namespace std;

int sum(int a = 10, int b=20)
{
    return a + b;
}
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;
}

在这里插入图片描述

(7)Lambda 函数与表达式

2.13:C++ 数字

(1)C++ 数学运算

在 C++ 中,除了可以创建各种函数,还包含了各种有用的函数供您使用。这些函数写在标准 C 和 C++ 库中,叫做内置函数。您可以在程序中引用这些函数。

C++ 内置了丰富的数学函数,可对各种数字进行运算。下表列出了 C++ 中一些有用的内置的数学函数。

为了利用这些函数,您需要引用数学头文件

函数 &描述
double cos(double); 该函数返回弧度角(double 型)的余弦。
double sin(double); 该函数返回弧度角(double 型)的正弦。
double tan(double); 该函数返回弧度角(double 型)的正切。
double log(double); 该函数返回参数的自然对数。
double pow(double, double); 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。
double hypot(double, double); 该函数返回两个参数的平方总和的平方根
double sqrt(double); 该函数返回参数的平方根。
int abs(int); 该函数返回整数的绝对值。
double fabs(double); 该函数返回任意一个浮点数的绝对值。
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)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.14:C++ 数组

C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。

(1)声明数组

在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:

type arrayName [ arraySize ];

这叫做一维数组。arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C++ 数据类型。

例如,要声明一个类型为 double 的包含 10 个元素的数组 balance,声明语句如下:

double balance[10];

现在 balance 是一个可用的数组,可以容纳 10 个类型为 double 的数字。

(2)初始化数组

在 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 作为它们第一个元素的索引。

(3)访问数组元素

数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如:

double salary = balance[9];
#include <iostream>
using namespace std;
#include <iomanip>
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 << std::setw( 7 )<< j << std::setw( 13 ) << n[ j ] << endl;
    }
    return 0;
}

上面的程序使用了 setw() 函数 来格式化输出。当上面的代码被编译和执行时,它会产生下列结果:

在这里插入图片描述

2.15:C++多维数组

C++ 支持多维数组。多维数组声明的一般形式如下:

type name[size1][size2]...[sizeN];

例如,下面的声明创建了一个三维 5 . 10 . 4 整型数组:

int threedim[5][10][4];
(1)二维数组

多维数组最简单的形式是二维数组。一个二维数组,在本质上,是一个一维数组的列表。声明一个 x 行 y 列的二维整型数组,形式如下:

type arrayName [ x ][ y ];

其中,type 可以是任意有效的 C++ 数据类型,arrayName 是一个有效的 C++ 标识符。

一个二维数组可以被认为是一个带有 x 行和 y 列的表格。下面是一个二维数组,包含 3 行和 4 列:

因此,数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识的,其中 a 是数组名称,i 和 j 是唯一标识 a 中每个元素的下标。

(2)初始化二维数组

多维数组可以通过在括号内为每行指定值来进行初始化。下面是一个带有 3 行 4 列的数组。

int a[3][4] = {  
 {0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
 {4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
 {8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};

内部嵌套的括号是可选的,下面的初始化与上面是等同的:

int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
(3)访问二维数组元素

二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。例如:

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;
}

在这里插入图片描述

(4)C++ 传递数组给函数
方式 1

形式参数是一个指针:

void myFunction(int *param){
	代码块
}
方式 2

形式参数是一个已定义大小的数组:

void myFunction(int param[10]){
    代码块
}
方式 3

形式参数是一个未定义大小的数组:

void myFunction(int param[]){
    代码块
}

举个栗子

#include <iostream>
using namespace std;
// 函数声明
double getAverage(int arr[], int size);
int main ()
{
    // 带有 5 个元素的整型数组
    int balance[5] = {1000, 2, 3, 17, 50};
    // 传递一个指向数组的指针作为参数
    double avg = getAverage( balance, 5 ) ;
    // 输出返回值
    cout << "平均值是:" << 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;
}

在这里插入图片描述

(5)C++ 从函数返回数组

C++ 不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。

如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数,如下:

int * myFunction(){
    
}
#include <iostream>
using namespace std;
int * getRandom( )
{
    static int  r[10];
    for (int i = 0; i < 10; ++i){
        r[i] = i*i;
    }
    return &r[0];
}
// 要调用上面定义函数的主函数
int main ()
{
    // 一个指向整数的指针
    int *p ;
    p = getRandom();//p[0] p[0+1]
    p++;
    p++;
    for ( int i = -2; i < 8; i++ ){
        cout << "*(p + " << i << ") : "  << *(p + i) << endl;
    }
    return 0;
}

这个涉及指针的运用我录了个小视频

2.16:C++ 字符串

C++ 提供了以下两种类型的字符串表示形式:

  1. C 风格字符串
  2. C++ 引入的 string 类类型
(1)C 风格字符串

下面的声明和初始化创建了一个 RUNOOB 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 RUNOOB 的字符数多一个。

char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};

依据数组初始化规则,您可以把上面的语句写成以下语句:

char site[] = "RUNOOB";

以下是 C/C++ 中定义的字符串的内存表示:

其实,您不需要把 null 字符放在字符串常量的末尾。C++ 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。

#include <iostream>
using namespace std;

int main ()
{
    char site[7] = {'R', 'U', 'N', 'O', 'O', 'B'};
    cout << "菜鸟教程: ";
    cout << site << endl;
    return 0;
}

在这里插入图片描述

C++ 中有大量的函数用来操作以 null 结尾的字符串:

序号 函数 & 目的
strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。连接字符串也可以用 + 号
strlen(s1); 返回字符串 s1 的长度。
strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回值小于 0;反之返回值大于 0。
strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
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)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;
}

在这里插入图片描述

3:C++指针

3.1: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;
}

在这里插入图片描述

(1)什么是指针?

指针是一个变量,其值为另一个变量的地址

即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。

指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

(2)C++中使用指针

使用指针时会频繁进行以下几个操作:

  1. 定义一个指针变量
  2. 把变量地址赋值给指针
  3. 访问指针变量中可用地址的值

这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。

#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;
}

在这里插入图片描述

3.2:C++ Null 指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。

然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr)     /* 如果 ptr 非空,则完成 */
if(!ptr)    /* 如果 ptr 为空,则完成 */

因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。

3.3:C++ 指针的算术运算

指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-。

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。(int型变量占四个字节)

这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。 (字符型变量占一个字节)

(1)递增一个指针

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。

#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)递减一个指针
#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;
}

在这里插入图片描述

(3)指针的比较

指针可以用关系运算符进行比较,如 ==、< 和 >。如果 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;
}

在这里插入图片描述

3.4: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 << "var[" << i << "]的内存地址为 ";
        cout << ptr << endl;
        cout << "var[" << i << "] 的值为 ";
        cout << *ptr << endl;
        // 移动到下一个位置
        ptr++;
    }
    return 0;
}

在这里插入图片描述

把指针运算符 * 应用到 var 上是完全可以的,但修改 var 的值是非法的。这是因为 var 是一个指向数组开头的常量,不能作为做值。

由于一个数组名对应一个指针常量,只要不改变数组的值,仍然可以用指针形式的表达式。

3.5:C++ 指向指针的指针

C++ 允许指向指针的指针。

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。

指针的指针就是将指针的地址存放在另一个指针里面。

通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。

int **var;

这种指向指针的指针一般用于,比如一个函数main调用函数A处理自己的一个变量,那么函数A传入的参数就是指针,但是A处理太复杂了又封装了一个B需要也需要A变量,那么这个时候就需要用到指向指针的指针(一般不会用到)

#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;
}

在这里插入图片描述

3.6: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;
}

在这里插入图片描述

能接受指针作为参数的函数,也能接受数组作为参数。

#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;
}

在这里插入图片描述

的理解中指针做参数主要就是两个作用

  1. 首先想返回多个参数,但是c++里面return只能return一个,所以这个时候在调用函数时创建变量,并把这个变量的指针传进函数去做参数。这个时候函数内部对这个指针进行改变就直接改变调用函数时创建的变量了。
  2. 第二就是有的像数组没办法作为参数直接传入函数,那就创建一个数组指针传参数也是极好的

3.7:C++ 从函数返回指针

C++ 允许函数返回指针到局部变量、静态变量和动态内存分配。

C++ 允许您从函数返回指针。为了做到这点,您必须声明一个返回指针的函数,如下所示:

int * myFunction(){
	代码块
}

另外,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();
    }
    return r;
}

// 要调用上面定义函数的主函数
int main ()
{
    // 一个指向整数的指针
    int *p;
    p = getRandom();
    for ( int i = 0; i < 10; i++ ){
        cout << "*(p + " << i << ") : ";
        cout << *(p + i) << endl;
    }
    return 0;
}

在这里插入图片描述

4:C++引用

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。

4.1:C++ 引用 vs 指针

引用很容易与指针混淆,它们之间有三个主要的不同:

  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
  • 引用必须在创建时被初始化。指针可以在任何时间被初始化。

4.2:C++ 中创建引用

试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位置中的第二个标签。因此,您可以通过原始变量名称或引用来访问变量的内容。例如:

int i = 17;

我们可以为 i 声明引用变量,如下所示:

int&  r = i;
double& s = d;

在这些声明中,& 读作引用。因此,第一个声明可以读作 “r 是一个初始化为 i 的整型引用”,第二个声明可以读作 “s 是一个初始化为 d 的 double 型引用”。

#include <iostream>

using namespace std;

int main ()
{
    // 声明简单的变量
    int    i;
    double d = 10;
    // 声明引用变量
    int&    r = i;
    double& s = d;
    i = 5;
    cout << "Value of i : " << i << endl;
    cout << "Value of i reference : " << r  << endl;
    i = i+ s;
    cout << "Value of d : " << i << endl;
    cout << "Value of d reference : " << r  << endl;
    return 0;
}

在这里插入图片描述

4.3:把引用创建为参数

#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;
}

在这里插入图片描述

4.4:C++ 把引用作为返回值

通过使用引用来替代指针,会使 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;
    }
    return 0;
}

在这里插入图片描述

当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用 。

int& func() {
   int q;
   //! return q; // 在编译时发生错误
   static int x;
   return x;     // 安全,x 在函数作用域外依然是有效的
}

5:C++时间与日期

5.1:tm 类及时间函数

C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 头文件。

有四个与时间相关的类型:clock_t、time_t、size_ttm。类型 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; // 夏令时
};

下面是 C/C++ 中关于日期和时间的重要函数。所有这些函数都是 C/C++ 标准库的组成部分,您可以在 C++ 标准库中查看一下各个函数的细节。

序号 函数 & 描述
1 该函数返回系统的当前日历时间,自 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(); 该函数可用于格式化日期和时间为指定的格式。

5.2:当前日期和时间

下面的实例获取当前系统的日期和时间,包括本地时间和协调世界时(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;
}

在这里插入图片描述

5.3:使用结构 tm 格式化时间

tm 结构在 C++ 中处理日期和时间相关的操作时,显得尤为重要。

#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;
}

在这里插入图片描述

6:C++ 基本的输入输出

C++ 标准库提供了一组丰富的输入/输出功能

C++ 的 I/O 发生在流中,流是字节序列。

  1. 如果字节流是从设备(如键盘、磁盘驱动器、网络连接等)流向内存,这叫做输入操作
  2. 如果字节流是从内存流向设备(如显示屏、打印机、磁盘驱动器、网络连接等),这叫做输出操作

6.1:I/O 库头文件

下列的头文件在 C++ 编程中很重要。

头文件 函数和描述
该文件定义了 cin、cout、cerrclog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。
该文件通过所谓的参数化的流操纵器(比如 setwsetprecision),来声明对执行标准化 I/O 有用的服务。
该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。

6.2:标准输出流(cout)

预定义的对象 coutiostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的.

#include <iostream>
using namespace std;
int main( )
{
    char str[] = "Hello C++";
    cout << "Value of str is : " << str << endl;
}

在这里插入图片描述

C++ 编译器根据要输出变量的数据类型,选择合适的流插入运算符来显示值。<< 运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项。

流插入运算符 << 在一个语句中可以多次使用,如上面实例中所示,endl 用于在行末添加一个换行符。

6.3:标准输入流(cin)

预定义的对象 ciniostream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用的。

#include <iostream>

using namespace std;

int main( )
{
    char name[50];
    cout << "请输入您的名称: ";
    cin >> name;
    cout << "您的名称是: " << name << endl;
}

在这里插入图片描述

C++ 编译器根据要输入值的数据类型,选择合适的流提取运算符来提取值,并把它存储在给定的变量中。

流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句:

cin >> name >> age;

这相当于下面两个语句:

cin >> name;
cin >> age;

6.3:标准错误流(cerr)

预定义的对象 cerriostream 类的一个实例。cerr 对象附属到标准输出设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。

#include <iostream>

using namespace std;

int main( )
{
    char str[] = "Unable to read....";
    cerr << "Error message : " << str << endl;
}

在这里插入图片描述

6.4:标准日志流(clog)

预定义的对象 clogiostream 类的一个实例。clog 对象附属到标准输出设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲区,直到缓冲填满或者缓冲区刷新时才会输出。

#include <iostream>
using namespace std;
int main( )
{
    char str[] = "Unable to read....";
    clog << "Error message : " << str << endl;
}

在这里插入图片描述

7:C++数据结构

C++ 数组允许定义可存储相同类型数据项的变量,但是结构是 C++ 中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。

7.1:定义结构

为了定义结构,您必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:

struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
.
.
} object_names;

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;

7.2:访问结构

#include <iostream>
#include <cstring>

using namespace std;

// 声明一个结构体类型 Books
struct Books
{
    char  title[50];
    char  author[50];
    char  subject[100];
    int   book_id;
};
int main( )
{
    Books Book1;        // 定义结构体类型 Books 的变量 Book1
    Books Book2;        // 定义结构体类型 Books 的变量 Book2
    // Book1 详述
    strcpy( Book1.title, "C++ 教程");
    strcpy( Book1.author, "Runoob");
    strcpy( Book1.subject, "编程语言");
    Book1.book_id = 12345;
    // Book2 详述
    strcpy( Book2.title, "CSS 教程");
    strcpy( Book2.author, "Runoob");
    strcpy( Book2.subject, "前端技术");
    Book2.book_id = 12346;
    // 输出 Book1 信息
    cout << "第一本书标题 : " << Book1.title <<endl;
    cout << "第一本书作者 : " << Book1.author <<endl;
    cout << "第一本书类目 : " << Book1.subject <<endl;
    cout << "第一本书 ID : " << Book1.book_id <<endl;
    // 输出 Book2 信息
    cout << "第二本书标题 : " << Book2.title <<endl;
    return 0;
}

在这里插入图片描述

7.3:结构作为函数参数

您可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。

#include <iostream>
#include <cstring>

using namespace std;
void printBook( struct Books book );
// 声明一个结构体类型 Books
struct Books
{
    char  title[50];
    char  author[50];
    char  subject[100];
    int   book_id;
};

int main( )
{
    Books Book1;        // 定义结构体类型 Books 的变量 Book1
    Books Book2;        // 定义结构体类型 Books 的变量 Book2
    // Book1 详述
    strcpy( Book1.title, "C++ 教程");
    strcpy( Book1.author, "Runoob");
    strcpy( Book1.subject, "编程语言");
    Book1.book_id = 12345;
    // Book2 详述
    strcpy( Book2.title, "CSS 教程");
    strcpy( Book2.author, "Runoob");
    strcpy( Book2.subject, "前端技术");
    Book2.book_id = 12346;
    // 输出 Book1 信息
    printBook( Book1 );
    // 输出 Book2 信息
    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;
}

在这里插入图片描述

7.4:指向结构的指针

您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:

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 的变量 Book1
    Books Book2;        // 定义结构体类型 Books 的变量 Book2
    // Book1 详述
    strcpy( Book1.title, "C++ 教程");
    strcpy( Book1.author, "Runoob");
    strcpy( Book1.subject, "编程语言");
    Book1.book_id = 12345;
    // Book2 详述
    strcpy( Book2.title, "CSS 教程");
    strcpy( Book2.author, "Runoob");
    strcpy( Book2.subject, "前端技术");
    Book2.book_id = 12346;
    // 通过传 Book1 的地址来输出 Book1 信息
    printBook( &Book1 );
    // 通过传 Book2 的地址来输出 Book2 信息
    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;
}

效果是一样的

7.5:typedef 关键字

typedef的第一个功能是定义类型的别名,比如Int*,比较麻烦,我们可以直接定义成pINt代表int型指针。

typedef int* TPINT; 

我们知道,编译宏可以很好的控制编译流,我们在编译跨平台程序的时候,很多时候是靠编译流的。编译流一般都是一些宏定义来控制。其他功能免不了用#define,但是类型定义有更好的typedef,所以关于类型的定义,我们完全可以使用typedef来替换#define
比如,在一些机器上,int为16位,long为32位,那么我们可以这样定义:

typedef int INT16
typedef long INT32

2.book_id = 12346;
// 输出 Book1 信息
printBook( Book1 );
// 输出 Book2 信息
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;
}


#### 7.4:指向结构的指针

您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:

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 的变量 Book1
    Books Book2;        // 定义结构体类型 Books 的变量 Book2
    // Book1 详述
    strcpy( Book1.title, "C++ 教程");
    strcpy( Book1.author, "Runoob");
    strcpy( Book1.subject, "编程语言");
    Book1.book_id = 12345;
    // Book2 详述
    strcpy( Book2.title, "CSS 教程");
    strcpy( Book2.author, "Runoob");
    strcpy( Book2.subject, "前端技术");
    Book2.book_id = 12346;
    // 通过传 Book1 的地址来输出 Book1 信息
    printBook( &Book1 );
    // 通过传 Book2 的地址来输出 Book2 信息
    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;
}

效果是一样的

7.5:typedef 关键字

typedef的第一个功能是定义类型的别名,比如Int*,比较麻烦,我们可以直接定义成pINt代表int型指针。

typedef int* TPINT; 

我们知道,编译宏可以很好的控制编译流,我们在编译跨平台程序的时候,很多时候是靠编译流的。编译流一般都是一些宏定义来控制。其他功能免不了用#define,但是类型定义有更好的typedef,所以关于类型的定义,我们完全可以使用typedef来替换#define
比如,在一些机器上,int为16位,long为32位,那么我们可以这样定义:

typedef int INT16
typedef long INT32

猜你喜欢

转载自blog.csdn.net/m0_55534317/article/details/127289156