1.序列容器——数组
概念:
- 代表内存里一组连续的同类型存储区
- 可以用来把多个存储区合并成一个整体
比如:
int arr[10] = {1,2,3,4,5,6,7,8};
数组声明
:
int arr[10]
;- 类型名称int表示数组里所有元素的类型
- 名称arr是数组的名称
- 整数10表示数组里包含的元素个数
- 数组里元素个数不可以改变
2.off-by-one error数组下标
-off-by-one error(差一错误)
假定x的边界条件x>=16并且x<=37,那么此范围内x的可能取值个数有多少?
我们进行思考时有两个思考问题的原则:
- 首先考虑最简单情况的特例,然后将结果外推;
- 仔细计算边界;
x的上界与下界重合时,即x>=16 && x <=16,显示个数为1;假定下界位low,上界位high;当low与high重合时,low=high时,个数为1;据此外推,high-low+1个元素;所以这里37-16+1=22
这里最容易出错就是high-low+1;
-
使用数学上的左闭右开区间[,)来表示 范围
问题表示位:x >= 16 并且 x <= 37 -> (x >= 16 && x < 38),这样38-16=22 -
C语言中设计数组下标的原则:
从0开始,使用非对称区间;
-
1.让这个区间是一个非对称的区间[,);
-
2.让下界(左侧)可以取到值,让上界(右侧)取不到值;
这样设计的好处
:
- 取值范围的大小:上界-下界;
- 如果这个取值范围为空,上界值=下界值;
- 即使取值范围为空,上界值永远不可能小于下界值;
3.数组增删改查及二维数组
一维数组
增加:O(n)
减少:O(n)
修改:O(1)
访问:O(1)
对数组a,
数组下标index方式访问:a[2] = 5;
指针方法访问:
int * p = a;
*(p+2) = 5;
二维数组
二维数组:包含行列两个维度的数组
二维数组的访问:
int a[2][4] = {{1,2,3,4},{5,6,7,8}};
for(int row = 0;row < 2;++row)
{
for(int col = 0;col < 4;++col)
{
cout<<a[row][col]<<" ";
}
cout<<endl;
}
能否一列一列访问,当然可以!
但是tips
:循环时尽可能要满足"空间局部性":
- 在一个小的时间窗口内,
访问的变量地址越接近越好
,这样执行速度快; - 也就是说:一般来说,需要将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数;
4.动态数组vector
- vector是面向对象方式的动态数组
- vector尾部添加操作
#include<vector>
using namespace std;
vector<int> vec = {1,2,3,4};
vec.push_back(5);
- vector的遍历操作(也可以使用迭代器)
for(int index = 0;index < vec.size();++index)
{
cout<<vec[index[<<endl;
}
注意:可以使用vec的capacity
和size
方法来查看vector当前的容量和已经存储的元素个数;
- vector的插入操作:
vec.insert(--vec.end(),4);//在尾部前一个元素插入4
-
vector的删除操作:
两种方法:- vec.pop_back();
- vec.erase(vec.end()-1);//删除尾部操作,需要减1
-
两个vector合并:
std::vector<string> vec1;
std::vector<string> vec2;
//将vec2插入到vec1尾部
vec1.insert(vec1.end(),vec2.begin(),vec2.end());
vector与数组互转:
(有空添加上)
5.字符串简介
字符串变量与常量
字符串变量
- 字符串是以
空字符('\0')结束
的字符数组
- 空字符’\0’自动添加到字符串的内部表示中
- 在声明字符串变量时,
应该为这个空结束符预留一个额外元素的空间
如:char strHelloWorld[11] = {"helloworld"};
字符串变量
-
字符串变量是一对双引号括起来的字符序列
-
字符串每个字符作为一个数组元素存储
-
例如字符串"helloworld"
-
0,’\0’与’0’
在计算机内部的机器码表示:
char c1 = 0; //0x00
char c2 = '\0'; //0x00
char c3 = '0'; //0x30
字符串与字符数组互转:
(有空添加上)
6.Unicode编码
- Unicode编码:最初的目的是把世界上的文字都映射到一套字符空间中
- 为了表示Unicode字符集,有3种(确切说是5种)Unicode的编码方式:
一、UTF-8:1byte来表示字符,可以兼容ASCII码;特点是存储效率高,变长(不方便内部随机访问),无字节序问题(可作为外部编码)
二、UTF-16:分为UTF-16BE、UTF-16LE;特点是定长(方便内部随机访问),有字节序问题(不可作为外部编码)
三、UTF-32:分为UTF-32BE、UTF-32LE;特点是定长(方便内部随机访问),有字节序问题(不可作为外部编码)
编码错误的根本原因在于编码方式和解码方式的不统一;
7.字符串的指针标识
- 指针表示方法:
char* pStrHelloWrold = "helloworld";
- char[]和char*的区别,把握两点:
- 地址和地址存储的信息;
- 可变与不可变;
如:
char strHelloWorld1[11] = {"helloworld"};
strHelloWorld不可变,strHelloWorld[index]的值可变;char* pStrHelloWorld = “helloworld”;
pStrHelloWrold可变,但是pStrHelloWrold[index]的值可变不可变取决于所指区间的存储区域是否可变(比如常量区域不可以改变)
8.字符串基本操作
头文件:#include<string.h>
字符串基本操作:
- 字符串长度:strlen(s)
返回字符串s的长度(注意:s的长度不包括'\0'
) - 字符串比较:strcmp(s1,s2)
如果s1和s2是相同的,则返回0;
如果s1<s2则返回小于0;
如果s1>s2则返回大于0;
两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇到’\0’为止。 - 字符串拷贝:strcpy(s1,s2)
复制字符串s2到字符串s1 - 复制指定长度字符串:strncpy(s1,s2,n)
将字符串s2中前n个字符拷贝到s1中,可能会出现缓冲器溢出
,安全版本strncpy - 字符串拼接:strcat(s1,s2)
将字符串s2接到s1后面,可能会出现缓冲器溢出
,安全版本strcat_s - 查找字符串:strchr(s1,ch)
指向字符串s1中字符ch的第一次出现的位置 - 查找字符串:strstr(s1,s2)
指向字符串s1中字符串s2的第一次出现的位置
注:请使用strnlen_s,strcpy_s,strncpy_s,strcat_s等API函数,更安全!!!
注:按住F1,VS可以查找该方法的使用方法。
字符串操作中的问题
C中原始字符串的操作在安全性
和效率
存在一定的问题;
举例:
1.缓冲区溢出问题举例
char strHelloWorld1[11] = {"helloworld"};
char strHelloWorld2[11] = {'h','e','l','l','o','w','o','r','l','d','\0'};
strcat(strHelloWorld2,"Welcome to the C++'s world");
2.strlen的效率可以提升:空间换时间
- Redis字符串的设计:https://redis.io/