c++ day4 第四章 (一) 数组,字符串,string类

复合类型

在这里插入图片描述在这里插入图片描述

数组

在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

通用就是说可以用于所有类型

在这里插入图片描述

在这里插入图片描述

字符串(但C++常用指针来处理字符串而不是数组)

在这里插入图片描述

strlen()函数只返回字符串的长度,且不包括空字符,所以存储字符串的数组长度至少是strlen(string)+1
在这里插入图片描述
这里先介绍数组方式和string类方式两种处理字符串的方式,但是C++和C一样,实际上还是用指针处理更多。
在这里插入图片描述在这里插入图片描述
验证一下上面这段话,确实很快就遇到了空字符(实际上是存了00000000的字节),注意’\0’需要一个位置,所以b的长度是6

#include<iostream>
int main()
{
    using namespace std;
    char a[5] = {'h', 'e', 'l', 'l', 'o'};
    char b[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
    cout << a << endl;
    cout << b << endl;
    return 0;
}
hello€pB
hello

用字符串常量初始化字符数组,可以不用像上面那样写很多单引号
在这里插入图片描述

#include<iostream>
int main()
{
    using namespace std;
    char a[] = "Hello World!"; // 让编译器数数组长度
    cout << a << endl;
    return 0;
}
Hello World!

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

#include<iostream>
int main()
{
    using namespace std;
    char b[] = "b";
    cout << sizeof b << endl;
    return 0;
}

两个字符,所以长度为2

2
字符串拼接

直接把两对双引号放在一起就好了,注意添加空格如果需要的话,第一个字符串的’\0’会自动被第二个字符串的第一个字符替代

#include<iostream>
int main()
{
    using namespace std;
    cout << "Hello World!" << endl;
    cout << "Hello" "World!" << endl;
    cout << "Hello" " World!" << endl;
    return 0;
}
Hello World!
HelloWorld!
Hello World!
字符串输入
#include<iostream>
int main()
{
    using namespace std;
    char name[20] = {};
    char food[20];
    cout << "Enter your name: \n";
    cin >> name;
    cout << "Enter your favorite food:\n";
    cin >> food;
    cout << name << " , I have some delicious " << food << " for you.\n";
    return 0;
}

程序没有等我输入食物,就直接显示了后面的内容,这是因为cin 使用空格,tab和换行符确定字符串结束位置,cin读入monica之后就自动加上’\0’, 输入队列中还剩下galler,于是第二次cin没让我输入就直接输出了

Enter your name:
Monica Galler
Enter your favorite food:
Monica , I have some delicious Galler for you.

另一个问题就是当输入的字符串长度大于定义的字符串长度时,不知道为啥会原样输出monica

#include<iostream>
int main()
{
    using namespace std;
    char name[5];
    cout << "Enter your name: \n";
    cin >> name;
    cout << name;
    return 0;
}

在这里插入图片描述

这两个问题的解决需要依靠cin的一些高级特性

cin一次只能输入一个单词的解决:getline(),get(),一次输入一行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<iostream>
int main()
{
    using namespace std;
    const int charSize = 20;
    char name[charSize];
    char food[charSize];
    cout << "Enter your name: \n";
    cin.getline(name, charSize);
    cout << "Enter your favorite food:\n";
    cin.getline(food, charSize);
    cout << name << " , I have some delicious " << food << " for you.\n";
    return 0;
}
Enter your name:
minica galler
Enter your favorite food:
cheese cake
minica galler , I have some delicious cheese cake for you.

如果直接把getline()换为get()则会出错,食物又不让输入,且为空字符

Enter your name:
monica galler
Enter your favorite food:
monica galler , I have some delicious  for you.

只要多加一行就行

#include<iostream>
int main()
{
    using namespace std;
    const int charSize = 20;
    char name[charSize];
    char food[charSize];
    cout << "Enter your name: \n";
    cin.get(name, charSize);
    cout << "Enter your favorite food:\n";
    cin.get(); // 把那个换行符先读取了
    cin.get(food, charSize);
    cout << name << " , I have some delicious " << food << " for you.\n";
    return 0;
}

其实这里用到了函数重载,cin.get(),cin.get(name, charSize), 编译器通过参数列表不一样知道这是两个一个函数的不同版本,于是使用适当的成员函数。

或者使用成员函数的拼接,牛了

cin.get(name, charSize).get();//cin.get(name, charSize)可以返回一个cin对象,让他把输入队列中留下的换行符读取了
cin.getline(name, charSize).getline(food, charSize);
#include<iostream>
int main()
{
    using namespace std;
    const int charSize = 20;
    char year[charSize];
    char food[charSize];
    cout << "Enter your born year: \n";
    (cin >> year).get(); // 加get读取输入year留下的换行符,否则无法输入食物
    cout << "Enter your favorite food:\n";
    cin.get(food, charSize);
    cout << "born year: " << year << endl << "food: " << food << endl;
    return 0;
}

(cin >> year).get();说明cin >> year会返回一个cin对象!!

Enter your born year:
1997
Enter your favorite food:
apple
born year: 1997
food: apple
示例2 getline()看到换行符就认为输入结束并丢弃换行符
#include <iostream>
int main()
{
    using namespace std;
    cout << "What year was your house built?\n";
    int year;
    cin >> year;
    cout << "What is its street address?\n";
    char address[80];
    cin.getline(address, 80);//getline()看到换行符就认为输入结束并丢弃换行符
    //所以不给用户输入的机会并存了空字符串
    cout << "Year built: " << year << endl;
    cout << "Address: " << address << endl;
    cout << "Done!\n";
    return 0;
}
What year was your house built?
1997
What is its street address?
Year built: 1997
Address:
Done!

如果不用getline(),直接用cin也不行,因为cin遇到空白就不继续读了,读不了一整行

cin >> address;
What year was your house built?
1997
What is its street address?
5645 unsigned short steet
Year built: 1997
Address: 5645
Done!

解决办法是用一个get()方法提前把换行符读掉

#include <iostream>
int main()
{
    using namespace std;
    cout << "What year was your house built?\n";
    int year;
    cin >> year;
    cin.get();//把剩下的换行符读掉
    cout << "What is its street address?\n";
    char address[80];

    cin.getline(address, 80);//getline()看到换行符就认为输入结束并丢弃换行符
    //所以不给用户输入的机会并存了空字符串
    cout << "Year built: " << year << endl;
    cout << "Address: " << address << endl;
    cout << "Done!\n";
    return 0;
}
What year was your house built?
1997
What is its street address?
5646 unsigned short street
Year built: 1997
Address: 5646 unsigned short street
Done!

在这里插入图片描述
如果使用get(),那么你只需查看下一个字符是不是换行符就知道停止读取是因为遇到换行符还是因为数组装不下了,但getline()就悄咪咪的,你只能去看数组的最后一个元素是不是空字符。

string 类的初始化,赋值,拼接(和c语言字符数组的对比)

string类是98年添加到标准的,他的出现扩展了c++库,从此我们除了字符数组外,还可以用string类的对象来存储字符串,这种方式把字符串当作一种数据类型来对待,他隐藏了字符串的数组性质,(实际上还是数组哈!),用起来比字符数组简单。

string类也在名称空间std中,std::string

string类实际上本质上和字符数组没啥区别,因为字符串他就是数组,string类的实现也是把他作为数组的,存在连续内存,那这两种方式到底有没有区别呢?其实主要区别就是string类使得他的对象(字符串)是一个简单变量而不是数组,char数组是一组存储字符串的存储单元。但是string类的object是一个表示字符串的实体。

另外,在使用上的区别(或者说是string类的优势):

  • 不能把一个char数组赋值给另一个char数组,要用strcpy()函数,但是可以把一个string类对象赋值给另一个string类对象!!方便吧
  • string类对象的字符串合并只需要一个加号就搞定,字符数组则需要用strcat()函数
    在这里插入图片描述
  • string类字符串的长度可以直接用成员函数size()求出,但是字符数组方式需要用strlen()函数

而且不只是需要用库函数的问题,最重要的是使用这些函数可能出现目标字符数组空间不够导致覆盖相邻内存的问题,虽然用strncpy(),strncat()函数可以克服这个问题,但是编程方面更复杂了,所以直接用string类可以避免这些问题,安全又简单地使用字符串,何乐而不为呢

#include<iostream>
#include<string>
#include<cstring>
int main()
{
    using namespace std;

    // 列表初始化
    char a[] = {"rose home"};
    char b[]{"lily beauty"};
    string c = {"big bridge"};
    string d {"curtain hall"};
    //char e = a; //报错,数组不可以赋值给另一个数组

    // 赋值
    string c1 = c;
    char a1[30];
    strcpy(a1, a);

    //拼接
    string f = c + d;
    // c语言完成字符串拼接要先复制出来,再拼接
    char g[30]; // 必须写明大小,否则报错
    strcpy(g, a);
    strcat(g, b);

    cout << a << endl << b << endl << c << endl << d << endl << f << endl << g << endl;
    cout << "there are " << c1.size() << " characters in " << c1 << endl;
    cout << "there are " << strlen(g) << " characters in " << g << endl;
}
rose home
lily beauty
big bridge
curtain hall
big bridgecurtain hall
rose homelily beauty
there are 10 characters in big bridge
there are 20 characters in rose homelily beauty
#include <iostream>
#include <string> // c++的string类
#include <cstring> // c的string.h
int main()
{
    using namespace std;
    char charr[20];
    string str;

    cout << "length of string in charr before input: "
         << strlen(charr) << endl;
    cout << "length of string in str before input: "
         << str.size() << endl;
    cout << "Enter a line of text:\n";
    cin.getline(charr, 20);//20是最大长度,cin是istream类对象,所以getline()是istream类的方法,第一个参数是目标数组,第二个参数是数组长度以避免超越数组边界
    cout << "You entered: " << charr << endl;
    cout << "Enter another line of text:\n";
    getline(cin, str);//把cin对象作为getline()的一个参数 !!这时候没有用句点表示法,说明这个getline()不是istream类的方法!!!cin指示去哪里找输入,不给数组长度是因为string类对象可以自动调整长度
    cout << "You entered: " << str << endl;
    cout << "Length of string in charr after input: "
         << strlen(charr) << endl;
    cout << "Length of string in str after input: "
         << str.size() << endl;
    return 0;
}

有两个getline()函数,一个是string类的方法,一个是istream类的方法,这是因为历史发展造成的,C++早就有istream类,所以那时候就有了istream类的getline()方法用于处理int等基本类型,后来才有了string类和string类的对象,所以string类只好自己弄了个getline()方法处理自己的读取一行输入的需求

cin >> str;//str是string类对象,虽然istream类最开始没考虑处理string类对象(因为string类还没出来),
//但是现在可以这么做了,实际上这行代码内部使用了string类的一个友元函数,后面会讲

可以看到,char数组在声明后的长度并不是0,实际上他是不确定的,也不一定像下面这样等于3哈,因为他是找空字符,内存中平时值为0的也多,这里碰巧从存储的地址开始第四个字符就是空字符,所以长度为3,也可能长度大于数组长度20哦

但是string类对象声明后没赋值之前的长度一定是0

length of string in charr before input: 3
length of string in str before input: 0
Enter a line of text:
peanut butter
You entered: peanut butter
Enter another line of text:
blueberry jam
You entered: blueberry jam
Length of string in charr after input: 13
Length of string in str after input: 13

可以看到,char数组长度只有20,只能装下最多19个字符,最后一个d没装,且剩的字符不会停留在输入队列中,但换行符还在队列中,于是没给用户输入的机会就直接吧换行符读取了

但是string类对象就可以动态调整长度

length of string in charr before input: 3
length of string in str before input: 0
Enter a line of text:
aaaaabbbbbcccccddddd
You entered: aaaaabbbbbcccccdddd
Enter another line of text:
You entered:
Length of string in charr after input: 19
Length of string in str after input: 0
length of string in charr before input: 3
length of string in str before input: 0
Enter a line of text:
aaaaa
You entered: aaaaa
Enter another line of text:
aaaaabbbbbcccccddddd
You entered: aaaaabbbbbcccccddddd
Length of string in charr after input: 5
Length of string in str after input: 20

总之,C的那种字符数组的这些缺点如果在编程时不被重视,就很容易出错,使用string类对象就相对安全一些,减少出错概率,但是实际上二者的原理都一样,只是string类做了更多的处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值