4万字c++讲解+区分c和c++,不来可惜了(含代码+解析)

Posted 阿玥的小东东

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4万字c++讲解+区分c和c++,不来可惜了(含代码+解析)相关的知识,希望对你有一定的参考价值。

目录

1 C++简介

1.1 起源

1.2 应用范围

1.3 C++和C

2开发工具

3 基本语法

3.1 注释

3.2关键字

3.3标识符

4 数据类型

4.1基本数据类型

4.2 数据类型在不同系统中所占空间大小

4.3 typedef声明

4.4 枚举类型

5 变量

5.1 变量的声明和定义

5.2 变量的作用域

6 运算符

7 语法结构

7.1 循环结构

7.2 判断结构

7.3 三元运算符

7.4 预处理命令

8 数组

8.1一维数组

8.2二维数组

8.3 指向数组的指针

8.4 数组与new(动态创建数组)

8.5 数组与函数

数组->函数

函数返回数组

8.6 获取数组的大小

9 函数

9.1 函数声明与定义

9.2 函数的参数与返回值

9.3 函数调用

9.4 函数重载

9.5 内联(inline)函数

9.6 洞悉内联函数底层原理

10 字符串(string)

10.1 C风格的字符串(字符数组)

10.2 C++中的字符串(string)

11 指针和引用

11.1 指针

11.2 引用

12 自定义数据类型

12.1 结构体

12.2 结构体大小和字节对齐

12.3 公用体(union)

12.4 枚举(enum)和typedef声明

13 面向对象

13.1 类

13.2 类成员的访问权限以及类的封装

13.3 对象

13.4 构造函数

13.5 析构函数

13.6 对象指针

13.7 静态成员

13.8 友元

13.9 类(class)与结构体(struct)的区别

14 继承和派生

14.1 继承和派生概述

14.2 多继承

14.3 虚基类

15 多态和虚函数

15.1 向上转型

15.2 多态

15.3 虚函数

15.4 纯虚函数

16 运算符重载

16.1 定义

16.2 形式

16.3 常用运算符的重载

16.4 实现类型转换

17 IO流

17.1 流类和对象

17.2 标准输入输出流

18 文件操作

18.1 文件类和对象

18.2 打开文件

18.3 文本文件的读写

18.4 二进制文件的读写

18.5 移动和获取文件读写指针

18.6 文本文件和二进制文件打开方式的区别

19 泛型和模板

19.1 函数模板

19.2 类模板

19.3 typename 和 class 的区别

19.4 强弱类型语言和c++模板的那点猫腻

20 命名空间和异常处理

20.1 命名空间

20.2 异常处理

21 STL

21.1 容器

21.2 迭代器

21.3 算法


1 C++简介

1.1 起源

-贝尔实验室20世纪80年代(1979)

1.2 应用范围

  • 文字处理程序以及电子表格
  • 编译器
  • 操作系统
  • 大型游戏等

1.3 C++和C

  • C语言是结构化和模块化的语言,面向过程。
  • C++保留了C语言原有的所有优点,增加了面向对象的机制,俗称“带类的C",1983年更名为C++

2开发工具

  • 记事本(Notepad++)+命令行
  • Visual C++ 6.0:经典开发工具,与流行操作系统有冲突
  • VS 2015等:功能强大,体积也强大
  • Code::Blocks:开源免费开发工具,专业开发人员推荐使用
  • 其他开发工具:DeV C++、CLion、C-Free、Xcode、C4droid

3 基本语法

  • 对象-对象具有状态的行为。对象是类的实例。
  • 类-类可以定义为对象行为、状态的模版。
  • 方法-从基本上讲,一个方法表示一种行为,一个类可以包含多种方法。
  • 变量

3.1 注释

//单行注释
/*
多行注释
多行注释
*/

3.2关键字

asmelsenewthis
autoenumoperatorthrow
boolexplicitprivatetrue
breakexportprotectedtry
caseexternpublictypedef
catchfalseregistertypeid
charfloatreinterpret_casttypename
classforreturnunion
constfriendshortunsigned
const_castgotosignedusing
continueifsizeofvirtual
defaultinlinestaticvoid
deleteintstatic_castvolatile
dolongstructwchar_t
doublemutableswitchwhile
dynamic_castnamespacetemplate

3.3标识符

  • 标识符是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。
  • 标识符内不允许出现标点字符,比如 @、& 和 %。C++ 是区分大小写的编程语言

4 数据类型

4.1基本数据类型

七种基本的C++数据类型:bool、char、int、float、double、void、wchar_t
类型修饰符:signed、unsigned、short、long
注:一些基本类型可以使用一个或多个类型修饰符进行修饰,比如:signed short int简写为short、signed long int 简写为long。

类型名占用字节数数值范围
void0
bool1true.false
wchar_t2或4个字节
char(signed char)1-128~+127
short(signed short)2-32768~+32767
int(signed int)4-2147483648~+2147483647
long(signed long)4-2147483648~+2147483647
long long(signed long long)8-9,223,372,036,854,775,808 ~9,223,372,036,854,775,807
float4-.34*1038~3.4*1038
double8-1.7*10308~1.7*10308
unsigned char10~255
unsigned shrot20~65525
unsigned(unsigned int)40~4294967295
unsigned long40~4294967295
unsigned long long80 ~ 18,446,744,073,709,551,615
//x64处理器 64位window10 vs2015 
#include <iostream>
using namespace std;
int main()

	bool b;
	char c;short s; int i; long l; long long ll; float f; double d; long double ld;long float lf;
	unsigned char uc; unsigned short us; unsigned int ui; unsigned long ul; unsigned long long ull;
	cout << sizeof(bool) <<  endl;
	cout << sizeof(char)<<" " << sizeof(short)<<" "<< sizeof(signed int) << " " << sizeof(long) << " " << sizeof(signed long long) << " " << sizeof(float) << " " << sizeof(double) << " " << sizeof(long float) << " " << sizeof(long double) << endl;
	cout <<sizeof(unsigned char)<<" "<< sizeof(unsigned short) << " " << sizeof(unsigned int) << " " << sizeof(unsigned long) << " " << sizeof(unsigned long long) << endl;
	cout << sizeof(unsigned) << endl;
	
	
	cout << "hello World!!!" <<endl;
	system("pause");
	return 0;

4.2 数据类型在不同系统中所占空间大小

这个与机器、操作系统、编译器有关。比如同样是在32bits的操作系统系,VC++的编译器下int类型为占4个字节;而tuborC下则是2个字节。
原因:

  • c/c++规定int字长和机器字长相同
  • 操作系统字长和机器字长未必一致
  • 编译器根据操作系统字长来定义int字长
类型16位操作系统32位操作系统64位操作系统
char111
char*248
short222
int244
long448
long long888

注:long类型在不同编译器中的占位不一样: 32位时,VC++和GCC都是4字节; 64位时,VC++是4字节,GCC是8字节。

4.3 typedef声明

//使用typedef为一个已有的类型取一个新的名字,语法如下:
typedef type newname
//eg:
typedef int feet
feet distance

4.4 枚举类型

C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合;枚举元素是一个整型,枚举型可以隐式的转换为int型,int型不能隐式的转换为枚举型。

//枚举类型的语法:
enum 枚举名
	标识符[=整型常数], 
     标识符[=整型常数], 
... 
    标识符[=整型常数]
枚举变量;

如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始;

  • 默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。
    例如:
enum course math,chinese,english,physics,chemistryc;
c = english;
cout<<c<<endl;  //2
//english为1 physics为2 chemistry为3,chinese仍为1,math仍为0
enum course math,chinese,english=1,physics,chemistry;

5 变量

变量其实只不过是程序可操作的存储区的名称。C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。

5.1 变量的声明和定义

  • 变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。
  • 可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。
  • 多个变量赋同一个值时,需要分别赋值。
int x = y = z = 66;//错误
int x = 3,y = 3,z = 3;
int x, y ,z = 3;
x = y = z;

变量的声明(不分配内存):extern 数据类型 变量名;
变量的定义:数据类型 变量名1,变量名2,...变量名n;

// 变量声明
extern int a, b;
int main ()

  // 变量定义
  int a, b;
  // 初始化
  a = 23;
  b = 25;
  return 0;

5.2 变量的作用域

局部变量:在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。
全局变量:在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。

  • 局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
  • 当局部变量被定义时,系统不会对其初始化;定义全局变量时,系统会自动初始化值:int float double 0,char ’\\0‘,指针 NULL
int i = 66;
int main ()

  int i = 88;
  cout << i<<endl;//8
  return 0;

float f;
double d;
char c;
int *p;
int main()

	cout << i << f << d << c << p << endl;//000 00000000
	return 0

6 运算符

  • 算术运算符:+ - * / % ++ --
  • 关系运算符:== != < > >= <=
  • 逻辑运算符:&& || !
  • 位运算符:& | ^ ~ << >>
  • 赋值运算符:= += -= *= /= %= <<= >>= &= ^= !=
  • 杂项运算符:
sizeof            //返回变量的大小,eg:sizeof(a)返回4 a是整型  sizeof(int)
Condition?X:Y     //三元运算符 Condition为true,值为X,否则值为Y
,                //逗号表达式,值为最后一个表达式的值
.和->            //用于引用类、结构和公用体的成员
Cast            //强制类型转换符  eg:int(2.202)返回2
&              //指针运算符 返回变量的地址
*             //指针运算符  指向一个变量
  • 运算符优先级
类别运算符结合性
后缀() [] -> . ++ - -从左到右
一元+ - ! ~ ++ - - (type)* & sizeof从右到左
乘除* / %从左到右
加减+ -从左到右
移位<< >>从左到右
关系< <= > >=从左到右
相等== !=从左到右
位与 AND&从左到右
位异或 XOR^从左到右
位或 OR|从左到右
逻辑与 AND&&从左到右
逻辑或 OR|
条件?:从右到左
赋值= += -= *= /= %=>>= <<= &= ^==
逗号,从左到右

7 语法结构

7.1 循环结构

  • while
while(conditon)//0为false,非0为true

	statement(s);

  • for
for(init;conditon;increment)//0为false,非0或什么也不写为true

	statement(s);

1.init首先被执,且只会执行一次,也可以不写任何语句。
2.然后会判断conditon,true执行循环主体,false跳过循环
3.执行完循环主体,执行increment,跳到2

int array[5] =  11, 22, 33, 44, 55 ;
for (int x : array)

	cout << x << " ";

cout << endl;
// auto 类型也是 C++11 新标准中的,用来自动获取变量的类型
for (auto x : array)

	cout << x << " ";

  • for each
    STL中的for增强循环。
int a[4] =  4,3,2,1 ;
for each (int var in a)

	cout << var << " ";

7.2 判断结构

  • if
if(expr)

	statement;//如果expr为true将执行的语句块 

if(expr)

   statement1;// 如果expr为true将执行的语句块

else

   statement2;// 如果expr为false将执行的语句

if(expr1)

   statement1;// 如果expr1为true将执行的语句块

elseif(expr2)

   statement2;// 如果expr2为true将执行的语句块

...
else

	statementElse;// 当上面的表达式都为false执行的语句块

  • switch
switch(expression)
    case constant-expression  :
       statement(s);
       break; 
    case constant-expression  :
       statement(s);
       break; 
    // 您可以有任意数量的 case 语句
    default : // 可选的
       statement(s);

  • 每个case后满的常量表达式必须各不相同。
  • case语句和default语句出现的顺序对执行结果没有影响。
  • 若case后没有break,执行完就不会判断,继续执行下一个case语句。直到遇到brerak。
  • default后面如果没有case,则break可以省略
  • 多个case可以用一组执行语句
char c = 'A';
	switch (c)
	
	
	case 'A':
	case 'B':
	case 'C':
		cout << "及格了" << endl;
		break;
	default:
		cout << "不及格" << endl;
	
	

7.3 三元运算符

//如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值
Exp1 ? Exp2 : Exp3;

7.4 预处理命令

预处理程序(删除程序注释,执行预处理命令等)–>编译器编译源程序

  • 宏定义:#define 标识符 字符串
  • 文件包含:#include<filename> 或者#include“filename”
  • 条件编译
//如果标识符被#define定义过,执行程序段1,否则执行程序段2
#ifdef 标识符
	程序段1
#else
	程序段2
#endif
//如果标识符没有被#define定义过,执行程序段1,否则执行程序段2
#ifndef 标识符
	程序段1
#else
	程序段2
#endif
//如果表达式为true,执行程序段1,否则执行程序段2
#if 表达式
	程序段1
#else
	程序段2
#endif

8 数组

一些具有相同数据类型或相同属性(类)的数据的集合,用数据名标识,用下标或序号区分各个数据。数组中的数据称为元素。

8.1一维数组

定义一维数组的形式:数据类型 数据名[常量表达式]
初始化的形式:数据类型 数组名[常量表达式] = 初值表;
为数组的某一个元素赋值:数组名[下标] =值(下标从0开始)
数组的引用:数组名[下标]

  • 初始化数组时,可以只给部分数组元素赋值
  • 对全部元素数组赋值时,可以不指定数组长度,编译系统会根据初值个数确定数组的长度。
  • static型数组元素不赋初值,系统会自动默认为0。
int arr1[4] = 1,2,3,4;
int arr2[4] =  1,2 ;
int arr[4] = 0];//所有元素为0
static int arr3[3];
int arr4[4];
cout << "arr1:"<<arr1[0] << arr1[1] << arr1[2] << arr1[3] << endl;
cout << "arr2:" << arr2[0] << arr2[1] << arr2[2] << arr2[3] << endl;
cout << "arr3:" << arr3[0] << arr3[1] << arr3[2] << arr3[3] << endl;
cout << "arr4:" << arr4[0] << arr4[1] << arr4[2] << arr4[3] << endl;

8.2二维数组

定义一维数组的形式:数据类型 数据名[常量表达式1][常量表达式2]
初始化的形式:数据类型 数组名[常量表达式1] [常量表达式2]= 初值表;
为数组的某一个元素赋值:数组名[行下标][列下标] =值(下标从0开始)
数组的引用:数组名[行下标][列下标]

  • 将所有数据写在一个花括号内,自动按照数组元素个数在内存中排列的顺序赋值
  • 可对部分元素赋值,其余元素的值自动取0.
  • 定义初始化数组时,可以省略第一维的长度,第二维不能省,系统会自动确认行数
int arr1[2][3];
int arr[2][3] = 0];//所有元素为0
int arr2[2][3] =  1,2,3,4,5,6 ;
int arr3[2][3] =  1,2,3 ,4,5,6 ;
int arr4[2][3] =  1,4,6 ;
int arr5[][3] =  1,2,3 ,4,5,6 ;

字符数组
char类型的数组,在字符数组中最后一位为’\\0’)时,可以看成时字符串。在C++中定义了string类,在Visual C++中定义了Cstring类。
字符串中每一个字符占用一个字节,再加上最后一个空字符。如:

//字符串长度为8个字节,最后一位是'\\0'。
char array[10] = "yuanrui";//yuanrui\\0\\0\\0
//也可以不用定义字符串长度,如:
char arr[] = "yuanrui";//yuanrui\\0

8.3 指向数组的指针

指针的概念会在后面详细讲解。

double *p;
double arr[10];
p = arr;//p = &arr[0];
*(p+3);//arr[3]

8.4 数组与new(动态创建数组)

一维数组:

int* arr1 = new int[2];//delete []arr1;
int* arr2 = new int[3] 1,2 ;//delete []arr2

二维数组

int m=2, n=3;
int** arr3 = new int*[2];//delete []arr3
for (int i = 0; i < 10; ++i)
 
	arr3[i] = new int[3]; // delete []arr3[i]

int* arr4 = new int[m*n];//数据按行存储 delete []arr3

8.5 数组与函数

数组->函数

  • 如果传递二维数组,形参必须制定第二维的长度。
    形式参数是一个指针:void function(int *param)
    形式参数是一个已定义大小的数组:void function(int param[10])
    形式参数是一个未定义大小的数组:void function(int param[])
    二维数组:void function(int a[][3],int size)

函数返回数组

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

8.6 获取数组的大小

  • 动态创建(new)的基本数据类型数组无法取得数组大小
int a[3];
//第一种方法
cout<<sizeof(a)/sizeof(a[0])<<endl;
//第二种方法
cout << end(a) - begin(a) << endl;
//二维数组
int arr[5][3];
int lines = sizeof(arr) / sizeof(arr[0][0]);
int row = sizeof(arr) / sizeof(arr[0]);//行
int col = lines / row;//列
cout << row << "::"<<col << endl;
cout << end(arr) - begin(arr) << endl;//5行

9 函数

函数是实现模块化程序设计思想的重要工具, C++程序中每一项操作基本都是由一个函数来实现的,C++程序中只能有一个主函数(main)

9.1 函数声明与定义

  • 函数类型-函数的返回值类型;函数名-必须符合C++标识符命名规则,后面必须跟一对括号;函数体-实现函数功能的主题部分;参数列表-函数名后面的括号内,用于向函数传递数值或带回数值。
  • 函数声明中,参数名可以省略,参数类型和函数的类型不能省略。
  • 函数声明可以放在主调函数内部,放在调用语句之前;也可以放在主调函数外,如果位于所有定义函数之前,后面函数定义顺序任意,各个主调函数调用也不必再做声明
  • 当函数定义在前,函数调用灾后,可以不用函数声明。
    后两条总结一下就是:调用函数前,程序得知道有这个函数,声明就是提前让程序知道有这么的玩意
    函数声明:
函数类型 函数名(参数列表);
eg:
int max(int a,int b);//声明函数时,a,b可以省略
int max(int,int);
void show();

函数定义:

函数类型 函数名(参数列表)

	函数体;

eg:
int max(int a,int b)

	int z;
	z = a>b?a:b;
	return z;

9.2 函数的参数与返回值

  • 形参:函数定义后面括号里的参数,函数调用前不占内存。
  • 实参:函数调用括号里的参数,可以是常量,变量或表达式等。
    形参和实参必须个数相同、类型一致,顺序一致
    函数传递方式:传值,指针,引用
        关于指针和引用后面有详细介绍。
//传值-修改函数内的形式参数对实际参数没有影响
int add(int value)

	value++;
	return value;

int main()

	int v = 10;
	cout << "add() = " << add(v) << endl;//add() = 11
		cout << "v = " << v << endl;//v = 10
	return 0;

//指针-修改形式参数会影响实际参数
int add(int* pValue)

	(*pValue)++;
	return *pValue;

int main()

	int v = 10;
	cout << "add() = " << add(&v) << endl;//add() = 11
	cout << "v = " << v << endl;//v = 11
	return 0;

//引用-修改形式参数会影响实际参数
int add(int &value)

	value++;
	return value;

int main()

	int v = 10;
	cout << "add() = " << add(v) << endl;//add() = 11
	cout << "v = " << v << endl;//v = 11
	return 0;

有默认值参数的函数

int sum(int a, int b=2)

  return (a + b);

 
int main ()

   cout << "Total value is :" << sum(100, 200);<< endl;//Total value is :300
   cout << "Total value is :" << sum(100);<< endl;//Total value is :102
   return 0;

函数的返回值

  • 返回值通过return给出,return后面跟表达式,且只能放回一个值;如果没有表达式,可以不写return;return后面的括号可有可无。
  • return语句中的表达式类型应与函数类型一致,否则自动转换类型(函数类型决定返回值类型)

9.3 函数调用

  • 函数可以单独作为一个语句使用。有返回值的函数,可将函数调用作为语句的一部分,利用返回值参与运算。
    函数调用形式:参数传递–>函数体执行–>返回主调函数
函数名(实参列表);
show();

函数的嵌套调用:

int a()

	return 666;

int b(int sum)

	return sum+a()

int main()

	cout<<b(222)<<endl;//888
	return 0;

函数的递归调用:直接递归调用和间接递归调用

  • 一个函数直接或间接递归调用该函数本身,称为函数的递归调用
  • 递归和回归:原问题=>子问题 子问题的解=>原问题的解
//直接递归调用:求1+...n的值
int total(int sum)

	if (sum == 1)
	
		return 1;
	
	return sum + total(sum - 1);

int main()

	cout << "total = " << total(10) << endl;//total = 55
	system("pause");
	return 0;

//间接递归调用
int f2();
int f1()

...
  f2()

int f2()

	f1();

9.4 函数重载

同一个函数名对应不同的函数实现,每一类实现对应着一个函数体,名字相同,功能相同,只是参数的类型或参数的个数不同。
多个同名函数只是函数类型(函数返回值类型)不同时,它们不是重载函数

int add(int a,int b)

	return a+b;

double add(double a,double b)

	return a+b;

int add(int a,int b,int c)

	return a+b+c;

9.5 内联(inline)函数

c++在编译时可以讲调用的函数代码嵌入到主调函数中,这种嵌入到主调函数中的函数称为内联函数,又称为内嵌函数或内置函数。

  • 定义内联函数时,在函数定义和函数原型声明时都使用inline,也可以只在其中一处使用,其效果一样。
  • 内联函数在编译时用内联函数函数的函数体替换,所以不发生函数调用,不需要保护现场,恢复现场,节省了开销。
  • 内联函数增加了目标程序的代码量。因此,一般只将函数规模很小且使用频繁的函数声明为内联函数。
  • 当内联函数中实现过于复杂时,编译器会将它作为一个普通函数处理,所以内联函数内不能包含循环语句和switch语句。
    内联函数格式如下:
inline 函数类型 函数名(形参列表)

	函数体;

inline int add(int a, int b)

	return a + b;

9.6 洞悉内联函数底层原理

1.使用Visual Studio 2015创建一个C++Win32控制台程序,点击项目->项目属性设置内联函数优化

2.编写内联函数代码,设置断点,debug启动

#include <iostream>
#include <string>
using namespace std;
inline int add(int a, int b)

	return a + b;//断点1

int main()

	int result = add(12, 34);
	cout << result << endl;//断点2
	return 0;

3.调试->窗口->反汇编,然后就能看到编译后的汇编程序

...
		int result = add(12, 34);
00B620DE  mov         eax,0Ch  
00B620E3  add         eax,22h  //对eax中和22h中值进行相加,赋值给eax
00B620E6  mov         dword ptr [result],eax  
		cout << result << endl;
00B620E9  mov         esi,esp  
00B620EB  push        offset std::endl<char,std::char_traits<char> > (0B610A5h)  
00B620F0  mov         edi,esp  
00B620F2  mov         eax,dword ptr [result]  
00B620F5  push        eax  
00B620F6  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0B6D098h)]  
00B620FC  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6D0A8h)]  
00B62102  cmp         edi,esp  
00B62104  call        __RTC_CheckEsp (0B611C7h)  
00B62109  mov         ecx,eax  
00B6210B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B6D0ACh)]  
00B62111  cmp         esi,esp  
00B62113  call        __RTC_CheckEsp (0B611C7h)   
		return 0;

4.从汇编代码中可以代码编译后内联函数直接嵌入到主函数中,并且断点1不会执行到,下面是没使用内联函数(去掉inline关键字)的汇编代码:

int result = add(12, 34);
00291A4E  push        22h  
00291A50  push        0Ch  
00291A52  call        add (02914D8h)  //调用add函数
00291A57  add         esp,8//移动堆栈指针esp,继续执行主函数
00291A5A  mov         dword ptr [result],eax  
		cout << result << endl;
00291A5D  mov         esi,esp  
00291A5F  push        offset std::endl<char,std::char_traits<char> > (02910A5h)  
00291A64  mov         edi,esp  
00291A66  mov         eax,dword ptr [result]  
00291A69  push        eax  
00291A6A  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029D098h)]  
		cout << result << endl;
00291A70  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (029D0A8h)]  
00291A76  cmp         edi,esp  
00291A78  call        __RTC_CheckEsp (02911C7h)  
00291A7D  mov         ecx,eax  
00291A7F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (029D0ACh)]  
00291A85  cmp         esi,esp  
00291A87  call        __RTC_CheckEsp (02911C7h)  
		system("pause");
00291A8C  mov         esi,esp  
00291A8E  push        offset string "pause" (0299B30h)  
00291A93  call        dword ptr [__imp__system (029D1DCh)]  
00291A99  add         esp,4  
00291A9C  cmp         esi,esp  
00291A9E  call        __RTC_CheckEsp (02911C7h)  
		return 0;

从以上代码代码可以看出,在主函数中调用(call)了add函数。
5.在内联函数中添加几个循环后,编译器就把内联函数当做普通函数看待了,代码如下:

inline int add(int a, int b)

	
	int sum = 0;
	for (int i = 0; i < 100; i++)
		a++;
	for (int i = 0; i < 100; i++)
	
		
		for (int i = 0; i < 100; i++)
		
			sum++;
		
	
	
	return a + b;

int main()

	int result = add(12, 34);
	cout << result << endl;
	return 0;

	int result = add(12, 34);
00181A4E  push        22h  
00181A50  push        0Ch  
00181A52  call        add (01814ECh)  ///
00181A57  add         esp,8  
00181A5A  mov         dword ptr [result],eax  
	cout << result << endl;
00181A5D  mov         esi,esp  
00181A5F  push        offset std::endl<char,std::char_traits<char> > (01810A5h)  
00181A64  mov         edi,esp  
00181A66  mov         eax,dword ptr [result]  
00181A69  push        eax  
00181A6A  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (018D098h)]  
	cout << result << endl;
00181A70  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (018D0A8h)]  
00181A76  cmp         edi,esp  
00181A78  call        __RTC_CheckEsp (01811C7h)  
00181A7D  mov         ecx,eax  
00181A7F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (018D0ACh)]  
00181A85  cmp         esi,esp  
00181A87  call        __RTC_CheckEsp (01811C7h)  
	return 0;
00181AA3  xor         eax,eax  

10 字符串(string)

10.1 C风格的字符串(字符数组)

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

  • 输入字符串长度一定小于已定义的字符数组长度,最后一位是/0终止符号;不然输出时无法知道在哪里结束。
    字符数组的定义和初始化
char a[5]
//字符个数不够,补0; 字符个数超过报错
char str[7] = 'h','e','i','r','e','n';
char str[] = 'h','e','i','r','e','n';
cin>>str;//输入 输入字符串长度一定小于已定义的字符数组长度
cout<<str;//输出

字符串的处理函数

strcat(char s1[],const char s2[]);//将s2接到s1上
strcpy(char s1[],const char s2[]);//将s2复制到s1上
strcmp(const char s1[],const char s2[]);//比较s1,s2 s1>s2返回1 相等返回1,否则返回-1
strlen(char s[]);//计算字符串s的长度 字符串s的实际长度,不包括\\0在内

10.2 C++中的字符串(string)

字符串的定义和初始化

//定义
string 变量;
string str1;
//赋值
string str2 = "ShangHai";
string str3 = str2;
str3[3] = '2';//对某个字符赋值
//字符串数组
string 数组名[常量表达式]
string arr[3];

字符串的处理函数

#include <iostream>
#include <algorithm>
#include <string>
string str;//生成空字符串
string s(str);//生成字符串为str的复制品
string s(str, strbegin,strlen);//将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值
string s(cstr, char_len);//以C_string类型cstr的前char_len个字符串作为字符串s的初值
string s(num ,c);//生成num个c字符的字符串
string s(str, stridx);//将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值
size()和length();//返回string对象的字符个数
max_size();//返回string对象最多包含的字符数,超出会抛出length_error异常
capacity();//重新分配内存之前,string对象能包含的最大字符数
>,>=,<,<=,==,!=//支持string与C-string的比较(如 str<”hello”)。  使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一得 比较,string (“aaaa”) <string(aaaaa)。    
compare();//支持多参数处理,支持用索引值和长度定位子串来进行比较。返回一个整数来表示比较结果,返回值意义如下:0:相等 1:大于 -1:
  
push_back() 
insert( size_type index, size_type count, CharT ch );//在index位置插入count个字符ch
insert( size_type index, const CharT* s );//index位置插入一个常量字符串
insert( size_type index, const CharT* s, size_type n);//index位置插入常量字符串
insert( size_type index, const basic_string& str );//index位置插入常量string中的n个字符
insert( size_type index, const basic_string& str, size_type index_str, size_type n);//index位置插入常量str的从index_str开始的n个字符
insert( size_type index, const basic_string& str,size_type index_str, size_type count = npos);//index位置插入常量str从index_str开始的count个字符,count可以表示的最大值为npos.这个函数不构成重载 npos表示一个常数,表示size_t的最大值,string的find函数如果未找到指定字符,返回的就是一个npos
iterator insert( iterator pos, CharT ch );
iterator insert( const_iterator pos, CharT ch );
void insert( iterator pos, size_type n, CharT ch );//迭代器指向的pos位置插入n个字符ch
iterator insert( const_iterator pos, size_type count, CharT ch );//迭代器指向的pos位置插入count个字符ch
void insert( iterator pos, InputIt first, InputIt last );
iterator insert( const_iterator pos, InputIt first, InputIt last );
append() 和 + 操作符
//访问string每个字符串
string s1("yuanrui"); // 调用一次构造函数
// 方法一: 下标法
for( int i = 0; i < s1.size() ; i++ )
     cout<<s1[i];
// 方法二:正向迭代器
for( string::iterator iter = s1.begin();; iter < s1.end() ; iter++)
     cout<<*iter;
 // 方法三:反向迭代器
for(string::reverse_iterator riter = s1.rbegin(); ; riter < s1.rend() ; riter++)
     cout<<*riter;
 iterator erase(iterator p);//删除字符串中p所指的字符
iterator erase(iterator first, iterator last);//删除字符串中迭代器区间[first,last)上所有字符
string& erase(size_t pos = 0, size_t len = npos);//删除字符串中从索引位置pos开始的len个字符
void clear();//删除字符串中所有字符
string& replace(size_t pos, size_t n, const char *s);//将当前字符串从pos索引开始的n个字符,替换成字符串s
string& replace(size_t pos, size_t n, size_t n1, char c); //将当前字符串从pos索引开始的n个字符,替换成n1个字符c
string& replace(iterator i1, iterator i2, const char* s);//将当前字符串[i1,i2)区间中的字符串替换为字符串s
//tolower()和toupper()函数 或者 STL中的transform算法
string s = "ABCDEFG";
for( int i = 0; i < s.size(); i++ )
     s[i] = tolower(s[i]);
transform(s.begin(),s.end(),s.begin(),::tolower);
size_t find (constchar* s, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找子串s,返回找到的位置索引,-1表示查找不到子串
size_t find (charc, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找字符c,返回找到的位置索引,-1表示查找不到字符
size_t rfind (constchar* s, size_t pos = npos) const;//在当前字符串的pos索引位置开始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串
size_t rfind (charc, size_t pos = npos) const;//在当前字符串的pos索引位置开始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符
size_tfind_first_of (const char* s, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符
size_tfind_first_not_of (const char* s, size_t pos = 0) const;//在当前字符串的pos索引位置开始,查找第一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
size_t find_last_of(const char* s, size_t pos = npos) const;//在当前字符串的pos索引位置开始,查找最后一个位于子串s的字符,返回找到的位置索引,-1表示查找不到字符
size_tfind_last_not_of (const char* s, size_t pos = npos) const;//在当前字符串的pos索引位置开始,查找最后一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串
sort(s.begin(),s.end());
substr(pos,n);//返回字符串从下标pos开始n个字符
strtok()
char str[] = "I,am,a,student; hello world!";
const char *split = ",; !";
char *p2 = strtok(str,split);
while( p2 != NULL )

    cout<<p2<<endl;
    p2 = strtok(NULL,split);

11 指针和引用

11.1 指针

指针是一个变量,其值为另一个变量的地址。即内存位置的直接地址。
声明的一般形式:

  • 数据类型是指针变量所指向的变量的数据类型,*表示其后的变量为指针变量
数据类型 *指针变量名;
int    *ip;    //整型的指针 
double *dp;    //double 型的指针 
float  *fp;    //浮点型的指针 
char   *ch;    //字符型的指针 

指针变量的初始化:

  • &是取地址运算符,&变量名表示变量的地址。
  • 变量的数据类型必须于指针变量的数据类型一致。
  • 为了安全起见,有时会把指针初始化为空指针(NULL或0)
数据类型 *指针变量名 = &变量名;
*指针变量名 = &变量名;
int a;
int *p = &a;
int *p2;
p2 = &a;

指针变量的引用:

  • & 取地址符 * 指针运算符(间接运算符),其后是指针变量,表示该指针变量所指向的变量。
  • & *的优先级是相同的,结合方式都是自左向右。比如 &*p等价于&(*p)。
int x = 3;
int y;
int *p;
p = &x;
y = *p;//y = a

指针运算(地址运算)

  • 算术运算(移动指针运算):加减,自增自减。
  • p+n运算得到的地址是p+n*sizeof(数据类型)。
  • 两个相同数据类型的指针可以进行加减运算,一般用于数组的操作中。
  • 关系运算:指针指向同一串连续存储单元才有意义,比如数组。与0比较,判断是不是空指针。
  • 赋值运算:变量地址赋值给指针变量,数组元素地址赋值给指针变量,指针变量赋值给其他指针变量。
int arr[10],len;
int *p1 = &arr[2],*p2 = &arr[5];
 len = p2-p1;//arr[2] 和arr[5]之间的元素个数 3

new和delete运算符

  • new-为变量分配内存空间;
  • 可以通过判断new返回的指针的值,判断空间是否分配成功。
  • delete-释放空间
指针变量 = new 数据类型(初值);
delete 指针变量;
delete[] 指针变量;//释放为多个变量分配的地址
int *ip;
ip= new int(1);
delete ip;
int *ip;
 ip= new int[10];
 for (int i = 0; i < 10;i++)
 
	 ip[i] = i;
 
 delete[] ip;
int a[3][4] = 0;

指针与数组