cdn网站网络加速器,如何看一个大型网站的源代码,title:(网站开发),推广赚钱app排行榜目录
一. 前言
二. auto关键字
2.1 auto的引入
2.2 auto简介
2.3 auto的使用细则
2.4 auto不能推导的场景
三. 基于范围的for循环(C11)
3.1 范围for的语法
3.2 范围for的原理
3.3 范围for的使用条件
四. 指针空值nullptr(C11) 一. 前言 上期我们介绍了c新增的两个重… 目录
一. 前言
二. auto关键字
2.1 auto的引入
2.2 auto简介
2.3 auto的使用细则
2.4 auto不能推导的场景
三. 基于范围的for循环(C11)
3.1 范围for的语法
3.2 范围for的原理
3.3 范围for的使用条件
四. 指针空值nullptr(C11) 一. 前言 上期我们介绍了c新增的两个重要语法引用和内联函数今天我们带来的内容是auto关键字、范围for以及nullptr指针本期也是初识C的最后一期。上期回顾
【C深入浅出】初识C中篇引用、内联函数http://t.csdn.cn/LCvY0 话不多说直接上菜
二. auto关键字
2.1 auto的引入 在我们写代码的过程中可曾发现随着程序越来越复杂程序中用到的类型也越来越复杂包括但不限于以下两点1. 类型难于拼写、2. 含义不明确导致容易出错。例如
#include string
#include map
int main()
{std::mapstd::string, std::string m{ { apple, 苹果 }, { orange,橙子 },{ pear,梨 } };std::mapstd::string, std::string::iterator it m.begin();while (it ! m.end()){//....}return 0;
} 你没有看错上面的std::mapstd::string, std::string::iterator 其实是一个类型是不是非常吓人。像这种长类型特别容易写错有的人可能已经想到了解决方案可以通过typedef给类型取别名。是的这无疑也是种好方法但使用typedef前我们必须先知道类型有时候这并不容易做到。那怎么办呢不急C11中的auto关键字就是为了解决这个问题。 2.2 auto简介 在早期C/C中使用auto修饰的变量是具有自动存储器的局部变量但由于用处不大一直没有人去使用它。 而在C11中C标准委员会赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一个新的类型指示符来指示编译器使用auto声明的变量类型编译器会在编译时期自动推导而得。 为了避免与C98中的auto发生混淆C11只保留了auto作为类型指示符的用法。
#includestring
int TestAuto()
{return 10;
}
int main()
{int a 10;string s;auto b a;auto c a;auto d TestAuto();auto e s.begin();cout typeid(b).name() endl; //typeid类似于sizeof一样是一个操作符其可以用来获取变量的类型。cout typeid(c).name() endl;cout typeid(d).name() endl;cout typeid(e).name() endl;//auto e; 无法通过编译使用auto定义变量时必须对其进行初始化return 0;
}输出结果如下 可以看出编译器自动帮我们将类型推导出来了是不是非常方便 2.3 auto的使用细则 学会了auto的基本使用接下来就是避坑时间惹 1. auto定义变量时必须对其进行初始化 auto并非是一种“类型”的声明而是一个类型声明时的“占位符”。在编译阶段编译器需要根据初始化表达式来推导auto的实际类型然后将auto替换为变量实际的类型。
int main()
{int val 10;auto a; //错误写法编译时会报错auto b val; //正确写法定义时进行初始化编译器才能进行推导然后将auto替换return 0;
} 2. auto和指针和引用 用auto声明指针类型时用auto和auto*没有任何区别但用auto声明引用类型时则必须加。
int main()
{int x 10;auto a x;auto* b x;auto c x;cout typeid(a).name() endl;cout typeid(b).name() endl;cout typeid(c).name() endl;*a 20;*b 30;c 40;return 0;
} 3. 在同一行定义多个变量 当在同一行声明多个变量时这些变量必须类型相同否则编译器将会报错因为编译 器实际只对第一个类型进行推导然后将auto进行替换最后用替换后的类型定义其他变量。
int main()
{auto a 1, b 2; // 正确写法auto c 3, d 4.0; // 编译失败因为c和d的初始化表达式类型不同return 0;
} 2.4 auto不能推导的场景 1、auto不能作为函数的参数
// 此处代码编译失败auto不能作为形参类型
// 原因函数调用传参发生在运行阶段故在编译阶段编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{} 2.、auto不能直接用来声明数组
int main()
{int a[] { 1,2,3 };auto b[] { 4,5,6 };return 0;
} 三. 基于范围的for循环(C11)
3.1 范围for的语法 在C98中如果要遍历一个数组可以按照以下方式进行
int main()
{int array[] { 1, 2, 3, 4, 5 };for (int i 0; i sizeof(array) / sizeof(array[0]); i){array[i] * 2; //利用下标访问}cout endl;for (int* p array; p array sizeof(array) / sizeof(array[0]); p){cout *p ; //利用指针访问}cout endl;} 但是对于array这个有范围的集合而言由程序员来说明循环的范围显然显得有点多余有时候还会容易犯错误产生越界。因此C11中引入了基于范围的for循环。for循环后的括号由冒号分为两部分第一部分是范围内迭代取得的变量第二部分则表示进行迭代的集合对象。上述代码使用范围for改写如下
int main()
{int array[] { 1, 2, 3, 4, 5 };for (auto e : array) //依次取数组的数据给引用变量e自动判断结束自动迭代 1{e * 2;}for (auto e : array) //依次取数组的数据赋值给变量e自动判断结束自动迭代 1{cout e ;}return 0;
} 可以看到我们使用到了之前学的auto关键字利用auto的自动类型推导我们无需显式地写出e的类型使得范围for的使用更加简洁方便这也是auto的常见优势用法之一。 在第一个范围for中由于e是引用变量因此e表示的是数组每个元素的别名对e进行修改就是对数组元素进行修改。 而在第二个范围for中e是普通变量表示的是数组每个元素的拷贝对e进行修改对数组元素没有影响。 注意与普通循环类似可以用continue来结束本次循环也可以用break来跳出整个循环。 3.2 范围for的原理 可能会有的小伙伴会好奇范围for这个东西这么智能既能自动迭代还能自动判断结束那么它的原理究竟是什么呢 实际上并没有想象中的那么复杂。范围for的底层原理实际上就是迭代器遍历编译器在编译时会自动将范围for的代码替换为迭代器遍历相关代码。迭代器的知识我们后续会介绍这里大家将其理解为指针即可。 下面是一段vector容器的遍历代码
int main()
{vectorint v;for (int i 1; i 5; i) //插入1-5的数据{v.push_back(i);}for (auto e : v) //范围for遍历{cout e ;}cout endl;return 0;
} 编译器编译时范围for会替换成类似于如下的代码
int main()
{vectorint v;for (int i 1; i 5; i) //插入1-5的数据{v.push_back(i);}vectorint::iterator it v.begin();//auto it v.begin(); //迭代器的类型较长也可以使用auto自动推导while (it ! v.end()) //迭代器遍历这里将it当做指针理解即可{cout *it ;it;}cout endl;return 0;
} 结论范围for其实就是编译器进行了替换本质上还是迭代器的遍历。 3.3 范围for的使用条件 1、for循环迭代的范围必须是确定的 对于数组而言for循环迭代的范围就是从数组中第一个元素到最后一个元素对于类而言应该提供begin和end的方法begin和end就是for循环迭代的范围。
void putArray(int array[])
{//数组传参发生降维array是个指针指向数组首元素for (auto e : array) //由于array只是个int*指针我们无法确定迭代的范围故这里的范围for会报错{cout e ;}
}
int main()
{vectorint v;for (int i 1; i 5; i) //插入1-5的数据{v.push_back(i);}for (auto e : v) //范围for遍历vector容器类。范围v.begin()~v.end(){cout e ;}cout endl;int array[5] { 1,2,3,4,5 };for (auto e : v) //范围for遍历array数组。范围从第一个元素的下标0到最后一个元素的下标4{cout e ;}putArray(array);return 0;
} 2、迭代的对象要实现和的操作 上面我们看到范围for替换为迭代器遍历的代码中使用迭代器it进行遍历时需要用到和的操作顾迭代器需要支持和的操作。目前不清楚的了解一下即可等到我们讲解迭代器时再深入讨论 四. 指针空值nullptr(C11) 在C语言中我们对指针进行初始化时经常会用NULL空指针进行初始化如下
int main()
{int* p1 NULL;return 0;
} 实际上NULL是一个宏在传统的C头文件(stddef.h)中可以看到如下代码 可以看到NULL在C中被定义为字面常量0在C语言中被定义为无类型指针(void*)常量。而字面常量0在编译器看来默认是整形常量这就会导致出现一些不可预料的错误例如
void f(int)
{cout f(int) endl;
}
void f(int*)
{cout f(int*) endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);return 0;
} 上述代码在C中的结果如下所示 本意我们是想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL在C中被定义成字面常量0因此调用了更符合的f(int)函数与程序的初衷相悖。如果我们执意要调用f(int*)函数只能将NULL强制类型转换为int*再进行调用即可但这样会显得非常奇怪 出于以上原因C11新增了一个关键字nullptr用来表示指针空值如下
void f(int)
{cout f(int) endl;
}
void f(int*)
{cout f(int*) endl;
}
int main()
{f(0);f(nullptr); //nullptr表示空指针return 0;
} 此时代码的结果就符合我们初衷了 关于nullptr的几点说明与建议 在使用nullptr表示指针空值时不需要包含头文件因为nullptr是C11作为新关键字引入的。在C11中sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。为了提高代码的健壮性在C中表示指针空值时最好使用nullptr。 以上就是本期的全部内容啦
制作不易能否点个赞再走呢