目录
目录
auto关键字
内联函数
概念
补·宏函数
在编译器中的特性
特性
指针空值nullptr(C++11)
C++98中的指针空值
结束语
1. 类型难于拼写2. 含义不明确导致容易出错
#include
using std::cout;
using std::endl;
//auto -- 根据a的类型推导b的类型
#include
#include
使用auto 定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导 auto 的实际类型 。因此 auto 并非是一种 “ 类型 ” 的声明,而是一个类型声明时的 “ 占位符 ” ,编译器在编 译期会将 auto 替换为变量实际的类型 。
//1. auto不能作为函数的参数
//此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a) // 这种写法是错误的
{}
//2. auto不能直接用来声明数组
void TestAuto()
{
int a[] = { 1,2,3 };
auto b[] = { 4,5,6 };
}
5.另一个注意事项如下代码
int main()
{
//一般情况下的C++中数组的遍历
int array[] = { 1,2,3,4,5 };
//范围for遍历 -- 使用auto
//依次取array中数据赋值给e,自动判断结束,自动迭代
for (auto e : array)
cout << e << " ";
cout << endl;
//但是这种写法是错误的
for (auto e : array) //这里e是array里面元素的拷贝所以这里改变e的值是影响不了array数组的
{
e *= 2;
}
for (auto e : array) //e是array里面元素的拷贝
cout << e << " ";
cout << endl;
//加一个引用就可以解决了,这里的e就是里面元素的别名
for (auto& e : array) //这里e是array里面元素的拷贝所以这里改变e的值是影响不了array数组的
{
e *= 2;
}
for (auto e : array) //e是array里面元素的拷贝
cout << e << " ";
cout << endl;
return 0;
}
还有一点只能用引用而不能用指针(*),不然就会通过auto之后会成为 (int*)明显是不对的
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。
//宏函数
#define Add(x,y) ((x) + (y)) //宏函数注意事项 1:不需要加类型 2.不需要加分号 3.需要注意括号的使用!4.宏函数本质上是替换
int main()
{
//宏函数的使用
cout << Add(1,2) << endl;
//3.外部括号不加容易影响优先级 例如:Add(1,2)*3
// 内部括号不加 例如:Add(a|b, a&b)
return 0;
}
宏函数注意事项
1:不需要加类型
2.不需要加分号
3.需要注意括号的使用!
4.宏函数本质上是替换
内联函数的使用
//内联函数(inline)
inline int ADD(int x, int y)
{
return x + y;
}
int main()
{
cout << ADD(1, 2) << endl;
return 0;
}
在debug版本中是不会像宏函数一样展开的。
我们可以在反汇编中查看到,ADD函数还是建立了栈帧,我们使用这个关键字的目的就是要让它不建立函数栈帧,那么是否违背了?
显然并不是这样的,这一行是编译器为了方便程序员调试而做出的行为
原因:因为需要 调试,假如展开就不可以调试了,当然在release版本中就会展开了(优化)
当然我们也可以让它在debug版本中展开,这需要如下在属性中设置
返回值比较小的情况下是直接使用寄存器来传递的哦
在C++中的经验条款
通常情况下在C++中要使用:inline 来替代宏函数(频繁调用的小函数) const enum 来替代常变量
当然我们需要注意以下的几个特性
1.递归函数肯定是不适用的,较长的函数也是不会展开的
以下是在属性让其展开的情况下,左边较短,右边较长
为什么函数长了以后就不展开了呢?
回答:代码膨胀
在上面的反汇编也可以看出,不展开的话就call指令一下就可以了,但是展开的话就长的多了
这里就是在说明,inline是建议,而不是强制。并且是以空间换取时间的一种行为。编译期间处理。
3.不要声明与定义分离
会出现链接错误!!!(语法没问题,但是编译器找不到了) 加了inline不会进入符号表,无论长不长
生成可执行程序过程为成四个步骤:
1.预处理(Preprocessing)
2.编译(Compilation),
3.汇编(Assemble),
4.链接(Linking)。1、由.c文件到.i文件,这个过程叫预处理。
2、由.i文件到.s文件,这个过程叫编译。
3、由.s文件到.o文件,这个过程叫汇编。
4、由.o文件到可执行文件,这个过程叫链接。
在良好的C/C++ 编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:
void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}
NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
void f(int) {
cout<<"f(int)"<
程序本意是想通过f(NULL) 调用指针版本的 f(int*) 函数,但是由于 NULL 被定义成 0 ,因此与程序的初衷相悖。在C++98 中,字面常量 0 既可以是一个整形数字,也可以是无类型的指针 (void*) 常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void*)0。
1.在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入 的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
最是人间留不住,朱颜辞镜花辞树。
——王国维《蝶恋花•阅尽天涯离别苦》