02-指针入门

一.什么是指针

1.1 指针的两种含义

  • 指针是内存中最小单元的编号, 也就是地址
  • 平时口语上的指针, 实际上是指针变量, 用来存放内存地址的变量.

1.2 指针表示含义

  • 指针表示细节
//以下这两种写法都是可以的, 但是第一种更加贴合实际含义, 注意保存代码库的风格一致性即可.
char* p = NULL; //这种写法更加强调p是(char*)类型的变量.
char *p = NULL; //这种写法更容易看出p是指针.

总结: 指针就是地址, 平时口头指针是指指针变量

二. 指针类型的意义

2.0 问题引出

  • 在32位机上, 指针都是占4个字节, 那为什么还要分不同的类型, 这4个字节不是可以存储任何类型数据的地址吗?
char* pa = NULL;
int*  pi = NULL;
float* pf = NULL;
...

2.1 不同类型指针步长不同

  • 以下这段代码通过指针加减整数, 来展示不同类型指针的步长.
#include <stdio.h>

int main()
{
 int n = 10;
 char *pc = (char*)&n;
 int *pi = &n;
 
 printf("%p\n", &n);      //0x010FF70C
 printf("%p\n", pc);      //0x010FF70C
 printf("%p\n", pc+1);    //0x010FF70D
 printf("%p\n", pi);      //0x010FF70C
 printf("%p\n", pi+1);    //0x010FF710
 return  0;
}

2.2 不同类型指针解引用的权限不同

  • 从以下这段代码可以清除观察到, char对空间操作的权限只有一个字节, 而int权限是4个字节
#include <stdio.h>

int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	int* pi = &n;

	*pc = 0;
	printf("%#0x\n", n); //0x11223300

	*pi = 0;
	printf("%#0x", n);   //0
	return 0;
}

总结: 指针类型主要有两个作用: 一是决定步长, 二是决定管理空间大小的权限

三. 野指针

3.1 指针未初始化造成野指针

  • 如下代码, p没有指向任何空间, 此时p中未随机值, 这时候指向的空间并没有获得系统的使用权限, 对这块空间赋值就是非法行为.
#include <stdio.h>
int main()
{ 
 int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
 return 0;
}

3. 2 指针越界访问造成野指针

  • 如下这段代码指针指向的空间越过了数组的范围, 所指向的空间也是没有获得系统使用权限, 会造成非法访问.
#include <stdio.h>
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   }
    return 0;
}

3.3 指针指向的空间释放(动态内存)

  • 如下代码中ptr被释放后, 再使用pt去指向该空间, 就会造成野指针.
#include <stdio.h>
#include <stdlib.h>

int main() {
    int* ptr = (int*)malloc(sizeof(int));  // 分配动态内存
    *ptr = 10;                             // 在分配的内存中存储值

    free(ptr);                             // 释放动态内存

    int* pt= ptr;                          // 尝试访问已释放的内存
    printf("%d", *pt);

    return 0;
}

3.4 如何规避野指针

  • 指针初始化
  • 小心指针越界
  • 指针空间释放及时置NULL
  • 避免返回局部变量的地址
  • 指针使用之前检测有效性
#include <stdio.h>
int main()
{
    int *p = NULL;
    //....
    int a = 10;
    p = &a;
    if(p != NULL)
   {
        *p = 20;
   }
    return 0;
}

总结: 野指针的形成主要出现在: 一. 指针未初始化. 二. 指针越界访问. 三. 指针指向被释放的内存空间

四. 指针运算

4.1 指针 +- 整数

  • 可以改变指针指向的位置前后
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
     *vp++ = 0;
}

4.2 指针 - 指针

  • 可以得到两个指针之间的空间大小(注意没有指针 + 指针因为没有实际应用场景)
int my_strlen(char *s)
{
       char *p = s;
       while(*p != '\0' )
              p++;
       return p-s;
}

4.3 指针运算注意事项

  • 实际上我门应该避免以下这种写法: 因为标准规定允许数组元素的指针与数组最后一个元素后面的存储位置的指针比较. 但不允许指向第一个元素的指针, 与其之前的那个内存位置的指针进行比较.
//写法一
for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0;
}

//简化
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}

五. 二级指针和指针数组

5.0 问题引出: 指针变量的地址放在哪里?

  • 下图指针pa的地址, 存放在ppa中, 而ppa就是二级指针变量.

5.1 二级指针运算

  • 主要注意: *ppa 和 **ppa的含义即可
int b = 20;
int* pa = &b
int** ppa = &pa;
*ppa = &b;  //等价于 pa = &b;
**ppa = 30; //等价于 b = 30; 和 *pa = 30;

5.2 数组指针的概念

  • 用于存放指针的数组(本质还是数组, 数组的每个元素都是指针或者说, 数组的每个内存空间都是用于存放指针的)
int arr1[5];
char arr2[6];

int* arr3[5];    //数组每个元素都是int*(整形指针类型)
char* arr4[6];   //数组每个元素都是char*(字符指针类型)
posted @   一步一磕头的菜鸡  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示