C 程序设计教程(20)—— 数组和指针(三):数组与指针的关系

news2024/11/15 1:25:09

C 程序设计教程(20)—— 数组和指针(三):数组与指针的关系

在这里插入图片描述
该专栏主要介绍 C 语言的基本语法,作为《程序设计语言》课程的课件与参考资料,用于《程序设计语言》课程的教学,供入门级用户阅读。

目录

  • C 程序设计教程(20)—— 数组和指针(三):数组与指针的关系
    • 一、指针与一维数组
    • 二、指针与二维数组
    • 三、指针与字符串
    • 四、指针数组

定义一个数组后,系统为其分配一块连续的存储空间,每个元素按照下标的顺序存放其中。事实上,数组名是一个指针常量,它是分配给数组的存储空间的起始地址。访问数组元素通过数组名和下标配合完成。

一、指针与一维数组

假设有以下数组:

int a[10];

则数组 a 一共有 10 个数组元素,每个数组元素中可以存放一个整型数据,占两个字节。数组 a 在内存中一共占据 20 个字节的连续存储空间。

假设存储空间的起始地址为 2000,即数组名 a 的地址为 2000,则 a+0 是 a[0] 的地址 2000(即:&a[0]),a+1 是 a[1] 的地址 2002(即:&a[1]),以此类推。

例如:

#include<stdio.h>
int main() 
{
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	printf("a的地址为:%p\n",a);
	printf("a[0]的地址为:%p\n",&a[0]);
	printf("a[1]的地址为:%p\n",&a[1]);
	printf("a[2]的地址为:%p\n",&a[2]);
}

以上程序的运行结果如下:

在这里插入图片描述

有以下数组的定义:

int a[10], *p;

则以下赋值语句是等价的:

p=a;
p=&a[0];

可以在定义指针变量时赋初值,例如:

int a[10], *p=&a[0];
//等价于
int a[10], *p;
p=&a[0];
//或
int a[10], *p=a;

例如:

#include<stdio.h>
int main() 
{
	int a[10]={1,2,3,4};
	int *p;
	p=&a[0];
	printf("%d",*(p+2));
}

以上程序的运行结果如下:

在这里插入图片描述

关于数组与指针的说明:

(1)在以上程序中,p、a、&a[0] 均指向同一存储单元,都是数组 a 的首地址,也是第一个数据元素 a[0] 的地址。p 是变量,而 a 和 &a[0] 是常量。

(2)如果 p 的值是 &a[0],则 p+i 和 a+i 都是数组元素 a[i] 的地址,或者说它们都指向 a 数组的第 i 个元素。*(p+i) 或 *(a+i) 就是 p+i 或 a+i 所指向的数组元素,即 a[i]。例如:*(p+5) 或 *(a+5) 或就是 a[5]。

(3)指向数组的指针变量也可以带下标。如 p[i] 与 *(p+i) 等价。

例如:

#include<stdio.h>
int main() 
{
	int a[10]={1,2,3,4};
	int *p=a;
	printf("%d\n",p[2]);
	printf("%d\n",*(p+2));
}

以上程序的运行结果如下:

在这里插入图片描述

(4)指针变量可以实现本身值的改变,如 p++ 是合法的,而 a++ 是错误的。因为 a 是数组名,它是数组的首地址,是常量。

(5)要注意指针变量的当前值。例如:

#include<stdio.h>
int main() 
{
	int a[10],i,*p=a;
	for(i=0;i<10;i++){
		*p++=i;  //*p++ 等价于 *(p++)
	}
	p=a;
	for(i=0;i<10;i++){		
		if(i%4==0){
			printf("\n");
		}
		printf("a[%d]=%d    ",i,*p++);
	}
}
/*
如果 p 当前指向 a 数组中的第 i 个元素,则:
(1)*(p--) 相当于 a[i--];
(2)*(++p) 相当于 a[++i];
(3)*(--p) 相当于 a[--i];
*/

以上程序的运行结果如下:

在这里插入图片描述

二、指针与二维数组

设有如下二维数组:

int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};

各数组元素的地址如下:

#include<stdio.h>
int main() 
{
	int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
	int i,j,*p;
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("&a[%d][%d]=%p  ",i,j,&a[i][j]);
		}
		printf("\n");
	}
	printf("\n");
	p=a[0];
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("&a[%d][%d]=%p  ",i,j,p++);
		}
		printf("\n");
	}
}

以上程序的运行结果如下:

在这里插入图片描述

数组 a 可分解为三个一维数组,即 a[0]、a[1]、a[2],每个一维数组又包含 4 个数组元素。例如:a[0] 数组,包含 a[0][0]、a[0][1]、 a[0][2]、 a[0][3] 四个元素。

从二维数组的角度来看,a 是二维数组名,a 代表整个二维数组的首地址,也是二维数组 0 行的首地址。a+1 代表第一行的首地址,a+2 代表第二行的首地址。

a[0] 是第一个一维数组的数组名和首地址。&a[0][0] 是二维数组 a 的 0 行 0 列的元素地址。

数组元素地址测试 :

#include<stdio.h>
int main() 
{
	int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
	printf("a的地址:%p\n",a);
	printf("a[0]的地址:%p    a[0][0]的地址:%p\n",a[0],&a[0][0]);
	printf("a[1]的地址:%p    a[1][0]的地址:%p\n",a[1],&a[1][0]);
	printf("a[2]的地址:%p    a[2][0]的地址:%p\n",a[2],&a[2][0]);
}

以上程序的运行结果如下:

在这里插入图片描述

a、a[0]、*(a+0)、*a、&a[0][0] 的值是相等的。同时 a+i、a[i]、*(a+i)、&a[i][0] 的值也是相等的。

测试代码如下:

#include<stdio.h>
int main() 
{
	int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
	printf("a的地址:%p         a+2的地址:%p\n",a,a+2);
	printf("a[0]的地址:%p      a[2]的地址:%p\n",a[0],a[2]);
	printf("*a的地址:%p        *(a+2)的地址:%p\n",*a,*(a+2));
	printf("&a[0][0]的地址:%p  &a[2][0]的地址:%p\n",&a[0][0],&a[2][0]);
}

以上程序的运行结果如下:

在这里插入图片描述

a[0] 可以看成是 a[0]+0,是一维数组 a[0] 的 0 号元素的地址,而 a[0]+1 则是 a[0] 的 1 号元素的地址,由此可以得出 a[i]+j 是一维数组 a[i] 的 j 号元素的地址,等于 &a[i][j]。

由 a[i]=*(a+i) 得 a[i]+j=*(a+i)+j。由于 *(a+i)+j 是二维数组 a 的第 i 行第 j 列元素的地址,所以,该元素的值等于 *(*(a+i)+j)。

测试代码如下:

#include<stdio.h>
int main() 
{
	int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
	printf("a[1]+2的地址:%p    &a[1][2]的地址:%p\n",a[1]+2,&a[1][2]);
	printf("a[1][2]的值:%d     *(a[1]+2)的值:%d\n",a[1][2],*(a[1]+2));
}

以上程序的运行结果如下:

在这里插入图片描述

定义指向二维数组的指针变量。

把二维数组 a 分解为一维数组 a[0]、a[1]、a[2] 之后,设 p 为指向二维数组的指针变量,格式为:

类型说明符 (*指针变量名)[长度];
说明:
(1)类型说明符:表示所指向数组的数据类型。
(2*:表示其后的变量时指针类型。
(3)长度:表示二维数组分解为一维数组时一维数组的长度,也就是二维数组的列数。
(4(*指针变量名):不能省略括号,如省略括号则表示是指针数组。
例如:
int (*p)[4];

以上定义表示 p 是一个指针变量,它指向包含 4 个元素的一维数组。若令 p=a,则 p+i 指向一维数组 a[i],*(p+i)+j 是二维数组第 i 行第 j 列的元素的地址,而 *(*(p+i)+j) 则是第 i 行第 j 列的值。

例如:

#include<stdio.h>
int main() 
{
	int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};
	int i,j;
	int (*p)[4];
	p=a;
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("%8d",*(*(p+i)+j));
		}
		printf("\n");
	} 
}

以上程序的运行结果如下:

在这里插入图片描述

三、指针与字符串

用字符指针指向一个字符串。如下所示:

#include<stdio.h>
int main() 
{
	char str1[]="I am a student.";
	//str1是字符数组名,它代表字符数组的首地址 
	char *str2="This is a book.";
	//首先定义指向字符串的指针变量str2,并把
	//字符串的起始地址存入str2
	printf("%s\n",str1); 
	printf("%s\n",str2);
}

指向字符串的指针变量的定义与指向字符变量的指针变量的定义方法相同。以上程序的运行结果如下:

在这里插入图片描述

输出字符串中第 5 个字符以后的所有内容。程序如下:

#include<stdio.h>
int main() 
{
	char str1[]="Management";
	char *str2="Classroom",*pch;
	pch=str1;
	str2+=5;
	pch+=5;
	printf("%s\n",pch); 
	printf("%s\n",str2);
}

以上程序的运行结果如下:

在这里插入图片描述

用字符数组和字符型指针变量都可以实现字符串的存储和运算。但两者是有区别的。

(1)字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身存放在以该首地址为首的一块连续的内存空间中,并以 ‘\0’ 作为结束符。字符数组是由若干个数组元素组成的,可用来存放整个字符串。

(2)可以向字符串指针变量赋值。例如:

char *ps;
ps="C program.";

四、指针数组

指针数组是指一个数组的元素值为指针。指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。定义指针数组的格式如下:

类型说明符 *数组名[长度];
说明:类型说明符表示指针所指向的变量的类型。
例如:
int *p[3];

以上定义表示 p 是一个指针数组,它有三个数组元素,每个元素都是一个指针,指向整型变量。

例如:利用一个指针数组来指向一个二维数组,并通过指针数组输出二维数组中的元素的值。

#include<stdio.h>
int main() 
{
	int a[3][3]={1,2,3,4,5,6,7,8,9};
	int i,j,*p[3];
	for(i=0;i<3;i++){
		p[i]=a[i];
	}
	for(i=0;i<3;i++){
		for(j=0;j<3;j++){
			printf("%8d",*(p[i]+j));
		}
		printf("\n");
	}
}

以上程序的运行结果如下:

在这里插入图片描述

上面的例子中,p 是一个指针数组,三个元素分别指向二维数组 a 的各行。

指针数组与二维数组指针变量的区别:二维数组指针变量是单个的变量,定义形式为:(*p)[n];而指针数组类型表示的是多个指针,定义形式为:*p[n]。例如:

int (*p)[3];  //表示指向二维数组的指针变量。该二维数组的列数为3
int *p[3];    //表示 p 是一个指针数组,有三个下标变量 p[0]、p[1]、p[2],均为指针变量

指针数组常用来表示一组字符串,指针数组的每个元素被赋予一个字符串的首地址。指向字符串的指针数组的初始化更为简单。例如:

#include<stdio.h>
int main() 
{
	char *name[]={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
	int i;
	for(i=0;i<7;i++){
		printf("%s\n",name[i]);
	}
}

以上程序的运行结果如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182529.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RESTful应用

AJAX技术 浏览器是多进程的&#xff0c;简单的说就是&#xff0c;浏览器每打开一个标签页&#xff0c;就相当于创建了一个独立的浏览器进程。但是js是基于单线程的&#xff0c;而这个线程就是浏览器的js引擎&#xff0c;浏览器无论在什么时候都只且只有一个线程在运行JavaScri…

sonic-ios-bridge(sib)性能监控之系统性能及应用性能

sib下载地址&#xff1a;Releases SonicCloudOrg/sonic-ios-bridge GitHub 可以看到最新版本为V1.3.7。下载到本地并解压后即可使用。 性能监控使用帮助&#xff1a;sib perfmon -h sib perfmon -f -j&#xff1a;以json格式化显示性能数据。 一、查看系统整体性能数据 查看…

10. POP3收取邮件

1. POP3协议 POP3&#xff0c;全名为“Post Office Protocol - Version 3”&#xff0c;即“邮局协议版本3”。是TCP/IP协议族中的一员&#xff0c;由RFC1939 定义。本协议主要用于支持使用客户端远程管理在服务器上的电子邮件。提供了SSL加密的POP3协议被称为POP3S。 POP 协议…

Kernel: sysctl: hung_task_panic、hung_task_check_count、hung_task_timeout_secs

文章目录 mutex 锁相关的一个例子这个和磁盘相关的一个例子hung_task_panic:hung_task_check_count:hung_task_timeout_secs:实例hung_task_warnings:相关的编译配置mutex 锁相关的一个例子 systemd-shutdown 卡在device-shutdown时调用的mutex-lock操作。 这个和磁盘相关的…

Android ANR触发机制及日志分析

1.ANR Application Not Responding&#xff0c;即应用程序未响应。Android系统要求一些事件在一定时间范围内完成&#xff0c;如果超过预定时间未得到有效响应或响应时间过长&#xff0c;就会造成ANR。 Android中有4种ANR发生场景&#xff1a; ①点击事件(按键和触摸事件&am…

PDF在线合并网页版有哪些?这几款你一定没用过

PDF在线合并网页版有哪些&#xff1f;很多人在工作中都需要给其他人发送一些重要文件&#xff0c;如果文件数量比较多的时候&#xff0c;就会出现耗时有耗力的情况&#xff0c;所以我们就需要想一个办法来解决问题&#xff0c;那就是将多个PDF文件进行合并&#xff0c;我们需要…

MATLAB算法实战应用案例精讲-【数模应用】概率生成模型(Generative Model)

前言 知识储备 表征学习 背后的核心思想representation learning ,不是试图直接对高维样本空间建模,而是使用一些低维潜在空间来描述训练集中的每个观察,然后学习一个映射函数,该函数可以在潜在空间中取一个点,将其映射到原始域中的一个点。换句话说,潜在空间中的每个…

[LeetCode 1664]生成平衡数组的方案数

题目描述 题目链接&#xff1a;[LeetCode 1664]生成平衡数组的方案数 给你一个整数数组 nums 。你需要选择 恰好 一个下标&#xff08;下标从 0 开始&#xff09;并删除对应的元素。请注意剩下元素的下标可能会因为删除操作而发生改变。 比方说&#xff0c;如果 nums [6,1,…

群晖(docker图形化界面)使用 SpeedTest 测速

群晖(docker图形化界面)使用 SpeedTest 测速 博主博客 https://blog.uso6.comhttps://blog.csdn.net/dxk539687357 本文主要介绍在群晖中安装 speedtest 进行网络测速。 一、安装 docker 在套件中心搜索并且安装 docker。 二、下载容器 在 注册表 中搜索 adolfintel/speedte…

UART、RS232、RS485和RS422

1.UART UART是通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;是一种通用的串行、异步通信总线&#xff0c;是设备间进行异步通信的关键模块。UART负责处理数据总线和串行口之间的串/并、并/串转换&#xff0c;并规定了帧格式&#…

PPT制作心得

1.插入形状&#xff1a; 这里有三个部分可以设置&#xff1a; 填充可以设置用什么颜色来填充 轮廓可以设置边框的颜色 (样式是设置 填充轮廓&#xff0c;也就是说有一些默认的填充轮廓组合&#xff09; 还有里面的文字的大小&#xff0c;字体&#xff0c;颜色 2.如何画水平…

深度解析2023世界人工智能大会

1、2020年世界人工智能大会&#xff0c;此次大会的主要目的是什么&#xff1f; 我们现在的社会是一个科技快速发展的国家&#xff0c;因为我们已经不会再为了温饱的问题而操心&#xff0c;而是越来越追求自己的精神享受。然而科技在这一方面也是发展非常迅速的&#xff0c;我们…

请求头 x-ca-key、x-ca-nonce、x-ca-signature 加密分析第一篇

本篇博客为大家开始着手分析 请求头 x-ca-key、x-ca-nonce、x-ca-signature 加密相关内容 目标站点在本文进行隐藏&#xff0c;如有需要&#xff0c;可直接联系 一般你能搜到这篇博客&#xff0c;代表你采集的站点使用类似加密。 请求头解密目录x-ca-key、x-ca-nonce、x-ca-sig…

[acwing周赛复盘] 第 88 场周赛20230128

[acwing周赛复盘] 第 88 场周赛20230128 一、本周周赛总结二、 4800. 下一个1. 题目描述2. 思路分析3. 代码实现三、4801. 强连通图1. 题目描述2. 思路分析3. 代码实现四、4802. 金明的假期1. 题目描述2. 思路分析3. 代码实现六、参考链接一、本周周赛总结 在T2卡了半天&#…

签到网站js逆向与python实现

登陆分析 数据分析 Chrome浏览器进入填报系统,选择对应学校,来到登陆界面。 打开Chrome开发者工具(快捷键F12),模拟手机端。 111111 222222 333333 点击登陆,分析网络数据包。 表单有四个必需字段: 身份(UserType) 学号(XGH) 姓名(Name) 密码(PassWord) 一个…

2023最新版会声会影有哪些新功能介绍?

会声会影是Corel制作的一款功能强大的视频编辑软件&#xff0c;英文名&#xff1a;Corel VideoStudio。会声会影2023作为当下最为受欢迎的视频编辑处理程序&#xff0c;其在业内可以说享有极高的知名度&#xff1b;而全新的2023版本更是如此&#xff0c;很多忠实的用户都想来体…

如何确保API 的稳定性与正确性?你只需要这一招

一、什么是rest-assured现在&#xff0c;越来越多的 Web 应用转向了RESTful的架构&#xff0c;很多产品和应用暴露给用户的往往就是一组 REST API&#xff0c;这 样有一个好处&#xff0c;用户可以根据需要&#xff0c;调用不同的 API&#xff0c;整合出自己的应用出来。从这个…

Linux常用命令——scp命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) scp 加密的方式在本地主机和远程主机之间复制文件 补充说明 scp命令用于在Linux下进行远程拷贝文件的命令&#xff0c;和它类似的命令有cp&#xff0c;不过cp只是在本机进行拷贝不能跨服务器&#xff0c;而且s…

C++基础入门丨7. 指针——一文搞懂指针

操作系统&#xff1a;Windows 10 IDE&#xff1a;Visual Studio 2019 文章目录1 什么是指针2 指针变量的定义和使用3 指针所占用的空间4 空指针和野指针5 const修饰指针6 指针和数组7 指针和函数8 指针、函数、数组1 什么是指针 我们知道每一个变量都有一个内存位置&#xff0…

【ESLint】ESLint的安装配置及vscode插件

一、什么是ESLintESLint是可组装的JavaScript和JSX检查工具。工作中常用&#xff0c;所以最好有所了解。可以在官网中查询相关规则&#xff1a;ESLint - Pluggable JavaScript linter - ESLint中文二、ESLint的安装配置在创建自定义vue项目时&#xff0c;要勾选CSS Pre-process…