C语言指针之必须要掌握的指针基础知识

目录

  • 一、指针概述
    • 指针的创建
    • 指针的大小
    • 如何使用指针
    • 二级指针
  • 二、野指针
    • 形成野指针的原因
    • 如何规避野指针
  • 三、指针的基本运算
    • 指针± 整数
    • 指针-指针
  • 四、指针和数组
    • 五、指针数组
      • 总结

        一、指针概述 指针是个变量,存放内存单元的地址(编号)。

        指针的创建
        在定义指针变量的时候,在变量前面加上' * ',代表这个变量是一个指针,再往前面加上一个类型名,就代表指针的类型,称为XX指针。
        指针的初始化:
        使用&(取地址操作符)可以获得变量的地址,将其赋值给已经定义好的指针变量,需要它们的类型相同,类型不同的时候可以使用强制转换。如果暂时不知道需要存放什么地址的时候,可以先让它指向NULL((void*)0),NULL本质就是0,0这个地址是不允许存储的。
        #include int main(){ int a = 10; //在内存中开辟一块空间 int *p = &a; //这里我们对变量a,取出它的地址,可以使用&操作符。//将a的地址存放在p变量中,p就是一个指针变量。 return 0; }


        指针的大小
        指针可以是不同的类型,char*、int*、float*、double*、long*等我们熟知的类型,那指针在内存中占多大的空间呢?
        对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或者0)
        那么32根地址线产生的地址就会是:
        00000000 00000000 00000000 00000000
        ……
        111111111 111111111 111111111 111111111
        共有232个地址,每一个地址能指向一个内存单元(一字节),那么有232个内存单元。
        我们知道一个内存单元的大小为一字节,一根地址线是32位01组成的,那么要存放指针(地址),也就需要32个bit,也即4字节。所以在32位平台下不论是什么类型的指针都是4个字节。
        同理,在64位平台下有64根地址线,每个地址是64位,所以需要8个字节。

        如何使用指针
        使用' * '操作符对指针进行解引用,可以获取到指针指向空间的值

        我们来运行下方代码。
        #include int main(){ int n = 0x11223344; char* pc = (char*)&n; //将n的地址(指针)强制转换成char* 类型 int* pi = &n; *pc = 0; //改变它指向空间的值 *pi = 0; return 0; }

        我们开始调试代码,查看n的内存,最左边是地址,右边是以16进制显示内存里存放的数据,而n也是以16进制存进去的,两个16进制代表一个字节,如下图:

        C语言指针之必须要掌握的指针基础知识
        文章图片


        当执行完语句 *pc=0;

        C语言指针之必须要掌握的指针基础知识
        文章图片

        因为pc指向的是char* 类型,只能访问一个字节,所以只能修改一个字节。

        再执行*pi = 0;

        C语言指针之必须要掌握的指针基础知识
        文章图片

        此时n的内存数据全部变成了0,说明int*类型的指针可以访问4个字节。
        小结:

        指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个节)。 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

        二级指针
        指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是 二级指针 。
        即存放指针变量的地址的指针,二级指针指向的空间的值是一个一级指针。
        int main(){ int a = 20; int* p = &a; int** pp = &p; printf("%d\n", *p); //解引用即可拿到指针指向空间里面的值 printf("%d\n", *p); //对二级指针,需要两次解引用才能拿到最开始的值 return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        C语言指针之必须要掌握的指针基础知识
        文章图片


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

        形成野指针的原因
        1.指针未初始化
        #include int main(){ int *p; //局部变量指针未初始化,默认为随机值 *p = 20; //往一个随机的地址(不属于本程序的空间)里面放入数据,是不允许的 return 0; }

        2.指针越界访问

        指针想要访问不属于本程序的空间,造成越界访问,程序出错,在使用数组的时候最容易发生。
        #include 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.指针指向的空间释放

        当动态开辟的内存被释放后,此时的指针就属于野指针,它指向的位置它不能正常访问。关于动态内存分配,后续会细讲。
        int main(){ //开辟一个整形空间 int* p = (int*)malloc(sizeof(int)); //释放该空间 free(p); //此时p即为野指针,因为它指向的空间已经无法访问,一般需要将其置空 //将其指向空指针,防止后续调用出错 p = NULL; return 0; }


        如何规避野指针
        • 指针初始化
        • 小心指针越界
        • 指针指向空间释放即使置NULL
        • 指针使用之前检查有效性

        三、指针的基本运算 关于指针的运算一般有两个,指针+指针,语义是合法,但没有意义。
        • 指针± 整数
        • 指针-指针

        指针± 整数
        对int和char类型的指针分别进行+1操作,%p以16进制打印
        int main(){ int n = 10; char* pc = (char*)&n; //将n的地址(指针)强制转换成char* 类型 int* pi = &n; printf("&n:%p\n", &n); printf("pc:%p\n", pc); printf("pc + 1:%p\n", pc + 1); printf("pi:%p\n", pi); printf("pi + 1:%p\n", pi + 1); return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        小结:

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

        指针-指针
        int main(){ int n = 10; char* pc = (char*)&n; //将n的地址(指针)强制转换成char* 类型 int* pi = &n; printf("&n:%p\n", &n); printf("pc:%p\n", pc); printf("pc + 1:%p\n", pc + 1); printf("pi:%p\n", pi); printf("pi + 1:%p\n", pi + 1); return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        小结:

        指针-指针等于它们之间相差的类型数据的个数。即本例的第10个元素和第1个元素之间相差9。

        四、指针和数组 一起来看一下数组名是什么
        #include int main(){ int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; printf("%p\n", arr); printf("%p\n", &arr[0]); return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        可见数组名和数组首元素的地址是一样的。
        结论: 数组名表示的是数组首元素的地址。也是一个指针
        那么我们就可以用一个指针来代替数组名,如下代码:
        int main(){ int arr[10] = { 0 }; int* p = arr; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); //计算数组元素个数 //对数组赋值 for (i = 0; i < sz; i++) {*(p + i) = i; } //打印数组数据 for (i = 0; i < sz; i++) {printf("%d ", *(p + i)); } return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        注意:

        数组名在下面两种情况下不是首元素的地址
        1.sizeof(数组名) - 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节
        2.&数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址
        int main(){ int arr[10] = { 0 }; int sz = sizeof(arr); printf("sizeof(arr)计算的是整个数组的大小:%d\n", sz); printf("数组首地址:%p\n", arr); printf("数组首元素地址:%p\n", &arr[0]); printf("数组的地址:%p\n", &arr); printf("数组首地址+1:%p\n", arr + 1); printf("数组首元素地址+1:%p\n", &arr[0] + 1); printf("数组的地址+1:%p\n", &arr + 1); //数组名确实是首元素的地址 //但是有2个例外: //1. sizeof(数组名)- 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节 //2. &数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址 // return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        五、指针数组 存放指针的数组。
        int main(){ int a = 1; int b = 2; int c = 3; int* arr[10] = { &a,&b,&c }; for (int i = 0; i < 3 ; i++) {printf("%d ", *(arr[i])); } return 0; }

        C语言指针之必须要掌握的指针基础知识
        文章图片


        总结 指针是C语言非常重要的一部分,内容繁多不易懂,本文仅介绍了一些基本知识,后续还会深入了解指针。
        【C语言指针之必须要掌握的指针基础知识】本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

          推荐阅读