在C++中,指针是一种特殊的变量,它存储了一个内存地址。C++指针在处理内存、数组、函数参数传递、文件I/O、动态内存分配等方面有着重要的应用。
一个指针变量通常被声明为特定类型的指针。例如,一个整数类型的指针可以指向一个整数。在声明指针变量时,应该在变量名前面加上星号(*)。
例如,下面的代码声明了一个整数类型的指针:
cpp
int* p;
要初始化一个指针,可以使用另一个变量(或其他类型的内存地址)来赋值给它。例如:
cpp
int x = 10;
int* p = &x; // p现在指向x的内存地址
在C++中,可以通过解引用操作(使用*运算符)来访问指针指向的值。例如:
cpp
std::cout << *p << endl; // 输出10,因为p指向x的内存地址*,p就是取出这个地址的值
获取对象地址,要想获取改地址,需要使用取地址符(操作符&);
cpp
int val = 56;
int *p = &val; //p存放变量val的地址,或者说p是指向变量val的指针
cpp
double val;
double *pd = &val; //正确:初始值是double型对象的地址
double *pd2 = pd; //正确:初始值是指向double对象的指针
int *pi = pd; //错误:指针pi的类型和pd的类型不匹配
pi = &val; //错误:试图把double型对象的地址赋值给int型指针
指针值
指针的值(即地址)应属于下列4种状态之一:
1.指向一个对象。
2.指向紧邻对象所占空间的下一个位置。
3.空指针,意味着指针没有指向任何对象。
4.无效指针,也就是上述情况之外的其他值。
利用指针访问对象如果指针指向了一个对象,则允许使用解引用符(操作符*)来访问该对象;
cppint val = 42; int *p = &val; //p存放着变量val的地址,或者说p是指向变量val的指针 cout << *p; //由符号*得到指针p所指向的对象,输出42; *p = 0; //由符号*得到指针p所指向的对象,即可有p为变量val赋值 cout << *p; //输出0
接引用操作,仅适用于那些确实指向了某个对象的有效指针
空指针空指针(null pointer)不指向任何对象,在试图使用一个指针之前代码可以先检查他是否为空。以下列出几个生成空指针的方法:
int *p1 = nullptr; //等价于int *p1 = 0;
int *p2 = 0; //直接将p2初始化为字面常量0
//需要首先#include cstdlib
int *p3 = null; //等价于int *p3 = 0;
得到空指针最直接的办法就是字面值nullptr来初始化指针。
赋值和指针指针和引用都能提供对其他对象的间接访问
记住:赋值永远改变的是等号左侧的对象
void* 指针
void*是一种特殊的指针类型,可用于存放任意对象的地址。一个void*指针存放着一个地址。
double obj = 3.14,*pd = &obj; //正确:void*能存放任意类型对象的地址
void *pv = &obj; //obj可以是任意类型的对象
pv = pd; //pv可以存放任意类型的指针
利用 void*指针能做的事儿比较有限:拿它和别的指针比较、作为函数的输入或输出,或者赋给另外一个void*指针。不能直接操作 void*指针所指的对象,因为我们并不知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。
总的概括:以void*的视角来看内存空间,没办法访问内存空间中所存的对象。
练习巩固:
1.c++编写指针代码,分别更改指针的值以及指针所指向对象的值
cpp
#include<iostream>
int main()
{
float x = 3.6; //定义一个整型变量x并初始化为3.6
float *ptr = &x ; //定义一个指向x的指针ptr
std::cout << "原始指针的值:ptr = " << ptr << std::endl;
std::cout << "原始指针所指向的对象的值:*ptr = " << *ptr << std::endl;
//更改指针的值
float y = 2.3;
ptr = &y;
std::cout << "更改后指针的值:ptr = " << ptr << std::endl;
std::cout << "更改后指针所指向对象的值: *ptr = " << *ptr << std::endl;
//更改指针所指向对象的值
*ptr = 3030;
std::cout << "再次更改指针所指向的对象的值: *ptr= " << *ptr << std::endl;
return 0;
}
2.说明指针和引用的主要区别:
C++中的指针和引用是两个重要的概念,它们都可以用来间接访问其他对象,但它们之间存在一些关键的区别:
- 基本性质:指针是一个变量,存储的是另一个变量的地址;而引用是变量的别名,它和变量指向同一块内存空间。
- 初始化:引用在定义时必须被初始化,并且初始化后不能改变;而指针在定义时可以不被初始化,初始化后也可以改变其所指向的对象。
- 空值:指针可以为NULL,但引用不能。
- 取值方式:引用直接访问其所指向的对象,像操作普通变量一样;而指针需要通过解引用操作符*来访问其所指向的对象。
- 安全性:引用比指针更安全,因为引用总是指向一个存在的变量,而指针则可能指向一个不存在的内存地址。
- 语法形式:引用使用起来就像使用普通变量一样简单,而指针需要使用解引用操作符*来访问其所指向的对象。
总的来说,指针和引用都可以用来间接访问其他对象,但它们的使用方式和设计目标不同。指针更加灵活,可以用来操作内存中的任意地址;而引用更加安全和方便,可以像使用普通变量一样使用。
cpp
#include<iostream>
int main()
{
int x = 10; // 定义一个整型变量x并初始化为10
int* ptr = &x; // 定义一个指向x的指针ptr
int& ref = x; // 定义一个引用ref,它是变量x的别名
// 输出指针、引用和变量的值
std::cout << "x = " << x << std::endl;
std::cout << "ptr = " << ptr << ", *ptr = " << *ptr << std::endl;
std::cout << "ref = " << ref << std::endl;
// 修改变量的值
x = 20;
ptr[0] = 30;
ref = 40;
// 输出指针、引用和变量的值
std::cout << "x = " << x << std::endl;
std::cout << "ptr = " << ptr << ", *ptr = " << *ptr << std::endl;
std::cout << "ref = " << ref << std::endl;
return 0;
}
3.请简述下面这段代码的作用:
cppint i= 42; int *p1 = &i; *p1 = *p1 * *p1;
这段代码的主要作用是使用指针来修改它所指向的变量的值。
int i= 42; //
定义了一个整型变量i
并初始化为42。int *p1 = &i;//
定义了一个整型指针p1
,并将它初始化为变量i
的地址。*p1 = *p1 * * p1;//
这里有两个指针解引用操作(*p1
),第一个解引用操作获取p1
所指向的值(即变量i
的值),第二个解引用操作获取p1
所指向的变量的地址。然后,代码将这两个值相乘,并将结果存回p1
所指向的变量中。因此,这段代码的作用是将变量
i
的值从42修改为42*42(即1764)。
4.假设p是一个int型指针,请说明下述代码的含义
cppif(p) // ... if(*p) //...
这两个条件判断语句的含义是不同的。
if(p) // ...
这个语句是在检查指针变量p
是否为非空(即指向一个有效的内存地址)。如果p
非空,则会执行后面的代码块;如果p
为空,则不会执行后面的代码块。if(*p) //...
这个语句是在检查指针p
所指向的内存地址中存储的值是否为非零。如果*p
非零,则会执行后面的代码块;如果*p
为零,则不会执行后面的代码块。
5.下面代码中为什么 p 合法而 lp 非法?
cppint i = 42; void *p = &i; long *lp = &i;
在C语言中,
int
和long
是两种不同的数据类型。int
通常用于存储整数,而long
通常用于存储较大的整数。在大多数平台上,int
和long
的长度是不同的。int
通常为32位,而long
通常为64位。在这段代码中:
|---|------------------|
| |int i = 42;
|
| |void *p = &i;
|
| |long *lp = &i;
|第一行代码定义了一个名为
i
的整数,并初始化为42。第二行代码定义了一个名为
p
的void
指针,并将它初始化为整数i
的地址。这是合法的,因为void
指针是一个通用指针类型,可以存储任何数据类型的地址。第三行代码试图定义一个名为
lp
的长整数指针,并将它初始化为整数i
的地址。这是非法的,因为整数
i
的地址不能被直接赋给一个长整数指针。虽然它们的位数不同,但它们的对齐要求可能不同,也就是说,它们的内存模型可能不同。因此,将一个int
的地址赋值给一个long
指针可能会导致未定义的行为。在C++中,你不能将一个类型的地址直接赋给另一个类型,除非它们是兼容的类型。例如,如果你有一个指向整数的指针,你可以将它转换为指向字符的指针(因为一个字符的大小和一个整数的大小通常是一样的),但你不能将它转换为指向长整数的指针。