C/C++入门知识

前言

编程的相关小知识。后面将持续更新…

内容

1.计算机语言

最开始是2进制代码0和1表示,这代表着计算机最基本的工作,电路的通断电,但太麻烦,便发展助记符,就有了汇编语言,后发展出现C语言,再后出现各种解释语言出现(Java,python等),这些与计算机运行机制结合,人们设计的一类,让人们能够向计算机传达信息,从而控制计算机依据人们的意愿来运行,所使用的语言。

2.常量,变量

常量:不会改变的量
变量:会随着需要改变的量

注意:但在常量和变量里又分很多种,存储属性不一。以后再谈。

3.类型转换(后加‘l’,‘f’,‘u’)

在计算机中,不管是常量还是变量,除了数值,还具有类型这一属性
我们直接写出的整数,并没有给定数据类型,编译器会默认为int类型,可以在后面加l或L就转为long(long int)类型。
同理,我们直接写出小数,编译器会默认为double类型,可以在后面加f或F就转为float类型,
这些都是有符号类型,可以在后面加u或U就转为unsigned类型。

4.转义字符

我们需要向计算机表达一些比较特殊的意思,比如“换行”,n可能只是表达一个字符‘n’的意思,但/n却具有换行的功能,因为在键盘上的按键不足以表达我们表达的意思时,我们就将某些字符经过特定的形式进行转变,用来表达我们所需要的其他含义,并且我们把转变后的这些字符叫做转义字符所以这些转义字符形式上虽然是由‘/’和‘n’这两个组成,但‘/n’被是我们设定成的转义字符,即与字符‘a’是一样的,是一个字符,而不是两个。
如何直观感受这一点呢?
我们可以利用strlen函数计算一下字符个数来明确这一点。

printf("%d\n"strlen("c:\test\328\test.c"));

输出的答案为14。

5.变量的作用域与生命周期

作用域
局部变量:局部变量所在的局部范围里
全局变量:整个工程

生命周期
局部变量:进入局部范围周期开始,离开结束
全局变量:程序的生命周期

注:当定义的变量重名时,局部范围里局部优先;
C语言与法规定,变量要定义在当前代码块最前面。

6.枚举常量&枚举变量

有一种特殊的常量叫做枚举常量:枚举在C/C++/c#中,是一个被命名的整型常数的集合。
枚举的说明与结构和联合相似, 其形式为:enum 枚举名{标识符[=整型常数],标识符[=整型常数],…标识符[=整型常数]} 枚举变量;如果枚举没有初始化,即省掉"=整型常数"时, 则从第一个标识符开始,顺次赋给标识符0, 1, 2, …。但当枚举中的某个成员赋值后,其后的成员按依次加1的规则确定其值。

枚举类型变量的赋值和使用枚举类型在使用中有以下规定:
1.枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值: sun=5;mon=2;sun=mon; 都是错误的。
2. 枚举元素本身由系统定义了一个表示序号的数值,从0 开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1, …,sat值为6。
3. 只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如: a=sun;b=mon; 是正确的。而: a=0;b=1; 是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换,如: a=(enum weekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于: a=tue; 还应该说明的是枚举元素不是字符常量也不是字符串常量, 使用时不要加单、双引号。

7.枚举常量&宏定义

让我们来比较比较这两个常量。
宏和枚举的主要区别是作用的时间和存储形式不同。宏定义是在编译预处理阶段作用的,也就是在编译预处理时,就会进行宏替换,将程序中的所有宏名替换为所定义的常量名,而枚举则是在程序运行之后才起作用的。宏定义不分配内存空间,而枚举常量存储的数据在静态存储区中。宏只占用代码段的空间,而枚举除了占用代码段空间外,还耗费CPU资源,但枚举是比宏更加便利,去定义一大堆的宏总是非常的不方便的。

8.左值和右值

当一个对象被用作右值的时候,用的是对象的值(内容),当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。左值和右值(内置类型为纯右值,自定义类型为将亡值)其实就是区分的值的存储属性和数值属性,一个强调的是存储属性,一个强调的是数值属性。

9.左值引用和右值引用

任何一个引用都不过是某个对象的另一个名字而已。
返回左值引用的函数,连同赋值,下标,解引用和前置递增/递减运算符,都是返回左值的表达式的例子。我们可以将一个左值引用绑定到这类表达式的结果上。
返回非引用类型的函数,连同算术,关系,位以及后置递增/后置递减运算符,都生成右值。我们不能将一个左值引用绑定到这类表达式上,但我们可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。甚至我们可以通过move的新标准库函数来显示地将一个左值转换为对应的右值引用类型。
注意:变量是左值,因此我们不能将一个右值引用直接绑定到一个变量上,即使这个变量是右值引用类型也不行。这也导致一个现象,右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置(这个存储是右值的存储,不是资源的存储,资源的地址不会改变,并且右值会接管该内存地址),且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1,如果不想rr1被修改,可以const int&& rr1去引用。
右值的特性:1.所引用的对象将要被销毁,2.该对象没有其他用户(这意味着,使用右值引用的代码可以自由地接管所引用的对象的资源,即资源的生命周期会因此延长(源资源就算被销毁,也会人为将所需资源保留,使得析构后“无害”)。
总的来说,左值引用和右值引用,一个是共享内存,一个是接管内存。说到底,就是对内存权限的操作

10.传值返回的编译器优化问题

临时对象:返回空间较大的对象时,上一个栈帧会预先扩增空间进行存储临时对象(返回值);返回空间较小的对象时,临时对象存储在寄存器中。(临时对象的存在是为了让返回值出作用域后被销毁前的数据得以临时保存起来)
移动构造的作用:资源的交换,将好的资源给左值得以留下,将移后源对象的指针都置为nullptr(进入可析构状态,且析构无害),移后源对象的析构函数将不会释放所需内存,此内存的所有权也将由新对象接管。相比拷贝构造,可以在对象进行赋值的时候直接移动源对象已经分配好的资源,从而省去重新分配内存再拷贝的过程。
移动赋值的作用:资源的交换,将好的资源给左值得以留下,不好的资源给移后源对象得以销毁。
拷贝构造:资源的分享,将好的资源给左值得以留下,不好的资源自行销毁,现代写法:模仿移动构造,用好的资源创建一个临时对象(此处用非拷贝/移动构造),将临时对象的资源给左值得以留下,不好的资源给临时对象得以销毁。
拷贝赋值:同拷贝构造。

构造优化场景分析:
不优化:返回的对象(此时还是一个左值)拷贝构造一个临时对象(右值)(返回对象出了作用域,被销毁),临时对象移动构造这个刚定义的对象,即拷贝构造+移动构造
优化:返回的对象被看作右值直接对这个刚定义的对象进行移动构造
赋值场景分析:
不优化:返回的对象(此时还是一个左值)拷贝构造一个临时对象(右值)(返回对象出了作用域,被销毁),临时对象移动赋值给的被赋值对象,即拷贝构造+移动赋值
不能优化原因(个人猜测):在对返回值储存时是创建了一个临时对象,这是拷贝/移动构造,而用返回值创建一个左值对象,也是拷贝/移动构造,这是两个完全相同的操作,可以直接把对临时对象的拷贝,改成对左值对象的拷贝。
但是对左值对象赋值是拷贝/移动赋值运算符函数,这与拷贝/移动构造是逻辑不一样的操作,不能用存储临时对象的机理去代替拷贝/移动赋值。所以不能优化,需要先有临时变量。

11.引用折叠和完美转发

当我们实现的函数是模版时,会用到类型模版参数,右值引用在与模版参数共用时,参数类型T&&会变成万能引用,在调用左值时会发生引用折叠,不仅如此,C++11中添加的一套引用折叠规则中还规定了不同引用类型相互作用的情况下不同的推导情况,详细如下。
模板类型 T实际类型 最终类型
T & int int &
T & int & int &
T & int && int &
T && int int &&
T && int & int &
T && int && int &&
上面的表格显示了引用折叠的推导规则,可以看出在整个推导过程中,只要有左值引用参与进来,最后推导的结果就是一个左值引用。只有实际类型是一个非引用类型或者右值引用类型时,最后推导出来的才是一个右值引用。
所以如果我们的类模板是T&&类型,只有模板参数T推导为左值引用时引用折叠后最终类型才会为左值引用其他都是右值引用。从而也可以利用这样的规则达成万能引用的目标。
完美转发:在引用参数传入后,不管是左值引用或者右值引用变成了左值,这将影响会对这两种引用的区分,完美转发就解决了这个问题,std:forword会根据左值引用和右值引用的实际情况,使得原有的左值属性和右值属性得以保留。

12.malloc/operator new/new和free/operator delete/delete

new/delete 和malloc/free最大区别是new/delete对于自定义类型除了开辟空间还会调用构造函数和析构函数,其次就是new申请失败会抛出异常,malloc申请失败返回null;而new/delete 操作符底层是调用库函数 operator new(申请失败也会抛出异常)/operator delete来申请和释放内存的,并且在此之后还会调用构造函数和析构函数(内置类型不进行)。

最后

若有错误,欢迎大家在评论区指出。

猜你喜欢

转载自blog.csdn.net/phantomthief1412/article/details/121897032