一、基础语法

1.1 C++ 简介

  • C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。
  • C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。
  • 使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。

面向对象程序设计

  • 完全支持面向对象的程序设计,包括面向对象开发的四大特性:封装、抽象、继承、多态。

标准库

  1. 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
  2. C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
  3. 标准模板库(STL),提供了大量的方法,用于操作数据结构等。

ANSI 标准

  • 所有的C++编译器制造商均支持ANSI 标准,能够保证其便携性(代码在Mac、UNIX、Windows、Alpha 计算机上都能通过编译)。

1.2 C++ 环境设置

文本编辑器

编译器

  • 写在源文件中的源代码是人类可读的源。它需要"编译",转为机器语言,这样 CPU 可以按给定指令执行程序。
  • 大多数的 C++ 编译器并不在乎源文件的扩展名,但是如果您未指定扩展名,则默认使用 .cpp。
  • 常用的免费可用的编译器是 GNU 的 C/C++ 编译器

1.3 C++ 基本语法

C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。

  • 对象 - 对象具有状态和行为,对象是类的实例。
  • - 类可以定义为描述对象行为/状态的 模板/蓝图。
  • 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。
  • 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。

C++ 程序结构

  • Eg:输出“Hello World”
  #include <iostream>using namespace std;// main() 是程序开始执行的地方int main(){cout << "Hello World"; // 输出 Hello Worldreturn 0;}
  • using namespace std; 告诉编译器使用 std 命名空间。

编译&执行 C++ 程序

  • 上述代码保存为“hello.cpp”,打开所在路径,输出“Hello World”
$ g++ hello.cpp
$ ./a.out

C++ 中的分号 & 语句块

  • 在 C++ 中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。
  • 语句块是一组使用大括号括起来的按逻辑连接的语句。

1.4 C++ 注释

  • /*开始*/结束(多行)
  • // 单行

1.5 C++ 数据类型

基本内置类型

类型 关键字
布尔型 bool
字符型 char
整型 int
浮点型 float
双浮点型 double
无类型 void
宽字符型 wchar_t

typedef 声明

  • 可以使用 typedef 为一个已有的类型取一个新的名字。typedef type newname;

枚举类型

  • 枚举类型(enumeration)是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。
  • 关键字 enum
enum 枚举名{ 标识符[=整型常数], 标识符[=整型常数],
... 标识符[=整型常数]
} 枚举变量;
  • 如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始。例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。
enum color { red, green, blue } c;
c = blue;
  • 默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0。
enum color { red, green=5, blue };

1.6 C++ 变量类型

  • 大写字母和小写字母是不同的,因为 C++ 是大小写敏感的。

C++ 中的变量声明

  • 使用extern声明变量,使用多个文件且只在其中一个文件中定义变量。
  • 变量可以声明多次,但只能定义一次。
  • extern作用:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。
  • 在文件t2.cpp中有代码:
int x = 10;

在文件t1.cpp中有代码:

#include <iostream>
using namespace std;extern int x;
int main() {cout << x;return 0;
}

输出:10
extern表示声明一个变量,声明使得这个名字为程序所指,而定义创建了和这个名字相关联的实体。
如:

int x = 10;

则是定义一个变量。
声明和定义分开,这样的意义就是可以在一个文件中使用另一个文件的变量,如上面的t1.cpp使用t2.cpp的变量。

C++ 中的左值(Lvalues)和右值(Rvalues)

  • 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
  • 右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
  • 变量是左值,数值型的数字是右值(Eg:10)

1.7 C++ 变量作用域

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

    • 在函数或一个代码块内部声明的变量,称为局部变量。
    • 在函数参数的定义中声明的变量,称为形式参数。
    • 在所有函数外部声明的变量,称为全局变量。
  • 在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
  • 初始化局部变量和全局变量
    • 当局部变量被定义时,系统不会对其初始化,必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:
      |数据类型| 初始化默认值|
      |-----|-----|
      |int| 0|
      |char| ‘\0’|
      |float |0|
      |double |0|
      |pointer |NULL|

1.8 C++ 常量

  • 常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。
  • 常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。
  • 常量就像是常规的变量,只不过常量的值在定义后不能进行修改。
  • 整数常量:
    • 可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)

定义常量

  1. 使用 #define 预处理器。
#define identifier value
// eg:
#define LENGTH 10
  1. 使用 const 关键字。
const type variable = value;
// eg:
const int  LENGTH = 10;
  1. 二者区别:
    用const的优点:会进行类型安全检查。而define没有安全检查,且可能会产生意料不到的错误。

1.9 C++ 修饰符类型

  • C++ 允许在 char、int 和 double 数据类型前放置修饰符。
  • 数据类型修饰符:signed、unsigned、long、short
  • 修饰符 signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。
  • 修饰符 signed 和 unsigned 也可以作为 long 或 short 修饰符的前缀。例如:unsigned long int。

类型限定符

限定符 含义
const const 类型的对象在程序执行期间不能被修改改变。
volatile 修饰符 volatile 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。
restrict 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。
  • volatile作用:
    volatile关键字是防止在共享的空间发生读取的错误。只保证其可见性,不保证原子性;使用volatile指每次从内存中读取数据,而不是从编译器优化后的缓存中读取数据,简单来讲就是防止编译器优化。

1.10 C++ 存储类

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

    • auto
    • register
    • static
    • extern
    • mutable
    • thread_local (C++11)
    • 从 C++ 11 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。

auto

  • 自 C++ 11 以来,auto 关键字用于两种情况:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。

register

  • register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)
  • Eg:register int miles;
  • 寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

static

  • static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。(局部静态变量不销毁)
  • static 修饰符也可以应用于全局变量。当 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;
}

结果:

变量 i 为 6 , 变量 count 为 9
变量 i 为 7 , 变量 count 为 8
变量 i 为 8 , 变量 count 为 7
变量 i 为 9 , 变量 count 为 6
变量 i 为 10 , 变量 count 为 5
变量 i 为 11 , 变量 count 为 4
变量 i 为 12 , 变量 count 为 3
变量 i 为 13 , 变量 count 为 2
变量 i 为 14 , 变量 count 为 1
变量 i 为 15 , 变量 count 为 0

extern

  • extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。
  • extern 是用来在另一个文件中声明一个全局变量或函数,通常用于当有两个或多个文件共享相同的全局变量或函数。

mutable

  • 允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。

thread_local

  • 使用 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;  // 本地变量
}
  • thread_local变量是C++ 11新引入的一种存储类型。它会影响变量的存储周期(Storage duration),C++中有4种存储周期

    1. automatic
    2. static
    3. dynamic
    4. thread
  • 有且只有thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例(Each thread has its own instance of the object)。

  • thread_local可以和staticextern关键字联合使用,这将影响变量的链接属性(to adjust linkage)。

  • 适用范围:

    1. 命名空间下的全局变量
    2. 类的static成员变量
    3. 本地变量
  • 既然每个线程都拥有一份独立的thread_local变量,那么就有2个问题需要考虑:

    1. 各线程的thread_local变量是如何初始化的?
    • 每个线程都会进行一次单独初始化
    1. 各线程的thread_local变量在初始化之后拥有怎样的生命周期,特别是被声明为thread_local的本地变量(local variables)?
    • 声明为thread_local的本地变量在线程中是持续存在的,不同于普通临时变量的生命周期,它具有static变量一样的初始化特征和生命周期

1.11 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

杂项运算符

运算符 描述
sizeof sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
, 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.(点)和 ->(箭头) 成员运算符用于引用类、结构和共用体的成员。
Cast 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
& 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
* 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

运算符优先级

  • 由高到低
类别 运算符 结合性
后缀 () [] -> . ++ - - 从左到右
一元 + - ! ~ ++ - - (type)* & sizeof 从右到左
乘除 * / % 从左到右
加减 + - 从左到右
移位 << >> 从左到右
关系 < <= > >= 从左到右
相等 == != 从左到右
位与 AND & 从左到右
位异或 XOR ^ 从左到右
位或 OR | 从左到右
逻辑与 AND && 从左到右
逻辑或 OR || 从左到右
条件 ?: 从右到左
赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左
逗号 , 从左到右
  • sizeof不是函数,而是运算符,如在malloc中常传入sizeof的运算结果,他是在运行之前就已经计算好了传入malloc函数中的

1.12 C++ 循环

循环类型

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环 多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环 您可以在 while、for 或 do…while 循环内使用一个或多个循环。

循环控制语句

break continue goto
跳出(包括loop、switch) 跳单次 不建议使用

1.13 C++ 判断

嵌套语句

语句 描述
if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if…else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。
switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句;switch case中最后用default表示余下所有情况
  • switch中括号里面的结果必须是数值型的,单个的char也相当于数值,要是整个字符串(字符数组或者string),铁定用不成。
  • 如果需要在switch-case里面定义局部变量,case的语句需要加大括号。

? : 运算符

A ? B : c;

A真则B,A假为C。

1.14 C++函数

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

    int max(int num1, int num2);int max(int, int);
    
  • 在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。应该在调用函数的文件顶部声明函数。

  • 函数参数:

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

    • 形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁

    • 当调用函数时,有三种向函数传递参数的方式:

      调用类型 描述
      传值调用 默认方法,该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
      指针调用 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数
      引用调用 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数
  • 参数默认值

    • 在函数定义中使用赋值运算符来为参数赋默认值。
    • 调用函数时,如果未传递参数的值,则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值。
  • Lambda 函数与表达式

    • C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。

    • Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。

    • 形式:

      [capture](parameters)->return-type{body}
      # eg:
      [](int x, int y){ return x < y ; }# 无返回值:
      [capture](parameters){body}
      # eg:
      []{ ++global_x; } # 返回值类型可以明确指定:
      [](int x, int y) -> int { int z = x + y; return z + x; }
      
    • 在Lambda表达式内可以访问 当前作用域 的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:

      []      // 沒有定义任何变量。使用未定义变量会引发错误。
      [x, &y] // x以传值方式传入(默认),y以引用方式传入。
      [&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
      [=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
      [&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
      [=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
      
    • 对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:

      [this]() { this->someFunc(); }();
      
  • 指针和引用的区别?

    • 指针是*,通过它能找到以它为地址的内存单元。
    • 引用是&,就是某个变量的别名,对引用操作与对变量直接操作完全一样。
    • 区别:
      1. 引用必须要初始化。
      2. 指针是个实体,而引用是别名。
      3. 引用只能在定义时被初始化一次,之后不可变,指针可以改变所指的对象。
      4. 可以有const指针,没有const引用。
      5. 指针自加,是指向下一次地址,而引用自加:是本身值的增加。
      6. 引用只能被初始化一次,不能再换成别的名字了。
  • 指针调用与引用调用的区别在于:

    • 指针变量存储的是一个指针,也就是一个存储地址,当使用指针作为形参的时候,可以改变指针的指向,访问该地址的数据时需要加上符号 * ;
    • 而引用调用他是直接将一个地址传递到函数内,相当于在该函数内给变量起了一个别名,不可改变指向,但可以改变该地址内所存储的值;

1.15 C++ 数字

  • int rand(void) 与 void srand(unsigned int seed)

    • 头文件:#include<stdlib.h>
    • 用户未设定随机数种子时,系统默认的随机数种子为1。rand()产生的是伪随机数字,每次执行时是相同的;若要不同,用函数srand()初始化它。
    • rand()和srand()要一起使用,其中srand()用来初始化随机数种子,rand()用来产生随机数。
    • 参数seed必须是个整数,通常可以利用time(0)的返回值或NULL来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。(失去随机性)
    • 可以把 seed 设为 time(0) 保证随机性。
    • 要产生 0–n 内的随机数:rand() % n;a–b 内的随机数 a + rand() % (b - a + 1);左闭右开

1.16 C++ 数组

  • 存储一个固定大小相同类型元素的顺序集合

  • 所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素

声明数组

  • arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C++ 数据类型

    type arrayName [ arraySize ];
    

初始化数组

  • 如果不指定数组大小,则为初始化时元素的个数。

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

多维数组

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

  • eg:初始化二维数组

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

指向数组的指针

  • 数组名是一个指向数组中第一个元素的常量指针

  • balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。

    double balance[50];// 把 p 赋值为 balance 的第一个元素的地址double *p;
    double balance[10];p = balance;
    
  • *(balance + 4) 等同于 *(p+4) 即访问 balance[4] 的数据

从函数返回数组

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

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

    int * myFunction()
    {
    .
    .
    .
    }
    
  • C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量

  • eg:生成 10 个随机数,并使用数组来返回它们

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

1.17 C++ 字符串

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

    • C 风格字符串
    • C++ 引入的 string 类类型

C 风格字符串

  • 字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。

  • 字符数组的大小比实际的字符数多一个。

  • C++ 编译器会在初始化数组时,自动把 ‘\0’ 放在字符串的末尾。

  • 初始化:

    char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};// 或者:char greeting[] = "Hello";
    
  • 可用函数:

序号 函数 & 目的
1 strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
3 strlen(s1); 返回字符串 s1 的长度。(不包括 "\0"
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[11] = "Hello";char str2[11] = "World";char str3[11];int  len ;// 复制 str1 到 str3strcpy( str3, str1);cout << "strcpy( str3, str1) : " << str3 << endl;// 连接 str1 和 str2strcat( str1, str2);cout << "strcat( str1, str2): " << str1 << endl;// 连接后,str1 的总长度len = strlen(str1);cout << "strlen(str1) : " << len << endl;system("pause");return 0;
    }// strcpy( str3, str1) : Hello
    // strcat( str1, str2): HelloWorld
    // strlen(str1) : 10
    

C++ 中的 String 类

  • C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。

  • 示例:

    #include <iostream>
    #include <string>using namespace std;int main ()
    {string str1 = "Hello";string str2 = "World";string str3;int  len ;// 复制 str1 到 str3str3 = str1;cout << "str3 : " << str3 << endl;// 连接 str1 和 str2str3 = str1 + str2;cout << "str1 + str2 : " << str3 << endl;// 连接后,str3 的总长度len = str3.size();cout << "str3.size() :  " << len << endl;system("pause");return 0;
    }// str3 : Hello
    // str1 + str2 : HelloWorld
    // str3.size() :  10
    

1.18 C++ 指针

  • 每个变量都有一个内存地址,用 “&” 访问地址。

  • 指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。声明:type *name;

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

  • 示例:

    #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;
    }// Value of var variable: 20
    // Address stored in ip variable: 0xbfc601ac
    // Value of *ip variable: 20
    
  • C++ 指针详解:

    概念 描述
    C++ Null 指针 C++ 支持空指针。NULL 指针是一个定义在标准库中的值为零的常量。
    C++ 指针的算术运算 可以对指针进行四种算术运算:++、–、+、-
    C++ 指针 vs 数组 指针和数组之间有着密切的关系。
    C++ 指针数组 可以定义用来存储指针的数组。
    C++ 指向指针的指针 C++ 允许指向指针的指针。
    C++ 传递指针给函数 通过引用或地址传递参数,使传递的参数在调用函数中被改变。
    C++ 从函数返回指针 C++ 允许函数返回指针到局部变量、静态变量和动态内存分配。

空指针

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

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

    #include <iostream>using namespace std;int main ()
    {int  *ptr = NULL;cout << "ptr 的值是 " << ptr ;return 0;
    }
    // ptr 的值是 0
    
  • 内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

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

  • 空指针尽可能的使用 nullptr

指针的算数运算

  • 指针是一个用数值表示的地址。因此,可以对指针执行算术运算。

  • 可以对指针进行四种算术运算:++(指向下一个变量的地址)、–(指向上一个变量的地址)、+、-。

  • 经常用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。示例:

    #include <iostream>using namespace std;
    const int MAX = 3;int main ()
    {int  var[MAX] = {10, 100, 200};int  *ptr;// 指针指向数组第一个元素地址ptr = var;// 指针指向数组最后一个地址// ptr = &var[MAX-1]for (int i = 0; i < MAX; i++){cout << "Address of var[" << i << "] = ";cout << ptr << endl;cout << "Value of var[" << i << "] = ";cout << *ptr << endl;// 移动到下一个位置ptr++;// 移动到上一个位置ptr--;}return 0;
    }
    
  • 指针的比较:

    指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

指针数组

  • 让数组存储指向 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;
    }
    // Value of var[0] = 10
    // Value of var[1] = 100
    // Value of var[2] = 200
    
  • 指针数组 与 数组指针

    • int *p[n]为指针数组:即存放指针的数组,数组中有n个元素,每个元素为一个int型的指针;
    • int (*p)[n]为数组指针:即指向数组的指针,p为一个指针,指向一个包含n个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;
    }
    // var 值为 :3000
    // *ptr 值为:3000
    // **pptr 值为:3000
    

传递指针给函数

  • 方法:声明函数参数为指针类型

  • 示例:传递一个无符号的 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;
    }
    

传递指针给函数

  • 只需要声明函数参数为指针类型,并将地址传递给函数

  • 示例:传递一个无符号的 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;
    }
    // Average value is: 214.4
    

从函数返回指针

  • 首先声明一个返回指针的函数:(C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。)

    int * myFunction()
    {
    .
    .
    .
    }
    
  • 示例:

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

1.19 C++ 引用

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

引用 vs 指针

区别:

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

创建引用

  • 变量名称是变量附属在内存位置中的标签,引用相当于是变量附属在内存位置中的第二个标签。可以通过原始变量名称或引用来访问变量的内容。

  • 示例:

    #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;
    }
    // Value of i : 5
    // Value of i reference : 5
    // Value of d : 11.7
    // Value of d reference : 11.7
    
  • 引用通常用于函数参数列表和函数返回值。

引用作为参数

#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;
}
// 交换前,a 的值: 100
// 交换前,b 的值: 200
// 交换后,a 的值: 200
// 交换后,b 的值: 100

引用作为返回值

  • 通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。函数返回一个引用的方式与返回一个指针类似。

  • 当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。

  • 示例:

    #include <iostream>using namespace std;double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};double& setValues( int i )
    {return vals[i];   // 返回第 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 在函数作用域外依然是有效的
    }
    

1.20 C++ 日期 & 时间

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

  • 有四个与时间相关的类型:clock_t、time_t、size_ttm。类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。

    结构类型 tm 把日期和时间以 C 结构的形式保存,tm 结构的定义如下:

    struct tm {int tm_sec;   // 秒,正常范围从 0 到 59,但允许至 61int tm_min;   // 分,范围从 0 到 59int tm_hour;  // 小时,范围从 0 到 23int tm_mday;  // 一月中的第几天,范围从 1 到 31int tm_mon;   // 月,范围从 0 到 11int tm_year;  // 自 1900 年起的年数int tm_wday;  // 一周中的第几天,范围从 0 到 6,从星期日算起int tm_yday;  // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起int tm_isdst; // 夏令时
    }
    
  • 函数:

    序号 函数 & 描述
    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(); 该函数可用于格式化日期和时间为指定的格式。
  • 参考:C/C++时间函数的用法 (vcsos.com)

1.21 C++ 基本输入输出

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

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

I/O 库头文件

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

标准输出流(cout)

  • 预定义的对象 coutiostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的。
  • cout << "Value of str is : " << 变量名 << endl;

标准输入流(cin)

  • 预定义的对象 ciniostream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用的,如下所示:
  • cin >> name
  • 流提取运算符 >> 在一个语句中可以多次使用,如果要求输入多个数据,可以使用如下语句:cin >> name >> age;

标准错误流(cerr)

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

  • cerr 也是与流插入运算符 << 结合使用的,示例:

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

标准日志流(clog)

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

  • clog 也是与流插入运算符 << 结合使用的,如下所示:

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

1.22 C++ 结构体

  • 结构体允许存储不同类型的数据项

  • 定义:

    struct type_name {member_type1 member_name1;member_type2 member_name2;member_type3 member_name3;..
    } object_names;
    
  • 访问结构成员

    • 使用成员访问运算符(.)
  • 结构体可以作为函数参数

  • 指向结构体的指针:

    struct Books *struct_pointer;
    

    使用指向该结构的指针访问结构的成员,使用 -> 运算符

  • typedef 关键字

    • 为创建的结构体起“别名”:直接使用 Books 来定义 Books 类型的变量,不需要使用 struct 关键字。

      typedef struct Books
      {char  title[50];char  author[50];char  subject[100];int   book_id;
      }Books;// 使用:
      Books Book1, Book2;
      
    • 也可以定义非结构体类型: x, y 和 z 都是指向长整型 long int 的指针。

      typedef long int *pint32;pint32 x, y, z;
      
    • typedef 与 #define 的区别:

      • typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
      • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

参考:

  • https://www.nowcoder.com/tutorial/10003/726e8520c91b40d8b26e2d9f8880b9e1

C++ 学习(基础语法篇)相关推荐

  1. Xamarin XAML语言教程基础语法篇大学霸

    Xamarin XAML语言教程基础语法篇大学霸 前  言 Xamarin是一个跨平台开发框架.它可以用来开发iOS.Android.Windows Phone和Mac的应用程序.使用Xamarin框 ...

  2. FPGA笔记之verilog语言(基础语法篇)

    文章目录 FPGA笔记之verilog语言(基础语法篇) 1. verilog 的基础结构 1.1 verilog设计的基本单元--module 1.2 module的使用 1.3 I/O的说明 1. ...

  3. Vue——基础语法篇

    Vue--基础语法篇 author:木子六日 学习视频来源 coderwhy老师的vue教学 文章目录 Vue--基础语法篇 author:木子六日 学习视频来源 01.hello vue 02.vu ...

  4. C/C++学习指南(语法篇) - 邵发

    C/C++学习指南(语法篇),清华大学出出版,作者:邵发 ,ISBN: 9787302419891,一部C/C++基础语法教材,配套100集视频讲解,在线题库.官网:阿发你好 本书简介 一部C/C++ ...

  5. 一起学Go吧! Go语言基础语法篇二

    文章目录 前言 Go 语言指针 Go语言结构体 Go语言切片(Slice) Go 语言范围(Range) Go 语言Map(集合) Go 语言类型转换 Go 语言接口 Go 错误处理 Go并发 通道( ...

  6. Markdown 快速入门上(基础语法篇)

    Markdown 快速入门上(基础语法篇) 1. 简介 Markdown 是一门轻量级的标记性语言,可以用来修饰纯文本使得文档具有一定的优美格式. 像Microsoft Word 文本编辑器, 你需要 ...

  7. MarkDown语法浅析:基础语法篇

    本篇学习笔记简述MarkDown基础语法.掌握了"MarkDown基本语法+简单HTML5标签"的综合运用,就可以把CSDN博文搞得美美哒✌ (本文获得CSDN质量评分[91]) ...

  8. 【教程】PHP快速学习教程(语法篇)

    [教程]PHP快速学习教程(语法篇) 备注 一.环境搭建 1.phpstudy一键搭建 2.apache+php+mysql手动配置 <1>apache加载php <2>php ...

  9. Protobuf 学习手册——语法篇

    一.Override Protobuf1 是一种语言中立.平台无关.可扩展的序列化数据的格式,可用于通信协议,数据存储等. ProtoBuf 在序列化数据方面,它是灵活的.高效的.相比于 XML 来说 ...

最新文章

  1. MySQL触发器的使用规则
  2. java8方法引用符合_java8,方法引用
  3. Python | 从用户输入数据,保存到文件,读取并打印
  4. 楷书和草书哪幅更具有艺术性?
  5. 【HDOJ】2266 How Many Equations Can You Find
  6. 为 Joomla 而生的 Kunena 论坛安装手册
  7. wiki oi3117 高精度练习之乘法
  8. javascript获取随机rgb颜色和十六进制颜色的方法
  9. Linux操作系统配置基础详解:GRUB入门 (转)
  10. 【优化求解】基于生物地理学结合重力引力搜索优化求解算法matlab代码
  11. kubernetes apiserver源码分析二之路由
  12. 【工具笔记】Microsoft数学求解器Math Solver
  13. 云信DUILIB 常用控件 小实例
  14. 【docker】gitlab + qqmail配置SMTP
  15. 程序人生 - 狗狗会“嫉妒”吗?
  16. 数据结构C语言实现高铁客运订票系统
  17. .NET ASP.NET 中web窗体(.aspx)利用ajax实现局部刷新
  18. 【学习记录】python matplotlib 自学入门(随缘更新)
  19. php随机图片github,GitHub - galnetwen/Random-Image: 随机图片服务
  20. 使用WebSocket实现网页声音提醒

热门文章

  1. “海大与我“ Scrum Meeting(第九周会议记录)
  2. angular : 自定义组件双向绑定 [(ngModel)]
  3. Git - 学习/实践 - 以及相关操作
  4. MySQL:环境搭建,初识数据库----Datawhale第一次打卡笔记
  5. 炫舞行书教学视频java_王羲之行书基本笔画教程 王羲之行书基本笔画讲解视频...
  6. android手机变微软手机号码,微软Your Phone未来将能让你用电脑远程控制拨打Android手机电话...
  7. python读取excel数据使用pyecharts展示
  8. windows-chrome安装Elasticsearch-head插件
  9. Java高级篇-----jdk1.8新特性
  10. 工作日志22-04-22