C++学习第四节:C和C++的区别第二节课
Note1:const 和 一级指针的结合
两种方式:C++ 里const修饰的是离他最近的类型 类型 类型!!!
(1):const int p;//这个 类型是int,修饰的p是个常量(不可以作为左值),但p可以被更改。p可以指向其他的整型变量,但是不能给指针解引用赋值,试图通过指针去修改它所指向的内存的值。
int const p;//这个 类型是int,修饰的p是个常量,但p可以被更改(这个一级指针可以指向其他的整型变量)。前两种一个意思。
int const p;//这个 类型是int,修饰的p是个常量,但p可以被更改。定要初始化(变量名被const了) 不能给p赋值,让p指向其他的整型变量,但是可以通过指针去修改它所指向的内存的值。
const int const p; //这第一个 类型是int,修饰的p是个常量。第二个类型是int,修饰的p是个常量。定要初始化
3和4是常量。变量名被const修饰 故而3 4定要初始化。
(2)错误:const int*——>int* p解引用就会修改a的值
int p = &a;//出错:常量的指针或者引用泄露 然后通过解引用就会修改a的值。&a类型是 const int;而p是int
(3)
int a=10;a是普通量 int *p=&a; const int p=&a; int const p=&a;(正确)
const int a=10; const int p=&a;(正确) int const p=&a;(错误)
(4)对于一级指针
const int ------> int 错误
int --------->const int 正确
#include<typeinfo>//打印类型的
int main()
{
/* int a = 10;
const int b = 20;
a = b;
//b = a;//这句话出错:常量不可以作为左值
int a = 10;
int *p = &a;//没问题 类型一致 p是int*型;而&a也是int*型
const int a = 10;
//int *p = &a;//出错:(间接方法)常量的指针或者引用泄露 然后通过地址解引用就会修改a的值。&a类型是整型常量地址 const int*;而p是普通整型地址 int*。const没有修饰*p,故而*p可以去修改值
const int*p = &a;//这样是类型合适。此时的*p不可以修改 而p可以被赋值。p本身不是常量 是变量,p值变了
const int b = 20;
p = &b;
cout << *p << endl;//*p=20;
int a = 10;//还可以更改成 const int a=10;
const int *p = &a;//正确:const int *<--------int*。p内放的是整型常量的地址,p可以放前者 也可以放后者
//int *q = p;//出错:int*<--------const int *
*/
int a = 10;//const 如果没有修饰指针和引用时 不用管const
const int *p = &a; cout << typeid(p).name() << endl;//int const *
int *const q = &a; cout << typeid(q).name() << endl;//int *
return 0;
}
不管是把a的地址还是 const a的地址放到p里面,编译器认为:p里面放的是整型常量的地址,所以不允许把一个整型常量的地址,放到普通的指针里面。
const 如果没有修饰指针符号*和引用符号&时,在考虑类型的时候,不用管const 。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
函数重载的第3个条件:
对实参的值是否有影响 const/volatile 右边 有指针/引用 可以作为函数重载的前提条件。
volatile:作用两个:
(1)防止编译器对汇编指令进行顺序上的优化。指的是:我们程序写出来之后会发生无规律的问题。高级源代码的运行顺序有可能跟编译器编译优化以后的指令的运行顺序不一样。对于指令的调优,也是为了代码运行的效率。
(2)防止寄存器存储变量的副本值 应用在多线程程序。
对指令的调优:
1 编译器对指令顺序上的调优 volatile
2 程序运行时CPU对指令顺序上的调优。 barrier() CPU不能把其上的指令调整到其下去。
int data=10;
func(&data);
void func(int *a)// int *
{
*a=20;//可以给*a赋值 改变data的值
}
void func(const int *a) //这两个类型不同 可以构成重载 const int *
{
保护实参data不能修改 不可以给*a赋值
}
/*
类型相同:
(int a) (const int a)
(int *a) (int *const a)
*/
Note2:const 和 引用的结合
**只有const int &b;和 int const &b;**定义引用变量的时候,引用的名字必须和变量名紧紧地挨在一起。在考虑类型的时候,一定要考虑const的。因为const的右边有引用的,那么(int const &b)和(int &b)作为函数的形参,两个函数可以构成重载。 如果int const &b 和 int &b可以构成函数的重载;int const b和int b 就不行。
const& 引用常量(可寻址的常量和不可寻址的常量)
引用不参与类型。指针参与类型。
要去引用一个不可寻址的常量的话,常引用。const 去修饰引用变量本身。
int main()
{
/*int a = 10;//没有问题
int &b = a;
const int &c = a;*/
//const int a = 10;
////int &b = a;//出错:b可以被赋值 进而修改a。a的引用泄露出去,也即a的地址泄露出去。
//const int &b = a;//这样可以
//int &a = 10;//出错:定义引用必须要初始化,右边必须是可以取地址。非常量引用的初始值必须是左值
//const int &a = 10;
/*相当于 引用临时量
const int temp=10;
const int &a=temp;
*/
//在 内存0x0018ff44上写一个4字节的整数10
//int *p = (int *)0x0018ff44; *p = 10;//p是普通的指针变量
//int *const &p = (int *)0x0018ff44;//常引用 const修饰的是引用变量p本身
//*p=10;
/*相当于
int * const ptemp=(int *)0x0018ff44;
int * const &p=ptemp;
*/
const int a = 10;
const int*p = &a;//这样是类型合适。此时的*p不可以修改 而p可以,p本身不是常量 是变量,p值变了
const int b = 20;
p = &b;
cout << *p << endl;
return 0;
}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Note3:const 和 二级指针的结合
错误情况:
const int * -------->int *
int** --------------->const int **
const int * *--------->int **
int main()
{
int a = 10;
int *p = &a;
int **q = &p;
return 0;
}
三种结合方式:
1 const int **q 类型: int ;表达式:q。不能给q赋值,来修改a内存的值。可以给q赋值,来让这个二级指针指向别的一级指针。也可以给*q赋值,来改变一级指针p指向其他的普通变量。
2 int * const * q 类型: int *;表达式:q。不能给q赋值,q和**q都是可以给赋值的。
3 int ** const q 类型: int** ;表达式:q。不能给q赋值,q和q都是可以给赋值的。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
错误一:
错误一 原因分析:const int ** q = &p;其中 ** q和 *p是指向同一块内存。*q和p指向同一块内存。 修改了q就是修改了p。不能给q赋值,所以q《=》*p这种情况不予考虑了。q可以去赋值,q的类型是整型常量的地址,也即给q赋值,只能找整型常量的地址,但是给q赋一个整型常量地址的值,那么就改变了p指向的地址,即此时的p指向整型常量的内存,p可以赋值,但是p一旦赋值就改动了那个整型常量的值。因此出错。
怎么修改:
const int constq = &p;
或者
const int *p = &a;
const int **q = &p;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
错误二:
错误二原因分析:const int *&q = p; q是个引用变量,和p等同。但是给q赋值只能拿整型常量的地址,然后和错误一等同。(还原成指针和上面一样)
修改方式:和上面两种一样。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
正确的情况两个:
int main()
{
int a = 10;
int *p = &a;
int *const*q = &p;//情况是:int* 转换为 const int * 正确
int b = 10;
int *p1 = &b;
int **const q1 = &p1;
return 0;
}
二级指针const如果出现在中间,变成一级指针。const之前的不予考虑。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
错误三:
错误三原因分析: int ** q = &p;其中 ** q和 p是指向同一块内存。q和p指向同一块内存。给p赋值不行,给p赋值(p的类型:整型常量的地址),同时也就是把这个地址给q了。但是*q是个普通指针 然后它就可以**q把这个整型常量的值改了。
修改方式:const int **q = &p;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
错误四:
错误四原因分析: int **q = &p;其中 ** q和 *p是指向同一块内存。q和p指向同一块内存。给p赋值不行(p本身是个常量),但是下面把p的地址泄露出去里,然后q就可以修改p的值。
修改方式:int *const *q = &p;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Note4:const * &在函数中的应用
函数的返回值:
<=4 都是由eax寄存器带出去(没有产生临时量)8个字节的通过
4< <=8 8字节的由两个四字节寄存器 eax edx分两次带出来
<8 产生临时量:这个临时量函数调用之前产生的,在main函数的栈帧上。为了把调用函数的局部变量temp带出来,不可能把临时量内存产生到被调用函数的栈帧上。在return temp时怎么知道main函数栈帧上的临时量内存的地址在哪里呢? 所以就要在调用sum函数时候把main函数栈帧上的临时量内存地址入栈(最后一个参数入栈)。所以说 访问main函数栈帧上的临时量内存时候 通过ebp+8是其地址。
临时量产生3 种情况:
1 函数调用之前:<8 产生临时量 接收返回值
2 函数的return语句处:
3 函数调用之后:const int &a = GetInt();
int GetInt()
{
int value = 10;
return value;
}
int main()
{
const int &a = GetInt();
return 0;
}
定义引用变量接收函数的返回值。int &a=10;这是不行的。那么要常引用。
const int &a = GetInt();相当于:临时量是在GetInt函数执行完了之后产生的,因为要引用它的返回一个不可寻址的10产生了一个临时量。即:const int temp=GetInt();然后const int &a=temp;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
int *p = &GetInt();//不行 GetInt()没法取地址
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
int * GetIntPtr()//我们不能写这样的代码:因为不能返回局部变量的地址或者引用。栈帧会消失
{
//int value = 10;//按照下面写就可以了
static int value = 10;//value 在data段
return &value;//返回一个整型变量的地址 在返回的时候,没有产生临时量(返回值小于等于4字节 eax带出)
}
int main()
{
int *p = GetIntPtr();//eax的值 放到p里面
return 0;
}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
int * GetIntPtr()//我们不能写这样的代码:因为不能返回局部变量的地址或者引用。栈帧会消失
{
//int value = 10;//按照下面写就可以了
static int value = 10;//value 在data段
return &value;//返回一个整型变量的地址 在返回的时候,没有产生临时量(返回值小于等于4字节 eax带出)
}
int main()
{
int *&p = GetIntPtr();//不行 GetIntPtr()不能取地址,它是通过一个eax带回来立即数的
return 0;
}
修改如下:引用常量(可寻址的 不可寻址的),都要用常引用
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
int* GetIntPtr()//我们不能写这样的代码:因为不能返回局部变量的地址或者引用。栈帧会消失
{
//int value = 10;//按照下面写就可以了
static int value = 10;//value 在data段
return &value;//返回一个整型变量的地址 在返回的时候,没有产生临时量(返回值小于等于4字节 eax带出)
//lea eax,dword ptr[value]
}//返回指针:接收的就是一个地址。
int& GetIntRef()
{
static int value = 10;//因为不能返回局部变量的地址或者引用。栈帧会消失 所以也加上 static
return value;
//lea eax,dword ptr[value]
}
int main()
{
int a = GetIntRef();//带出来是一个地址,但是要把它赋给一个整型变量,直接给这个地址解引用。访问的还是GetIntRef()中的value本身,但是出了GetIntRef() value已经没了。赋值前解引用 即:*eax ----->a
int &b = GetIntRef();//GetIntRef()可以取地址:取的地址就是value的地址。b就是引用value
int *p1 = &GetIntRef();//p1指向了value
return 0;
}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
上一篇: java注解学习体会,并自己实现一个注解
推荐阅读