【5】指针
目录
- 常量也有地址
cout<<&"emm"<<endl;
- 不同编译器输出的地址的格式有不小的区别
- 所有指针占用的空间都一样(4 字节)
int** k;
p=&a;
k=&p;
//p为地址
//*p为地址上的值
p[i] // 等价于*(p+i)
void* d = NULL
指针型变量可进行+,-,++,--运算
如果 2 个指针变量都指向同一个数组中的元素,则 2 个指针变量之差为 2 个指针之间的元素个数
p±n 实际位置为 (p)±n*数据长度(字节数)//(数据长度在定义指针时定义,与传入的地址无关)
指向同类型相关数据(如同一个数组的)时,可进行比较(<,>,==)
int a[],*p={1,2,3,4,5};
*p=a;//取a[0]的地址
*p=&a[1];//取a[1]的地址
void* m=c;
p=(int*)m;//void指针需要强制类型转换
p==0 和 p!=0 可用于判断 p 是否为空指针
char* s;
s="Hello world";
char* s="Hello world";//正确
开辟了一个字符数组来存放,末尾自动+‘\0’
注意:因为 ++ 和 * 是单目右运算符,所以
*p++
和*(p++)
一样
*(p++)
和*(++p)
不一样
专业课笔记
- 空指针一般意味着未申请?
- 野指针一般意味着指针指向的内存区域已经被释放
常量指针和指针常量
const 和指针的结合用法
常量指针
int a=0;
const int* p=&a;
*p=1;//不可以
a=1;//可以
//const好像一般只在局部有效果
//且似乎只针对定义时的变量名,一般用其他方法还是可以修改值的
指针常量
int a=0,b=1;
int * const p=&a;
*p=1;//可以
p=&b;//不可以
指针数组
int *p[4];
数组的奇怪的用法//CSDN
int a[3];
int* p=a// int (*p)[3]=a时p指向一个有3个元素的数组,p++则位置向前12个内存单位
//这里a被转化(可理解为衰退)为一个指针,所以可以给*p赋值
*a //等价于*p
char a[10];
char* p = a; //这里的a被转换为 char* 型指针,所以可以赋值给p
a = p; //ERROR! a虽然被转换为指针了,但转换后得到的指针无确切地址,不是lvalue,不能被赋值
char (*pa) [10] = &a; //a是&的操作数,没有发生转换,&a意为取数组a的地址,得到一个指向数组a的指针
sizeof(a); //a未发生转换,仍是数组,所以表达式得到的值是数组的大小
&a; //a未发生转换,仍是数组,所以表达式得到的值是数组的地址
*a; //a被转换为指针,操作符*是作用于指针的,而非数组
*(a+1); //a被转换为指针,所以并不是数组a与1相加,而是转换后得到的指针在参与运算
a[0]; //a被转换为指针,所谓数组的下标本质是指针运算
a == a; a - a; //a被转换为指针,本质是指针运算
test//vs2019
const char* s[] = { "Student","Teacher","Father","Mother" }, * ps = s[2];
cout << *s[1] << ',' << ps << ',' << *ps << '\n';
cout << ps;//输出数组
cout << (int)ps;//输出指针指向
cout << (int)&ps;//输出指针地址
cout << &ps;
【5.5】引用变量
本质还是指针?呸!不一样的好吧
数据类型 &别名 = 原名
int main() {
int a = 10;
int &b = a; //声明b是a的引用
// &是引用声明符,并不代表地址
//声明变量b为引用类型,并不需要另外开辟内存单元来存放b的值
//b和a占内存中的同一个存储单元,它们具有同一地址
b = 100;
return 0;
}
int a=10,*p;
p=&a;
int &b=*p;
cout<<b<<endl;
- 引用必须初始化
- 在声明变量 b 是变量 a 的引用后,在它们所在函数执行期间,企图使 b 又变成 a2 的引用(别名)是不行的
{
int a = 10;
int b = 20;
//int &c; //错误,引用必须初始化
int &c = a; //一旦初始化后,就不可以更改
c = b; //这是赋值操作,不是更改引用
//a=b=c
return 0;
}
引用做函数参数//函数
//引用传递
void mySwap03(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl;
return 0;
}
引用做函数返回值//函数调用作为左值
- 不要返回局部变量引用
//返回局部变量引用
int& test01() {
int a = 10; //局部变量
return a;
}
//返回静态变量引用
int& test02() {
static int a = 20;
return a;
}
int main() {
//不能返回局部变量的引用
int& ref = test01();
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
//如果函数做左值,那么必须返回引用
int& ref2 = test02();
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
test02() = 1000;//给返回的变量赋值?
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
return 0;
}
引用的本质?//只能说可以这么理解
类似一个指针常量
//发现是引用,转换为 int* const ref = &a;
void func(int& ref){
ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
int a = 10;
//自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
int& ref = a;
ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;
cout << "a:" << a << endl;
cout << "ref:" << ref << endl;
func(a);
return 0;
}
常量引用//和 const 联动
作用:常量引用主要用来修饰形参,防止误操作
//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
//v += 10;
cout << v << endl;
}
int main() {
//int& ref = 10; 引用本身需要一个合法的内存空间,因此这行错误
//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
const int& ref = 10;
//ref = 100; //加入const后不可以修改变量
cout << ref << endl;
//函数中利用常量引用防止误操作修改实参
int a = 10;
showValue(a);
return 0;
}
引用的本质?//CSDN
从编译上看,程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。这是使用指针不安全而使用引用安全的主要原因。从某种意义上来说引用可以被认为是不能改变的指针。
不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。
理论上,对于指针的级数没有限制,但是引用只能是一级。如下: int*_ p1; // 合法。指向指针的指针 int_& p2; // 合法。指向指针的引用 int&* p3; // 非法。指向引用的指针是非法的 int&& p4; // 非法。指向引用的引用是非法的 注意上述读法是从左到右。
有趣//一种猜测//doge//存疑
//首先
int add() {
return 50;
}
++add();//报错,表达式必须是可修改的左值
++50;//报错,表达式必须是可修改的左值
//然后
int &add() {
return 50;//报错,非常量引用的初始值必须为左值
}
int& add();
int& a = 5;//报错,非常量引用的初始值必须为左值
++add();
++a;
所以int& add();类似于int& a = 5;
推测发生了int& add=50导致错误
//then
int &add() {
int a=50
return a;//不报错
}
++add();//不报错
//then
static int a;
int &add() {
a=50
return a;//不报错
}
cout<<++add()<<endl;
cout<<a<<endl;
//输出2个51
推测发生了int& add=a;建立了引用
//this指针//来自后面的案例
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
Person& PersonAddPerson(Person p){
this->age += p.age;
//返回对象本身
return *this;
}
推测Person& PersonAddPerson=*this;
因为*this=p2
等价于Person& PersonAddPerson=p2;