1.std::endl 有一个作用是来冲洗buffer
2.声明语句时理解方式typedef
typedef char* pstring;
//这两看似一样,但是实际不同
const pstring cstr = 0; //char*为基本数据类型
const char* cstr = 0; //char 为基本数据类型,*变成声明
3. const和constexpr
常量表达式:
constexpr 指定的一定是常量表达式(编译期可求值);
constexpr受到的限制,constexpr指针必须是nullptr或0,或者是存储于某个固定地址中的对象.
const和constexpr的区别:
对象常量性可分为两种: 物理常量性(每个bit都不可能改变)和逻辑常量性(对象的表现保持不变).
C++中采用的是物理常量性.
例如:
class ME{
public:
int *p;
}
int a = 5, b = 6;
const ME tmpME = {&a}; //编译器创建的构造函数
tmpME.p = &b; //编译器报错:tmpME.p指针所指地址不能变
*(tmpME.p) = 10; //不报错: tmpME.p的地址没变,地址保存的"值"变了
constexpr值可用于enum,switch,数组长度等场合,有很强的约束更安全,编译器对constexpr的优化很大,可将用到constexpr表达式的地方直接替换成最终结果.
3.auto声明
多条声明语句类型必须一致
auto i=0,*p=&i;
4.std::size_t
是一种std::string里内置的变量类型,是无符号的,所以要小心跟负值的int比较
5.std::string 字符串相加遵守原则:
两相加的对象中至少一个必须是string
6.std::string 类的输入运算符和getline函数分别是如何处理空白字符的
输入运算符 自动忽略空格,制表符(/t),换行符(/n), getline保留输入时的空白符.
7.include头文件
处理的一组标准函数.用于字符串的处理和判断
但是输入时 int _C ,用于检测单个字符类型的库吧.
8.decltype(things)
用于检测things的对象类型
可用于一些类型模糊的对象提取他们的对象类型
string str("hello");
decltype(str) punct_cnt = 0; //类型是:std::size_t
9.std::string的下标合法性
//合法不合法跟运行成不成功没啥关系
//下标合法性,就是说0~string.size()之间的下标为合法(c++primer说的),不合法即下标超过size()
//所以string是空时 0~0是合法的下标
//这句能运行成功
//string为空,则s[0]的结果将是未定义的.s[1]就直接报错
std::cout <<"输出空字符串中的tt[0]:"<< tt[0] << std::endl;
10.初始化列表在vector中应用时
vector<string> v5{"hi","me"}; //列表初始化,v5有2个元素
vector<string> v6("hi"); //错误:不能使用字符串字面值构建vector对象
vector<string> v7{10}; //v7有10个默认的初始化元素
vector<string> v8(10,"hi"); //v8有10个值为"hi"的元素
11.遍历vector时顺便修改值
std::vector<int> v{ 1,2,3,4,5,6,7,8,9 };
for (auto &i : v) //可以修改v的值
i *= i;
for (auto i : v)
std::cout << i << " ";
std::cout << std::endl;
vector对象的下标只能用于访问已存在的元素,而不能用于添加元素;
下标形式访问不存在的元素不会被编译器发现,而是在运行时产生一个不可预知的值,即缓冲区溢出错误(buffer overflow);
12.简化vector访问元素的方法->箭头运算符
//字符串向量中对象为空时停止循环,关键点位it->empty() 检测字符串是否为空
//it->empty() 访问 string对象,比起(*it).empty()更便捷
std::vector<std::string> text = {"wo","cao","li","","made","bi","zui"};
for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
cout << *it << endl;
13.vector的一个限制
:不能在for循环中向vector对象添加元素,但是可以删除元素.
14.迭代器位置类型名为 difference_type 的带符号整型数,因为距离可正可负
15.数组由内向外,由右向左解读
int *(&array)[10] = ptrs; //arry是数组的引用,该数组含有10个指针
首先知道arry是个引用,然后观察右边知道arry引用的对象时一个大小为10的数字,最后观察左边知道,数组的元素类型是指向int的指针.
int (*parray)[10] = &arr; //parray指向一个含有10个整数的数组
首先圆括号起来的部分意味着parray是个指针,接下来观察右边可知oarray是个指向大小为10的数组指针
最后看左边可知数组元素时int型.
int *ptrs[10]; //ptrs是含有10个整型指针的数组
从右向左,首先可知定义的是一个大小为10的数组,他的名字是ptrs,然后再看类型是(int*)整型指针
char st[11] = "fundamental" ; //错误,没有空间放空字符串"\0"
16.数组与vector的区别
数组运行时性能较好,但是不能像vector随意增加元素,损失了灵活性
//c++ primer 第五版 练习题 3.28
//问下列数组中元素的"值"是什么?
#include <iostream>
using namespace std;
string sa[10];
int ia[10]; //没明白为什么这个全局变量是会自动初始化,后续再找找补充(以补充)
char a[5]; //也会被初始化为空字符串
int main(){
string sa2[10];
int ia2[10];
}
//sa,sa2里面的值都10个空字符串
//ia里面是10个0值
//ia2里面是10个-858993460(垃圾值:未初始化的值)
//可以看到c的值为-858993460这个值即为0xcccccccc的十进制表示。
//原因是:未初始化的栈区编译器默认(在vs2017下)都按照cc去填充了。另外补充,在gcc编译器则是按照0填充的
(补充如下)
默认初始化规则:
定义基本数据类型变量的同时可以指定初始值,如果未指定会默认初始化
1.栈中的变量和堆中的变量会保有不确定值
2.全局变量和静态变量(包含局部静态变量)会初始化为零
静态和全局变量的初始化
未初始化的和初始化为零的静态/全局变量编译器是同样对待的,把它们存储在进程的BSS段(这是全零的一段内存空间)中.所以它们会被"默认初始化"为零
成员变量的初始化
成员变量分为成员对象和内置类型成员,其中成员对象总是会被初始化. 而我们要做的就是在构造函数中初始化其中的内置类型成员.
内置类型的成员变量的"默认初始化"行位取决于所在对象的存储类型,而存储类型对应的默认初始化规则时不变的.所以为了避免不确定的初值,通常会在构造函数中初始化所有内置类型的成员.
封闭类嵌套成员的初始化
同样还是只关注于基本数据类型的成员.取决于当前封闭类对象的存储类型,而存储类型对应默认初始化规则仍然是不变的
17.c++ 11标准引入的两个名为 begin和end的函数(头文件iterator)
// 头文件iterator
int a[] = {0,1,2,3,4};
int *beg = begin(a); //指向a的首元素
int *last = end(a); //指向a的尾元素下一位置指针,注意尾指针不能解引用和递增
18.给指针加上一个整数,得到的新指针仍需指向同一数组的其他元素,或者指向同一数组的尾元素的下一位置:
constexpr size_t sz = 5;
int arr[sz] = {1,2,3,4,5};
int *p = arr + sz; //使用警告: 不要解引用
int *p2 = arr + 10; //错误:p2的值未定义
//编译器发现不了,程序运行到这里就会指针越界崩溃
int *b = arr, *e = arr + sz;
while(b<e){++b;}
//如果两个指针分别指向不相关的对象,就不能比较
19.c风格字符串函数注意:
strlen,strcmp(p1,p2),strcat(p1,p2),strcpy(p1,p2)
此类函数的指针必须以空字符串为结束的数组.
20.下面程序运行结果是什么?
void test_337()
{
const char ca[] = { 'h','e','l','l','o' };
const char* cp = ca; //const 表示所指物为常量
while (*cp) {
cout << *cp << endl;
++cp;
}
}
// 因为ca是C风格字符串,字符串结尾必须带'0'
// 字符串ca在内存中的位置不断向前寻找直到遇到空字符串才停下来
21.两个指针相加为何没有意义
因为相加后的指针指向的值,跟相加的两个指针没什么关系,而且指向的值也不是我们想要的结果.
22.能用数组初始化vector,通过指定指针首尾
//需要头文件#include<vector>
int int_arr = {1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr)); //begin和end需要头文件#include<iterator>
//也可以部分赋值
vector<int> subvec(int_arr + 1,int_arr +4); //赋值int_arr[1]~[3]
// 列表初始化vector
vector<int> v{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
不允许使用一个数组为另一个内置类型的数组赋初值
不允许使用vector初始化数组