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语言

  • 13
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,可以使用循环和下标来逐个打印数组的元素。下面是一个示例代码: ```c #include <stdio.h> int main() { int arr\[5\] = {1, 2, 3, 4, 5}; for (int i = 0; i < 5; i++) { printf("%d ", arr\[i\]); } return 0; } ``` 在这个示例中,我们使用了一个for循环来遍历数组,并使用下标`i`来访问数组的每个元素。然后使用`printf`函数打印出每个元素的值。输出结果将会是`1 2 3 4 5`。\[1\] 另外,你也可以使用指针来打印数组的元素。下面是一个示例代码: ```c #include <stdio.h> int main() { int arr\[5\] = {1, 2, 3, 4, 5}; int *p = arr; for (int i = 0; i < 5; i++) { printf("%d ", *(p + i)); } return 0; } ``` 在这个示例中,我们定义了一个指针`p`,并将其指向数组`arr`的首地址。然后使用指针的算术运算来访问数组的每个元素,并使用`printf`函数打印出每个元素的值。输出结果也将会是`1 2 3 4 5`。\[1\] 希望这个回答对你有帮助! #### 引用[.reference_title] - *1* [输出数组元素的方法(含指针)](https://blog.csdn.net/m0_69012020/article/details/127798389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C语言数组——字符数组](https://blog.csdn.net/csdn_wangchong/article/details/104364693)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [[C语言]数组的传递](https://blog.csdn.net/qq_43559301/article/details/129219719)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值