9.C基础_指针与数组

数组指针(一维数组)

数组指针就是" 数组的指针 ",它是一个指向数组首地址的指针变量。

1、数组名的含义

对于一维数组,数组名就是一个指针,指向数组的首地址。

基于如下代码进行分析:

cpp 复制代码
int a[5] = {1,2,3,4,5};
int* p = a;
  • a是数组a5的数组名,在这里a是个常量,一定指向的是a5的首地址,不能进行" ++ "改值运算。
  • p是一个指针变量,它指向了a5的首地址,所以它是一个数组指针。在这里p是一个变量,可以进行" ++ "改值运算

2、数组名索引与指针索引

当我们想要访问1这个值的时候,可以有a0、*(a+0)、*(p+0)、p0 这四种访问形式。

  • a0:基本的下标法访问
  • *(a+0):a可以看作首地址,偏移量为数组的类型int大小,找到地址后解引用也可访问1这个值
  • *(p+0):p保存了数组首地址,偏移量为int大小,通过解引用访问地址
  • p0:特殊,记住即可

注意:*(p+0)、p0这两种方法是指针偏移的方法,它实际表示的值与p指向的位置有关。比如p指向的是a1这个位置,那么*(p+0)、p0访问的不是a0而是a1,而*(p-1)、p-1访问的才是a0

3、数组名与数组元素取地址

数组名a与数组元素取地址&a i 它们的值一样,偏移量也一样。但数组名表示整个数组,数组元素取地址仅仅是一个指针。下面分析" a、&a0区别 "、" a+1、&a0+1 区别"

a、&a0的相同点:

  • 地址值相同、偏移量也相同。
  • 都是常量,都不能进行++等变值操作。

a、&a0的不同点:

  • a不仅仅指向数组首地址,还可以代表数组;
  • &a0只是一个指针,什么都不代表。

4、&a是什么

一维数组可以看成列指针,取地址之后变为行指针。

&a的作用与二维数组名类似:a看成列指针,偏移一个数据元素,&a看成行指针偏移一行。

数组指针(二维数组)

1、一维指针遍历二维数组

由于二维数组的元素在内存中是按行序进行排列的,内存排列依旧是连续的。所以在内存方面,它与一维数组没有任何区别,具体的二维数组内存分配如下:

遍历的代码如下:

cpp 复制代码
#include <stdio.h>

/* 计算二维数组总大小 */
#define ARRAY2_SIZE(array,type) 	sizeof(array)/sizeof(type)
/* 计算二维数组中一维数组大小(列大小) */
#define ARRAY2_COL_SIZE(array) 		sizeof(array[0])
/* 计算二维数组的列数 */
#define ARRAY2_COL_NUM(array,type)	ARRAY2_COL_SIZE(array)/sizeof(type)
/* 计算二维数组的行数 */
#define ARRAY2_ROW_NUM(array,type) 	sizeof(array)/ARRAY2_COL_SIZE(array)

int main(){
	
	int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
	int *p = NULL; 
	int col,row;
	int i;
	
	col = ARRAY2_COL_NUM(a,int);
	row = ARRAY2_ROW_NUM(a,int);
	p = &a[0][0];//注意这里的赋值,p赋值的是a[0][0]的地址,而不是直接赋值数组名a 
	
	for(i=0;i<row*col;i++){
		printf("%d %d\n",*(p+i),p[i]);
	} 
	
	return 0;
}

上述代码有一个注意点,p=&a00而不是p=a。在二维数组中,尽管&a00的值与a相同,都是指向数组首地址,但是它们的偏移量不同。

2、二维数组的数组指针

二维数组名是一个指向二维数组的首地址的常量。它是一个代表二维数组,偏移量以行为单位。例如:int a23它每3列为一行,假设首地址为0x00,那么a+1 = 0x00 + sizeof(int) * 3。

指向二维数组的数组指针被称为" 行指针 "。它的定义形式为:<数据类型>(*变量名)列数,如上述的int a23,它的数组指针为:int (*p)3 = a。

3、二维数组元素分析

二维数组可以看成两部分组成:二维数组由多个一维数组组成,一维数组由多个数据组成。

所以二维数组名解引用之后得到的是一维数组名,一维数组名解引用后得到的是数据元素。

即:*a+i = ai,&a0 = a

a 与 &a0 的区别

相同点:

  • 地址值一样,偏移量一样。
  • 都是常量,都不能进行++等变值操作。

不同点:

  • a不仅仅指向数组首地址,还可以代表数组;
  • &a0只是一个指针,sizeof(&a0)返回的是指针大小,而不是二维数组大小

下面是用二维数组的数组指针来遍历的代码:

cpp 复制代码
#include <stdio.h>
/* 计算二维数组总大小 */
#define ARRAY2_SIZE(array,type) 	sizeof(array)/sizeof(type)
/* 计算二维数组中一维数组大小(列大小) */
#define ARRAY2_COL_SIZE(array) 		sizeof(array[0])
/* 计算二维数组的列数 */
#define ARRAY2_COL_NUM(array,type)	ARRAY2_COL_SIZE(array)/sizeof(type)
/* 计算二维数组的行数 */
#define ARRAY2_ROW_NUM(array,type) 	sizeof(array)/ARRAY2_COL_SIZE(array)
int main(){
	
	int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
	int (*p)[4] = a;
	int row,col;
	int i,j; 
	
	row = ARRAY2_ROW_NUM(a,int);
	col = ARRAY2_COL_NUM(a,int);
	for(i=0;i<row;i++){
		for(j=0;j<col;j++){
			printf("%-2d ",*(*p+j));//p是二维数组指针,*p就是a[i]即一维数组的指针,偏移量为int 
		}
		printf("\n");
		p++;//p是二维数组指针,偏移量为sizeof(int)*col即按行偏移	
	} 	
	return 0;
} 

上述代码中也可以用一个指针来寻址:例如a34,寻址a21只需访问&a00+2*4+1

练习:使用a来访问a21这个元素

首先需要偏移到第2行,之后偏移到第1列,最后取值

  • 偏移到第2行:a+2
  • 偏移到第1列之前需要先转换成一维数组名:*(a+2)即a2
  • 偏移到第1列:*(a+2)+1
  • 取值:*(*(a+2)+1)

4、&a是什么

二维数组的&a分析方法与一维数组的分析类似。

  • 在一维数组中,我们把a看作列地址,&a看成行地址。
  • 在二维数组中,我们把整个二维数组看成列地址,代表偏移量为一行。&a看成行地址,代表偏移量为一整个二维数组。

数组指针(字符串)

字符串是一维数组的一种形式,数组指针的性质与一维数组的数组指针性质完全一样。

下面分析字符串常量与字符串变量的区别:

指针直接赋值字符串,而不是字符数组名时,该指针指向的就是字符串常量。

cpp 复制代码
/* p指向字符串常量,不可修改值 */
char* p = "Hello";

/* p指向字符串变量,可以修改值 */
char a[] = "hello";
char*p = a;

相同点:

  • 都是存放的字符串的首地址,都可以用%s进行打印

不同点:

  • 对于char* p = "Hello";因为它是一个常量,所以不允许修改,即:*p = 'a'是不合法的。
  • 对于char* p = a;因为它是一个变量,所以允许修改,即:*p = 'a'是合法的。
相关推荐
世辰辰辰19 分钟前
批量修改图片/文本名子
开发语言·python·批量修改文件名
雨白1 小时前
哈希:以时间换空间的算法实战
算法
z落落2 小时前
C# 四种特殊类:抽象类、密封类、静态类、部分类
开发语言·c#
caimouse3 小时前
Reactos 第 4 章 对象管理 — 4.5 几个常用的内核函数
c语言·windows·架构
VidDown3 小时前
Webhook 调试器:让第三方回调“原形毕露”
java·开发语言·javascript·编辑器·postman
San813_LDD3 小时前
[数据结构]LeetCode学习
数据结构·算法·图论
x138702859573 小时前
c语言排雷游戏(基础版9*9)
c语言·算法·游戏
装不满的克莱因瓶3 小时前
基于 OpenResty 扩展开发实现动态服务注册与发现能力
java·开发语言·架构·openresty
caimouse3 小时前
Reactos 第 4 章 对象管理 — 4.3 句柄和句柄表(Handle & Handle Table)
c语言·windows·架构