C语言-基础了解-14-C指针
创始人
2024-05-29 04:20:33
0

@C指针

一、指针

通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的
每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。

请看下面的实例,它将输出定义的变量地址:

#include int main ()
{int var_runoob = 10;int *p;              // 定义指针变量p = &var_runoob;printf("var_runoob 变量的地址: %p\n", p);return 0;
}

var_runoob 变量的地址: 000000000062FE14

在这里插入图片描述

二、什么是指针

指针也就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var_name;

在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var_name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int ip; / 一个整型的指针 */
double dp; / 一个 double 型的指针 */
float fp; / 一个浮点型的指针 */
char ch; / 一个字符型的指针 */

所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。

不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同

三、如何使用指针

使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include int main ()
{int  var = 20;   /* 实际变量的声明 */int  *ip;        /* 指针变量的声明 */ip = &var;  /* 在指针变量中存储 var 的地址 */printf("var 变量的地址: %p\n", &var  );/* 在指针变量中存储的地址 */printf("ip 变量存储的地址: %p\n", ip );/* 使用指针访问值 */printf("*ip 变量的值: %d\n", *ip );return 0;
}

var 变量的地址: 000000000062FE14
ip 变量存储的地址: 000000000062FE14
*ip 变量的值: 20

四、C中的null指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。

NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序

#include int main ()
{int  *ptr = NULL;printf("ptr 的地址是 %p\n", ptr  );return 0;
}

ptr 的地址是 0000000000000000


在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr) /* 如果 p 非空,则完成 /
if(!ptr) /
如果 p 为空,则完成 */

五、C指针详解

5.1 指针的算术运算

C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-。

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

我们概括一下:

指针的每一次递增,它其实会指向下一个元素的存储单元。
指针的每一次递减,它都会指向前一个元素的存储单元。
指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。

递增一个指针
在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:

#include const int MAX = 3;int main ()
{int  var[] = {10, 100, 200};int  i, *ptr;/* 指针中的数组地址 */ptr = var;for ( i = 0; i < MAX; i++){printf("存储地址:var[%d] = %p\n", i, ptr );printf("存储值:var[%d] = %d\n", i, *ptr );/* 指向下一个位置 */ptr++;}return 0;
}

存储地址:var[0] = 000000000062FE00
存储值:var[0] = 10
存储地址:var[1] = 000000000062FE04
存储值:var[1] = 100
存储地址:var[2] = 000000000062FE08
存储值:var[2] = 200

递减一个指针
同样地,对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:

#include const int MAX = 3;int main ()
{int  var[] = {10, 100, 200};int  i, *ptr;/* 指针中最后一个元素的地址 */ptr = &var[MAX-1];for ( i = MAX; i > 0; i--){printf("存储地址:var[%d] = %p\n", i-1, ptr );printf("存储值:var[%d] = %d\n", i-1, *ptr );/* 指向下一个位置 */ptr--;}return 0;
}

存储地址:var[2] = 000000000062FE08
存储值:var[2] = 200
存储地址:var[1] = 000000000062FE04
存储值:var[1] = 100
存储地址:var[0] = 000000000062FE00
存储值:var[0] = 10

指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:

#include const int MAX = 3;int main ()
{int  var[] = {10, 100, 200};int  i, *ptr;/* 指针中第一个元素的地址 */ptr = var;i = 0;while ( ptr <= &var[MAX - 1] ){printf("存储地址:var[%d] = %p\n", i, ptr );printf("存储值:var[%d] = %d\n", i, *ptr );/* 指向上一个位置 */ptr++;i++;}return 0;
}

存储地址:var[0] = 000000000062FE00
存储值:var[0] = 10
存储地址:var[1] = 000000000062FE04
存储值:var[1] = 100
存储地址:var[2] = 000000000062FE08
存储值:var[2] = 200

5.2 指针数组

讲解指针数组的概念之前,先让我们来看一个实例,它用到了一个由 3 个整数组成的数组:

#include const int MAX = 3;int main ()
{int  var[] = {10, 100, 200};int i;for (i = 0; i < MAX; i++){printf("Value of var[%d] = %d\n", i, var[i] );}return 0;
}

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

可能有一种情况,我们想要让数组存储指向 int 或 char 或其他数据类型的指针。下面是一个指向整数的指针数组的声明:

int *ptr[MAX];

在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

#include const int MAX = 3;int main ()
{int  var[] = {10, 100, 200};int i, *ptr[MAX];for ( i = 0; i < MAX; i++){ptr[i] = &var[i]; /* 赋值为整数的地址 */}for ( i = 0; i < MAX; i++){printf("Value of var[%d] = %d\n", i, *ptr[i] );}return 0;
}

Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200

也可以用一个指向字符的指针数组来存储一个字符串列表,如下:

#include const int MAX = 4;int main ()
{const char *names[] = {"Zara Ali","Hina Ali","Nuha Ali","Sara Ali",};int i = 0;for ( i = 0; i < MAX; i++){printf("Value of names[%d] = %s\n", i, names[i] );}return 0;
}

Value of names[0] = Zara Ali
Value of names[1] = Hina Ali
Value of names[2] = Nuha Ali
Value of names[3] = Sara Ali

5.3 指向指针的指针

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置
在这里插入图片描述
一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:

int **var;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:
在这里插入图片描述

#include int main ()
{int  V;int  *Pt1;int  **Pt2;V = 100;/* 获取 V 的地址 */Pt1 = &V;/* 使用运算符 & 获取 Pt1 的地址 */Pt2 = &Pt1;/* 使用 pptr 获取值 */printf("var = %d\n", V );printf("Pt1 = %p\n", Pt1 );printf("*Pt1 = %d\n", *Pt1 );printf("Pt2 = %p\n", Pt2 );printf("**Pt2 = %d\n", **Pt2);return 0;
}

var = 100
Pt1 = 000000000062FE14
*Pt1 = 100
Pt2 = 000000000062FE08
**Pt2 = 100

5.4 传递指针给函数

C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

#include 
#include void getSeconds(unsigned long *par);int main ()
{unsigned long sec;getSeconds( &sec );/* 输出实际值 */printf("Number of seconds: %ld\n", sec );return 0;
}void getSeconds(unsigned long *par)
{/* 获取当前的秒数 */*par = time( NULL );return;
}

Number of seconds: 1678023852

能接受指针作为参数的函数,也能接受数组作为参数,如下所示:

#include /* 函数声明 */
double getAverage(int *arr, int size);int main ()
{/* 带有 5 个元素的整型数组  */int balance[5] = {1000, 2, 3, 17, 50};double avg;/* 传递一个指向数组的指针作为参数 */avg = getAverage( balance, 5 ) ;/* 输出返回值  */printf("Average value is: %f\n", avg );return 0;
}double getAverage(int *arr, int size)
{int    i, sum = 0;      double avg;          for (i = 0; i < size; ++i){sum += arr[i];}avg = (double)sum / size;return avg;
}

Average value is: 214.400000

5.5 从函数返回指针

C 允许从函数返回指针。为了做到这点,您必须声明一个返回指针的函数,如下所示:

int * myFunction()
{
.
.
.
}

另外,C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。

现在,让我们来看下面的函数,它会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下

#include 
#include 
#include  /* 要生成和返回随机数的函数 */
int * getRandom( )
{static int  r[10];int i;/* 设置种子 */srand( (unsigned)time( NULL ) );for ( i = 0; i < 10; ++i){r[i] = rand();printf("%d\n", r[i] );}return r;
}/* 要调用上面定义函数的主函数 */
int main ()
{/* 一个指向整数的指针 */int *p;int i;p = getRandom();for ( i = 0; i < 10; i++ ){printf("*(p + [%d]) : %d\n", i, *(p + i) );}return 0;
}

26010
25398
26040
9439
10580
21608
28484
14871
30809
20117
*(p + [0]) : 26010
*(p + [1]) : 25398
*(p + [2]) : 26040
*(p + [3]) : 9439
*(p + [4]) : 10580
*(p + [5]) : 21608
*(p + [6]) : 28484
*(p + [7]) : 14871
*(p + [8]) : 30809
*(p + [9]) : 20117

菜鸟教程,学习记录

相关内容

热门资讯

iPhone手机怎么玩安卓系统... 你有没有想过,你的iPhone手机竟然也能玩安卓系统?没错,就是那个一直以来让你觉得遥不可及的安卓世...
平板删安卓系统更新不了,原因及... 最近是不是你也遇到了这样的烦恼?平板电脑上的安卓系统更新不了,是不是让你头疼得要命?别急,今天就来给...
苹果组装机安卓系统卡,卡顿背后... 你有没有发现,最近用苹果手机的时候,有时候系统有点卡呢?这可真是让人头疼啊!你知道吗,其实这背后还有...
安卓系统原生浏览器,功能与体验... 你有没有发现,每次打开手机,那个小小的浏览器窗口总是默默无闻地在那里,陪你浏览网页、搜索信息、看视频...
安卓机如何上苹果系统,跨平台体... 你是不是也和我一样,对安卓机和苹果系统之间的切换充满了好奇?想象你的安卓手机里装满了各种应用,而苹果...
安卓导入系统证书失败,原因分析... 最近在使用安卓手机的时候,你是不是也遇到了一个让人头疼的问题——导入系统证书失败?别急,今天就来给你...
安卓原生系统有哪些手机,盘点搭... 你有没有想过,为什么有些手机用起来就是那么流畅,那么顺心呢?这背后可大有学问哦!今天,就让我带你一起...
安卓系统关机了怎么定位,安卓系... 手机突然关机了,是不是有点慌张呢?别担心,今天就来教你一招,让你的安卓手机即使关机了,也能轻松定位到...
安卓系统游戏加速器,畅享无延迟... 你有没有发现,手机游戏越来越好玩了?不过,有时候游戏体验可能并不那么顺畅,是不是因为手机性能不够强大...
安卓4系统天气功能,尽在掌握 安卓4系统天气功能大揭秘在当今这个数字化的世界里,手机已经不仅仅是一个通信工具,它更是一个集成了各种...
安卓系统如何玩碧蓝幻想,攻略与... 你有没有想过,在安卓系统上玩《碧蓝幻想》竟然可以这么酷炫?没错,就是那个让你沉迷其中的二次元大作!今...
安卓系统搜不到图朵,图朵生成之... 最近是不是你也遇到了这样的烦恼?手机里明明有那么多美美的图片,但是用安卓系统搜索的时候,却怎么也找不...
魁族8刷安卓系统,系统升级后的... 哇,你知道吗?最近在安卓系统圈子里,有一个话题可是引起了不小的轰动,那就是魁族8刷安卓系统。你是不是...
微信正版安装安卓系统,畅享沟通... 你有没有想过,你的微信是不是正版安装的安卓系统呢?这可不是一个小问题哦,它关系到你的微信使用体验和隐...
电视能刷安卓系统吗,电视也能刷... 电视能刷安卓系统吗?揭秘智能电视的无限可能想象你家的电视不再只是用来观看节目的工具,而是变成了一个功...
安卓系统开通通知功能,畅享智能... 你知道吗?最近安卓系统更新后,新增了一个超级实用的功能——开通通知功能!这可是个大喜事,让咱们的生活...
苹果系统安卓爱思助手,系统兼容... 你有没有发现,手机的世界里,苹果系统和安卓系统就像是一对欢喜冤家,总是各有各的粉丝,各有各的拥趸。而...
安卓系统占用很大内存,揭秘内存... 手机里的安卓系统是不是让你感觉内存不够用,就像你的房间堆满了杂物,总是找不到地方放新东西?别急,今天...
安卓系统p30,安卓系统下的摄... 你有没有发现,最近安卓系统P30在手机圈里可是火得一塌糊涂呢!这不,我就来给你好好扒一扒这款手机的那...
siri被安卓系统进入了,智能... 你知道吗?最近科技圈可是炸开了锅,因为一个大家伙——Siri,竟然悄悄地溜进了安卓系统!这可不是什么...