c语言指针相关知识(入门到入土)

 1.指针是什么

 想要明白指针是什么,首先要知道我们创建变量其实是在内存中申请一块若干个字节的空间,而每个字节都有一个地址,这就类似于一栋宿舍楼中每个房间都有一个编号一样,而指针其实就是就是地址,指针变量就是用来储存指针的变量,并且相邻字节间地址的编号和相邻的房间的编号一样都是连续的,例如202房间的附近是201和203,编号为10的上一个字节编号为9,下一个为11。 

2.指针的简单类型

1.简单类型指针变量的定义方法 

既然变量类型是有多种的,如char型int型,那么不同的类型自然也对应了不同类型的指针变量,而对于一部分简单的内置类型的指针变量的定义格式是:变量类型  *变量名。例如 

char *p;

就是根据这个格式定义了一个char类型的指针变量。包括其他的各种整形,浮点型的指针变量的定义方法是一样的。那么如何对指针变量赋值呢?对指针变量赋值就要经常用到&操作符,这个操作符的作用是对某个变量取地址,例如

char a='b';
char*p=&a;

就是通过&操作符取得变量a的地址然后将它赋给p。这时候就可以通过"*"操作符对指针的解引用访问变量本身,例如:

char a='b';
char*p=&a;
char c=*p;

这时就通过对p的解引用获取到到变量a,将a的值赋给c,此时变量c中就储存了'b'这个值,注意此时的“*”符号和定义指针变量的“*”是不同的。那么此时p中就储存了变量a的地址。

2.用指针定义字符串

这里也一起介绍一种用指针的比较特殊的定义字符串的方法:

char*p="abcdef";

这种写法看上去比较奇怪,但是它是正确的,其实这样写代表这把这个字符串的首地址传入到指针变量中,因为其实"abcd"这种写法本质上就相当于一个值,而这个值就是这个字符串的首地址。但是要注意这种写法其实代表了一个常量而不是变量,也就是说这个字符串是不可以被修改的。

3.void*型指针 

然后重点介绍一种特殊类型的指针变量:void*,void*类型的指针不对应任何的变量类型,但是可以接受任何变量类型的地址,但是要注意不可以对void*类型的指针进行解引用。那么void*类型的指针变量有什么用呢?我们只根据变量本身的类型创建对应的指针类型来储存地址不就可以了吗?但是其实在有时候我们其实并不能确定我们需要什么类型,例如我们需要编写一个可以对各种类型比较大小的函数,此时我们并不知道要用到什么类型的指针。就需要用void*类型的形参接收实参。但是由于不能对void*类型指针进行解引用,所以要指针获取数值时,要先对它进行类型强转,才可以使用。

3.指针类型的意义

看到这里此时面临一个问题,一个变量不一定是只有一个字节的,而每一个字节又都有各自的地址,一个指针变量到底储存的是对应的变量中哪个字节的地址?其实指针变量存储的是变量的首字节的地址,并且指针变量类型本身还代表着对应的变量占几个之间,例如一个int*类型的指针变量如果存了一个int型,那么它本身存了该int型的首字节地址,并且还代表了这个指针对应的变量占4个字节,如果对它解引用就会先找到它储存的地址的位置,再向后访问3个字节,加上它储存的地址本身,就找到了一个完整的整形,如果我们用一个char*类型的指针来储存一个int型的变量的地址,那么在对指针解引用的时候就只能找到一个字节,因为这个指针本身是char*类型的,那么它就会认为它所指向的变量只占一个字节,自然只访问一个字节,但是int型本身是4个字节的,就有可能导致通过指针找到的数和这个数本身不一样。

4.与指针相关的其它复杂类型

前面讲了一些简单的指针变量的类型,但是有时候仅靠那些是不够用的,比如我们需要存一个数组的地址,函数的地址或者是需要一个储存指针变量的数组。例如:

int (*pf3) (int x, int y);//函数指针
int (*p)[10];//数组指针即存数组地址的指针
int *p[10];//指针数组即存指针变量的数组

看到这里可能会感觉这些类型非常复杂,根本无法区分和记忆,但是其实这些都是有规律可循的,那就是根据操作符的优先级,例如:

int *p[10];

是指针数组是因为[ ]的优先级大于*所以p先与[ ]结合代表p是一个数组,这条代码语句的剩下的就是对p的补充说明,例如这里p与[ ] 结合后与*结合代表这个数组中储存的元素是指针,int则代表储存的指针是指向int类型的指针。

int(*p)[10];

而这种形式由于有小括号,所以p先与*结合代表这是一个指针,然后与[ ] 结合代表这是指向数组的指针,int代表数组中的元素是int型,所以这是一个数组指针,数组储存了10个int型。

其实包括文章前面说到的简单的指针类型都是符合这个规律的,先看变量与什么结合决定这是什么类型的变量,剩下的就是补充说明。接下来我们看一个复杂的例子:

int (*(*p)[10])(int *);

这个例子看起来很复杂,但是根据上面的规律是可以一步步分析出p是什么,首先由于最里层的小括号,p首先肯定是最早与*结合,所以p本质是一个指针,然后p与[ ]结合代表这个指针指向一个数组,剩下的信息显然说明这个数组里储存的是函数指针,并且这个函数指针对应的函数的形参是int*,返回值是int。所以总的来说这是一个指向包含 10 个元素的数组的指针,而这个数组的每个元素都是一个函数指针,这些函数指针指向的函数接受一个 int * 类型的参数并返回一个 int 类型的值。

5.指针变量的大小与多级指针

指针变量本身本质上也是一个变量,那么它也有自己的地址和大小,它的大小取决于环境如果是32位环境就是4个字节,如果是64位环境就是8个字节。以上所介绍的都是一级指针而一个一级指针变量的地址就要用二级指针二级指针的地址就要用三级指针,以此类推,例如:

int a=0;
int*p=&a;//一级指针
int**pp=&p;//二级指针

6.传值调用和传址调用

指针变量用来储存地址,而通过地址可以访问到指针指向的变量的值,但是为什么不能直接用变量本身,而要用指针来找到它呢?因为如果我们在向一个函数传参的时候,其实是创建了新的变量再复制一份传的值,就是相当于函数接收参数时只是得到了传的变量的一个复制,而不是变量本身,这就导致了如果在函数内部对参数变量修改,只是对复制品进行了修改,无法影响传的变量本身,例如:

void fun(int a)
{
a++;
}
int main()
{
int a=1;
fun(a);
return 0;//此时a仍为1,而不是2
}

如果我们需要在函数内部对变量a进行修改,就必须用到指针变量,因为可以通过指针找到a的地址对a进行修改,虽然函数接收到的也只是我们传的指针变量的一个复制品,它储存的仍是a的地址,自然可以找到a,对它进行修改,就像是两张不同的纸上记录了同一个地点的地址,虽然是两张纸,但是都可以找到同一个地点。例如:

void fun(int* pa)
{
*pa=*pa+1;
}
int main()
{
int a=1;
fun(&a);//此时a为2
return 0;
}

这样就可以在函数中对a的值修改了。

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值