C语言的指针

本人之前也学过C/C++、Java、Python等,但也只是学会了一点皮毛。然后因为高考的原因放弃了,现在要准备程序设计大赛。重新学一下,总结一下知识点,如有不足的地方,还请指点。

指针(pointer)是C语言最重要的(有时也是最复杂的)概念之一,是一个值为内存地址的变量,用于存储变量的地址。指针的第一个基本用法就是在函数间传递信息。概括地的说,如果主调函数不使用return返回的值,则必须通过地址才能改变主调函数(Main函数)中的变量值。

声明指针

在学习指针之前我们已经很熟悉怎么声明变量了,那么如何声明指针呢?我们知道指针就是指向变量的内存地址的变量。所以声明指针变量时必须指定指针所指向变量的类型。另外,我们必须知道储存在指定地址上的变量的数据类型。因为每一种数据类型在内存中的储存方式是不同的。所以我们声明指针的格式为:

数据类型 * 指针变量名;

例如int * pc;其中数据类型说明符指明了指针所指向对象的类型,星号(*)说明了pc是一个指针。所以总体的意思是pc是一个指针,*pc是int类型的值。

注意:*和指针名之间的空格可有可无。通常,程序员在声明时使用空格,在解引用变量时省略空格。

地址和指针

首先,我们了解一下变量在主存(或称为内存)中的储存方式。在程序的运行过程中,变量根据其定义时使用的数据类型所占用的大小(以字节为单位)储存在计算机内存中。

int a = 20; int b = 30;

因为在C语言中,int类型(整型)占用4个字节。当然在不同的系统中,int类型所占用的存储空间是不确定的,我们假定有一组内存空间,从1000 - 1010。编译器将对他们进行分配内存空间,如下图所示:

在这里插入图片描述
因为在我的电脑上,int类型的变量占用4个字节,其中,可以看出变量a储存的位置是1000,变量b存储的位置是1005。

为了看着方便,标出了所有的内存地址,但是在实际的指针应用中,只有首个地址是变量的地址。

那么问题来了,怎么获取变量在内存中的地址呢?

查找地址: &运算符

一元运算符&给出变量的地址,所以如上面的a是变量名,那么&a就是变量的地址,也就是变量在内存中的位置。例如下面的代码:

ptr = &poth;	//把pooh的地址赋给ptr;

另外,我们知道在printf()函数中,分为两个参数:一个是字符串和变量。每个变量都有对应的格式控制符,那么指针(pointer)对应的转换说明符为%p。

int a = 10;
int * ptr1;
ptr1 = &a;
printf("%p",ptr);
间接运算符:*

间接运算符*(indirection operator)找出储存在指针中的值,该运算符有时也称为解引用运算符。注意不要把间接运算符和二元乘法运算符混淆,虽然它们的符号相同,但是它们的功能却不是相同的。

int a = 10,b;
int * ptr;
ptr = &a;
b = *ptr;

最后一条语句,b = *ptr;就是将ptr地址中的值赋值给变量b。所以b = a。

指针和数组应用

在讲解指针的操作之前先铺垫一些知识,在之前我们知道了声明指针、解引用指针等操作。那么问题来了,如果换成数组呢?尤其是在自定义函数中使用指针作为形式参数(或称为形参)时,函数的原型、函数的定义以及调用怎么写呢?

int a[3] = {1,2,3};

假设定义一个数组a,并为其赋值,我们知道它在内存中如下图所示
在这里插入图片描述
举个变相的例子:数组名是数组首元素的地址,也就是说在数组中,当我们将数组的变量名赋值给一个指针变量时,就相当于把数组中首个元素的地址赋值给变量。所以下面的赋值表达式是相同的:

const int a[3] = {1,2,3};
int * a1,*a2;
a1 = a;
a2 = &a[0];

其中a和&a[0]都是常量。
那么我们怎么获取数组元素的第2个地址呢,在我们之前的学习中,我们知道变量(仅限于整型和浮点型)和整数相加得到的就是计算后的数值。但是在指针中就完全不是这样,我们知道使用指针是获取在内存中变量的地址,所以在给指针+1(或者-1时)是去寻找下一个变量在内存中地址。例如:

int a[3] = {1,2,3};
int * ptr;
ptr = &a;
printf("%p",*(ptr+1));

关于指针的操作,下面会再次提到。

需要注意的问题:在我们的系统中,地址按字节编址。指针变量在64位的电脑中占用8个字节。

指针的操作

C语言提供了一些指针的基本操作,尤其是在数组中使用指针,可以使程序更高效的运行,指针能有效的处理数组。下面我们介绍关于指针的八种操作:包括赋值、解引用、取址、指针与整数相加、递增指针、指针减去一个整数、递减整数、指针求差。
1、赋值:在前面说到,数组名是数组首元素的地址。所以我们如果想获取数组的地址时,我们就可以将指针指向数组或将指针指向数组中的某个元素。如以下代码所示。

int num[3] = {1,2,3};
int * ptr1,* ptr2,* ptr3;
ptr1 = num;
ptr2 = &num[0];

2、解引用:在间接运算符中我们说到了*运算符,我们就不在这里继续讲解了。
3、取地址操作:
在这里我们很容的联想到将变量的地址拿出来赋值给指针,但是真的是这样吗?当然不是。如所有的变量一样,指针也有自己的地址。在我们的系统中,我们知道电脑中所有的文件夹和文件都是存储在内存中的数据(更准确点说是数字)。所以我们的程序和变量甚至是指针都会在内存中有自己的狭小的空间用于存放自己。所以如以下代码所示我们定义一个指针并为其初始化,然后将自己的地址赋值给另外一个指针。

int a = 10;
int * ptr,* ptr1;
ptr = &a;
ptr1 = &ptr;

4、指针和整数相加或相减
在上面关于如何获取数组中的第二个元素,我们提到了关于指针和整数的加减操作。可以使用+运算符把指针与整数相加,或整数和指针相加。无论哪种方法,整数都会和指针所指向类型的大下(以字节为单位)相乘,然后把结果与初始值相加。

int num[3] = {1,2,3};
int * ptr1,* ptr2;
ptr = num;

所以在上面的代码中,ptr1 + 1和&num[1]相同,并且是指向的同一个元素。
5、递增和递减指针:关于自增和自减运算符我们经常会在循环语句中使用,例如for()、while()循环中。同样在指针中也有自增和自减的应用。当知道指针和整数相加或相减后,我们也就能够想到,自增和自减是如何进行运算符的。

在递增和递减指针时还要注意一些问题:编译器不会检查指针是否仍指向数组的元素,C只能保证数组任意元素的指针和指向数组后面第1个位置的指针有效。但是,如果递增和递减后,超出了这个数组的范围,则是未定义的。另外,可以解引用指向数组任意元素的指针,即使超出范围也是可以的。

6、指针的求差:这个操作是本人在学习中觉得很有意思的一个地方,可以计算两个指针的差值。通常,求差的两个指针指向同一数组的元素,通过计算机求出两个指针之间的距离。将用下图作为例子:
在这里插入图片描述

int a[3] = {1,2,3};
int * ptr1, * ptr2;

我们定义了两个指针ptr1、ptr2,他们分别指向数组中的a[0]和a[2],那么在进行指针求差时,也就是ptr2 -ptr1时,得到的结果为2。也就是两个int类型,即8个字节。差值的单位与数组类型的单位相同。

注意:必须是指向同一数组的元素,如果指向的是两个数组,则无法进行计算。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值