目前为止我们使用的库函数基本都属于命名空间 std,如 std::cin
、std::cout。其中::
我们称其为作用域操作符,编译起编译起从操作符左侧名字的作用域寻找右侧那个名字。
但是上面很繁琐、允许我们通过 using 声明
//example1.cpp
#include<iostream>
using std::cout;
using std::endl;
int main(int argc,char**argv){
const char*name="gaowanlu";
<<name<<endl;//gaowanlu
coutstd::cout<<name<<std::endl;//gaowanlu
return 0;
}
这样在这个 cpp 内使用 std::cout 与 std::endl 时就可以省略写 std::了,但是仍然允许我们显式指定其明明空间
头文件一般不使用 using 声明,因为头文件的内容会被拷贝到,include 它的 cpp 去,如果头文件有 using 声明,则那些 cpp 内也会有这些 using 声明,可能会引起明明冲突
//example2.h
#ifndef __EXAMPLE2_H__
#define __EXAMPLE2_H__
using std::cout;
#endif
当第 4 行代码不被注释掉时,则会引入 using std::cout; 当 main 函数内使用 cout,编译器则不会知道知道我们要使用 std::cout 还是自定义的 cout,进而产生命名出错
总之不要在头文件内使用 using 声明
//example2.cpp
#include<iostream>
#include<cstdio>
//#include"./example2.h"
int cout(){
("printf hallo");
printf}
int main(int argc,char**argv){
();//printf hallo
coutreturn 0;
}
首先要导入 #include<string>
其命名空间为
std::string
在 C 语言中是没有字符串类型的,但可以用字符数组进行存储,以
\0
表示字符串结束
6 种直接初始化方式、1 种拷贝初始化方式
1、string s5; //空串
2、string s6(s5); // s6为s5的副本 也就是拷贝s5到s6
3、string s7 = "ak47"; // s7为字面值的副本
4、string s8 = s7;
5、string s9("94");
6、string s10(1, 'h');
7、string s11=std::string("hello world");
样例程序
//example3.cpp
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
int main(int argc, char **argv)
{
; //默认初始化为空字符串
string s1= "gaowanlu";
string s2 = s2; // s3为s2内容的副本
string s3 (4, 'a');
string s4<< s2 << endl; // gaowanlu
cout << s3 << endl; // gaownalu
cout << s4 << endl; // aaaa
cout
// string的7种初始化方式
// 6种直接初始化方式
; //空串
string s5(s5); // s6为s5的副本 也就是拷贝s5到s6
string s6= "ak47"; // s7为字面值的副本
string s7 = s7;
string s8 ("94");
string s9(1, 'h');
string s10<< s5 << endl; //
cout << s6 << endl; //
cout << s7 << endl; // ak47
cout << s8 << endl; // ak47
cout << s8 << endl; // ak47
cout << s9 << endl; // 94
cout << s10 << endl; // h
cout // 1种拷贝初始化
= std::string("hello world");
string s11 << s11 << endl; // hello world
cout return 0;
}
可以使用 basic_string 模板类
#include <string>
using namespace std;
int main(int argc, char **argv)
{
<char16_t> str1;
basic_string<char32_t> str2;
basic_string<wchar_t> str3;
basic_string.append(u"cdsfd");
str1.append(U"cdfd");
str2.append(L"vfdvd");
str3<< str3 << endl; // vfdvd
wcout return 0;
}
如果我们需要存储含有\0 的字符串数据,请勿使用 std::string 进行存储,因为一旦使用它是从\0 后的字符进行了忽略,例如在 http 的报文内如果请求体或响应体内为二进制数据,那么我们按照字符来读取,极有可能造成大祸,甚至一整天不知道 bug 在哪里,所以我们应该在学习的时候就知道这回事
//example41.cpp
#include <iostream>
#include <string>
int main(int argc, char **argv)
{
std::string str = "bcjhdf\0fej";
std::cout << str.length() << std::endl; // 6
std::string str1 = str + "sc\0ncjsk";
std::cout << str1 << std::endl; // bcjhdfsc
return 0;
}
在 C++中 string 是一种标准库里的对象,其支持丰富的操作
//example4.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
("hello world");
string s1= "hello world"; string s2
支持写入到输出流 outputstream<<str
<< s1 << endl; // hello world cout
同理支持从输入流写入到字符串 inputstream>>s1
// cin>>s1;
从输入流中读取一行到字符串 getline(inputstream,str)
// getline(cin, s1); //这里我们使用标准输入流
// cout << s1 << endl;
检测字符串是否为空字符串 str.empty()
<< s1.empty() << endl; // false 则s1不为空 cout
获取字符串长度
<< s1.size() << endl; // 11 cout
获取第 n 个字符的引用 n 0 开始为第一个字符
char &ch = s1[0];
= 'p';
ch [3] = 'k';
s1<< s1 << endl; // pelko world cout
字符串拼接
= s1 + s2;
string s3 +="";//支持+=
s3<< s3 << endl; // pelko worldhello world
cout << s3 + "HAHA" << endl; // pelko worldhello worldHAHA cout
字符串复制
= s3; string s4
s4 与 s3 没有关系,只是内容相同,它们的数据存放在不同的内存上面
<< s4 << endl; // pelko worldhello world cout
字符串的比较
<< (s3 == s4) << endl; // 1 即true
cout << (s3 != s4) << endl; // 0 即false cout
字典顺序比较
= "abcd";
string s5 = "abda";
string s6 << (s5 < s6) << endl; // 1 abcd abda c<d
cout << (s5 <= s6) << endl; // 1 abcd abda c<=d
cout << (s5 > s6) << endl; // 0 abcd abda c<d
cout << (s5 >= s6) << endl; // 0 abcd abda c<=d
cout return 0;
}
//example5.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
;
string s1while (getline(cin, s1)) // getline返回文件到达末尾也就是cin流的内容是否全部到头
{
<< s1 << endl;
cout }
//只有退出程序时cin才会关闭以至于getline返回false
//与其类似的操作还有
/*
while (cin >> s1)
{
cout << s1 << endl;
}*/
return 0;
}
其字符串 size()方法返回值用什么类型存储比较好,C++为我们提供了 std::string::sizetype 类型
//example6.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
std::string s1("hello");
std::string::size_type s1_length = s1.size();
<< s1_length << endl; // 5
cout << sizeof(std::string::size_type) << endl; // 4
cout
// str.size()返回一个无符号整数
//当然我们可以使用我们前面学到的auto 与 decltype
auto l1 = s1.size();
decltype(s1.size()) l2 = s1.size();
<< l1 << endl; // 5
cout << l2 << endl; // 5
cout
//当然可以用unsigned 或者 int
unsigned l3 = s1.size();
int l4 = s1.size();
<< l3 << " " << l4 << endl; // 5 5
cout return 0;
}
字符串字面值不能与字符串字面值相加、相加对于 string 对象有效,即+号的左右至少有一个 string 对象
//example7.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
//字符串对象与字符串对象相加返回字符串对象
//字符串对象与字面值相加返回字符串对象
//字面值与字面值相加发生错误
// string s1 = "12" + "sdc";
// invalid operands of types 'const char [3]' and 'const char [4]' to binary 'operator+'
//字面值被作为const char[] 处理
= "a";
string s2 = s2 + " b " + "c ";
s2 //(s2+" b ")+"c "
<< s2 << endl; // a b c
cout
return 0;
}
这是为什么呢,C++为了与 C 语言兼容,所以 C++语言中的字符串并不是作为 std::stirng 对象处理的
#include<cctype>
(c);//当c是字母或数字时为真
isalnum(c);//当c是字母时返回真
isalpha(c);//当c是控制字符时为真
iscntrl(c);//当c是数字时为真
isdigit(c);//当c不是空格但可以打印时为真
isgraph(c);//当c是小写字母时为真
islower(c);//当c是可打印字符时为真(即c是空格或c具有可视化形式)
isprint(c);//当c是标点符号时为真(不是控制字符、数字、字母、可打印空白)
ispunct(c);//当c是空白时为真(即是空格、横向制表符、纵向制表符、回车符、换行符、进纸符)
isspace(c);//当c为大写字母时为真
isupper(c);//当c是十六进制数字时为真
isxdigit(c);//如果c是大写字母,输出对应的小写字母,否则原样输出c
tolower(c);//如果是小写字母、输出对应的大写字母,否则原样输出 toupper
如 toupper 使用
//exmaple8.cpp
#include <iostream>
#include <cctype>
using namespace std;
int main(int argc, char **argv)
{
= "abc";
string s1 [1] = toupper(s1[1]);
s1<< s1 << endl; // aBc
cout return 0;
}
C++对于字符串的遍历支持迭代器模式
//example9.cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main(int argc, char **argv)
{
= "abc";
string s1 for (char &ch : s1)
{
<< ch << endl; // abc
cout = toupper(ch);
ch }
<< s1 << endl; // ABC
cout //当然我们可以使用auto
for (auto ch : s1)
{
<< ch << endl; // ABC
cout = tolower(ch);
ch }
<< s1 << endl; // ABC
cout //可见auto是类型char而不是char&
return 0;
}
//example10.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
= "abcd";
string s1 //例如访问最后一个字符
if (s1.empty() == false)
{
<< s1[s1.size() - 1] << endl; // d
cout }
//字符下标索引从0开始
// a b c d
// 0 1 2 3
return 0;
}
std::vector 表示对象的集合,其所有元素类型相同,每个集合中对每个对象有唯一的对应索引,用于随机访问,因为 vector 容纳其他对象,所以也被称为容器。其背后有一个重要的概念叫做类模板的东西在支持着它,类模板是 C++特性之一,其非常强大。
std::vector<T> T 可以为任意数据类型
七种初始化方式 以 T 为 int 为例
vector<int> v1; //空vector
vector<int> v2(v1); //拷贝v1 v2拥有v1元素的副本
vector<int> v3 = v2; //拷贝v2 v3拥有v2元素的副本
vector<int> v4(5, 10); // vector内有5个10
vector<int> v5(5); // 5个int类型初始默认值元素即有5个0
vector<int> v6 = {1, 2, 3, 4}; // 元素序列为1 2 3 4
vector<int> v7{1, 2, 3, 4}; //等价于vector<int> v6={1, 2, 3, 4}
使用样例
//example11.cpp
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
void printIntVector(vector<int> &v);
int main(int argc, char **argv)
{
<int> v1; //空vector
vector<int> v2(v1); //拷贝v1 v2拥有v1元素的副本
vector<int> v3 = v2; //拷贝v2 v3拥有v2元素的副本
vector<int> v4(5, 10); // vector内有5个10
vector<int> v5(5); // 5个int类型初始默认值元素即有5个0
vector<int> v6 = {1, 2, 3, 4}; // 元素序列为1 2 3 4
vector<int> v7{1, 2, 3, 4}; //等价于vector<int> v6={1, 2, 3, 4}
vector(v1); //
printIntVector(v2); //
printIntVector(v3); //
printIntVector(v4); // 10 10 10 10 10
printIntVector(v5); // 0 0 0 0 0
printIntVector(v6); // 1 2 3 4
printIntVector(v7); // 1 2 3 4
printIntVectorreturn 0;
}
/**
* @brief 打印vector<int>元素
*
* @param v vector<int>
*/
void printIntVector(vector<int> &v)
{
for (int i = 0; i < v.size(); i++)
{
<< v[i] << " ";
cout }
<< endl;
cout }
vector 允许我么在定义初始化后,对其内部的元素再进行操作,例如向其后面追加元素
//example12.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void printStringVector(vector<string> &v)
{
for (int i = 0; i < v.size(); i++)
{
<< v[i] << " ";
cout }
<< endl;
cout }
int main(int argc, char **argv)
{
<string> v1 = {"a", "b", "c"};
vector(v1); // a b c
printStringVector.push_back("d");
v1.push_back("e");
v1(v1); // a b c d e
printStringVector<string> v2 = v1; //拷贝v1到v2
vector[0] = "p";
v2(v1); // a b c d e
printStringVector(v2); // p b c d e
printStringVectorreturn 0;
}
vector 提供的方法与 string 提供的方法类似,可以向上翻到 string 进行对比学习
vector.empty()、vector.size()、vector.push_back(T)、下标引用、拷贝、列表替换、==、!=、<、<=、>、>=
//example13.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<char> v;
vector.empty(); //如果v不含任何元素,则返回真,否则返回假
v.size(); //返回元素个数
v.push_back('a'); //向末尾追加元素
vchar &ch_index_0 = v[0]; //获取第n个位置元素的引用
<char> v1;
vector= v; //将v内的元素拷贝给v1
v1 = {'a', 'b', 'c'}; //使用列表替换vector内存储的内容
v1 <char> v2(v1);
vector//当vector二者元素个数相同,且每个位置上一一对应都是相等的则为true否则为false
<< (v1 == v2) << endl; // 1
cout << (v1 != v2) << endl; // 0
cout //二者元素数量不同或者对应位置有元素不相等返回true,否则返回false
= {'a', 'b', 'c'};
v1 = {'a', 'd'};
v2 //支持字典顺序比较
<< (v1 <= v2) << endl; // 1
cout << (v1 < v2) << endl; // 1
cout << (v1 >= v2) << endl; // 0
cout << (v1 > v2) << endl; // 0
cout return 0;
}
关于下标访问,有一点我们必须要知道下标的范围是从 0 开始到 vector.size()-1,无论新开发者还是有经验的大佬,在写程序时预检下标访问越界问题都是很常见的
//example14.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<char> v{'a', 'b', 'c'};
vector//下标访问
for (vector<int>::size_type i = 0; i < v.size(); i++)
{
<< v[i] << endl; // a b c
cout }
// vector<int>::size_type背后是
// typedef std::size_t std::vector<int>::size_type的功劳
//迭代器访问
for (auto &item : v)
{
<< item << endl; // a b c
cout += 1;
item }
for (auto item : v)
{
<< item << endl; // b c d
cout }
//当然可以不用auto
for (char item : v)
{
<< item << endl; // b c d
cout }
return 0;
}
迭代器是什么,不要慌迭代器在下一小节即将学习
只要知道我们不能在下标访问时发生访问越界就好了
//example15.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<int> v; //空vector
vectordecltype(v.size()) i = 0;
//错误方式 向数组添加元素
// for (; i < 10; i++)
// {
// v[i] = i;
// }
//我们会发现其本质还是下标访问越界了
//要向vector中追加元素就要使用vector.push_back()
//否则只能使用下标0到vector.size()-1的位置的空间
for (i = 0; i < 10; i++)
{
.push_back(i);
v}
for (auto item : v)
{
<< item << endl;
cout }
// 0 1 2 3 4 5 6 7 8 9
return 0;
}
因为 std::vector<T>可以 T 可以为任意类型,那么 T 可以为 vector 也是情理之中的
//example16.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<vector<int>> v;
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {4, 5};
vector.push_back(v1); //将向量添加到vector v
v.push_back(v2);
v//下标访问vector v内的元素,其元素类型为vector<int>
<int> &item_1 = v[0];
vectorauto &item_2 = v[1];
<< item_1.size() << endl; // 3
cout << item_2.size() << endl; // 2
cout << v.size() << endl; // 2
cout return 0;
}
vector 还有许多有用的操作、我们后学进行学习、慢慢地展开循序渐进学习
我们学过我们可以通过下标来访问 string 的字符、vector 的元素的引用。有一种更通用的方式叫做迭代器,迭代器不是仅仅限于 vector 的,其他的容器等也都支持。string 是字符串不是容器,但其也支持迭代器的使用。
begin()获取容器第一个位置迭代器
end()获取容器最后一个元素后面一个位置的迭代器
--、++、-、+、+=、-= 等迭代器移动
== 迭代器比较
= 迭代器赋值
-> 使用元素内部成员
* 解引用操作
//example17.cpp
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<int> v{1, 2, 3, 4};
vectorstd::vector<int>::iterator start = v.begin(); // start指向第一个元素
auto end = v.end(); // end指向最后一个元素的下一个位置
//*iterator获取元素引用
int &firstEl = *start; //获取start所在位置元素的引用
<< firstEl << endl; // 1
cout = 9;
firstEl << v[0] << endl; // 9
cout
// iter->mem 获取元素的成员
<string> v1 = {"hello", "world", "ok"};
vectorauto v1_b = v1.begin();
<< v1_b->size() << endl; // 5
cout << (*v1_b).size() << endl; // 5
cout
//++iter 让迭代器向后移动一个位置
++;
v1_b<< *v1_b << endl; // world
cout //--iter 让迭代器向前移动一个位置
--;
v1_b<< *v1_b << endl;
cout //+ 与 - 数值 移动迭代器多少次
+= 2;
v1_b << *v1_b << endl; // ok
cout
// iter1==iter2 判断迭代器是都相等
++;
v1_b<< (v1_b == v1.end()) << endl; // 1
cout
// iter1!=iter2 判断迭代器是否不相等
<< (v1_b != v1.end()) << endl; // 0
cout
//遍历
for (auto b = v1.begin(); b != v1.end(); b++)
{
<< *b << " length " << b->size() << endl;
cout // hello length 5 \n world length 5 \n ok length 2
}
return 0;
}
不同容器有不同类型的迭代器类型,string 类型的迭代器为 C++为我们指定好的为 string::iterator 类型,string 与 vector 类似支持 begin()与 end()方法
//example18.cpp
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
//存放int类型的vector
<int> v{1, 2, 3};
vectorstd::vector<int>::iterator b = v.begin();
//当然可以使用decltype与auto进行自动推断类型
decltype(v.begin()) b1 = v.begin();
auto b2 = v.begin();
<< *b1 << endl; // 1
cout
// string迭代器
= "hello";
string str ::iterator str_iter = str.begin();
stringwhile (str_iter != str.end())
{
<< *str_iter << endl; // hello
cout *str_iter = 'N';
++;
str_iter}
<< str << endl; // NNNNN
cout return 0;
}
可见 iterator 允许我们进行*操作得到相应元素的引用、进而我们可以改变元素的值,有时我们需要 const 的功能,不允许使用迭代器改变元素,只能读,这时候就要派 const_iterator 上场了
//example19.cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
<int> v{1, 2, 3};
vector<int>::const_iterator b = v.begin();
vectorwhile (b != v.end())
{
<< *b << endl; // 1 2 3
cout //*b = 9;
// assignment of read-only location 'b.__gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator*()'
++;
b}
= "hello";
string str ::const_iterator ch = str.begin();
stringwhile (ch != str.end())
{
//*ch = 'P';
// assignment of read-only location 'ch.__gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >::operator*()'
<< *(ch++) << endl; // hello
cout }
return 0;
}
const vector 的迭代器类型 const_iterator,显式获取 const_iterator cbegin()与 cend()
//example20.cpp
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
const vector<int> v1 = {1, 2, 3};
<int> v2 = {4, 5, 6};
vector// v1 = v2;
// passing 'const std::vector<int>' as 'this' argument discards qualifiers [-fpermissive]
// v1是不可变的
const vector<int> v3 = {7, 8, 9};
// const vector的迭代器
// vector<int>::iterator b = v3.begin();
// conversion from '__normal_iterator<const int*,[...]>' to non-scalar type '__normal_iterator<int*,[...]>' requested
//可见begin与end返回的不是普通的iterator而是const_iterator
<int>::const_iterator b1 = v3.begin();
vector//*b1 = 4;
// assignment of read-only location 'b1.__gnu_cxx::__normal_iterator<const int*, std::vector<int> >::operator*()'
// cosnt_iterator不允许使用迭代器改变元素
//如何指定获取const_iterator 可以使用cbegin() 与 cend()
auto v2_b = v2.cbegin();
auto v2_e = v2.cend();
//*v2_b = 1; //error
return 0;
}
改变 vector 长度的操作会使得迭代器失效,也就是当 vector 的 size 发生改变时我们仍要使用迭代器就要重新使用 begin 或者 end 方法获取新的迭代器
//example21.cpp
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<int> v = {1, 2, 3};
vectorauto b = v.begin();
// for (int i = 0; b != v.end(); b++, i++)
// {
// v.push_back(i);
// cout << *b << endl;
// }
//会输出乱序的数值序列,并且程序崩溃
// vector的size改变后,原来的迭代会失效
<int> v2 = {5, 6, 7};
vector<int>::iterator v_b = v.begin();
vector.push_back(8);
v<< *v_b << endl; // 9674392
cout return 0;
}
迭代器支持 +、-、+=、-=、<、>、<=、>=等操作 在算数运算中、iterator 犹如一个存放当前下标数字类型
//example22.cpp
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
<int> v = {1, 2, 3};
vectorauto b = v.begin();
auto e = v.end();
<< e - b << endl; // 3
cout << b - e << endl; //-1
cout /* el 1 2 3
* index 0 1 2 3
* b e
*/
// 3-0=3 0-3=-3
//这样只是辅助我们理解,其背后的元素不是这样的
//同理iter+=n; iter=iter+n; ++iter; 就是将iterator向右移动几下
// iter-=n; --iter; iter=iter-n; 就是向左移动
//迭代器的算术运算返回的是迭代器而不是数字,数字只是辅助我们理解
//如
auto r = b + (e - b) / 2;
<< *r << endl; // 2 0+(3-0)/2=3/2=1 即得到index=1位置的迭代器
cout
//同理 比较运算也是类似于index的比较
<< (b > e) << endl; // 0 即false
cout return 0;
}
数组是一块连续内存、将这些内存分割成相同的大小、每个大小为数据类型需要的大小。
//example23.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int agrc, char **argv)
{
int arr[10]; //元素类型为int的大小为10的数组
int *parr[10]; //元素类型为int*
constexpr int size = 10;
int arr1[size];
//默认情况数组内元素被初始化按照元素类型的默认初始化值
//显示初始化数组
int arr1[5] = {0, 1, 2, 3, 4};
int arr2[] = {0, 1, 2};
int arr3[5] = {0, 1, 2}; // arr3[0]=1,arr3[1]=1,arr3[2]=2
std::string arr4[3] = {"hello", "world", "tom"};
return 0;
}
字符数组可以用来存储 C 风格字符串
在 C++规范内,在定义数组时[]内必须为常量表达式(但有的编译器允许使用变量),当常量表达式为空是定义数组必须使用列表赋值={elements…}进行初始化。
//example24.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int agrc, char **argv)
{
char a3[] = "HELLO"; // a3={'H','E','L','O','\0'}
[1] = 'P';
a3<< a3 << endl; // HPLLO
cout const char a4[6] = "HELLO";
<< a4 << endl; // HELLO
cout // a4 = nullptr; error: assignment of read-only variable 'a4'
//数组不允许拷贝和对数组赋值
/*
int a5[] = {0, 1, 2};
int a6[] = a;
a6 = a5;*/
return 0;
}
值得一提的是引用没有数组,即不存在引用的数组
//example25.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int agrc, char **argv)
{
int *ptrs[10]; //存储10个int*
// int &refs[10]; //不能使用数组存储int&
int arr[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int(*arr1)[10] = &arr; // arr1指向一个含有10个整数的数组
int(&arr2)[10] = arr; // arr2引用一个含有10个整数的数组
<< *(arr1)[0] << endl; // 0
cout << arr2[1] << endl; // 1
cout int *(&array)[10] = ptrs; // array是数组的引用、该数组存储10个int类型的指针
return 0;
}
一看到这么多的、肯定学习的同学马上放弃 C++、想要理解数组声明的含义,好办法是从数组的名字开始按照由内向外顺序阅读。
//example26.cpp
#include <iostream>
#include <string>
#include <stddef.h>
using namespace std;
int main(int agrc, char **argv)
{
//数组的元素访问方式为下标访问
//与vector一样下标是从0开始的
int arr[10] = {1};
for (int i = 0; i < 10; i++)
{
[i] = i;
arr}
for (size_t i = 0; i < 10; i++)
{
<< arr[i] << " ";
cout }
<< endl;
cout // 0 1 2 3 4 5 6 7 8 9
//数组的索引值通常使用 size_t 类型表示
//其本质为 unsigned long long 类型
//其定义在<stddef.h>
//虽然表示的范围比较大、但是它需要的内存空间为4字节
return 0;
}
数组在 C++同样支持迭代器模式,当然我们仍可使用 for 循环配和下标访问
//example27.cpp
#include <iostream>
#include <string>
#include <stddef.h>
using namespace std;
int main(int agrc, char **argv)
{
constexpr int size = 5;
[size] = {};
string arrfor (auto &str : arr)
{
<< str << endl; //输出空字符串
cout = "HELLO";
str }
for (auto str : arr)
{
<< str << endl; //五个HELLO
cout }
for (int i = 0; i < size; i++)
{
<< arr[i] << endl; //五个HELLO
cout }
return 0;
}
在 C 语言中,指针与数组有很大的联系
//example28.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
[] = {"1", "2", "3"};
string nums*p = nums;
string << *p << endl; //
cout //本质上nums是数组第一个元素的地址
//且数组的内存是连续的
<< *(p + 1) << endl; // 2
cout // p+1即可得到nums数组第二个元素的地址
//配和auto使用
// auto p1 = nums;
auto p1(nums);
<< *p1 << endl; // 1
cout
//&nums[0]与nums等价
<< *(&nums[0]) << endl; // 1
cout
decltype(nums + 0) ptr = nums + 1;
<< *ptr << endl; // 2 nums+0返回的是string*类型
cout //指针也是迭代器
++;
ptr<< *ptr << endl; // 3
cout
return 0;
}
//example29.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
//尽管我们可以通过&array[0]、&array[n-1]获得尾后指针
//但C++为我们提供了begin和end函数
int nums[] = {1, 2, 3, 4};
auto beg = begin(nums);
int *last = end(nums);
<< *beg << endl; // 1
cout << *(last - 1) << endl; // 4
cout ++;
lastreturn 0;
}
//example30.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
//指针的运算类似于迭代器的运算
//但是二者也有不同之处
int nums[4] = {1, 2, 3, 4};
int *ptr = nums;
++; //向后移动一个位置
ptr--ptr; //向左移动
+ 1; //返回ptr右边元素的地址
ptr - 1; //返回ptr左边元素的地址
ptr << end(nums) - begin(nums) << endl; // 4
cout int *beg = begin(nums);
int *eptr = end(nums);
<< (eptr > beg) << endl; // 1
cout
//解引用要注意的事情
<< *nums + 1 << endl; // 2 为(*nums)+1 即nums[0]+1
cout << *(nums + 1) << endl; // 2 为*(nums+1) 即 nums[1]
cout return 0;
}
下标本质是一种特殊的指针运算
//example31.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int nums[] = {1, 2, 3};
auto ptr = nums + 1;
<< *ptr << endl; // 2
cout << ptr[-1] << endl; // 1
cout // ptr[-1] 等价于 *(ptr-1)
<< ptr[1] << endl; // 3
cout // ptr[1] 等价于 *(ptr+1)
return 0;
}
在 C 语言中是没有 string 类型的,而是使用 char 数组来进行存储字符串,以元素’\0’标志字符串结束。
#include<cstring>
1、strlen(p) 返回p的长度,空字符不计算在内
2、strcmp(p1,p2) 返回p1和p2的相等性,如果字符串字典排序比较,相等时返回0、p1>p2 返回正数、p1<p2时返回负数
3、strcat(p1,p2) 将p2附加到p1之后,返回p1
4、strcpy(p1,p2) 将p2拷贝给p1,返回p1
样例
//example32.cpp
#include <iostream>
#include <cstring>
using namespace std;
int main(int argc, char **argv)
{
const char *name = "gaowanlu";
const char name1[] = "gaowanlu";
<< name << endl;
cout << name1 << endl;
cout // name[0] = '1'; error name[0]不可变
// name1[0] = '1'; error name[0]不可变
char name2[10] = {'1', '2', '3', '4', '\0'};
char name3[] = {'1', '2', '3', '5', '\0'};
<< strlen(name2) << endl; // 4
cout << strcmp(name2, name3) << endl; // 0
cout (name2, name3);
strcat<< name2 << endl; // 12341235
cout (name2, name3);
strcpy<< name2 << endl; // 1235
cout
//注:要注意的是如strcat与strcpy,字符串操作结果会存进做第一个参数数组,如果数组存放不下结果则会报错,因为内存根本不够用
//但对于有些编译器不会报错,总之我们不要使用一下写法
char kk[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
char ll[] = "uecdiwhdw";
(kk, ll);
strcat<< kk << endl; // hellouecdiwhdw
cout return 0;
}
虽然在 C++中我们仍然可使用 char 数组存储字符串不用 string 或者不适用 vector 使用数组、这并不是一个好习惯、因为之所以有 C++就是为了增强 C 里面没有的东西,难道 vector 和 string 不香吗
//example33.cpp
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int agrc, char **argv)
{
//数组与string
("hello world"); //使用字符串字面量初始化s
string sconst char *str = s.c_str();
<< strlen(str) << endl; // 11
cout << str[1] << endl; // e
cout // string.c_str()返回const char*
//数组与vector
int arr[] = {0, 1, 2, 3, 4};
<int> ivec(begin(arr) + 1, end(arr));
vectorfor (auto e : ivec)
{
<< e << " "; // 1 2 3 4
cout }
<< endl;
cout return 0;
}
多维数组就是数组里面存放数组
//example34.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
// mul_arr每个元素都是长度为4的int类型数组
int mul_arr[3][4];
// mul_arr_1每个元素都是一个5*5二维数组
char mul_arr_1[5][5][5] = {0}; //将元素全部初始化为0
return 0;
}
内嵌花括号可以指定确切的位置,省略内嵌的花括号就是从第一个位置初始化了
//example35.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int arr[5][5] = {
{0, 1, 2, 3, 4},
{0, 1, 2, 3, 4},
{0, 1, 2, 3, 4},
{0, 1, 2, 3, 4},
{5}}; //花括号初始化
//还有更简洁的形式,有时候内嵌的花括号不是必须的
int arr1[5][5] = {
0, 1, 2, 3, 4,
0, 1, 2, 3, 4,
0, 1, 2, 3, 4,
0, 1, 2, 3, 4,
5};
//也就是会从arr[0][0]向后按顺序初始化
//初始化每行第一个元素
int arr2[5][5] = {
{1},
{2},
{3},
{4},
{5}};
<< arr2[4][0] << endl; // 5
cout << arr2[4][4] << endl; // 0
cout return 0;
}
多维数组的元素的引用的使用方式与一维数组是类似的
//example36.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int arr[3][3] = {{0, 1, 2},
{3, 4, 5},
{6, 7, 8}};
<< arr[0][0] << endl; // 0
cout << arr[1][0] << endl; // 3
cout << arr[0][2] << endl; // 2
cout << arr[2][0] << endl; // 6
cout << arr[2][2] << endl; // 8
cout
//数组与引用
int(&row1)[3] = arr[0];
<< row1[2] << endl; // 2
cout return 0;
}
一种是自定义范围配和下标引用,还可以使用数组迭代器
//example37.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
//使用迭代器
int arr[5][5];
std::size_t count = 0;
for (auto &row : arr)
{
for (auto &item : row)
{
= count++;
item }
}
//使用下标访问
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
<< arr[i][j] << " ";
cout }
}
<< endl;
cout // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
= 0;
count for (auto row : arr)
{
[0] = 0;
row<< arr[count++][0] << endl; // 0 0 0 0 0
cout }
//可见没有使用auto&而是使用auto则在遍历数组时row是arr每个元素的拷贝
//而不是引用
return 0;
}
非常重要的一句话、当程序使用多维数组的名字时,会自动转换成指向数组首元素的指针
//example38.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
//存放指针的多维数组
int num = 999;
int *ptr_mul_arr[5][5];
[0][0] = #
ptr_mul_arr*ptr_mul_arr[0][0] = 222;
<< num << endl; // 222
cout
//指向多维数组的指针
int(*mul_arr_ptr)[5][5];
int nums[5][5];
= &nums;
mul_arr_ptr (*mul_arr_ptr)[2][2] = 999;
<< nums[2][2] << endl; // 999
cout
//多维数组首个元素的地址
int(*first)[5] = nums;
(*first)[0] = 888;
<< nums[0][0] << endl; // 888
cout
//第一个元素地址
int *p = &nums[0][0];
//使用指针进行实现下标访问
int(*first_ptr)[5] = nums; // nums为int(*)[5]即存放int[5]类型的地址
*(*(first_ptr + 1) + 2) = 123;
<< nums[1][2] << endl; // 123
cout return 0;
}
使用 auto、decltype、begin、end
//example39.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int arr[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
auto el = arr[2][2];
= 12;
el << arr[2][2] << endl; // 9
cout
auto &el1 = arr[2][2]; //引用即别名
= 999;
el1 << arr[2][2] << endl; // 999
cout
auto &el2 = arr[2]; //引用即别名
<< el2[2] << endl; // 999
cout
decltype(begin(arr)) p1 = begin(arr); //返回第一个元素的地址
(*p1)[2] = 1234; //解引用p1,获得二维数组第一个元素的引用
<< arr[0][2] << endl; // 1234
cout
decltype(end(arr)) p2 = end(arr) - 1;//end获取最后一个元素后面一个位置的地址
<< (*p2)[2] << endl; // 999
cout return 0;
}
typedef、using 与数组
//example40.cpp
#include <iostream>
using int_array = int[3]; // int_array等价int[3]
typedef int int_array[3]; // int_array等价int[3]
using namespace std;
int main(int argc, char **argv)
{
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
*row = a; // int_array为指向int[3]数组的指针
int_array << *(*(row + 1) + 1) << endl; // 5
cout return 0;
}
我们会发现在我们学习的过程中都是一些细节令我们感到困惑、比如引用、指针、auto、const、配和其他类型进行使用的时候往往搞得我们一头雾水、但是自己不要失去自信心,没有人把他们记得一清二楚、多谢代码在实践中运用才能使得我们经验丰富起来