[C++ 面试基础知识总结]字符串,向量和数组

[C++ 面试基础知识总结]字符串,向量和数组

参考书籍:《C++ Primer》

目录

  • C 面试基础知识总结字符串向量和数组
    • 目录
    • string
      • string的读写
      • stringsize_type类型
      • string对象和字面值相加
    • vector容器
      • vector的初始化
      • 使用vector的注意事项
    • 迭代器
      • 迭代器运算符
      • 使用迭代器实现二分查找
    • 数组
      • 初始化和赋值
      • 字符数组
      • 数组与指针
      • C风格字符串
      • 多维数组中的指针

string

string的读写

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
    string s;
    cin >> s;
    cout << s << endl;
    return 0;
}

如果输入” Hello World! “,得到的结果为“Hello”,因为在执行读取操作时,string对象会自动忽略开头的空白(空格符,换行符,制表符等)并从第一个真正的字符开始读起,直到遇到下一处空白为止。

string::size_type类型

string类型的size()函数返回类型为string::size_type,这个类型是一个无符号类型的值,且能足够放下任何string对象的大小。所以尽量不要在一个表达式中混用string::size_type和int,不然可能会带来问题。

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
    string s = "Hello";
    int n = -1;
    bool flag = s.size() < n;
    cout << flag << endl;
    return 0;
}

从表面上看,字符串s的长度应该为5(用字面值给字符串赋值时,不会复制字面值最后的那个空字符,所以不是6),而n为-1,5>-1,flag值应为0(假),但实际输出的值却是1(真),这是由于string::size_type是一个无符号整数,在与-1进行比较的时候会将-1转换成一个很大的无符号整数,导致最后结果为真。

string对象和字面值相加

当string对象和字符串字面值混在一条语句中使用时,必须保证每个加法运算符都至少有一运算对象为string,两个字符串字面值不能直接相加。

string s1 = "Hello", s2 = "World";
//正确
string s3 = s1 + "!" + s2;
//错误 不能把字符串字面值直接相加
string s4 = "Hello" + "!" + s2;
//正确
string s5 = "Hello" + ("!" + s2);

注意:字符串字面值和string是不同的类型,如上述的s1和”Hello”的类型是不同的!

vector容器

vector的初始化

用一个整数来初始化vector<int>时,整数的含义可能是vector对象的容量也可能是元素的值,如果用圆括号,可以说提供的值是用来构造vector对象的,而用花括号,可以表述成列表初始化vector对象。具体区别如下所示:

// v1有10个元素,每个元素的值都为0(int类型的默认初始值)
vector<int> v1(10);
// v2有1元素,值为10
vector<int> v2{10};
// v3有10个元素,每个元素值都为1
vector<int> v3(10,1);
// v4有2个元素,值分别为10和1
vector<int> v4{10,1};

如果使用花括号但提供的值又不能列表初始化时,会用这样的值来构造vector对象。

// 错误,不能用字面值来构建vector对象
vector<string> v1("hi");
// v2有1元素,值为"hi",列表初始化
vector<string> v2{"hi"};
// v3有10个元素,每个元素值都为空string对象,不能列表初始化,用默认值构造vector对象
vector<string> v3{10};
// v3有10个元素,每个元素值都为"hi",不能列表初始化,用提供的值构造vector对象
vector<string> v4{10,"hi"};

使用vector的注意事项

vector的size()函数与string的同名函数功能完全一致,但要使用size_type时,需指定它是由哪种类型定义的,vector对象的类型总是包含着元素的类型。

vector<int>::size_type //正确
vector::size_type      //错误

不能用下标的形式添加元素,在vector对象中使用下标运算符时一定要保证该元素确知已存在,不然会产生很严重的后果。

vector<int> v;
for (decltype(v.size()) i = 0; i != 10; i++) {
    v[i] = 1; //错误,v初始为空,不包含任何元素
}
vector<int> v;
for (decltype(v.size()) i = 0; i != 10; i++) {
   v.push_back(1); //正确
}

迭代器

迭代器运算符

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
    string s("Hello");
    //确保字符串s非空
    if (s.begin() != s.end()) {
        auto t = s.begin();
        ++*t;
        cout << *t << endl;
        ++t;
        if (t != s.end()) {
            cout << *t << endl;
        }
    }
    return 0;
}

++*t是将t指向的字符加1,而++t是将t移动到下一个元素。所以两次输出的结果分别为I(第一个字符H加1),e(第二个字符)。

使用迭代器实现二分查找

示例,在有序的int型vector对象中找到等于2的元素,并打印出查找路径上的元素。

#include <iostream>
#include <vector>
using namespace std;

int main(int argc, const char * argv[]) {
    vector<int> v{1,2,3,4,5,6,7,8,9,10};
    auto begin = v.begin(), end = v.end();
    auto mid = begin + (end - begin)/2;
    while (mid != end) {
        cout << *mid << endl;
        if (*mid == 2) {
            cout << "success" << endl;
            break;
        }
        else if (*mid > 2){
            end = mid;
        }
        else{
            begin = mid + 1;
        }
        mid = begin + (end - begin)/2;
    }
    return 0;
}

运行结果:

6
3
2
success

需要注意的是,由于v.end()指向的是v中尾元素的的下一位置,所以第一次查找的数是6,而不是5。调整范围时end = mid; begin = mid + 1;有区别,begin是指向mid的后一个元素,而end是直接指向mid的元素。

数组

初始化和赋值

数组声明时必须初始化维度(数组中的元素个数)或进行列表初始化根据初始值的数量计算并推测出来,且维度必须是一个常量表达式(什么是常量表达式?

unsigned sz1 = 10;
constexpr unsigned sz2 = 10;
// 正确,声明一个含有10个整数的数组
int a1[10];
// 错误,sz1不是常量表达式
int a2[sz1];
// 正确,声明一个含有10个整数的数组
int a3[sz2];
// 错误,未初始化维度
int a4[];
// 正确,维度是3的数组
int a5[] = {0,1,2};
// 错误,不能用数组内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值
int a6[] = a5;

字符数组

字符串字面值结尾处有一个空字符,赋值给字符数组时会一起拷贝过去。

// 列表初始化,没有空字符
char a1[] = ['C','+','+'];
// 列表初始化,含有显式空字符
char a2[] = ['C','+','+','\0'];
// 自动添加结尾处的空字符
char a3[] = "C++";
// 错误,没有空间存放空字符
char a4[3] = "C++";

数组与指针

// 含有10个整型指针的数组
int *p1[10];
// 指向一个含有10个整数的数组的指针
int (*p2)[10];
// 指向一个含有10个整型指针的数组的指针
int *(*p3)[10];

int a[2] = {1,2};
//因为无法用数组的内容初始化另一个数组,所以用auto可以推断出p4类型为整型指针,指向a的第一个元素
auto p4 = a;

数组的指针支持vector和string的迭代器的全部运算。C++11还引入了两个名为begin和end的函数,功能与容器中的两个同名函数类似。

int a[5] = {0,2,4,6,8};

// 以下3种写法等价,都是将指针指向数组的第一个元素
int *p1 = a;
int *p2 = &a[0];
int *p3 = begin(a);

// n1值为8(将p1前进4个元素后解引的值即a[4])
int n1 = *(p1 + 4);
// n2值为4(将p1解引后加4的值即a[0]+4)
int n2 = *p1 + 4;

C风格字符串

字符数组是C风格字符串,充满安全风险,容易引发严重错误,所以推荐使用标准库string。

char a[] = {"C","+","+"};
// strlen函数是计算字符串长度的,这里会出错,因为没有以空字符结尾,函数将可能沿着数组的内存位置不断向前寻找,直到遇到空字符才停下来。
count << strlen(a) << endl;

比较和拼接字符串的时候要注意:

char s1[] = "Hello", s2[] = "World";
//错误,这里比较的将是s1和s2的地址,而不是字符串本身
if (s1 < s2)
//正确比较字符串
if (strcmp(s1,s2))

//错误,s1和s2是两个指针相加
char s3[] = s1 + s2;
//声明一个足够放下s1和s2的字符数组,如果无法估计准确,会出现安全风险
char s4[11];
//将s1复制到s4之后返回s4
strcpy(s4,s1);
//将s2加到s4之后返回s4
strcat(s4,s2);

多维数组中的指针

#include <iostream>

int main(int argc, const char * argv[]) {

    int a[2][3] = {{0,2,4},{6,8,10}};
    // 等价于 int (*p)[3] = a,p指向含有3个整数的数组
    auto p = a;
    cout << **(p+1) << endl;
    cout << *(*p+1) << endl;
    cout << (**p+1) << endl;
    return 0;
}

输出结果分别为:

6
2
1

**(p+1)是将指针p向后推进1个元素再进行两次解引,第一解引结果*(p+1)为指向数组{6,8,10}首元素的指针,再次解引则为整数6。
*(*p+1)是先将p进行解引,解引结果*p为指向数组{0,2,4}首元素的指针,往后推进1个元素后指向第二个元素,解引得整数2。
(**p+1)是将p连续解引两次,得到数组{0,2,4}首元素0,加1后得到整数1。

时间: 2024-08-03 19:49:23

[C++ 面试基础知识总结]字符串,向量和数组的相关文章

[C++ 面试基础知识总结] 顺序容器

[C++ 面试基础知识总结] 顺序容器 参考书籍:<C++ Primer> 目录 C 面试基础知识总结 顺序容器 目录 顺序容器与选择 迭代器 容器的初始化和赋值 顺序容器操作 添加元素 访问元素 删除元素 改变容器大小 迭代器失效 vector对象的增长 string 操作 改变string 搜索string 数值转换 容器适配器 栈stack 队列queue 顺序容器与选择 顺序容器类型: vector 可变大小数组 deque 双端队列 list 双向链表 forward_list 单向

[C++ 面试基础知识总结]表达式和函数

[C++ 面试基础知识总结]表达式和语句 参考书籍:<C++ Primer> 目录 C 面试基础知识总结表达式和语句 目录 运算符优先级 算数运算符 运算对象转换 商和余数 逻辑运算符 强制转换类型 数组形参和返回 不能返回局部函数的指针和引用 重载函数 预处理器变量 函数指针 运算符优先级 算数运算符 运算对象转换 小整数类型(bool,char,short)进行运算时通常会提升成较大的整数类型int. bool b = true; bool b2 = -b; 最终得到b2值为true,原因

[C++ 面试基础知识总结] 泛型算法

[C++ 面试基础知识总结] 泛型算法 参考书籍:<C++ Primer> 目录 C 面试基础知识总结 泛型算法 目录 基础泛型算法 只读算法 写容器算法 重排容器元素算法 定制操作 向算法传递函数 lambda表达式 参数绑定 特殊迭代器 插入迭代器 iostream迭代器 反向迭代器 5类迭代器 链表的特定容器算法 基础泛型算法 泛型算法本身运行于迭代器之上,不会执行容器的操作,可以改变容器中保存元素的值,也可以在容器内移动元素,但永远不会直接添加或删除元素.插入迭代器是一种特殊的迭代器,

[C++ 面试基础知识总结] 关联容器

[C++ 面试基础知识总结] 关联容器 参考书籍:<C++ Primer> 目录 C 面试基础知识总结 关联容器 目录 关联容器类型 关联容器概述 定义关联容器 关键字类型的要求 pair 关联容器操作 关联容器迭代器 添加元素 删除元素 访问元素 无序容器 关联容器类型 标准库共提供了8个关联容器 map 关联数组:保存关键字-值对 set 关键字即值,即只保存关键字的容器 multimap 关键字可重复出现的map multiset 关键字可重复出现的set unordered_map 用

[C++ 面试基础知识总结] 类

[C++ 面试基础知识总结] 类 参考书籍:<C++ Primer> 目录 C 面试基础知识总结 类 目录 类的基础 成员函数 构造函数 访问控制和封装 友元 名字查找与类的作用域 类的静态成员与普通成员 类的基础 成员函数 在成员函数中,可以直接访问数据成员,而在这个过程中实际上隐式地使用了一个名为this的隐式指针,该指针指向正是这个类对象. #include <iostream> using namespace std; struct People{ string name

Java核心技术卷I基础知识3.10 数组

3.10 数组 数组是一种数据结构,用来存储同一类型值的集合.通过一个整型下标可以访问数组中的每一个值.例如,如果a是一个整型数组,a[i]就是数组中下标为i的整数. 在声明数组变量时,需要指出数组类型(数据元素类型紧跟[])和数组变量的名字.下面声明了整型数组a:   不过,这条语句只声明了变量a,并没有将a初始化为一个真正的数组.应该使用new运算符创建数组.   这条语句创建了一个可以存储100个整数的数组.数组长度不要求是常量:new int[n]会创建一个长度为n的数组.      

[Python学习] 专题三.字符串的基础知识

        在Python中最重要的数据类型包括字符串.列表.元组和字典等.该篇主要讲述Python的字符串基础知识. 一.字符串基础         字符串指一有序的字符序列集合,用单引号.双引号.三重(单双均可)引号引起来.如:         s1='www.csdn.net'   s2="www.csdn.net"   s3='''aaabbb'''         其中字符串又包括:        1.转义字符串         像C语言中定义了一些字母前加"\

Mysqli基础知识_Mysql

       相信原来在开始学习php的时候,很多人使用的数据库首选MySQL,连接数据库的扩展首选mysql扩展,但随着php版本的提高,mysql扩展正逐渐被mysqli和PDO所取代.正如使用mysql函数时给出的deprecated: The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead.学习mysqli扩展势在必行了. 相对于mysql扩展,mysql

举例讲解JavaScript中将数组元素转换为字符串的方法_基础知识

首先来看一下从一个数组中选择元素的方法slice(): 源代码: <!DOCTYPE html> <html> <body> ​ <p id="demo">Click the button to extract the second and the third elements from the array.</p> ​ <button onclick="myFunction()">Try it