基础篇--指针

最近为了面试重新整理了下指针

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值