C语言指针变量的理解

都说指针是C语言的灵魂,这只有正真理解掌握了C语言的大神才会有体悟吧,虽说我也学习过,但是感觉并没有学到它的灵魂之处,特别是指针这一方面,总是会出现当时理解了,但是过滤一段时间又会忘记,这次干脆写下来,方便以后忘记了有去查。

C语言指针是什么?

我们要知道计算机中的所有数据都必须放在内存中,不同类型的数据占用的字节数不一样;

例如 int 占用 4 个字节,char 占用 1 个字节。为了正确地访问这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编号是唯一的,根据编号可以准确地找到某个字节。

我们将内存中字节的编号称为地址或指针,地址从0开始依次增加(内存中的地址编号是以十六进制表示的)

一切皆地址

这让我想到linux中是一切皆文件是类似的

C语言用变量来存储数据,用函数来定义一段可以重复使用的代码,它们最终都要放到内存中才能供 CPU 使用。
数据和代码都以二进制的形式存储在内存中,计算机无法从格式上区分某块内存到底存储的是数据还是代码。当程序被加载到内存后,操作系统会给不同的内存块指定不同的权限,拥有读取和执行权限的内存块就是代码,而拥有读取和写入权限(也可能只有读取权限)的内存块就是数据。(这就是一切皆文件)
CPU只能通过地址来去的内存中的代码和数据,程序在执行过程中会告知要执行的代码以及要读写的数据的地址,如果CPU读取到程序出错时,那么就会发生访问错误会被硬件和操作系统拦截,强制程序崩溃。

CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。
也就是说我们所了解的一些函数以及头文件里包含的所有的函数数据都是有地址的,CPU在运行时都会转化为地址来运行。
变量名和函数名为我们提供了方便,让我们在编写代码时可以使用易于阅读和理解的英文字符串,而不是直接面对二进制地址。

需要注意的是,虽然变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符,但在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、字符串名和数组名表示的是代码块或数据块的首地址。

C语言指针变量的定义和使用

指针变量:数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称为指针变量。
指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数、也可以是另外的一个普通变量或指针变量。

现在假设有一个 char 类型的变量 c,它存储了字符 'K'(ASCII码为十进制数 75),并占用了地址为 0X11A 的内存(地址通常用十六进制表示)。另外有一个指针变量 p,它的值为 0X11A,正好等于变量 c 的地址,这种情况我们就称 p 指向了 c,或者说 p 是指向变量 c 的指针。

定义指针变量

指针变量的定义和普通变量的定义非常类似,就是要在变量前面加一个*号
int *name
这样就定义了一个指针变量name ,name的值就是整形数据的地址


int a = 100;
int *p_a = &a;

在定义指针变量p_a的同时对她进行初始化,并将变量a的地址赋予它,此时p_a就指向了a;要注意的是p_a需要的是一个地址,所以a前面要加一个&表示一个地址,就是定义一个指针变量p_a,这个指针变量的值为a的地址,也可以这样
int *p_a
p_a=&a
只有在定义指针变量是才要加‘’*‘’号,是用他是是不需要的。

指针变量和普通变量一样,可以多从被写入,随时可以改变指针变量的值,如以下代码


//定义普通变量
float a = 99.5, b = 10.6;
char c = '@', d = '#';
//定义指针变量
float *p1 = &a;
char *p2 = &c;
//修改指针变量的值
p1 = &b;
p2 = &d;

是一个特殊符号,表明一个变量是指针变量,定义 p1、p2 时必须带。而给 p1、p2 赋值时,因为已经知道了它是一个指针变量,就没必要多此一举再带上*,后边可以像使用普通变量一样来使用指针变量。也就是说,定义指针变量时必须带*,给指针变量赋值时不能带*。

通过指针变量取得数据

通过指针变量存储了数据的地址,通过指针变量能够获取该地址上的数据,格式为:
*pointer
这里的*称为指针运算符,用来取得某个地址上的数据
例:


#include <stdio.h>
int main()
{
int a = 15;
int *p = &a;
printf("%d, %d\n", a, *p);
//两种方式都可以输出a的值
return 0;
}

运行结果:15,,15
虽然p和a获取的数据是一样的,但它们运行的过程确是不一样的,a只需要依次运算就能得到值,而p需要经过两次运算

假设变量 a、p 的地址分别为 0X1000、0XF0A0,它们的指向关系如下图所示:

程序被编译和链接后,a、p 被替换成相应的地址。使用 *p 的话,要先通过地址 0XF0A0 取得变量 p 本身的值,这个值是变量 a 的地址,然后再通过这个值取得变量 a 的数据,前后共有两次运算;而使用 a 的话,可以通过地址 0X1000 直接取得它的数据,只需要一步运算。也就是说,使用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高。

在定义是时使用的和使用指针变量时的意义完全不一样,
int *p = &a; //这里是定义指针变量并将值赋予p,&a表示取a的地址,将a的地址赋予了p
*p = 100; //这里的*确是用来获取指针p指向的数据
需要注意的是给指针变量本身赋值时不能加*
例如p=&a;
这里就不能在p前加*;

关于 * 和 & 的谜题

假设有一个 int 类型的变量 a,pa 是指向它的指针,那么*&a和&pa分别是什么意思呢?&a可以理解为*(&a),&a表示取变量 a 的地址(等价于 pa),*(&a)表示取这个地址上的数据(等价于 pa),绕来绕去,又回到了原点,&a仍然等价于 a。&*pa可以理解为&(*pa),*pa表示取得 pa 指向的数据(等价于 a),&(*pa)表示数据的地址(等价于 &a),所以&*pa等价于 pa。

对星号*的总结

在我们目前所学到的语法中,星号*主要有三种用途:
表示乘法,例如int a = 3, b = 5, c;  c = a * b;,这是最容易理解的。表示定义一个指针变量,以和普通变量区分开,例如int a = 100;  int *p = &a;。表示获取指针指向的数据,是一种间接操作,例如int a, b, *p = &a;  *p = 100;  b = *p;。

参考链接:

http://c.biancheng.net/c/

发布了23 篇原创文章 · 获赞 21 · 访问量 5172

猜你喜欢

转载自blog.csdn.net/LTtiandd/article/details/104256089