这篇博客主要整理和总结了 inline 、 auto 、nullptr 关键字的用法。
首先我们知道在c环境下的程序编译要经过,预处理、编译、链接等等。 在预处理环节当中,c语言会展开头文件、将注释消除、替换define所定义的宏。
宏相对于函数来说它将宏函数在预处理阶段进行展开,替换宏定义的变量。 对于一些短小的函数,在预处理阶段将其替换,会加快程序的执行效率。 但是相对的宏函数有自己的副作用。这些副作用来自于它不会对参数进行检验,并且一旦改变参数的输入格式可能会产生未知的后果
#include<iostream> using namespace std; #define MAX(a,b) ((a)>(b)?(a):(b)) //这是一个比大小的宏 int main() { int a = 10, b = 11; cout << "a : " << a << endl; cout << "b : " << b << endl; cout<< MAX(++a, b) << endl; cout << "a : " << a << endl; cout << "b : " << b << endl; return 0; }以上代码可以看出虽然使用了宏函数比出大小,但是出现了不希望的结果。比较了一次永久的改变了a的值。
内联函数是指在一个函数前加一个关键字inline,内联函数会将inline函数修饰的直接展开。与define定义的宏函数相似的是inline修饰的内联函数也是在编译的是偶进行替换。
#include<iostream> using namespace std; inline int Add(int left, int right) { return left + right; } int main() { int a = 10, b = 11; Add(a, b); return 0; }在vs2013的编译器下,我们可以看到好像这个内联函数并没有展开,而是使用了call指令进行调用。那么inline到底用了没有?
随后使用了 release 进行调试,这次好玩了连main()函数都没了。
最后经过查阅相关资料,要想看到inline的调用过程,
右击源文件–> 属性 --> c/c++常规 --> 信息调用模式 -->选择程序数据库
右击源文件–> 属性 --> 优化 --> 内联函数扩展 -->选择只适用于扩展inline
由此可以看到,没有使用call指令进行调用函数。而是使用了加法指令add。
inline只是一个建议性的关键字。所谓建议指的是编译器在进行编译的时候,对其是否使用inline的优化加以建议。编译器一般比较喜欢那些,函数体短的,没有循环的,没有递归的代码来当做inline处理。
例如,如果这个函数又臭又长,你还加入了inline关键字,编译器自然不愿意去把这份代码复制多份加入到main()里面。耗时耗力
inline int Add( int a ,int b) { Add(a,b); }像这个无限递归的函数不可能当内联函数处理,不然代码段就会被撑爆了。
在c里有一个很好的能够定义全局变量的东西就是宏定义。但是这个宏定义也有着自己的缺陷。#define 它会检查后面参数的类型,直接往该替换的位置去替换。
c++中有一个非常好用的且安全的方法来使用常量的定义.
const 关键字。相对于#define 1.它可以进行类型检查 2.能够进行安全替换
#include<iostream> using namespace std; int main() { const int a = 10; int *pa = (int *)&a; *pa = 100; cout <<"a : "<< a << endl; cout <<"*pa : "<< *pa << endl; return 0; }以上代码的输出结果是 a : 10和 *pa = 100。这里输出结果和预期的不一样。在编译器里边a 和 pa所指向的是同一个空间。 100也确实被修改了,但是自动窗口输出的却不是预想的值。
这是因为const指令在预处理阶段就将const int a 所定义的值替换为 10 而不是在程序运行的时候。 如何验证const定义的是一个常量呢?你可以定义一个数组让它的大小用const 定义的一个变量去初始化。如果成果就说明const定义的变量具有常属性。
auto 关键字 是在 c++11里添加的一个新的关键字。它区别于c里的auto自动变量。好像在 c里这个auto不是很常用,基本没见过。但是 c++里将这个auto赋予了新的含义。
#include<iostream> using namespace std; int main() { int a = 10; auto b = 20; auto c = 'c'; auto d = 12.34; // auto e; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; return 0; }auto 定义的变量会在编译期间,编译器自动推导这个变量的类型。值得注意的是,auto变量不能定义一个未初始化的变量。就像引用一样,定义的时候必须初始化。
以上代码表现了 , auto 和 auto * 没有特别的区别。但是如果要定义一个引用的话 ,auto ra没有办法自动推算出来,auto &ra 才可以。
auto 可以定义多个参数,但是参数的类型必须一样。
以上代码是错误的。auto 不能作为参数类型,函数要初始化需要确定类型,auto 没有推算的依据 所以是错误的。缺省定义也是不可以的。
以上代码是错误的。首先auto 是可以推算类型。但是推算的结果应该是固定的,不应该是多结果。在 c 当中 sizeof 和 & 获取的是数组的首地址。其他时刻 a 代表着数组首元素的地址。所以这个是多结果的。 auto 无法推算出来
1.让代码变的简单。
#include<iostream> using namespace std; namespace N1 { int a; namespace N2 { typedef int Datatype; Datatype b = 10; }; }; int main() { N1::N2::Datatype c = N1::N2:: b; auto d = N1::N2::b; cout << "c = " << c << endl; cout << "d = " << d << endl; return 0; }以上代码 c = 10, d = 10。可以发现,在有复杂定义的时候可以使用 auto 来减少代码量。方便编程的时候进行定义。
2.避除类型声明的麻烦
#include<iostream> using namespace std; int main() { const float pi = 3.14; double r = 2.0; auto c = 2 * pi*r; //给变量 c 到底是 double 类型好还是float 类型好 编译器可以自己推导出来 cout <<"C : "<< typeid(c).name() << endl; return 0; }如果我们拿不定 c 到底是什么类型的,可以让编译器自己推到。
3.可以在for循环的时候缩减代码。
#include<iostream> using namespace std; int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; for (auto &e : arr) { e *= 2; } for (auto &e: arr) { cout << e << endl; } return 0; }这是来自于 c++11 的新型语法。这种语法条件是:for循环的范围必须是确定的。并且迭代的对象必须有 ++ , --的操作。
以下是两种错误的使用方法。
#include<iostream> using namespace std; void TestArray(int array[3]) { for (auto & e : array) //虽然函数TestArray的形参array像是一个数组,但是实际上却是一个int类型的指针。 { e * 2; } } int main() { int *p; for (auto & e : p) //p无法确定具体的范围 { cout << e << endl; } return 0; }让我们先来回顾一下C中的 NULL。
#include<iostream> using namespace std; void TestFunc(int a) { cout << "TestFunc(int)" << endl; } void TestFunc(int *a) { cout << "TestFunc(int *)" << endl; } int main() { int *p1 = NULL; int *p2 = 0; //这里的 p1 和 p2 有什么不同吗? //转到定义 : //#define NULL 0 //#define NULL((void *)0) TestFunc(NULL); TestFunc(0); TestFunc(nullptr); return 0; }以上函数中 p1的 NULL,其实有两种定义。一种是立即数 0,另一种是空指针类型的 0号地址。为了区分 0 和空指针类型。C++特地把 (void * ) 0拿出来用关键字 nullptr 代表空指针。