• 第八章 指针1


    1、概念

    ● 每个变量都被存放在从某个内存地址(以字节为单位)开始的若干个字节中。

    ● “指针”,也称为“指针变量”,大小为4个字节(或8个字节)的变量,其内容代表一个内存地址。

    ● 通过指针,能够对该指针指向的内存区域进行读写。

    ● 如果把内存的每个字节都想像成宾馆的一个房间,那么内存地址相当于就是房间号,而指针里存放的,就是房间号 。

    2、定义

    类型名 * 指针变量名;

    int * p;      //p是一个指针,变量 p 的类型是 int*
    char * pc;    //pc是一个指针,变量 pc 的类型是 char*
    float * pf;   //pf 是一个指针,变量 pf 的类型是 float*
    
    • 1
    • 2
    • 3

    3、指针的内容

    int * p=(int *)40000; //类型强制转化 40000为int类型,p为指针类型

    p的内容:
    十进制:40000 ----> 十六进制:0x9C40

    二进制每个比特 0000 0000 0000 0000 1001 1100 0100 0000

    p 指向地址 40000,地址 p 就是地址 40000,*p 就代表地址 40000 开始处的若干字节的内容

    4、通过指针访问其指向的内存空间

    定义指针变量:

    int * p = (int *)40000;
    
    • 1

    往地址 40000 处起始的若干字节的内存空间里写入 5000:
    (真实操作的时候,一般报错,因为40000处可能存放的是操作系统的指令。)

    *p = 5000;
    
    • 1

    将地址 40000 处起始的若干字节的内容赋值给 n:
    (“若干” = sizeof(int),因为 int* p;)

    int n = *p;
    
    • 1

    5、指针定义总结

    T * p; //T可以是任何类型的名字,比如int,double,char等等

    p的类型:T *      *p的类型:T
    
    通过表达式 *p,可以读写从地址 p 开始的 sizeof(T) 个字节
    
    *p 等价于存放地址 p 处的一个 T 类型的变量
    
    * 为间接引用运算符
    
    sizeof(T*) 4 字节(64 位计算机上可能 8 字节)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6、指针的用法

    char ch1 = 'A';
    
    char * pc = &ch1;  //使得pc指向变量ch1
    
    &:取地址运算符 
    
    &x:变量 x 的地址(即指向 x 的指针)
    
    对于类型为 T 的变量 x,&x 表示变量 x 的地址(即指向 x 的指针)&x 的类型是 T*
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    用法:

    char ch1 = 'A'; 
    char * pc = &ch1; //使得pc指向变量ch1
    *pc = 'B';  //使得ch1='B'
    char ch2 = *pc;//使得ch2=ch1
    pc = &ch2; //使得pc指向变量ch2
    *pc = 'D'; //使得ch2='D'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7、指针的作用

    problem:
    变量的赋值直接就可以进行,为什么还有先访问地址,再去进行赋值呢,岂不多此一举?
    answer:
    有了指针,就有了自由访问内存空间的手段。

    • 不需要通过变量,就能对内存直接进行操作。通过指针,程序能访问的内存区域就不仅限于变量所占据的数据区域 。
    • 在 C++ 中,用指针 p 指向 a 的地址,然后对 p 进行加减操作,p 就能指向 a 后面或前面的内存区域,通过 p 也就能访问这些内存区域。

    8、指针的互相赋值

    不同类型的指针,如果不经过强制类型转换,不能直接互相赋值。

    int * pn , char * pc , char c = 0x65;
    
    pn = pc;  //类型不匹配,编译出错
    
    pn = &c;  //类型不匹配,编译出错
    
    pn = (int *)&c;
    
    int n = *pn;  //n的值是不确定:char 一个字节  int 四个字节
    
    *pn = 0x12345678; //编译能过但运行可能出错,从 pn 指向的位置开始四个字节写入 0x12345678,会覆盖掉原来的内容,或者操作系统根本不让访问这一块区域
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    9、指针的运算

    1. 两个同类型的指针变量,可以比较大小。
    地址p1 < 地址p2 <=> p1 < p2值为真
    
    地址p1 = 地址p2 <=> p1 == p2值为真
    
    地址p1 > 地址p2 <=> p1 > p2值为真
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 两个同类型的指针变量,可以相减。
    两个 T* 类型的指针 p1 和 p2
    
    p1 - p2 = (地址 p1 - 地址 p2) / sizeof(T)   //p1和p2之间能存放多少个T类型的变量
    
    例:int * p1 , * p2;
    
    若 p1 指向地址 1000,p2 指向地址 600,则 p1 - p2 = (1000 - 600)/sizeof(int) =(1000-600) / 4 = 100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 指针变量加减一个整数的结果是指针
    p:T* 类型的指针
    
    n:整数类型的变量或常量
    
    p + n:T* 类型的指针,指向地址:p + n * sizeof(T)
    
    n + p、p - n、*(p+n)*(p-n),含义一样
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 指针变量可以自增、自减
    T* 类型的指针 p 指向地址 n
    
    p++++p:p 指向 n + sizeof(T)
    
    p----p:p 指向 n - sizeof(T)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 指针可以用下标运算符 “[]” 进行运算
    p 是一个 T* 类型的指针,n 是整数类型的变量或常量
    
    p[n] 等价于 *(p + n)
    
    • 1
    • 2
    • 3

    10、常见运算

    problem:
    如何访问 int 型变量 a 前面的那一个字节?

    int a;
    
    char * p = (char *) &a; //&a 是 int* 类型
    
    --p;  //一个字节,为char,不能直接int,所以强制转化为char
    
    printf("%c" , *p); //可能导致运行错误,操作系统要能让访问才可以  
    
    *p = 'A';   //可能导致运行错误 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    经典应用:

    #include
    #include
    using namespace std;
    int main()
    {
    	int * p1 , * p2;
    	int n = 4;
    	char * pc1 , * pc2;
    	p1 = (int *) 100;
    	p2 = (int *) 200;
    	cout << p2 - p1 << endl;
    	//输出 25,(200-100)/sizeof(int)=100/4=25
    	pc1 = (char *) p1;
    	pc2 = (char *) p2;
    	cout << pc1 - pc2 << endl;
    	//输出 -100,(100-200) / sizeof(char)=-100
    	cout << (p2 + n) - p1 << endl;
    	//输出 29,(200 + 4 * 4 - 100) / 4 
    	int * p3 = p2 + n;
    	cout << p3 - p1 << endl; 
    	//输出 29
    	cout << (pc2 - 10) - pc1 << endl;
    	//输出 90,pc1 和 pc1 都为 char 类型,占一个字节 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 地址 0 不能访问,指向地址 0 的指针就是空指针。

    • 可以用 “NULL” 关键字对任何类型的指针进行赋值。NULL 实际上就是整数 0,值为 NULL 的指针就是空指针。

      int * pn = NULL;
      char * pc = NULL;
      int * p2 = 0;
      
      • 1
      • 2
      • 3
    • 指针可以作为条件表达式使用。如果指针的值为 NULL,则相等于为假,值不为NULL,就相等于为真。

      if(p) <=> if(p != NULL)
      if(!p) <=> if(p == NULL)
      
      • 1
      • 2

    11、指针作为函数参数

    #include
    using namespace std;
    void Swap(int * p1 , int * p2)
    {
    	int tmp = * p1; //p1 指向的变量的值,赋给tmp 
    	* p1 = * p2;  //将 p2 指向的变量的值,赋给 p1 指向的变量 
    	* p2 = tmp;  //将 tmp 的值赋给 p2 指向的变量 
    }
    int main()
    {
    	int m = 3 , n = 4;
    	Swap(&m , &n);  //p1 指向 m , p2 指向 n 
    	cout << m << " " << n << endl; //输出 4 3 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    12、指针和数组

    • 数组的名字是一个指针常量,指向数组的起始地址

      T a[N];
      
      a 的类型是 T*,可以用 a 给一个 T* 类型的指针赋值
      
      a 是编译时其值就确定了的常量,不能够对 a 进行赋值
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 作为函数形参时,T * pT p[] 等价

      void Func(int * p)
      {
      	cout << sizeof(p);
      }
      
      <=>
      
      void Func(int p[]) 
      {
      	cout << sizeof(p);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 应用1

      #include
      using namespace std;
      int main()
      {
      	int a[200];
      	int  * p;
      	p = a;  //p 指向数组 a 的起始地址,亦指向了a[0]
      	* p = 10; //a[0] = 10
      	* (p + 1) = 20; //a[1] = 20
      	p[0] = 30;  //p[i] 和 *(p+i)是等效的,使得a[0]=30 
      	p[1] = 40;  //a[4]=40
      	for(int i = 0; i < 10; i++)
      		*(p + i) = i;
      	++p; //p指向a[1]
      	cout << p[0] << endl; //输出1,p[0]等效于*p,p[0]即是a[1]
      	p = a + 6;  //p 指向a[6]
      	cout << * p << endl;  //输出6 
      	return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    • 应用2

      #include
      using namespace std;
      void Reverse(int * p , int size)
      {
      	for(int i = 0; i < size / 2; i++)
      	{
      		int tmp = p[i];
      		p[i] = p[size - i - 1];
      		p[size - i - 1] = tmp;
      	}
      }
      int main()
      {
      	int a[5] = {1 , 2 , 3 , 4 , 5};
      	Reverse(a , sizeof(a) / sizeof(int));
      	for(int i = 0; i < 5; i++)
      		cout << *(a + i) << " "; 
      	return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
  • 相关阅读:
    TMS Diagram Studio 一组组件Crack版
    如何根据性能需求进行场景设计?
    218. 扑克牌 - 记忆化概率dp
    007-BSP学习笔记-手动构建rootfs(busybox)构建
    Llama2模型的优化版本:Llama-2-Onnx
    推荐几个好用的redis可视化工具
    用插入法对数组a进行降序排序。
    Blazor 在开发环境保存机密(User Secrets)
    PAT Head of a Gang(字符串转化成数字,图的dfs)
    PCL 基于任意四点计算球心坐标
  • 原文地址:https://blog.csdn.net/lylzsx20172018/article/details/133761075