• c风格字符串,数组,string ,vector


    c风格字符串,数组,string ,vector

    数组

    数组下标
    • 数组下标通常定义为 size_t
    type arrayName [ arraySize ][arraySize1];    arraySize必须是一个大于等于零的整数常量。
    1
    
    • 1
    • 2

    因为sizeof的计算值是个常量,所以sizeof的计算值和字符串常量可做arraySize。

    指针和数组

    使用 auto 推断数组名 得到的是指针

    使用 decltype推断数组名 得到的是数组

     int a []={1,2,3,4,5,6,7,};
     auto b =a;
     //数组名退化为  指向数组首元素的指针
      cout<<abi::__cxa_demangle(typeid(a).name(),0,0,0 )<<endl;// int [7]
     cout<<abi::__cxa_demangle(typeid(b).name(),0,0,0 )<<endl;//int*
     decltype(a) c;
     cout<<abi::__cxa_demangle(typeid(c).name(),0,0,0 )<<endl; //int [7]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    传递数组给函数

    当一维数组作为函数参数传入时,数组退化成指针。数组的数组会退化成数组的指针。
    因此,数组永远都不会真正地被传递给一个函数。

    
    
    ```
    void  myFunc1 ( int*  param )
    {
    	......	
    }
    
    void  myFunc2 ( int  param[ 10 ] )
    {
    	......
    }
    
    
    ```
    
    void  myFunc3 ( int  param[ ] )
    {
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    一维数组传入时,以上的三种写法效果是一样的,都是传入的指针

    void myFunc4 ( int param[ ][10] )
    {
    	//param实际类型是int(*)[10],即int[10]数组的指针
    }
    
    void myFunc5 ( int(*param)[10] )
    {
    	//param实际类型是int(*)[10],即int[10]数组的指针
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    多维数组传入时,变为数组的指针
    需要注意的是,虽然编译器会做调整,但我们不应将数组类型的形参声明都改为指针类型。因为数组和指针意义不同,在定义函数时不要理会编译器的调整有助于我们写出意义更加明确可读性更高的代码。

    void f(char str[])   				//这会被调整为: void f(char *str)
    {
        cout << sizeof(str) << endl;  	//str是指针,而不是数组
        if(str[0] == '\0')
            str = "none";   			//这里并不是数组被赋值,而是指针str被赋值
        cout << str << endl;
    }
    int main()
    {
        char a[3]; 
        f(a);
        a[0] = 'x';
        f(a);
        return 0;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    多维数组传入时,变为数组的指针

    在上面例子中,函数f声明了一个字符数组型的参数str,实际上在编译时str会被调整为指向字符的指针类型。在f内,尽管str被我们显式地声明为数组类型,但函数内部它就是作为一个指针来用的,sizeof操作符给出的结果是指针大小,而且我们看到str可以被直接赋值,要知道数组是没有这个特性的。

    字符数组a出现在函数调用时的实参列表中时,并不代表a本身,而是被转换为指向a首元素的指针,因此真正的实参是一个指针而非数组a。如果忽略f中sizeof那一行,那么f的功能就可被描述为:输出一个字符数组,如果字符数组为空则输出“none”。

    数组函数作为返回值

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

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

    int * myFunction()
    {
    …
    }
    
    • 1
    • 2
    • 3
    • 4
    数组的遍历
    使用库函数
        int a []={1,2,3,4,5,6,7,};
    
        for(auto i=std::begin(a); i != std::end(a); ++i)
        {
    cout<< *i<<endl;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    使用 范围for 循环
        for(auto i :a)
        {
            cout<< i<
    • 1
    • 2
    • 3
    • 4
    指针
    int main( ){
        int a[10] = {1,2,3,4,5,6,7,8,9,10};
        for (int *p=a;p!=a+10;p++){
            cout<<*p<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    注意

    使用库函数时应注意,库函数不能传入指针,要传入数组名

    不要把数组和指针搞混了,指针只能定位到头,而数组有头有尾
    
    • 1
    #include
    #include
    #include  								// 使用abi
    using namespace std;
    
    void print( int a[]){
    
    int b[100]={1,2,3};
        cout<<abi::__cxa_demangle(typeid(a).name(),0,0,0 )<<endl;// int*
        cout<<abi::__cxa_demangle(typeid(b).name(),0,0,0 )<<endl;//   int [3]
        
            for(auto i=std::begin(a); i != std::end(a); ++i)  //将报错
        {
          //cout<< *i<
        }
                for(auto i=std::begin(b); i != std::end(b); ++i)  //不会报错
        {
          //cout<< *i<
        }
    
    //https://www.zhihu.com/question/368331215
    //为何std::begin()的参数不能是一个指针?
    
    }
    
    int main(){
    
        int i[100]={1,2,3};
        print(i);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    多维指针
    constexpr size_t rowCnt =3,colCnt=4;
    int ia[rowCnt][colCnt];
    
    size_t cnt=0;
    for(auto &row :ia)
       for(auto &col :row)
       {
       
       cout<<abi::__cxa_demangle(typeid(row).name(),0,0,0 )<<endl;
                cout<<abi::__cxa_demangle(typeid(col).name(),0,0,0 )<<endl;
            
       col=cnt;
       cnt++;
       }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    第一个for 循环遍历 ia的所有元素,这些元素是大小为4 的数组,因此row 的类型就应该是含有4个整数的数组的引用
    第二个for 循环遍历那些4元素数组中的一个,因此col的类型是整数的引用
    我们之所以选择引用作为循环控制变量,是为了防止发生数组转换为指针的情况(p114 中)
    
    
    • 1
    • 2
    • 3
    • 4

    假设不用引用类型:

    for(auto row :ia)
    {
       for(auto col :row)
       {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    程序将无法通过编译。这是因为,像之前一样第一个循环遍历1a的所有元素,注意这些元素实际上是大小为4的数组。因为row不是引用类型,所以编译器初始化row时会自动将这些数组形式的元素(和其他类型的数组一样)转换成指向该数组内首元素的指针。这样得到的row的类型就是it*,显然内层的循环就不合法了,编译器将试图在一个int*内遍历,这显然和程序的初衷相去甚远(应该是int[4]内循环,因为指针(int*)是一个地址,int[4]才是一个数组,有头有尾,才能循环)。
    
    • 1

    image-20221120114603069

    image-20221120114649352

    不存在引用的数组
    (2).c++中,引用可以说只是某个变量的别名,所谓别名,是和指针类型区分开的:指针类型也可以
    指向某个变量,但指针类型本身也是一个变量,而引用实际上不是一个变量。更本质来说,可以
    理解为引用没有自身的地址,不占用内存空间(这里为了简化问题可以这样考虑)。因此,
    声明引用数组没有办法分配空间,因为根本就没有空间可以分配给引用。所以不能声明和定义引用数组
     
    (3).C++不支持传统意义的复制:
    传统的复制为:int a = b;
    这里a和b在内存中分别占用不同的内存空间,但是内容一致。
    如果int& a = b; 这个时候,内存中a并不被分配内存,所以没有复制可言。
    所以对于数组元素是引用来说,没法完成元素的复制操作,没有给数组分配内存,所以数组中的元素不能是引用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    不仅仅对于数组是这样,对于容器而言,也不存在引用的 vector list 等。。。。

    vector


    定义和初始化vector对家
    空vector
    
    vector(string>mystr:/伧创建一个string类型的空的vectorj对象(容器),目前这个mystr里不包含任何元素;
    
    mystr.push_back(abcde");
    mystr.push_back(def);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    元素拷贝初始化
    vector(string>mystr2(mystr):/把mystr元素拷贝给了mystr2;
    vector(string>mystr3=mystr:/把mystr元素拷贝给了mystr3;
    
    • 1
    • 2
    用列表初始化
    vectormystr4 ={“aa”,"bb"};用列表初始化方法给值,用花括号 {} 括起来;
    
    • 1
    创建指定数量的元素。

    有元素数量概念的东西,一般会用圆括号;

    vector il (10)  //表示10个元素,每个元素值是缺省的0;
     vector il (10 ,1 )  //表示10个元素,每个元素值是1;
    vectors23(10,"he11o”};//10个元素,每个元素的内容都是"he11o
    
    • 1
    • 2
    • 3

    特殊情况 当<参数类型> 和初始化列表中的类型不一致时

    vectors22{10}:/10个元每个元素 都是 “”.
    vectors22{10, "hello"}:/10个元每个元素 都是 “hello”
    
    • 1
    • 2

    再次强调: 不存在引用的vector


    这里引入一篇之前的vector笔记

    1–容器的创建

    #include 
    #include 
    #include
    using namespace std;
    int main()
    {
    // 1-容器创建
    // a ---不带⻓度的创建
    vector object_1;
    //在创建了空容器后可以使⽤reserve, 容器的容量是动态分配的
    object_1.resize(100);
    object_1.reserve(20);
    /*这样就设置了容器的内存分配,即⾄少可以容纳 20 个元素。注意,如果 vector 的容量在执⾏此语
    句之前,
    已经⼤于或等于 20 个元素,那么这条语句什么也不做;另外,调⽤ reserve() 不会影响已存储的元
    素,
    也不会⽣成任何元素,即 values 容器内此时仍然没有任何元素。
    */
    cout << "容量 "< object_2(100);
    // vectorobject_4(5,3);//第⼆个参数指定初始化的值,第⼀个值指定初始化值的个数 ,
    这两个参数可以是变量
    //c-----带初始化的创建
    vector object_3{1, 2, 3, 4, 5, 6, 7};
    //d------通过存储元素类型相同的其它 vector 容器,也可以创建新的 vector 容器,例如:
    vector object_4(object_3);
    cout << "i4 : ";
    for (auto i4 : object_4)
    {
    cout << i4 << " ";
    }
    //e--------在此基础上,如果不想复制其它容器中所有的元素,可以⽤⼀对指针或者迭代器来指定初
    始值的范围,例如:
    vector object_5(object_3.begin(), object_3.begin() + 3);
    cout << endl;
    cout << "i5 : ";
    2 -容器的初始化
    for (auto i5 : object_5)
    {
    cout << i5 << " ";
    }
    cout << endl;
    cout << "i7 : ";
    arraya_1{1,2,3};
    arraya_2{4,5,6};
    // vectorobject_7(a_2);//对于vector以外的容器,不能使⽤拷⻉构造,但可以使⽤迭代
    器或者指针
    vectorobject_7(a_1.begin(),a_1.begin()+3);
    for (auto i7 : object_7)
    {
    cout << i7 << " ";
    }
    object_3.begin(), object_3.begin() + 3);
    cout << endl;
    cout << "i8 : ";
    //采⽤指针---从数组中获得初值
    int b[7]={1,2,3,4,5,6,7};
    vector object_8(b,b+7);
    for (auto i8 : object_8)
    {
    cout << i8 << " ";
    }
    //f---------对于空的容器,不能使⽤迭代器初始化,
    cout << endl;
    cout << "i6 : ";
    vector object_6;
    for(auto i6=object_6.begin();i6!=object_6.end();i6++)
    {
    *i6=*i6++;
    cout<<*i6;
    }//可⻅没有打印结果。
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    2 -容器的初始化

    //对于不带⻓度的创建,只能采取push_back(),进⾏尾插
    //初始化
    #include 
    #include 
    using namespace std;
    int main()
    {
    vector str;
    // for(auto i=str.begin();inum(5);
    for(int i=0;i

3-vector容器的操作

#include
vector a,b;
//b为向量,将b的0-2个元素赋值给向量a
a.assign(b.begin(),b.begin()+3);
//a含有4个值为2的元素
a.assign(4,2);
//返回a的最后⼀个元素
a.back();
//返回a的第⼀个元素
a.front();
//返回a的第i元素,当且仅当a存在
a[i];
//清空a中的元素
a.clear();
//判断a是否为空,空则返回true,⾮空则返回false
a.empty();
//删除a向量的最后⼀个元素
a.pop_back();
//删除a中第⼀个(从第0个算起)到第⼆个元素,也就是说删除的元素从a.begin()+1算起(包括它)⼀直
到a.begin()+3(不包括它)结束
a.erase(a.begin()+1,a.begin()+3);
//在a的最后⼀个向量后插⼊⼀个元素,其值为5
注意:vector的插⼊和删除的position 左闭右开,右边的位置是不被包括的
vector 使⽤resize ⽅法和reserve⽅法之后,会销毁旧的空间,所以迭代器的值会改变,所以要重新定
义⼀次迭代器的指向
a.push_back(5);
//在a的第⼀个元素(从第0个算起)位置插⼊数值5,
a.insert(a.begin()+1,5);
//在a的第⼀个元素(从第0个算起)位置插⼊3个数,其值都为5
a.insert(a.begin()+1,3,5);
//b为数组,在a的第⼀个元素(从第0个元素算起)的位置插⼊b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin()+1,b+3,b+6);
//返回a中元素的个数
a.size();
//返回a在内存中总共可以容纳的元素个数
a.capacity();
//将a的现有元素个数调整⾄10个,多则删,少则补,其值随机
a.resize(10);
//将a的现有元素个数调整⾄10个,多则删,少则补,其值为2
a.resize(10,2);
//将a的容量扩充⾄100,
a.reserve(100);
//b为向量,将a中的元素和b中的元素整体交换
a.swap(b);
//b为向量,向量的⽐较操作还有 != >= > <= <
a==b;
//替换下标为2的元素
a.emplace(a.begin()+2,100);
//和push_back()相似的尾部插⼊的函数。但是效率快
a.emplace_back()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

注意:vector的插⼊和删除的position 左闭右开,右边的位置是不被包括的 vector 使⽤resize ⽅法和reserve⽅法之后,会销毁旧的空间,所以迭代器的值会改变,所以要重新定 义⼀次迭代器的指向

#include 
using namespace std;
#include 
int main()
{
vector aa{1, 2, 3, 4, 5};
auto i = aa.begin();
auto j = aa.end();
while (i != j)
{
cout << *i << " ";
i++;
}
aa.resize(8);//额外增加的三个数据初始化为0
//aa.reserve(8);//不会改变原有数据
cout<

vector的swap⽅法只能交换同样类型⼤小的vector容器

4-利⽤swap函数收缩⼤小(resize⽅法多删少补,但删去 ⼀个元素之后,其容量不会变小)

#include 
using namespace std;
#include 
void print(vector<int>&vv)
{
cout<<"容量" <<vv.capacity()<< "⼤小:"<<vv.size()<<endl;
}
int main()
{
vector<int> aa{1, 2, 3, 4, 5};
print(aa);
aa.resize(8);
print(aa);
aa.pop_back();aa.pop_back();aa.pop_back();aa.pop_back();aa.pop_back();aa.pop_back
();
print(aa);
vector<int>(aa).swap(aa);//匿名对象会根据对象aa实际的⼤小拷⻉⼀个匿名对象,然后匿名对象的⼤
小就是实际的size,然后匿名对象调⽤swap函数,交换实际的空间,完成空间的收缩,匿名对象的⽣命结束,
交换后的空间销毁。
//这⾥也可以使⽤ aa.shrink_to_fit();将内存减少到等于当前元素实际所使⽤的⼤小。
print(aa);
}
//输出结果
容量5⼤小:5
容量10⼤小:8
容量10⼤小:2
容量2⼤小:2

5-利⽤reserve预留空间提⾼程序效率

#include 
using namespace std;
#include 
void print(vector<int>&vv)
{
cout<<"容量" <<vv.capacity()<< "⼤小:"<<vv.size()<<endl;
6-vector的常⽤算法
}
int main()
{
vector<int>aa;
// aa.reserve(100000);
int *address=nullptr;
int num=0;
for(auto i=0;i<100000;i++)
{
aa.push_back(99);
if((&aa[0]!=address))
{
address=&aa[0];
num++;
}
}
cout<<num;
}
//num的输出结果是18,可⻅容器aa的地址改变了18次,也就是经历了18次扩⼤容量
//我们⼀开始就使⽤reserve⽅法预留⼀定⼤小的空间,就不必在运⾏时多次扩容.

6-vector的常⽤算法

#include
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进⾏从小到⼤排列
sort(a.begin(),a.end());
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为
1,3,2,4,倒置后为4,2,3,1
reverse(a.begin(),a.end());
//把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置
(包括它)开始复制,覆盖掉原有元素
copy(a.begin(),a.end(),b.begin()+1);
//在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位
置
find(a.begin(),a.end(),10);
使⽤sort排序:需要头⽂件#include,
sort(vec.begin(),vec.end());(默认是按升序排列,即从小到⼤).
可以通过重写排序⽐较函数按照降序⽐较,如下:
定义排序⽐较函数:
bool Comp(const int &a,const int &b)
{
return a>b;
}
调⽤时:sort(vec.begin(),vec.end(),Comp),这样就降序排序

7-⽤swap⽅法强⾏释放STL Vector所占内存


template < class T> void ClearVector( vector& v )
{
vectorvtTemp;
vtTemp.swap( v );
}
如
vector v ;
nums.push_back(1);
nums.push_back(3);
nums.push_back(2);
nums.push_back(4);
vector().swap(v)

c风格字符串(字符数组)****


const char *cp = "hello world";//最后有一个空字符
char cp2[] = "hello world";//最后有一个空字符
char cp3[] = { 'h', 'e' };//最后没有空字符
    const char d[]={'1','2','3'};
  cout<<strlen(d)<<endl;//8   如果不加反斜杠,strlen 将沿着d在内存中的位置不断向后寻找,直到遇上\0 . 此时的长度将不准。
  cout<<sizeof(d)<<endl;//3

    const char d[] ="123";
    cout<<strlen(d)<<endl;//3
  cout<<sizeof(d)<<endl;//4

    const char *d ="123";
   cout<<strlen(d)<<endl;//3
  cout<<sizeof(d)<<endl;//8   指针的大小

string 类型和 c 字符数组的接口

// char a[]={'1','2','3'};
//字符数组转string
char a[]="1234";
    string b=a;
    cout<<sizeof (a)<<b.size();
//    加法运算中 允许一个运算对象是字符数组,但不能全为字符数组
    string c = a+b;
    // 加法运算中 允许一个对象是字面值。但不能全为字面值
    string d=b+"waaec";

//    string 转 char 类型

const char* e =d.c_str();
cout<<e;

string 对象的操作

1.用cin获取键盘输入的值

用法很简单,和int、double等内置类型的cin一样使用。不过需要说明一点:
string对象会自动忽略开头的空白(既空格、换行符、制表符等),并从第一个真正的字符开始读入,直到遇到下一处空白
看下面的示例:

string s1;
cin >> s1;
cout << s1 << endl;

image-20221116215804203

忽略了输入Hello world前的空白。从H开始读取字符,在o后面遇到了一处空白,此时不再读取后面的字符。注意,world其实还在缓冲区内,如果再用cin读取,你无法从键盘输入,会直接读到world。

2.用getline读取一整行

getline的函数格式:getline(cin,string对象)

getline的作用是读取一整行,直到遇到换行符才停止读取,期间能读取像空格、Tab等的空白符。实例如下:

string s1;
getline(cin, s1);
cout << s1 << endl;

image-20221116220004349

注意:getline函数和cin一样,也会返回它的流参数,也就是cin,所以可以用getline循环读取一行:

string s1;
while(getline(cin, s1))
	cout << s1 << endl;
如何获取和处理string中的每个字符
1.使用下标运算符[]

直接看下面的例子:

string s = "Hello world!";
cout << s[0] << endl;
cout << s[s.size()-1] << endl;

cout << s << endl;
s[0] = 'h';
cout << s << endl;

image-20221116220106219

2.使用迭代器

看下面的例子:

string s = "Hello world!";
for (auto i = s.begin(); i != s.end(); i++){
	cout << *i  << ",";
}
cout << endl;
3.使用基于范围的for语句

string str("some string");
for (auto c : str)
	cout << c << ",";
cout << endl;

构造string对象的其他方法

1拷贝数组

形式:string s(cp,n)
解释:将cp所指的数组的前n个字符拷贝给string对象s,n为可选参数。

直接看下面的例子:
定义3个字符数组,注意: cp和cp2以空字符结尾,cp3没有空字符结尾。

const char *cp = "hello world";//最后有一个空字符
char cp2[] = "hello world";//最后有一个空字符
char cp3[] = { 'h', 'e' };//最后没有空字符

(1) string s1(cp);//s1为”hello world”,长度为11
(2) string s2(cp2);//s2为”hello world”,长度为11
(3) string s3(cp3);//因为cp3不以空字符结尾,所以这是未定义行为

(4) string s4(cp,5);//s4为”hello”,长度为5。将cp改为cp2一样
(5) string s5(cp,13);//s5为”hello world  ”,长度为13,后面有两个空字符。将cp改为cp2一样
(6) string s6(cp3,2);//s6为”he”,长度为2
2.拷贝string对象

形式:

string s(s1,pos)
string s(s1,pos,len)
解释:

第一个将s1从下标pos开始拷贝到结尾。当pos>s1.size()时,为未定义行为;当pos=s1.size(),拷贝一个空字符
第二个将s1从下标pos开始拷贝,拷贝len个字符。当pos>s1.size()时,为未定义行为;当pos=s2.size(),拷贝一个空字符
例子:

string s1("value");

(1) string s2(s1, 1);//s2为” alue”,长度为4
(2) string s3(s1, 5);//s3为””,长度为0
(3) string s8(s1, 6);// 错误,未定义的行为,抛出异常

(4) string s4(s1, 1,3);// s4为” alu”,长度为3
(5) string s5(s1, 1,8);// 正确,s5为” alue”,长度为4
(6) string s6(s1, 5,8);// s6为””,长度为0
(7) string s7(s1, 6,1);// 错误,未定义的行为,抛出异常

总结:相比于数组,拷贝string对象时,只能拷贝到string的结尾,这一点可以看数组的(5)和string对象(5)。

3.使用substr成员函数

格式:s.substr(pos,n)
解释:返回一个string对象,返回的对象包含s从pos下标开始的n个字符。pos和n均为可选参数。pos默认为下标0;n默认为s.size()-pos。
示例:

string s ("value");

(1)string s2 = s.substr();//s2为”value”,大小为5

(2)string s3 = s.substr(2);//s3为”lue”,大小为3
(3)string s4 = s.substr(5);//s3为””,大小为0
(4)string s5 = s.substr(6);//错误,s5的大小为pos = 5,小于s.size()

(5)string s6 = s.substr(1,2);// s6为”al”,大小为2
(6)string s7 = s.substr(1,7);// s7为”alue”,大小为4
(7)string s8 = s.substr(5,7);// s8为””,大小为0
(8)string s9 = s.substr(6,7);// 错误,s9的大小为pos = 5,小于s.size()

总结:和第2部分“拷贝string对象”的形式2基本相同。

string对象的insert()

string的insert操作有两种版本的insert:除了顺序容器通用的迭代器版本外还可以使用下标版本。下面分别介绍这两种版本。

迭代器insert

有4种形式:

因为是顺序容器通用的迭代器版本,这里不过多介绍,只简单举例说明一下使用。
在list容器的insert操作中,我详细介绍了insert的使用,感兴趣可以看看。C++ STL::list常用操作及底层实现(中1)——实现list常用操作之插入(insert、push_front、push_back、splice)

pos是调用insert的string对象的迭代器位置;ch是单个字符;count是ch的个数。下面是例子。

string s1("value");

s1.insert(s1.begin(), 's');//执行后,s1为"svalue"
s1.insert(s1.begin(), 1, 's');//执行后,s1为"ssvalue"
s1.insert(s1.begin(), s1.begin(), ++s1.begin());//执行后,s1为"sssvalue"
s1.insert(s1.end(), {'1','2'});//执行后,s1为"sssvalue12"

下标insert
  1. basic_string& insert( size_type index, size_type count, CharT ch )

解释:在下标index前插入count个字符ch。

例子:

string s1("value");

s1.insert(0,2,’s’); //执行后,s1为” ssvalue”
s1.insert(5,2,’s’); //执行后,s1为” valuess”

2 basic_string& insert( size_type index, const CharT* s );
basic_string& insert( size_type index, const basic_string& str );
解释:在下标index前插入一个常量字符串或者string对象。
例子:

string s1("value");
string s3 = "value";
const char* cp = "value";

s1.insert(0,s3);//执行完后,s1为" valuevalue"
s1.insert(0,cp); //执行完后,s1为" valuevalue"

3 basic_string& insert( size_type index, const basic_string& str,
size_type index_str, size_type count );
解释:在下标index前插入str中的从str[index_str]开始的count个字符
例子:

string s1("value");
string s3 = "value";
const char* cp = "value";

下面在s1的下标0前插入s3的从s3[1]开始的2个字符

s1.insert(0,s3,1,2);//执行后,s1为” alvalue”

4 basic_string& insert( size_type index, const CharT* s, size_type count );
解释:在index前插入常量字符串的count个字符

例子:

string s1("value");
const char* cp = "value";

下面在s1的下标0前插入cp的前3个字符

s1.insert(0, cp,3); //执行后,s1为” valvalue”

string对象的erase()

erase有3种形式,其方法原型如下:

例子:

string s1("value");
string s2("value");
string s3("value");
string s4("value");

s1.erase();//执行后,s1为空
s2.erase(0,2); //执行后,s2为”lue”
s3.erase(s3.begin());//执行后,s3为”alue”
s4.erase(s4.begin(),++s4.begin());//执行后,s4为”alue”

string对象的append()和replace()
append

append是在string对象的末尾进行插入操作。这一点使用+运算符也能做到。

string s("i love China!");
s.append("forever");//执行完后,s=” i love China! forever”
replace

replace可看作是erase和insert的结合体,它删除指定的字符,删除后再插入指定的字符。

和insert一样,可以通过下标或者是迭代器指定位置。

情形1:下标指定删除的位置
string s("i very love China!");

const char* cp1 = "truly";
const char* cp2 = "truly!!!";

string str1 = "really";
string str2 = "really!!!";

//1.将s从下标2开始删除4个字符,删除后在下标2处插入cp1
s.replace(2,4,cp1);//s=” i truly love China!”
//2.将s从下标2开始删除5个字符,删除后在下标2插入cp2的前5个字符
s.replace(2, 5, cp2,5); //s=” i truly love China!”
//3.将s从下标2开始删除5个字符,删除后在下标2插入str1
s.replace(2, 5, str1);//s=”i really love China!”
//4.将s从下标2开始删除6个字符,删除后在下标2插入str2从下标0开始的6个字符
s.replace(2, 6, str2,0,6);//s=”i really love China!”
//5.将s从下标2开始删除6个字符,删除后在下标2插入4个’*’字符
s.replace(2, 6, 4, '*');//s=”i **** love China!”
情形2:迭代器指定删除的位置
string s1("bad phrase");

const char* cp3 = "sample";
const char* cp4 = "sample!!!";

string str3 = "useful";
string str4 = "useful!!!";

//1.删除[s1.begin(),s1. begin()+3)区间字符,删除后插入cp3
s1.replace(s1.begin(),s1.begin()+3,cp3);//s1="sample phrase"
//2.删除[s1.begin(),s1. begin()+6)区间字符,删除后插入cp4的前6个字符
s1.replace(s1.begin(),s1.begin()+6,cp4,6);//s1="sample phrase"
//3.删除[s1.begin(),s1. begin()+6)区间字符,删除后插入str3
s1.replace(s1.begin(),s1.begin()+6, str3);//s1="useful phrase"
//4.删除[s1.begin(),s1. begin()+6)区间字符,删除后插入str4[str4.begin(),str4. begin()+6)区间字符
s1.replace(s1.begin(),s1.begin()+6, str4.begin(),str4.begin() + 6);//s1="useful phrase"
//5. 删除[s1.begin(),s1. begin()+6)区间字符,删除后插入4个’*’字符
s1.replace(s1.begin(),s1.begin()+6, 4, '*');//s1="**** phrase"
//6. 删除[s1.begin(),s1. begin()+4)区间字符,删除后插入初始化列表
s1.replace(s1.begin(), s1.begin() + 4, {'3','4','5'});//s1="345 phrase"
string对象的assign()

assign方法可以理解为先将原字符串清空,然后赋予新的值作替换。

格式如下,可以发现,就输入参数而言,和“总结insert和replace”这一节中的表的args参数是一样的,这里不在多做说明,直接给出7个例子,在例子中说明。

实例:

std::string str;
std::string base = "The quick brown fox jumps over a lazy dog.";

//1.参数形式1
str.assign(base);
std::cout << str << '\n';
//2.参数形式2:将base从下标10开始的9个字符赋值给str
str.assign(base, 10, 9);
std::cout << str << '\n';         // "brown fox"
//3.参数形式4:将"pangrams are cool"的前7个字符赋值给str
str.assign("pangrams are cool", 7);
std::cout << str << '\n';         // "pangram"
//4.参数形式3:将"c-string"赋值给str
str.assign("c-string");
std::cout << str << '\n';         // "c-string"
//5.参数形式5:将10个字符'*'赋值给str
str.assign(10, '*');
std::cout << str << '\n';         // "**********"
//6.参数形式6:将[base.begin() + 16, base.end() - 12)赋值给str
str.assign(base.begin() + 16, base.end() - 12);
std::cout << str << '\n';         // "fox jumps over"
//7.参数形式7:将初始化列表{'l','o','v','e'}赋值给str
str.assign({ 'l', 'o', 'v', 'e' });
std::cout << str << '\n';         // "love"
string对象的搜索操作

string提供6个不同的搜索函数,每个函数都有4个重载版本,所有函数的返回值都为string::size_type值,表示匹配发生位置的下标。

函数形式:

image-20221116222422184
其中args参数格式如下:

image-20221116222445651

1.find()函数的实列

下面的例子包含了args对应的4种参数形式。在后面介绍其他函数时将不在针对args都给出例子。

std::string str("There are two needles in this haystack with needles.");
std::string str2("needle");
//1.对应参数args为s2,pos
std::size_t found = str.find(str2);//返回第一个"needles"n的下标
if (found != std::string::npos)
			std::cout << "first 'needle' found at: " << found << '\n';
//2.对应参数args为cp,pos, n
found = str.find("needles are small", found + 1, 6);
if (found != std::string::npos)
	std::cout << "second 'needle' found at: " << found << '\n';
//3.对应参数args为cp,pos
found = str.find("haystack");
if (found != std::string::npos)
	std::cout << "'haystack' also found at: " << found << '\n';
//4.对应参数args为c,pos
found = str.find('.');
if (found != std::string::npos)
			std::cout << "Period found at: " << found << '\n';

// 替换第一个needle:
str.replace(str.find(str2), str2.length(), "preposition");
std::cout << str << '\n';

image-20221116222531922

2.rfind()函数实例

把最后一个”sixth”换成” seventh”。

cout << " rfind()函数:" << endl;
std::string str("The sixth sick sheik's sixth sheep's sick.");
std::string key("sixth");

std::size_t found = str.rfind(key);//找到最后一个sixth的下标
if (found != std::string::npos)
	str.replace(found, key.length(), "seventh");//替换找到的sixth

std::cout << str << '\n';

image-20221116222630032

3.find_first_of()函数例子

把str中的所有元音字母aeiou换成*。

std::string str("Please, replace the vowels in this sentence by asterisks.");
std::size_t found = str.find_first_of("aeiou");
while (found != std::string::npos)
{
	str[found] = '*';
	found = str.find_first_of("aeiou", found + 1);
}

std::cout << str << '\n';

image-20221116222712163

4.find_first_not_of函数例子

找到str中的第一个非小写字母或空格的字符。

std::string str("look for non-alphabetic characters...");

std::size_t found = str.find_first_not_of("abcdefghijklmnopqrstuvwxyz ");

if (found != std::string::npos)
{
	std::cout << "The first non-alphabetic character is " << str[found];
	std::cout << " at position " << found << '\n';
}

image-20221116222755011

string对象的compare操作

在前面,我们介绍了用比较运算符比较两个字符串的方法,同时string对象中有一个成员函数compare,它也可以比较字符串,并且有6种不同的参数形式,比较字符串时更加灵活。compare的参数形式如下:

image-20221116222946198

注意:

例子:

std::string str1("green apple");
std::string str2("red apple");

//1.str1和str2比较:参数形式1
if (str1.compare(str2) != 0)
	std::cout << str1 << " is not " << str2 << '\n';
//2.str1的下标6开始的5个字符和"apple"比较:参数形式5
if (str1.compare(6, 5, "apple") == 0)
	std::cout << "still, " << str1 << " is an apple\n";
//3.str2的下标str2.size() - 5(就是下标6)开始的5个字符和"apple"比较:参数形式5
if (str2.compare(str2.size() - 5, 5, "apple") == 0)
	std::cout << "and " << str2 << " is also an apple\n";
//4.str1的下标6开始的5个字符和str2的下标4开始的5个字符比较:参数形式3
if (str1.compare(6, 5, str2, 4, 5) == 0)
	std::cout << "therefore, both are apples\n";
//5.str1的下标6开始的5个字符和"apple pie"的前5个字符比较:参数形式6
if (str1.compare(6, 5, "apple pie",5) == 0)
	std::cout << "apple pie is maked by apple\n";

//6.str1和"poisonous apple"比较:参数形式4
if (str1.compare("poisonous apple") < 0)
	std::cout << "poisonous apple is not a apple\n";

字符串和数值间的转换

image-20221116223134400

注意:

std::string pi = "pi is " + std::to_string(3.1415926);
std::string perfect = std::to_string(1 + 2 + 4 + 7 + 14) + " is a perfect number";
std::cout << pi << '\n';
std::cout << perfect << '\n';

image-20221116223217844

stoi(s,p,b) 例子(stol stoll stoul stoull类似,不再举例)
std::string str_dec = "2001, A Space Odyssey";
std::string str_hex = "40c3";
std::string str_bin = "-10010110001";
std::string str_auto = "0x7f";

std::string::size_type sz;   // alias of size_t

//1.转换基数为10进制,sz保存','下标,i_dec = 2001
int i_dec = std::stoi(str_dec, &sz);
//2.转换基数为16进制。所以i_hex = 0x40c3,十进制为16579
int i_hex = std::stoi(str_hex, nullptr, 16);
//3.转换基数为2进制。所以i_bin = -10010110001B,十进制为-1201
int i_bin = std::stoi(str_bin, nullptr, 2);
//4.自动确定 转换基数
int i_auto = std::stoi(str_auto, nullptr, 0);

std::cout << str_dec << ": " << i_dec << " and [" << str_dec.substr(sz) << "]\n";
std::cout << str_hex << ": " << i_hex << '\n';
std::cout << str_bin << ": " << i_bin << '\n';
std::cout << str_auto << ": " << i_auto << '\n';


stof(s,p)例子

image-20221116223305879

cout << "stof示例" << endl;
std::string orbits("686.97 365.24");
std::string::size_type sz;     // alias of size_t

//1.mars = 686.97,sz保存空格下标
float mars = std::stof(orbits, &sz);
//1.将" 365.24"转换为float类型,earth = 686.97
float earth = std::stof(orbits.substr(sz));
std::cout << "One martian year takes " << (mars / earth) << " Earth years.\n";

umber";
std::cout << pi << ‘\n’;
std::cout << perfect << ‘\n’;


[外链图片转存中...(img-Q4PYhWu9-1668916254005)]

```c
stoi(s,p,b) 例子(stol stoll stoul stoull类似,不再举例)
std::string str_dec = "2001, A Space Odyssey";
std::string str_hex = "40c3";
std::string str_bin = "-10010110001";
std::string str_auto = "0x7f";

std::string::size_type sz;   // alias of size_t

//1.转换基数为10进制,sz保存','下标,i_dec = 2001
int i_dec = std::stoi(str_dec, &sz);
//2.转换基数为16进制。所以i_hex = 0x40c3,十进制为16579
int i_hex = std::stoi(str_hex, nullptr, 16);
//3.转换基数为2进制。所以i_bin = -10010110001B,十进制为-1201
int i_bin = std::stoi(str_bin, nullptr, 2);
//4.自动确定 转换基数
int i_auto = std::stoi(str_auto, nullptr, 0);

std::cout << str_dec << ": " << i_dec << " and [" << str_dec.substr(sz) << "]\n";
std::cout << str_hex << ": " << i_hex << '\n';
std::cout << str_bin << ": " << i_bin << '\n';
std::cout << str_auto << ": " << i_auto << '\n';


stof(s,p)例子

[外链图片转存中…(img-HQTpXK9H-1668916254006)]

cout << "stof示例" << endl;
std::string orbits("686.97 365.24");
std::string::size_type sz;     // alias of size_t

//1.mars = 686.97,sz保存空格下标
float mars = std::stof(orbits, &sz);
//1.将" 365.24"转换为float类型,earth = 686.97
float earth = std::stof(orbits.substr(sz));
std::cout << "One martian year takes " << (mars / earth) << " Earth years.\n";
  • 相关阅读:
    Github 星标 57.9K!阿里巴巴 Java 面试突击汇总(全彩版)首次公开
    Java框架 SpringMVC--完全注解配置
    生产者消费者模型
    (10)svelte 教程:Slots
    【Vue基础】路由以及axios详解与使用案例
    渗透测试 | IP信息收集
    【Android安全】安卓app 防篡改方案总结
    深度优先搜索遍历与广度优先搜索遍历
    chatglm.cpp使用手记
    存储压测工具— — Cosbench教程
  • 原文地址:https://blog.csdn.net/qq_55125921/article/details/127947240