最近为了面试重新整理了下指针
1、指针是什么?
指针(Pointer)就是内存的地址,C语言允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组、函数以及其他指针变量的地址。
计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如 int 占用 4 个字节,char 占用 1 个字节。但是需要注意指针的长度与系统位数有关,32位系统占用4位字节,64位系统占用8位字节。
int* p1;
char *p2;
// 输出int和char类型的数据长度
std::cout << "sizeof(*p1)=" << sizeof(*p1) << " sizeof(*p2)=" << sizeof(*p2) << std::endl;
// 输出指针int*和char*类型的数据长度
std::cout << "sizeof(p1)=" << sizeof(p1) << " sizeof(p2)=" << sizeof(p2) << std::endl;
// 输出:
// sizeof(*p1)=4 sizeof(*p2)=1
// sizeof(*p1)=8 sizeof(*p2)=8 // 测试环境是x64
2、指针运算符 & 和 *
这里&是取地址运算符,*是间接运算符。
int p = 12;
&p 的运算结果是一个指针,指针所指向的地址就是 p 的地址;
指针的类型是 p 的类型加个*,指针所指向的类型是 p 的类型。
示例1:指针表达式
int a=12;
int b;
int *p;
int **ptr;
// &a 的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a 的地址。
p = &a;
// *p 的类型是int,它所占用的地址是 p 所指向的地址,显然,*p 就是变量a。
*p = 24;
// &p 的结果是个指针,该指针的类型是 p 的类型加个*,在这里是int **。
// 该指针所指向的类型是 p 的类型,这里是int*。该指针所指向的地址就是指针p 自己的地址。
ptr = &p;
// *ptr 是个 int* 指针,&b 的结果也是个 int* 指针,
// 这两个指针的类型是一样的,所以用&b 来给*ptr 赋值就是毫无问题的了。
*ptr = &b;
//*ptr 的结果是ptr 所指向的东西,在这里是一个指针,
//对这个指针再做一次*运算,结果是一个int 类型的变量。
**ptr=34;
示例2:指针和数组
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=array[0]; //也可写成:value=*array;
value=array[3]; //也可写成:value=*(array+3);
value=array[4]; //也可写成:value=*(array+4);
实例3:指针和结构体
指针指向结构体成员
int main()
{
struct student st1, *st2;
st2 = &st1;//指向结构体变量
scanf("%s", st2->name);
scanf("%d", &st2->score);
printf("%s %d\n", st1.name, st1.score);
printf("%s %d\n", (*st2).name, (*st2).score);
printf("%s %d\n", st2->name, st2->score);
return 0;
}
//输入: aha 12
//输出:aha 12
// aha 12
// aha 12
注意:
(1)如果要将一个结构体对象赋给一个结构体指针变量,那么它们的结构体类型必须相同
(2)指针 *st2 两边的括号不可省略,因为成员运算符“.”的优先级高于指针运算符“*”,所以如果 *st2 两边的括号省略的话,那么 *st2.name 就等价于 *(st2.name) 了。但是我们可以使用下面两种方式访问结构体成员:
(*指针变量名).成员名
指针变量名->成员名
示例4:指针运算符
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int* p = a;
std::cout << "*p++=" << *p++ << " *(p++)=" << *(p++) << " (*p)++=" << (*p)++ << std::endl;
// 输出:*p++=1 *(p++)=1 (*p)++=0
-
*p++
: 这个表达式先使用后递增。它先解引用指针p
,然后返回指针所指向的值。接着,指针p
递增到下一个元素的位置。换句话说,它等效于*(p++)
。 -
*(p++)
: 这个表达式也是先使用后递增。它先将指针p
的当前值用于解引用,然后返回该位置上的值。接着,指针p
递增到下一个元素的位置。 -
(*p)++
: 这个表达式首先解引用指针p
,然后对指针所指向的值进行递增操作。它相当于对指针所指向的值进行后递增操作。换句话说,它会递增p
所指向的值,并且该递增操作会改变原始值。
3、错误用法总结
-
一个函数想修改另一个函数的值,必须传指针和解引用
-
野指针、空悬指针:没有访问权限的地址
-
空指针(NULL):表示当前指针无效
错误1:将整数值赋值给指针变量
// 错误的将整数值赋值给指针变量
int *p = 6;
// 修正用法:
int* p = NULL; // 声明一个指向整数的指针
int value = 6; // 声明一个整数变量并赋值为 6
p = &value; // 将 'value' 的地址赋给指针 'p'
错误2:函数参数通过值传递传递不会影响到原始的参数
Swap
函数接收两个整数参数 a
和 b
,但并没有使用指针进行交换操作。在这种情况下,函数参数 a
和 b
是通过值传递传递进来的,而函数内部对它们的操作不会影响到原始的参数。
void Swap(int a, int b)//未传指针
{
int tmp = a;
a = b;
b = tmp;
}
错误3:没有解引用
在函数内部,你只是对指针进行了赋值操作,并没有对指针所指向的值进行交换。这种情况下,指针变量 a
和 b
的交换并不会影响原始的指针指向的值。!
void Swap(int* a, int* b)//未解引用
{
int *tmp = a;
a = b;
b = tmp;
}
修正后
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
错误4、野指针
tmp的值未初始化,没有申请tmp指针指向的内存空间
void Swap(int* p1, int* p2)//有野指针 tmp的值未初始化
{
int* tmp;
*tmp = *p1;
*p1 = *p2;
*p2 = *tmp;
}