文章目录
一、什么是指针
首先先了解什么是内存地址:对程序进行编译时,系统就会为这个变量分配内存单元。编译系统根据程序中定义的变量类型分配一定长度的空间,内存的基本单元是字节,一字节有8位,内存是以字节为单位的连续编址空间, 每一个字节单元对应着一个唯一的编号, 这个编号被称为内存单元的地址
而指针:
1、指针是存储对象的内存地址的一个特殊变量。
2、每个内存地址是可以存放一些数据的。这些数据就叫做“指针所指的数据”或者“指针指向的数据”
3、其主要有三个目的:
1. 在堆上分配新对象,
2. 将函数传递给其他函数
3. 迭代数组或其他数据结构中的元素
二、指针的作用
一、指针就是指向一个特定内存地址的一个变量。C\C++中指针可以有效地表示复杂的数据结构、动态分配内存、高效地使用数组和字符串、使得调用函数时得到多个返回值等。
二、指针使得不同区域的代码可以轻易的共享内存数据。
三、指针使得一些复杂的链接性的数据结构的构建成为可能。
四、在使用new等操作时,必须使用到内存,可以更高效的处理内存
二、指针的使用
在使用指针前必须对指针进行声明
//声明格式 type *var-name;
int a= 20;
int* b; // 指针变量的声明
b = &a; // 存储 a 的地址
我们使用vs来调试下
“*”的作用是按照指向关系访问所指向的对象的值
从中可以得出:
1、指针也是存在地址的
2、指向的值,也就是指向对象的地址
1.关于指针的运算
这里的var并不是数组本身
int var[6] = { 1,2,3,4,5,6 };
int* ptr;
//int* ptr = &a 初始化指针
// 指针中的数组地址
ptr = var;
for (int i = 0; i < 6; i++)
{
cout << "ptr=[" << i << "]";
cout << ptr << endl;
cout << "*ptr[" << i << "] = ";
cout << *ptr << endl;
// 指针向下一个进行移动
ptr++;
}
结果很正确,一个int占用4个字节
2、指针数组
int var[6] = { 1,2,3,4,5,6 };
int* ptr[6];
for (int i = 0; i < 6; i++)
{
ptr[i] = &var[i]; // 赋值为整数的地址
cout << "var[" << i << "]=" << *ptr[i] << endl;
}
return 0;
结果依然和预期的一样,我们接下来调试一下
每次运行到 ptr[i] = &var[i] 时,赋予指针一个元素
3、常量与指针
常量就是不可变的数据
常量指针:指向常量的指针,在指针定义语句的类型前加const,表示指向的对象是常量。
先看如下代码`,这里调试可能会出现不必要的问题
普通指针
int a = 5,z=0;
int* ra = &a; //整型指针 指向a的地址
a = a + 1;
cout << ra << endl;//00D5FB54
ra = &z; // 改变指针的地址
cout << ra << endl;`//00D5FB48
结论:指针指向的地址是可以改变的
常量指针
const int b =6;
int* rb = &b;
//error C2440: “初始化”: 无法从“const int *”转换为“int *”
编译器报错,原因: 因为b是一个常量,所以 &b也不能被改变,所以rb指向的值也不可以被改变
const int b =6;
const int* rb = &b; //常量指针
*rb = a; //error “rb”: 不能给常量赋值
在看
cout << *rb<<endl; //6
rb = &z; //但是可以改变指针本身
cout << *rb; //0
结论:指针指向的值不能被改变,指针本身的值可以被改变
指针常量
int x = 7;
int* const rx = &x; // 指针常量
int z =0;
rx = &z;// error C3892: “rx”: 不能给常量赋值
(*rx) = z; //正常
运行如下
结论:不能给常量赋值,可以改变指针指向的值,与常量指针相反
常量指针常量
const int c = 5;
const int* const rc = &c;
rc = 100;
rc = &z;
*rc = &z;
全报错,结论:不能改变指向的值,本身也不能被改变
- (指针)和 const(常量) 谁在前先读谁 ;* 代表被指的数据,名字代表指针地址
const在谁前面谁就不允许改变。
4、指针与类
1、访问类成员
我们先定义一个类
class Circle {
public://设为公有
double radius;
Circle() {
radius = 1;
}
Circle(double newRadius) {
radius = newRadius;
}
double getArea() {
return radius * radius * 3.14159;
}
};
箭头运算符 -> :用指针访问对象成员 就不用直接加*号了
Circle circle1;
Circle* pCircle = &circle1;
cout << "The radius is " << (*pCircle).radius << endl;
cout << "The area is " << (*pCircle).getArea() << endl;
(*pCircle).radius = 5.5;
cout << "The radius is " << pCircle->radius << endl;
cout << "The area is " << pCircle->getArea() << endl;
运行如下
2、在堆中创建对象
在函数中声明的对象都在栈上创建,函数返回,则对象被销毁
你可以用new运算符在堆上创建它,为保留对象
new Circle{}
如果请求的存储空间为零字节,则 operator new 返回指向不同对象的指针。 也就是说,重复调用将
operator new 返回不同的指针。 如果分配请求没有足够的内存,则会 operator new 引发 std::bad_alloc
异常。 或者, nullptr 如果已在非引发支持中链接,则返回 operator new 。
delete
使用运算符动态分配的内存 new 可以使用运算符来释放 delete 。 Delete 运算符调用 operator delete 函数,该函数将内存释放回可用池。 使用 delete 运算符还会导致调用类析构函数(如果存在)。
Circle *pCircle1 = new Circle{}; //用无参构造函数创建对象
Circle *pCircle2 = new Circle{5.9}; //用有参构造函数创建对象
//程序结束时,动态对象会被销毁,或者
delete pObject; //用delete显式销毁
四、引用
1、什么是引用
引用变量是一个别名,它它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
int i = 10;
int& ref = i; //ref引用了i
ref++;
下面这是错误的写法,引用必须初始化,不存在空引用
int i = 10;
int& ref ;
ref = i;
ref++;
内存地址相同,对ref的操作会作用到i上
2、 把引用作为返回值
double vals[] = { 10.1, 12.6, 33.1, 24.1, 50.0 };
double& setValues(int i)
{
return vals[i]; // 返回第 i 个元素的引用
}
其结果为
3、常量和引用
const char* t = "word";
const char* s = "Hello";
const char*& rs = s;
std::cout << s << std::endl;
rs = t;//rs引用被重新赋值为t
std::cout << rs << std::endl;
4、引用传参和指针传参
二、引用参数
一下两穿代码作用相同
指针,交换的是地址
void swap(int *a,int *b)
{
int temp;
temp=*a;
a=*b;
*b=temp;
}
引用传参,交换的是实参
void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int main() {
int a = 1, b = 42;
swap(a, b);//引用传参,实参
swap(&a, &b); //指针传参,地址
return 0;
}
当大型对象被传递给函数时,使用引用参数可使参数传递效率得到提高,因为引用并不产生对象的
副本,也就是参数传递时,对象无须复制
五、指针和引用的区别
1、引用不可以为空,但指针可以为空。定义一个引用的时候,必须初始化。指针随意
2、引用不可以改变指向,但是指针可以改变指向。虽然引用不可以改变指向,但是可以改变初始化对象的值,而对指针的操作,会使指针指向其他对象
3、引用的大小是所指向的变量的大小,因为引用只是一个别名而已,内存地址和对象相同;指针是指针本身的大小,4个字节,本身就存在一个内存地址中
4、引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL。const
指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。
5、“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
6、指针和引用的自增(++)运算意义不一样;
指针传递和引用传递
转载:C++中引用和指针的区别
这位大佬写的挺不错的
1、指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
2、引用传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。
从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。
而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。
在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:
总结
提示:这里对文章进行总结:
相同点:
都是地址的概念;
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点:
1、引用不可以为空,但指针可以为空。定义一个引用的时候,必须初始化。指针随意
2、引用不可以改变指向,但是指针可以改变指向。虽然引用不可以改变指向,但是可以改变初始化对象的值,而对指针的操作,会使指针指向其他对象
3、引用的大小是所指向的变量的大小,因为引用只是一个别名而已,内存地址和对象相同;指针是指针本身的大小,4个字节,本身就存在一个内存地址中
4、引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL。const
指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。
5、“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
6、指针和引用的自增(++)运算意义不一样;