• 初阶指针(1)


    1. 指针是什么?

    指针是什么?

    指针理解的2个要点:

    1.指针是内存中一个最小单元的编号,也就是地址

    2.平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

    总结:指针就是地址,口语中说的指针通常指的是指针变量。

    最小的内存单元,也就是一个地址的空间是一个字节。

    指针变量

    我们可以通过&(取地址操作符)取出变量的内存地址,把地址可以存放到一个变量中,这个变量就是指针变量。

    #include 
    int main()
    {
    	int a = 10;
    	int* pa = &a;
    	printf("%p", pa);
    	return 0;
    }
    //--------------------
    //编译器运行结果为
    //00F3FA6C
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在pa变量中,pa就是一个之指针变量。

    pa中存放的是a变量的地址,同样pa也有自己所对应的地址。

    总结:

    指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)

    那这里的问题是:

    一个小的单元到底是多大?(1个字节)

    如何编址?

    对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);

    那么32根地址线产生的地址就会是:

    00000000 00000000 00000000 00000000

    00000000 00000000 00000000 00000001

    11111111 11111111 11111111 11111111

    这里就有2的32次方个地址。

    每个地址标识一个字节,那我们就可以给 (2^32Byte ==2^32/1024KB==2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址。

    同样的方法,那64位机器,如果给64根地址线,那能编址(2^64==2^32*2^32==2^32*4GB)2的32次方个4GB空间,

    这里我们就明白:

    • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
    • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

    总结:

    • 指针变量是用来存放地址的,内存单元的唯一标志是地址。
    • 指针的大小在32位平台是4个字节,在64位平台是8个字节

    当想通过地址进行访问的时候使用解引用操作符(间接访问操作符)

    #include 
    int main()
    {
    	int a = 10;
    	int* pa = &a;
    	*pa = 20;
    	printf("%d",a);
    	return 0;
    }
    //--------------------
    //编译器运行结果为
    //20
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. 指针和指针类型

    #include 
    int main()
    {
    	int* pa;
    	char* pc;
    	float* pf;
    
    	printf("%d\n", sizeof(pa));
    	printf("%d\n", sizeof(pc));
    	printf("%d\n", sizeof(pf));
    	return 0;
    }
    //------------------
    //编译器运行结果为
    //4
    //4
    //4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    char* 类型的指针是为了存放 char 类型变量的地址。

    float *类型的指针是为了存放 short 类型变量的地址。

    int* 类型的指针是为了存放 int 类型变量的地址。

    既然不同类型的指针变量大小相同,为何不只创建一个类型?

    那指针类型的意义是什么?

    2.1 指针的解引用

    #include 
    int main()
    {
    	int a = 0x11223344;
    	int* pa = &a;
    	*pa = 0;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    #include 
    int main()
    {
    	int a = 0x11223344;
    	char* pa = &a;
    	*pa = 0;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上述两个代码都能存储a的地址,但是进行解引用操作的时候会有不同。

    image-20220825164825840

    总结:

    指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

    比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

    double*的解引用能访问八个字节。

    2.2 指针±整数

    #include 
    int main()
    {
    	int a = 0x11223344;
    	int* pa = &a;
    	char* pc = &a;
    	printf("pa=%p\n", pa);
    	printf("pc=%p\n", pc);
    	
    	printf("pa+1=%p\n", pa + 1);
    	printf("pc+1=%p\n", pc+1);
    
    	return 0;
    }
    //--------------------
    //编译器运行结果为
    //pa = 012FFC40
    //pc = 012FFC40
    //pa + 1 = 012FFC44
    //pc + 1 = 012FFC41
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    **总结:**指针的类型决定了指针向前或者向后走一步有多大距离(步长)。

    int*指针+1,意思是跳过一个整型,也就是向后走4个字节。

    char*指针+1,意思是跳过1个字节.

    double*指针+1,意思是跳过8个字节。

    short*指针+1,意思是跳过2个字节。

    应用

    存储1-10在数组中

    方法一:

    #include 
    int main()
    {
    	int arr[10] = { 0 };
    	int* pa = arr;
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		*pa = i + 1;
    		pa++;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    方法二:

    #include 
    int main()
    {
    	int arr[10] = { 0 };
    	int* pa = arr;
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		*(pa+i) = i + 1;	
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    因为知道int*类型的步长,所以++以后对应的地址正好是下一个空间的地址。如果用char*只能访问10个字节。

    3. 野指针

    概念: 野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的)。

    3.1 野指针成因

    1.指针未初始化

    #include 
    int main()
    {
    	int* p;  //野指针
    	*p = 20;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    由于指针没有初始化,所以p存放的是随机地址,这是p就是野指针。

    2.指针越界访问

    #include 
    int main()
    {
    	int arr[10] = { 0 };
    	int i = 0;
    	int* p = arr;
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	for (i = 0; i <= sz; i++)
    	{
    		*p = i;
    		p++;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    当指针指向的范围超出数组arr的范围时,p就是野指针。

    3.指针指向的空间释放

    #include 
    int* test()
    {
    	int num = 100;
    	return #
    }
    int main()
    {
    	int* p =test();
    	*p = 200;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    虽然返回了num的地址,但是num的空间已经被释放,再通过p访问**他人空间(原num空间)**时,p就是野指针。

    3.2 如何规避野指针

    1.指针初始化

    2.小心指针越界

    3.指针指向空间释放,及时置为NULL(空指针)

    4.避免返回局部变量的地址

    5.指针使用之前检查有效性

    1.指针初始化

    #include 
    int main()
    {
    	int a = 10;
    	int* pa = &a;  //明确初始化
    	//NULL--本质是0,是用于初始化指针的
    	int* p = NULL;  
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    NULL–空指针–本质是0,是用于初始化指针的。

    2.小心指针越界

    #include 
    int main()
    {
    	int arr[10] = { 0 };
    	int i = 0;
    	int* p = arr;
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	for (i = 0; i <= sz; i++)
    	{
    		*p = i;
    		p++;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.指针指向空间释放,及时置为NULL

    这里涉及到动态内存分配问题,仅简单介绍使用。

    动态内存分配

    //申请空间
    int* p = malloc(40);
    //使用空间
    
    //使用后释放空间
    free(p);//释放空间
    p = NULL;//将p置为空指针,防止成为野指针。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.避免返回局部变量的地址

    #include 
    int* test()
    {
    	int num = 100;
    	return #
    }
    int main()
    {
    	int* p =test();
    	*p = 200;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    由于局部变量存放在栈区,所以也叫作避免返回栈空间的地址。

    5.指针使用之前检查有效性

    对于空指针是不能进行访问。

    #include 
    int main()
    {
    	int* p = NULL;
    	
    	printf("%d\n", *p);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    空指针进行解引用,也就是把0作为地址进行访问,这个时候程序会崩掉。

    #include 
    int main()
    {
    	int* p = NULL;
    
    	if (p != NULL)
    	{
    		printf("%d\n", *p);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    #include 
    int main()
    {
    	int a = 10;
    	int* p = &a;
    	
    	printf("%d\n", *p);
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这是指针初阶的前半章节语法,至于初阶指针剩下的内容,我们在下一篇文章当中再进行介绍。

  • 相关阅读:
    C# winform chart 饼状图的使用布局
    (2022.9)raspberry 4安装HP 1020 plus打印机,利用树莓派4制作无线打印服务器
    SpringBoot -集成Druid
    如何调试一个C++程序?以Visual Studio 2019为例的保姆级教程。
    MySQL高级SQL语句
    回溯算法 | 子集问题(递增子序列) | leecode刷题笔记
    Mac Catalina安装配置hadoop hive hbase
    PostgreSQL | CTE | 使用with子句的通用表达式
    [山东科技大学OJ]1107 Problem A: 编写函数:Swap (I) (Append Code)
    xxl-job 2.2之后版本高版本executor未授权访问漏洞
  • 原文地址:https://blog.csdn.net/m0_71545436/article/details/127785620