一种理解方式:指针就是地址,地址被存到变量里面之后,这个变量就是指针变量
指针的大小是固定的4/8个字节,因为指针中存的是地址,而地址的大小是固定,32位空间--地址由32个bit位组成,所以地址的大小是4个字节,64位则是8个字节
char* --- 字符指针
字符指针不仅仅可以指向一个指针,它还可以指向一个字符串
不过字符指针指向字符串的本质其实是字符指针指向了字符串的第一个字符的地址
此时如果我们用%c字符的角度去看待指针变量ps 的话,我们能够打印的结果只有h,但是如果是以字符串的角度%s去看待的话,我们得到的就是以首字符地址作为开头的字符串了
不要从代码表面的字符去看待代码,而是要从代码深层的内存等数据结构的角度来看待代码
指针变量指向的字符串和数组中存储的字符串在内存的角度来看是完全不同的:
原因如下:首先数组内存空间的开辟可能在堆也可能在栈上,但是最重要的是无论是那个区,存到它里面的内容都是可以改变的。
而指针变量指向的只是数据的地址,它并没有存储数据,所以它指向的非变量均为常量,而常量在常量区,是无法进行读写 *** 作的
一个常量只会存一份,不会重复存储。
const char* p --- 就是指针指向的内容是常量无法修改 ; char* const p --- 指针变量不能被修改
2.指针数组
指针数组是一个存放指针的数组
指针数组的作用是存放数组! ---- 多维数组的雏形!!
正如我前面所说,下标引用 *** 作符的本质是一个解引用 *** 作,只不过这个解引用 *** 作有两个 *** 作数,分别下标引用 *** 作符左边的和括号里的数据
arr[ b ] [ c ] ---》 其实分为两部拆分 : 1. *(arr[b] + c )== *( *(arr + b) + c )
注意我们用指针数组模拟的二维数组和我们实际创建的二维数组是相似但不同!!!
因为二维数组的元素的地址都是按顺序取的!!,而指针数组中模拟出的二维数组中只有每一行的元素的地址是按顺序取的,而一旦跨行地址就会失去连续性。
3.数组指针
数组指针的建立
int arr[ 10 ]
以 int (*parr) [10] = &arr 为例
首先我们想要一个指针变量parr 所以*和parr要用括号括在一起,然后我们想要这个指针变量指向数组,所以要有 [ 10 ] 来告诉我们这个指针指向的是数组,且元素个数为10
已经直到了指针指向数组之后我们还需要知道它指向的是一个什么类型的数组,所以我们需要一个int
综上我们就创建了一个指向数组的数组指针
数组的类型 (*指针变量名) [指针指向的数组中包含的元素的个数]
其中数组指针的类型就是上面这个格式去掉变量名: 数组的类型 * [ 指针指向的数组中的元素个数 ]
首先有 * 告诉我们这是一个指针 ,然后 [ 数组中的元素 ] 这个告诉我们我们指向的是一个有多少个元素的数组,然后最前面的类型告诉我们指向的数组的类型
(数组的类型与数组中元素的类型一致) 所以指针数组的类型和指针的类型一致
数组名是数组首元素的地址,而取地址数组名才是整个数组的地址
arr 和 &arr的区别
arr是数组首元素的地址,而&arr则是数组的地址
(补充知识,一个类型或者数组开辟一个空间,这个空间是由多个内存单元复合起来的,且内存单元是按照顺序排列的,每个内存单元都有自己的地址,而当我们规定这些按顺序排列的内存单元的第一个内存单元的地址为这个内存空间的地址)
综上我们知道 arr 和 &arr 在地址的表示上是一样的,那它们的不同在哪呢?,答案是它们的步长不一样。
假设是一个整型数组,arr存到整型指针中 --- 其1步长大小为4
而&arr则是存在一个数组指针中,其1步长大小为它指向的数组所占的内存空间大小
步长的不同使得arr和&arr尽管起始地址相同,但是指向的对象和访问的权限以及结果完全不同
4.数组指针的使用
数组指针的应用
数组名的本质是数组首元素的地址,它不是一个一级指针,但它能够被解引用,且它解引用的结果是数组首元素
而数组指针则是一个指向数组的一级指针 ---- 尽管数组指针中装的是数组名,而数组名的本质是地址,但是数组名不是一个指针,而只有指针指向指针的情况下才会有多级指针的出现。
综上定义有:
数组指针中装着的是特殊指针数组名的地址,数组名中装着的则是数组首元素的地址,尽管数组指针指向的是整个数组,但是当我们对数组指针解引用的时候,我们只能得到数组名这个特殊指针的地址
尽管数组指针指向整个数组,但是它依然只存了数组名的地址,所以对数组指针解引用的时候,我们只能够得到数组名,而不能得到整个数组
补充:一维数组的元素就是最基本的元素,二维数组的元素则是组成它的一行行的一维数组,同理三维数组的基本元素则是由一个面面的二维数组组成
所以二维数组的数组名的本质是首行数组的地址
多维数组内存剖析:拿到一个一维数组名arr[ a ] [ b ] , 若想得到二维数组的任意一个一维数组中的一个元素(通过地址访问内存),首先我们的第一步是拆分:
先将二维数组拆分为一维数组,然后再将一维数组拆分为基本元素
首先是 *(arr + i) 拆分到对应行,然后是 *(*(arr+i)+j),拆分到对应列
在将二维数组进行传参的时候我们就要用到数组指针了,首先传参传的依然是首元素的地址,而二维数组的首元素的地址是第一行数组的地址,而当我们传一个数组的地址的时候我们就需要用对应的数组指针来接收这个地址
数组的地址 : &arr
数组首元素的地址 ; arr
对数组的地址解引用 --- 得到的是数组首元素的地址
数组指针指向的是数组--- 其内存的地址是数组的地址&arr ,指向的是数组, 对数组指针解引用,得到数组首元素的地址 arr(arr不是一个指针,所以此处不存在指针指向指针,所以就没有二级指针的出现)
&符号的两种应用场景:
对于普通的变量等待,&就是取出它们所在的内存空间的地址
但是对于数组来说,arr是它的首元素地址,而&arr则不是取出存储存放arr的内存空间的地址,而是起到一个转义字符的作用,此时的它是将数组首元素的地址转义为了数组的地址,使得存储它的指针类型和步长的大小发生了改变
因此尽管arr和&arr依然是地址,且地址的数值都相等,但是它们的意义已经完全不同了,一切都由于&的转义,
尽管两个地址的数值相等,但它们的意思已经完全不同了,所以它们在解引用的时候得到的结果也完全不同,其中 &arr 是整个数组的地址,它的解引用得到的是数组名 --- 即数组首元素的地址
而arr就是数组首元素的地址,所以它的解引用就是数组首元素
&转义的作用就使得arr从数组首元素的地址转义为(升级为了)数组的地址
*(&arr) --- 就是将arr 降级会首元素的地址
数组的地址解引用降级为数组首元素的地址(二者数值相等,意义完全不等),数组的地址再解引用 --- 降级为数组的首元素
数组指针+1 --- 步长为1,步长的大小是数组所开辟的内存空间的大小,+1其实就是跨过一个数组
怎样找数组的类型呢? ---》 将数组名和 [ ] 解引用 *** 作符去掉,我们就可以得到数组的类型了
首先parr3和 [ ] 连在一起,所以parr3[ ] 是数组,而去掉这两时我们就能找到这个数组的类型了,所以有parr[ 10 ] 是一个能够存放10个数组指针的数组。
数组参数。
指针参数
变量名+ [ ] 黏在一起出现的时候,首先判断这是一个数组,然后再做其它的判断。
数组传参传的是首元素的地址(数组名)
函数用对应类型的数组来接受的时候:函数用来接收的数组的数组名表示的地址会被传参传过来的首元素地址替代
函数用正确类型的指针来接收时:指针会存放传参传过来的首元素的地址
二维数组传参,函数接收时只能省略行不能省略列
二维数组用指针来接收时要用数组指针来接收,数组指针是一级指针,不是二级指针!!,数组指针中存的是一个地址,即数组的地址(数组的地址和数组首元素的地址之间存在转义和上下级的关系,解引用可以起到降低的作用)
用数组来接受时要用二维数组来接收
(在c语言中同一组二进制数有很多不同的解读方式,而不同的 解读方式就会带来很多不同的结果,所以说c语言中数的类型以及输入输出格式控制符很重要,这两个语法决定了我们用哪一种解读方式来解读一组二进制数,语言的本质就是对数的访问, *** 作和解读,或者说数字就是我们的单词,而我们组织单词成句,成文的方式不同得到的表现效果也是完全不同的,一个浅显的理解就是:一个内存,一个二进制数,这就是计算中的微点,但同时它又是计算机的全部,在我们的遣词造句下,它成为了语言,数字语言,神奇啊)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)