当前位置:主页C 语言入门教程

7. 指针

文章来源:知付 更新时间:2022-05-28 16:12 热度:152

指针是 C 语言最重要的概念之一,也是最难理解的概念之一。

目录
  • 简介

    • 运算符
  • & 运算

  • 指针变量的初始化

  • 指针的运算

简介

指针是什么?首先,它是一个值,这个值代表一个内存地址,因此指针相当于指向某个内存地址的路标。

字符 * 表示指针,通常跟在类型关键字的后面,表示指针指向的是什么类型的值。比如, char* 表示一个指向字符的指针, float* 表示一个指向 float 类型的值的指针。

int* intPtr;

上面示例声明了一个变量 intPtr ,它是一个指针,指向的内存地址存放的是一个整数。

星号 * 可以放在变量名与类型关键字之间的任何地方,下面的写法都是有效的。

int   *intPtr;
int * intPtr;
int*  intPtr;

本书使用星号紧跟在类型关键字后面的写法(即 int* intPtr; ),因为这样可以体现,指针变量就是一个普通变量,只不过它的值是内存地址而已。

这种写法有一个地方需要注意,如果同一行声明两个指针变量,那么需要写成下面这样。

// 正确
int * foo, * bar;

// 错误
int* foo, bar;

上面示例中,第二行的执行结果是, foo 是整数指针变量,而 bar 是整数变量,即 * 只对第一个变量生效。

一个指针指向的可能还是指针,这时就要用两个星号 ** 表示。

int** foo;

上面示例表示变量 foo 是一个指针,指向的还是一个指针,第二个指针指向的则是一个整数。

* 运算符

* 这个符号除了表示指针以外,还可以作为运算符,用来取出指针变量所指向的内存地址里面的值。

void increment(int* p) {
  *p = *p + 1;
}

上面示例中,函数 increment() 的参数是一个整数指针 p 。函数体里面, *p 就表示指针 p 所指向的那个值。对 *p 赋值,就表示改变指针所指向的那个地址里面的值。

上面函数的作用是将参数值加 1 。该函数没有返回值,因为传入的是地址,函数体内部对该地址包含的值的操作,会影响到函数外部,所以不需要返回值。事实上,函数内部通过指针,将值传到外部,是 C 语言的常用方法。

变量地址而不是变量值传入函数,还有一个好处。对于需要大量存储空间的大型变量,复制变量值传入函数,非常浪费时间和空间,不如传入指针来得高效。

& 运算符

& 运算符用来取出一个变量所在的内存地址。

int x = 1;
printf("x's address is %pn", &x);

上面示例中, x 是一个整数变量, &x 就是 x 的值所在的内存地址。 printf()%p 是内存地址的占位符,可以打印出内存地址。

上一小节中,参数变量加 1 的函数,可以像下面这样使用。

void increment(int* p) {
  *p = *p + 1;
}

int x = 1;
increment(&x);
printf("%dn", x); // 2

上面示例中,调用 increment() 函数以后,变量 x 的值就增加了1,原因就在于传入函数的是变量 x 的地址 &x

& 运算符与 * 运算符互为逆运算,下面的表达式总是成立。

int i = 5;

if (i == *(&i)) // 正确

指针变量的初始化

声明指针变量之后,编译器会为指针变量本身分配一个内存空间,但是这个内存空间里面的值是随机的,也就是说,指针变量指向的值是随机的。这时一定不能去读写指针变量指向的地址,因为那个地址是随机地址,很可能会导致严重后果。

int* p;
*p = 1; // 错误

上面的代码是错的,因为 p 指向的那个地址是随机的,向这个随机地址里面写入 1 ,会导致意想不到的结果。

正确做法是指针变量声明后,必须先让它指向一个分配好的地址,然后再进行读写,这叫做指针变量的初始化。

int* p;
int i;

p = &i;
*p = 13;

上面示例中, p 是指针变量,声明这个变量后, p 会指向一个随机的内存地址。这时要将它指向一个已经分配好的内存地址,上例就是再声明一个整数变量 i ,编译器会为 i 分配内存地址,然后让 p 指向 i 的内存地址( p = &i; )。完成初始化之后,就可以对 p 指向的内存地址进行赋值了( *p = 13; )。

为了防止读写未初始化的指针变量,可以养成习惯,将未初始化的指针变量设为 NULL

int* p = NULL;

NULL 在 C 语言中是一个常量,表示地址为 0 的内存空间,这个地址是无法使用的,读写该地址会报错。

指针的运算

指针本质上就是一个无符号整数,代表了内存地址。它可以进行运算,但是规则并不是整数运算的规则。

(1)指针与整数值的加减运算

指针与整数值的运算,表示指针的移动。

short* j;
j = (short*)0x1234;
j = j + 1; // 0x1236

上面示例中, j 是一个指针,指向内存地址 0x1234 。你可能以为 j + 1 等于 0x1235 ,但正确答案是 0x1236 。原因是 j + 1 表示指针向内存地址的高位移动一个单位,而一个单位的 short 类型占据两个字节的宽度,所以相当于向高位移动两个字节。同样的, j - 1 得到的结果是 0x1232

指针移动的单位,与指针指向的数据类型有关。数据类型占据多少个字节,每单位就移动多少个字节。

(2)指针与指针的加法运算

指针只能与整数值进行加减运算,两个指针进行加法是非法的。

unsigned short* j;
unsigned short* k;
x = j + k; // 非法

上面示例是两个指针相加,这是非法的。

(3)指针与指针的减法

相同类型的指针允许进行减法运算,返回它们之间的距离,即相隔多少个数据单位。

高位地址减去低位地址,返回的是正值;低位地址减去高位地址,返回的是负值。

这时,减法返回的值属于 ptrdiff_t 类型,这是一个带符号的整数类型别名,具体类型根据系统不同而不同。这个类型的原型定义在头文件 stddef.h 里面。

short* j1;
short* j2;

j1 = (short*)0x1234;
j2 = (short*)0x1236;

ptrdiff_t dist = j2 - j1;
printf("%dn", dist); // 1

上面示例中, j1j2 是两个指向 short 类型的指针,变量 dist 是它们之间的距离,类型为 ptrdiff_t ,值为 1 ,因为相差2个字节正好存放一个 short 类型的值。

(4)指针与指针的比较运算

指针之间的比较运算,比较的是各自的内存地址哪一个更大,返回值是整数 1 (true)或 0 (false)。

分享到:

#免责声明#

版权声明:《 7. 指针 》为作者 知付 原创文章,转载请注明原文地址!
本站所有文章,如无特殊说明或标注,均为本站原创或整合发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
本文地址:https://www.yoppunion.com/C%20%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/93.html
同类推荐
评论列表
签到

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

支付宝扫一扫打赏

微信扫一扫打赏

微信扫一扫打赏