C指针知识总结
一.指针简介及定义C语言中指针是一种数据结构其定义为存储地址值的数据它的存在为访问数据提供了一种新的方式使用指针按照规律取用数据变得更加快捷高效也让数组、结构体等大型数据能够作为函数参数使得灵活分配内存成为可能即使是一个函数也可以作为参数输入另一个函数。也就是说一个指针变量可以指向每一种数据类型每一种数据都可以通过指针来访问和传递并且通过地址值的传递能够做到直接修改。指针变量指的是一类变量他们有相同的功能和特点包括普通类型指针、指向行向量的指针、指针数组、空指针、函数指针。二.指针的运算2.1取地址通过地址符号可以将普通数据类型的地址取出并赋值给指针量.2.2解运算通过*p可以获得指针p所指向变量的值亦可以对其进行赋值则将改变p指针所指变量本身的值。2.3对指针变量进行加减对指针变量进行加减时务必要注意指针的初值最重要的是注意指针变量对应的单位地址长度例如整形指针加一表示其实际地址值加4int类型大小为4个字节。尤其是定义了数组指针时要注意该数组的大小。我们知道数组的名是一个指针常量它可以参与运算但是不能改变他本身的值。指向同一数组的不同元素地址的指针可以进行减法运算不能进行加法运算。2.4指针变量进行比较指向同一数组的不同元素地址的指针可以进行比较我们知道在定义一个变量或者数组亦或者结构体时他们的地址是系统随机分配的并不连续可能差距极大所以对不同变量的指针是不允许进行比较的。三.对不同指针变量的详细介绍3.1指向基本数据类型的指针。int a,*pa NULL(表示指针不指向任何数NULL 0唯一可以赋值给指针的整数);float b,*pb NULL;char c,*pc NULL;pa a;pb b;pc c;也可以将数组中的某个元素的地址值赋给指针变量通过取地址并赋值的形式可以将一个普通数据类型的地址值直接给到指针进行赋值。从而可以用该指针对所指的变量进行取用于是当一个普通变量作为参数传入函数中时可以将形参定义为普通类型也可以定义为指针的形式区别在于指针传递可以改变原变量的值int array[3] {0};(外部数组和自动数组忘记赋初值默认为0函数内部定义的数组忘记赋初值则会随机赋值)float f[5] {0};char string[] Hello world;pa array;pb float;pc string;(字符类型的指针还可以通过直接将字符串赋值的方式进行赋值实际上是把该字符串的第一个字符的地址给到了pc)printf(%s,pc;pa、pb、pc都满足以下等式通过连续取地址加解地址的方式取用数组中的值效率更高、运算速度更快。*pa f[0] pa[0];*(pa1) f[1] pa[1];……3.2指向行向量的指针变量定义方式为以整形行向量为例,浮点数类型和字符类型行向量均相同int*pa[4];int array [3][4] {0};pa array;(可以将一个二维数组的数组名赋给它)pa array1;pa array2;以上三个式子表示的pa分别指向array的每一行的第一个元素但他的物理含义不是一个基本指针变量那样他的单位长度是一个4个整形变量的行向量需要通过解运算才可以将他的转化成一个指向该行第一个变量的指针。3.3数组指针数组指针顾名思义该数组的每一个变量都是一个指针且都是同一类型的指针。定义方式如下int*p[4];float*pf[5];char*pc[6](可用于处理字符串数组);3.4指向指针的指针跟套娃一样就不多赘述了。3.5空指针void *p;表示指针p是一个通用类型的指针其他类型的指针量可以直接赋值给他而将他赋值给其他类型指针时必须进行强制类型转换可用于通用函数的撰写。3.6指向函数的指针该指针储存的实际上是一个函数被记录在内存区的首地址通过指向数组的指针可以将函数作为参数传入另一个函数中,从而可以利用不同函数间的组合灵活的实现一些功能。double func1(int a,int b);(一个函数的声明)double (*p)(int a,int b);p func1;于是p(1,6) func1(1,6) (*p)(1,6);四.指针用于动态分配内存空间和创建动态数组。动态一维数组创建#includestdlib.h#define n 10int *p malloc(n*sizeof(int));使用p[0]、p[1]或者int*q p;再使用q的自增使用数组元素free(p);记得释放内存否则这段内存将一直被占用。动态二维数组创建;int**p (int**)malloc(row*sizeof(int *));for(int i 0;irow;i)//list是二维数组的列数{p[i] (int*)malloc(list*sizeof(int));}释放内存for(int i 0;irow;i)//list是二维数组的列数{free(p[i]);}free(p);四.C数组与指针的联系与区别1.关联数组的名本身是个指针常量他不占据内存空间。所有变量名在编译前作为字符两存储在硬盘中形成一章映射着所储存对象的初始地址、内存占用大小、类型等信息的表然后在编译过程中会查表来进行相应操作编译完成后这个表就被销毁了所以不会占用内存。对于一维数组来说数组名的值为数组的第一个元素的地址值所以一维数组的取用方式包括两种int array[10] {0};//定义一个一维整型数组int *p array;可以通过元素序号和将数组名作为一个指针常量计算进行遍历for(int i 0;i10;i){printf(%d\n,array[i]);//方式一printf(%d\n,*(ai));//方式二}for(;parray[9];){printf(%d\n,*p);//方式三}二维数组要稍显复杂一些二维数组的名同样是一个指针常量但是他的所属指针类型是指向数组的指针他的单位长度是该二维数组一行的大小对于二维数组同样有两种取用的方式int array[4][3] {0};//定义一个二维数组。int (*p)[3] array;//定义了一个数组指针指向行向量的指针可以将数组的名给到这个指针。int *q array[0];//或者q array[0][0];都是将数组的第一个元素地址给到一个整型指for(int i0;i4;i)for(int j 0;j3;j){printf(%d%c,a[i][j],(j2)?\n: );//方式一printf(%d%c,*(*(ai)j),(j2)?\n: );//方式二}for(;qarray[4][3];q){printf(%d%c,q,(q4-array[0][0])%160?\n: );//方式三}不论是一维数组还是二维数组这两种方式本质上都是通过指针的方式进行的取值例如a[5]来计算机内就是*(a5)先计算元素的地址值然后取用由于数组的名本身就是一个指针常量所以可以将数组名赋值给一个同类型的指针一旦进行了赋值该指针就指向了该数组的第一个元素。然后就可以利用指针来进行对数组的调用。如以上代码中的方式三所示。方式三通过指针的规律性的增加来取用数组元素效率更高。同样由于数组名本身是一个指针常量所以可以将数组名给到一个函数的指针形参从而将数组首地址传过去再利用指针进行取用即使函数的形参就是一个数组并后面跟上了该数组的大小也是将数组名给到函数从而进行地址值的传递不过在函数中我们往往将他按照数组元素索引的方式进行取用。2.区别与数组名类型相同的指针变量可以获取数组名的值但他们在有些用法上存在差异由于在C语言中数组总是在定义时就完成了内存地址的分配所以数组名的值即数组第一个元素的地址值是固定不变的后续也不能发生改变这也就意味着对于一个数组不可以进行整个数组的赋值只能遍历元素来改变其值例如char string[] Hello World;string good morning;//出错一方面后一个数组比string的内存要大其次string是不可以发生改变的左值即不可以把右边这个字符串的首地址值给到string,从而完成数组的传递。但是对于一个指针变量来说就完全不是问题可以通过多次赋值使得指针指向不同的数组。总结对于一个数组来说分别对他的数组名、首元素取地址以及他数组名本身给到一个指针得到的地址值都是一样的但所蕴含的信息不同。五.数组指针与二级指针1.数组指针可以通过动态内存分配完成一个二维数组的建立而二级指针可以形成一个高级的数组其中元素为一个数组。而二维数组在传递参数以及获取地址的时候会自动降维成数组指针。例如想用一个数组指针构造一个二维数组int(*p)[10] (int(*)[10])malloc(sizeof(int[10])*maxsize);而用二维数组构建一个动态的高级数组就比较麻烦了;int**arr (int**)malloc(sizeof(int*)*maxsize);此后还需要每次对arr这个指针数组中的每个指针进行动态分配或者赋值arr[0] (int*)malloc(sizeof(int)*maxsize);(以此类推)2.函数的指针传递其实质仍然是地址传递例如;void func(int**nums,int numsSize;int* numsColumnSize);六.函数传递参数与指针当你希望一个指针在函数内部改变其指向的时候应该传递一个他的二级指针进去。七.对于C语言中数据的存储在使用指针和数组的情况下的思考。1.声明变量可以获得自动分配的栈内存用来存储数据但会在作用域结束后被自动销毁。2.有了变量可以将变量的地址给到相同类型的指针于是就有两种可以取用数据的方式一中是变量一种是指针解引用二者都可以没有好坏之分。3.因为数组变量在声明时就必须给出一个用常量声明的固定的大小而不能说用变量来声明其大小所以有了手动管理内存分配的动态分配而动态分配的结果是分配一段内存并将内存的头地址给到某个指针这意味着这段动态内存就只能使用指针来访问了且不会自动销毁必须手动释放。4.指针是一种非常灵活的数据大类其下除了最基本的内置类型的指针还有多级指针、数组指针、函数指针而且可以将指针也组合成数组的形式一维数组指针、二维数组指针如此嵌套就有很多很多不同的存储形式十分的灵活。而我们要使用这些组织形式各异类型各异的指针最重要的就是搞清楚我究竟想存储的是什么类型的数据这些数据单独来看需要哪些指针最后综合成一个整个的指针映射框架。5.C语言中函数是不能接受大量的数据以及返回大量的数据的所以指针用来传递地址从而可以将一段连续的数据或者一些可以由指针访问的数据传入函数中从而进行访问和修改const变量可以禁止修改并且也只有动态分配的内存指针可以将局部作用域产生的大量数据传递出来此外我们可以看出在C语言中想要将一些数据真正的拷贝而不是值传递就需要对他们进行取地址。注程序在编译成机器语言并执行之前只是一段文本而已其中的变量名也好指针名也好所有的代号都只是为了人类阅读方便在真正编译的时候会将这些名称按照内部的映射表进行转换为对应的信息然后参与运算所以变量名在程序运行时是不需要存储空间的。一个对象的名称在编译时会映射成一张表而一个名称对应的关系不仅仅是起始地址还包括他的大小类型这些信息都会在编译时用到用完即销毁。而利用变量名进行参数传递或者赋予指针值时确实是将变量名本身携带的信息中的首地址给到了指针但如果仅看一个变量或者一个数组名绝对不是一个常量指针这些变量名在定义的时候就与一个地址绑定与一个类型绑定甚至与一块内存的大小绑定了与这个数据的信息绑定。数组名会在需要的时候进行退化C语言设计哲学按理来说应该一个一维数组的名应该给到的是数组指针但实际并不是而是降维成了一个普通指针因为计算机底层始终是那几种基础内置实体在互相计算其余的数据类型不过是组织形式逻辑契约罢了为了方便取用这些实体数据数组名会自动进行一次降维但是在获取其信息例如SIZEOF的时候仍然是取的数据名绑定的信息中的大小。这也是为什么我们在将二维数组名传递和划分一块连续内存作为二维数组的动态内存时我们会定义一个数组指针而结构体变量并不会自动降维成指针是可以整个进行拷贝值传递的但是通常不推荐int arr[2][3] {{}};int(*p)[3] arrint *p[10] (int(8)[10]) malloc (sizeof(int[10])*size);就已经分配好了二维数组的内存。以及为什么对于一维数组我们是如下操作int arr[10] {};int*p arr;int*p (int*)malloc(sizeof(int)*size);