C指针的总结


本篇是我针对指针板块认为比较重要的一些点做的一个梳理总结

如有错误和补充欢迎dd


 一、指针

  • 指针是啥

如下图所示,指针p-1、p、p+2就是指针,其实就是一个变量,而它的值是所指元素在内存中的地址

  • 指针的长度

指针的长度跟CPU的位数相等

当CPU是32位时,不管这个指针的指向类型是什么,指针长度都为32bit,也就是4个字节。同理得到CPU是64位时,指针长度就是8字节

所以,sizeof(指针)都是固定,不会随类型的改变而改变。

  • 关于指针的初始化

和数组一样,对于指针来说初始化同样重要

在指针的初始化中,“=”的右操作数必须为内存中数据的地址,不可以是变量,也不可以直接用整型地址值(但int *p = 0除外,该语句表示指针为空)。

举个例子:

int *t;      //未初始化的指针
*t = 10;    //nonooo

这是错误的写法。实际上第二行是指将10存入了t指向的位置,但t未被初始化,它的值就是一个随机值(因为创建指针时,计算机只分配了存储指针本身的内存,而没有分配存储数据的内存),所以计算机根本不知道要将10存在哪里。

所以,调用指针前,要先把人家的地址给安排明白啦!

(当然 也可以用malloc函数安排一块内存)

  • 指针运算中的优先级

来一串代码测试下就能明白个七七八八了:

#include <stdio.h>
int data[2] = { 100, 200 };
int moredata[2] = { 300, 400};
int main ()
{
    int *p1, *p2, *p3;

    p1 = p2 =data;
    p3 = moredata;

    printf(" *p1 = %d, *p2 = %d, *p3 = %d\n",*p1,*p2,*p3);
    printf(" *p1++ = %d, *++p2 = %d, (*p3)++ = %d\n",*p1++,*++p2,(*p3)++);
    printf(" *p1 = %d, *p2 = %d, *p3 = %d\n",*p1,*p2,*p3);

    return 0;
}

输出结果为:

第一行为初始值,指针名表示数组的首元素;

再来看到第二行打印中,*p1++和(*p3)++的值都与原始值一样,即说明*优先级比++运算符的优先级高

最后来到第三行打印值,发现只有(*p3)++改变了数组元素的值,剩余两个分别把p1和p2的值指向了数组的下一个元素,造成这种差异是因为++的对象不同。

对“p”++表示递增至p的下一个元素,而对“*p”++则表示对*p的值+1。

  • 指针与指针求差

前提是求差的两指针需要是同一数组中的不同元素。两指针相减可以求出两指针之间的距离,同时其差值单位与数组单位相同


二、指针和一维数组

  • 数组名就是数组首元素的地址

假设“ yr ”是一个数组,即:

yr == &yr[ 0 ]

两者等价,都是常量,可直接将其赋值给指针变量。

若设置一个 *a,那么你可以:a = yr,即将数组yr首元素的地址赋给了指针

同时注意对于一个指针来说(以*a为例):

a表示该指针指向元素的地址
&a表示该指针自身的地址
  • 关于数组和指针的等价表示

ar[ i ]  == *( ar+i )               //相同的

&ar[ i ] == ar + i              //相同的地址

注意:*( yr + 1 ) 不等同于 * yr + 1,前者表示yr第二个元素的值,后者表示yr第一个元素的值加1


三、指针与多维数组

我们以二维数组(数组的数组)举例

    1.  二维数组的声明

int (*a) [2]

int a [ ] [2]

两者等价,表示a是一个指向2个int类型值的指针,即a[][2]

一般声明一个指向多维数组的指针时,只能省略最左边方括号中的值,因为它只用于表示这是个指针,而其他方括号则用于描述指针所指向数据对象的类型

    2. 解引用(*)一个多维数组指针

我感觉多少有点套中套的意思。举个例子就能理解了:

a[0] == &a[0][0](地址)

**a == *&a[0][0](值)

相当于一个包含关系,a里的一层是a[0],然后a[0]里又有一层是a[0][0]。

总结:二维中a是地址的地址,必须要*两次才能得到原始值

方便理解的测试代码:

 输出结果为:

a[0][0] = 2
*a[0] = 2
**a = 2
a[2][1] = 3
*(*(a+2)+1) = 3

看到没看到没,a[0][0]、a[0]和*a的值是一样的!刚开始是多少会有点晕头转向的,但只要理解了嵌套逻辑和包含关系,就能够理清思路了。

然后我们再来分析下最后那一行看起来略微复杂的*(*(a+2)+1),咱给它拆开看看:

a 二维数组首元素地址

a+2 二维数组第三个元素(一维)的地址

*(a+2) 二维数组第三个元素的首元素(一个类型值)的地址

*(a+2)+1 二维数组第三个元素的第二个元素的地址

*(*(a+2)+1)  二维数组中第三个元素([2])的第二个元素([1])的

(注意两层*才能得到二维数组的值哦)

说了那么多,其实*(*(a+2)+1)与a[2][1]是一样的,只不过一个指针表示法一个数组表示法。

用的话就乖乖用数组表示法吧,简单易懂,直接明了。

    3.指针和二维数组的声明

{
    int thing[ 10 ][ 6 ];
    zippo( 10 ,6 , thing );
}               //主函数体内调用函数

void zippo( int n, int m, int ar[ n ][ m] )    //ar是一个指向数组(含m个int类型值) 的指针
{
    int temp [ n ][ m];   //temp是一个n*m的int型数组
    temp[ 0 ][ 0 ] = 2;    //设置temp的第一个元素为2
    ar[ 0 ][ 0 ] = 2;          //设置thing[ 0 ][ 0 ]为2
}

刚开始看了可能会暴起:这明明就是数组!它怎么又成指针了!!

(多少呢有点混淆是吧)这就要熟悉数组和指针之间声明的联系了。如上,当调用zippo函数时,ar就成为了指向thing[ 0 ]的指针,而temp则被创建为10*6的数组。

实际上ar和thing都是指向thing[ 0 ][ 0 ]的指针,所以ar[ 0 ][ 0 ]和thing[ 0 ][ 0 ]访问的数据位置是相同的


四、指针数组和数组指针

int *ar[ ]指针数组
int (*ar)[ ]

数组指针

  1. 指针数组:实际上是个数组,而数组中的元素都为一个个的指针(ar是一个含有n个*int的数组
  2. 数组指针:实际上是个指针,而它指向一个数组(ar是一个指向大小为n个整形的数组的指针

那咱试试思考写一个数组:

数组有5个元素每个元素是一个指针,且指针指向一个有3个整形元素的数组。

  应该就写成int  ( *arr[5])  [3]  ,对着了没


五、指针传参

使用指针作为参数,可实现两种功能:

(1)可以读取上一层函数中的变量的值*p

(2)可以修改上一层函数中变量中的值*p(普通参数无法实现)或者实现两数的交换

#include<stdio.h>
void test(int* p)
{
	printf("内层a:%d \n",*p);//读取上一层参数的值 
	*p=1;//修改上一层参数的值 
}
int main()
{
	int a=0;
	test(&a);
	printf("外层a:%d \n",a);
	return 0;
}

输出结果为:


总之,传指针就相当于传地址,那传值和传地址又有什么区别呢?

传值相当于传递一个拷贝,占用空间,且复制元素占用时间;

传地址只需传一次地址就可以了,可以直接访问,效率高。因此对于变量所占空间大的情况,适合传地址,也就是传指针。

指针大法好。

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

颜 然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值