C语言——指针和数组_c语言指针与数组-程序员宅基地

技术标签: c语言  # C语言  

C语言

  • 知识基础
  • 控制语句
  • 函数
  • 输入输出
  • 指针和数组
  • 用户自定义数据类型
  • 文件操作


  • C程序中数据存储在哪里呢?在计算机中操作系统会给每一个进程分配一定的存储空间(这个存储空间是经过抽象的虚拟地址空间,其结构一致),一个数据就被存储在一个存储单元(存储单元的大小由数据类型决定);
  • 程序运行时如何通过变量名找到相应的存储单元的呢?实际上,在编译阶段之后,程序并不通过变量名确定数据的位置,而是通过存储单元在内存中的地址来寻找数据(这种由变量名到地址操作的转换由编译程序完成);
  • 什么是地址?操作系统对于分配给进程的空间的每一个byte都进行编号,这样的话一个编号就对应一字节的存储单元,程序只需要知道数据的编号和数据类型就能够找到想要的数据并进行操作了;这里的编号就是地址。

综上所述,我们在使用C语言编程过程中对变量的命名在经过编译之后就是直接对地址内容的操作了(通过寄存器中的地址直接找到存储单元),也就是说变量==地址内容
通过变量名对变量的访问称为直接访问

变量名
地址
变量
int i = 2;
printf("%d", i);//直接访问

一、指针

1.指针的含义

指针==地址,地址就像是指向一个变量(也就是一个存储单元)的箭头,可以根据箭头找到变量。
C语言中允许对内存空间的直接操作,也就是说,只要用户知道一个地址就可以直接去访问该地址的内容(将用户的指针值写入寻址寄存器找到存储单元),这种访问方式称为间接访问*(指针值)

等于
指针值
地址
变量
int i = 2;
printf("%d ", *(&i));//间接访问
//& 取地址;* 解引用     
//如果 &i == 0x0f000011
printf("%d ", *(0x0f000011));
//输出结果 为 2 2

2.指针变量

指针值的三种出现方式:

  • 使用&操作符,得到一个变量的地址;
  • 用户输入一个合法的指针值;
  • 经过编译之后,自动定位的寄存器中的地址;

其中,对编译器自动定位的地址,用户不需要关心,系统对变量的定位对用户是透明的,用户对变量名直接操作就是对一个地址内容直接操作;

对于前两种方式得到一个地址之后,如何来存储呢?
指针变量:存放地址的变量;

指针名
等于
变量名
地址1
指针变量
地址2
变量

如上图,有以下访问操作

操作 内容 说明
变量名 变量值 直接访问
&变量名 地址2 取址操作符的作用
—— —— ——
指针名 指针值 直接访问,可以存储地址2
*(地址2) 变量值 间接访问
*(指针名) 变量值 间接访问
—— —— ——
&指针名 地址1 可以用多重指针存储地址1

程序中可操作的均为变量名/指针名,以及操作符& *

指针变量的定义:基类型 *指针名
指针变量是一类特殊的变量,其数据类型有两个部分:

  • * 确定变量为指针类型,存储内容为一个地址,对一个机器其地址的大小是确定的,32位机器的地址长度为32b(也就是一个int的大小);
  • 基类型确定指针指向的空间的数据类型,基类型保证了对指针的移动,增减的操作的正确性。
int a;//普通变量
int *p;//指针变量p,指向一个整型空间,以四字节为单位处理p存储的地址指向的存储单元
a = 999;//直接引用,a对应存储单元直接设为999
p = &a;//直接引用,p对应存储单元直接设为a的地址
*p = 1;//间接引用,使得指针p指向的存储单元(即a)设为1
printf("%d", a); //输出 1

3.指针作为形参

函数的定义中要设置形参列表,指针类型同样是一种数据类型可以作为函数的形参出现,函数声明中有基类型 * 形参指针名
在函数调用语句中,传递的实参应该是基类型的一个地址,(实参可以是指针变量或者直接取地址得到的常量)被调用函数会在调用栈中复制一份这个地址,这样的间接访问能够实现对调用函数中的变量值进行编辑;

变量名
&变量名
传参
形参指针名
地址1
变量
地址常量
形参指针值
地址2
指针名
等于
传参
形参指针名
地址2
指针变量
地址1
变量
形参指针值
地址3

可以发现在指针的传递过程中,地址被复制了多份并存储在不同的存储单元中,并且有不同的名字,但对这些指针变量进行解引用,操作的均为目标数据的数据单元

地址
变量
指针变量1
指针变量2
指针变量3

指针同样可以指向函数,你可以声明一个指针是用来指向函数的,从而选择要赋值的函数名,从而实现选择要调用的函数,本质上来讲还是指针灵活运用的一种,有点面向对象语言的模板的意思

二、数组

在解决问题、处理数据的过程中,我们总会遇到这样的情况:

  • 批量数据的排列有一定的关系,也就是有序的
  • 这些数据有相同的数据类型
  • 我们希望对这样一组数据按序全部进行处理

以下我们称这样的一组数据为批量数据。。。
如果我们对每一个单独的数据进行命名,再按照名称挨个处理,我们的源程序就会重复的为每一个数据进行操作,这是不现实的;
我们需要一种方式将这样的批量数据按照一定结构进行存储,使得程序在处理完第一个数据之后能够自动的找到下一个数据,从而使用循环结构对批量数据进行处理。

组织数据的方式多种多样,不同结构有其特点和独有的操作方式,这是计算机能够处理海量数据的基础理论之一,我们会在《数据结构》课程中学习

1.数组的定义

如何组织数据?最简单的就是顺序存储,我们将批量数据一个挨一个的存储在内存中连续的一块地址空间;
如何访问批量数据呢?只需要记录这块连续空间的起始地址,由于批量数据具有相同的数据类型,想要访问的第i个数据的地址起始地址+i*数据类型的长度(注意,我们对批量数据的计数是从零开始的);
在程序设计中如何实现这样的存储结构呢?程序设计语言提供基础的顺序存储结构 ——>数组
定义:数据类型 数组名[常量表达式]
含义:在内存中开辟一块连续的空间,大小为常量表达式*数据类型长度,其中数组名也就是这块空间的起始地址,也就是一个指针,但特别的,数组名为常量指针,它指向的位置固定,不能被赋值!!!

数组名
第0个元素

在定义中必须确定数组的大小,也就是确定开辟空间的边界!!!
引用数组元素:程序设计语言对数组类型提供了下标运算[]

  • 指针常量/指针变量[整数]等价于*(指针常量/指针变量 + 基类型长度*整数)
    方便了对数组元素的引用。

2.数组与指针的联系

数组类型等价于指针变量基类型:规定了一个元素的长度、操作方式;
数组名类似于指针变量的值:数组名是一个静态地址,不可变;指针变量值存储一个地址,可变;
所以本质上,数组只是对指针操作进行了一些包装,都是利用指针的便利对数据进行访问。

int a[10];
int* p;
p = a;//指针变量指向了数组a的起始地址
for(int i = 0; i < 10; i++){
    
	p[i] = i;
	printf("%d ", a[i]);//输出0 1 2 3 4 5 6 7 8 9
}

3.动态内存分配

在数组定义时,一定要确定数组的大小,在程序运行过程中批量数据的数量不能完全确定,想要开辟合适大小的内存空间,我们可以类比数组的方式,动态分配内存;
首先,定义一个指针变量用来存储起始地址;
然后,使用库函数malloc分配空间,并将返回值赋值给第一步定义的指针变量;
最后,可以通过指针变量和下标操作访问所有的元素。

指针名/数组名
地址1
指针变量
地址2
第0元素
int* a;
a = (int*)malloc(sizeof(int)*n);//开辟大小为n的整型数组
for(int i = 0; i < n; i++){
    
	a[i] = i;//可以为数组里每一个元素赋值
}

对这写库函数的使用可以查找资料,这里我们主要理解动态分配内存与数组的定义的相似性

动态分配的空间在读写操作上与定义的等大的数组没有区别,不同之处在于动态分配的指针变量可以变化指向新的空间,使用之后需要将分配的空间free

4.多维数组、指针数组、多重指针

使用指针可以实现数组这种简单的数据结构,进一步如何实现更加复杂的数据结构呢?例如一个二维表,表中的每一项数据的数据类型都是一致的,可以根据(行,列)定位到一个表项;

  • 二维数组
名词 说明
char a[row][list] 定义二维数组,在物理上是全体连续的
所占空间大小为row*(list*sizeof(char))
a 静态地址,整个存储空间的起始地址
a[i] 静态地址,并不存储在栈中,不是一个具体的存储单元
a[i][j] char型变量,对应一个一字节的存储单元
等价于*(a + i*(list*sizeof(cahr)) + j*sizeof(char))
  • 指针数组
名词 说明
char *a[row] 定义指针数组,数组a[row]是连续的
指针变量a[…]占空间地址长度*row
a 静态地址,存储指针的空间的起始地址
a[i] 指针变量,需要动态分配空间,为其存储一个地址
a[i] = (char*)malloc(sizeof(char)*list);
a[i][j] char型变量,对应一个一字节的存储单元
是和开辟的空间内多个元素连续的
  • 二重指针
名词 说明
char **a 定义双重指针,
a 指针变量,需要动态分配空间,为其存储一个地址
a = (char**)malloc(sizeof(char*)*row);
a[i] 指针变量,需要动态分配空间,为其存储一个地址
a[i] = (char*)malloc(sizeof(char)*list);
a[i][j] 字符型变量,对应一个一字节的存储单元

综上,可以将它们类比至多维的情况:

  • 多维数组总是存储在物理上连续的内存空间内,最低维下标变化最快,其余高维的表达式a[i][j]...
  • 指针数组,多重指针的特殊情况,也就是最高位是固定的一块空间,由静态地址指向
  • 多重指针,每一维会存储在连续的空间内,除最低维之外的空间内都存储着指针类型变量,最高维被一个指针变量指向。

练习

编程语言练习 / C语言

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_63489464/article/details/124001677

智能推荐

git 常用命令_git 强制更新-程序员宅基地

文章浏览阅读6.8k次。目前由于项目需要,了解了一些基本的git命令1、首先是GIt强制pull ; git强制更新 git fetch --all git reset --hard origin/master git pull2、ubuntu系统有多个git账号,如何同时正常使用两个账号由于自己的git账号和目前公司的账号不一致,因此有此需求首先是切换到~/.ssh/目录下,执行下面命令:在此目录下新建config :touch config执行命令:v..._git 强制更新

python封装接口用于调用_python接口自动化(三十五)-封装与调用--流程类接口关联(详解)...-程序员宅基地

文章浏览阅读84次。简介流程相关的接口,主要用 session 关联,如果写成函数(如上篇),s 参数每个函数都要带,每个函数多个参数,这时候封装成类会更方便。在这里我们还是以博客园为例,带着小伙伴们实践一下。接口封装大致流程1、在接口测试中,有些接口经常会被用到比如登录的接口,这时候我们可以每个接口都封装成一个方法,如:登录、保存草稿、发布随笔、删除随笔,这四个接口就可以写成四个方法2、接口封装好了后,后面我们写用..._pycharm接口自动化需封装接口内容

AngelScript -- C++程序最好的脚本语言-程序员宅基地

文章浏览阅读2.1k次。AngelScript是一门静态类型语言, C++风格. AngelScript拥有最好的原生支持bindings. 通常, 一个函数或者是类只需要注册到AS的虚拟机中, 就可以在AS脚本中使用了. 本文中的其他脚本语言都需要中间插件帮助才能实现函数的binding. 当然, 如果本地函数没有先注册的话, AS脚本也是编不过的. 这给那些想要预编译AS byte-code的人增加了一个额外步骤. AngelScript不支持table, 事实上这一点不会造成麻烦, 因为AS是静态类型脚本. .._angelscript

ORA-12154: TNS: 无法解析指定的连接标识符-程序员宅基地

文章浏览阅读93次。发生这种情况,大多是网络服务名配置错误的原因。另外,也有可能是笔者的这种情况--误删除系统默认的箭筒程序名(eg. LOCAL_LISTENER)和网络服务程序名(eg. LISTENER_ORCL)。对于第一种原因,只要修改正确网络服务的配置(特别是里面的数据库名称和IP一定要正确)就可以了。对于笔者这种情况,可以的解决方法如下。1. 重启电脑,..._sqlplus ora-12154: tns

基于知名微服务框架go-micro开发gRPC应用程序_go-micro开发实例-程序员宅基地

文章浏览阅读647次。go-micro是golang的一个微服务框架。这篇文章将介绍使用go-micro最新版本v4开发gRPC服务的方式。_go-micro开发实例

九度OJ题目1074-对称平方数_大学oj网1074题-程序员宅基地

文章浏览阅读110次。题目描述:打印所有不超过n(n<256)的,其平方具有对称性质的数。如11*11=121输入:无任何输入数据输出:输出具有题目要求的性质的数。如果输出数据不止一组,各组数据之间以回车隔开。样例输入:样例输出:参考代码:#include<cstdio>using namespace std;int fun(int x) {//判断一个数..._大学oj网1074题

随便推点

jsp在线预览pdf和word文件(在浏览器中预览pdf文件和Word文件)_jsp 在线预览word 文档-程序员宅基地

文章浏览阅读8.7k次。相关jar包和插件的下载地址:在线浏览pdf的插件在线浏览word文件的插件在浏览器中预览pdf文件和Word文件,下载完这些插件,导入到自己项目中,至于导入到哪个目录,web.xml中需要配置什么,在下载的文档中有具体的文档说明,按照文档中的步骤去配置就好了!我们先看看效果图:后面直接上代码!直接上代码!pdfAndWord.jsp页面&lt;%@ ..._jsp 在线预览word 文档

深入理解 iOS 开发中的锁_深入理解 ios 中的锁-程序员宅基地

文章浏览阅读1.7k次。摘要本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比、iOS 常见知识点(三):Lock。本文也不会详细介绍锁的具体实现原理,这会涉及到太多相关知识,笔者不敢误人子弟。本文要做的就是简单的分析 iOS 开发中常见的几种锁如何实现,以及优缺点是什么,为什么会有性能上的差距,最终会简单的_深入理解 ios 中的锁

MSI-X总结-程序员宅基地

文章浏览阅读1.8k次。PCIe有三种中断,分别为INTx中断,MSI中断,MSI-X中断,其中INTx是可选的,MSI/MSI-X是必须实现的。MSI, message signal interrupt, 是PCI设备通过写一个特定消息到特定地址,从而触发一个CPU中断。特定消息指的是PCIe总线中的Memory Write TLP, 特定地址一般存放在MSI capability中。1、 MSI-X Capabiliity结构MSI-X中断机制提出目的是扩展PCIe设备使用的中断向量个数(次要),同时解决MSI中断要求使_msi-x

duilib 可编辑ComboBox-程序员宅基地

文章浏览阅读1.4k次。直接贴码 头文件 #ifndef__UICOMBOBOXEx_H__#define__UICOMBOBOXEx_H__#pragmaoncenamespaceDuiLib{ /*********编辑框************/ classCComboEditWnd..._duilib 可编译的combox

第二个项目---EMOS企业在线办公小程序_慕课网 emos sql-程序员宅基地

文章浏览阅读5k次,点赞2次,收藏14次。代码量:移动端:30000+后端:5000+技术栈:移动端:uni-app + Vue + JavaScript + Less + 微信小程序后端:SpringBoot + SpringMVC + MyBatis + Shiro+ JWT + Quartz + ThreadPool + RabbitMQ + Docker源码地址:移动端源码后端源码第一章 安装软件数据库:MySQL + MongoDB + Redis后端:IDEA前端:微信小程序开发工具 + HBuilderX虚拟_慕课网 emos sql

Spring Data Jpa如何实现审计和乐观锁功能_spring data jpa 如何使用mysql数据库乐观锁-程序员宅基地

文章浏览阅读572次。在具体业务中跟踪数据库数据操作记录有时是很强烈的需求:谁何时创建了这条记录,谁何时修改了这条记录,越是大点的公司这个需求越强烈。这些需求实现较为机械和简单,所以我们不想手动去做,所以很多相关框架都提供了相应的方案,今天我们就看下Spring Data JPA是如何实现的。以前一直在使用MyBatis,现在看来JPA也不赖,关键是平时用的是SpringData JPA,Spring全家桶给你安排的明明白白。这里也不是说MyBatis不好,还是的针对自己组织和项目来取舍。又要过大年了,又要长一岁了…_spring data jpa 如何使用mysql数据库乐观锁

推荐文章

热门文章

相关标签