【C语言】_指针与数组

目录

[1. 数组名的含义](#1. 数组名的含义)

[1.1 数组名与数组首元素的地址的联系](#1.1 数组名与数组首元素的地址的联系)

[1.3 数组名与首元素地址相异的情况](#1.3 数组名与首元素地址相异的情况)

[2. 使用指针访问数组](#2. 使用指针访问数组)

[3. 一维数组传参的本质](#3. 一维数组传参的本质)

[3.1 代码示例1:函数体内计算sz(sz不作实参传递)](#3.1 代码示例1:函数体内计算sz(sz不作实参传递))

[3.2 代码示例2:sz作为实参传递](#3.2 代码示例2:sz作为实参传递)

[3.3 结论](#3.3 结论)

[4. 指针数组](#4. 指针数组)


1. 数组名的含义

1.1 数组名与数组首元素的地址的联系

从值的角度来看,数组名 = 数组首元素的地址,即arr = &arr[0] ;

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

int main() {
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("&arr[0] = %p\n", &arr[0]);
	return 0;
}

运行结果如下:

1.3 数组名与首元素地址相异的情况

大多数情况下,数组名=数组首元素地址,但是有两个例外:

1、sizeof(数组名):此时数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小(单位为B)

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

int main() {
	int arr[10] = { 0 };
	printf("sizeof(&arr[0]) = %d\n", sizeof(&arr[0]));
	printf("sizeof(arr) = %d\n", sizeof(arr));
	return 0;
}

运行结果如下:

2、&(数组名):此时数组名也表示整个数组,&(数组名)得到的是整个数组的地址

(虽然在值上体现为相同,但本质/指针类型是不同的)

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

int main() {
	int arr[10] = { 0 };
	printf("&arr[0] = %p\n",&arr[0]);
	printf("arr     = %p\n", arr);
	printf("&arr    = %p\n", arr);
	printf("---------------------\n");
	printf("&arr[0]+1 = %p\n", &arr[0]+1);
	printf("arr+1     = %p\n", arr + 1);
	printf("&arr+1    = %p\n", &arr + 1);
	return 0;
}

运行结果如下:

由于指针+1的具体跨度与指针类型有关:

&arr[0]与arr均表示数组首元素地址,指针类型为int*,故指针+1则跳过4B;

&arr表示数组的地址,指针类型为数组指针,故指针+1则跳过整个数组即10×4B=40B;

2. 使用指针访问数组

代码示例1:

cpp 复制代码
int main() {
	int arr[10] = {0};
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++) {
		scanf("%d", p + i);
	}
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(p+i));
	}
	return 0;
}

输出结果为:

代码示例2:

cpp 复制代码
int main() {
	int arr[10] = { 0 };
	int* p = arr;
	int* start = p;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++) {
		scanf("%d", p);
		p++;
	}
	for (int i = 0; i < sz; i++) {
		printf("%d ", *start);
		start++;
	}
	return 0;
}

运行结果为:

代码示例3:

cpp 复制代码
int main() {
	int arr[10] = { 0 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++) {
		scanf("%d",p+i);
	}
	for (int i = 0; i < sz; i++) {
		printf("%d ",arr[i]);
	}
	return 0;
}

运行结果为:

注:关于下标引用操作符:

理解数组元素与指针解引用的对应:arr [ i ] 即 * ( arr + i ),其中 [ ] 为下标引用操作符,

编译时,arr [ i ] 会被处理为* ( arr + i ),实际上i [ arr ] 也会被处理为* ( i + arr );

3. 一维数组传参的本质

现要求编写程序,使用函数实现一维数组arr的元素打印;

3.1 代码示例1:函数体内计算sz(sz不作实参传递)

cpp 复制代码
void Print(int arr[10]) {
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(arr + i));
	}
}
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	Print(arr);
	return 0;
}

运行结果如下:

调试如下:

可见在函数Print内计算sz出现错误,从而导致函数功能未能正确实现;

分析代码如下:(见注释)

cpp 复制代码
void Print(int arr[10]) {  
	// 形参arr[10]:数组传参时,可以写为数组形式;但其本质是指针变量,而非完整的数组
	int sz = sizeof(arr) / sizeof(arr[0]);  // sz求得指针变量大小=1
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(arr + i));
	}
}
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	Print(arr);  // 实参arr:数组首元素地址
	return 0;
}

3.2 代码示例2:sz作为实参传递

cpp 复制代码
void Print(int* arr, int sz) {
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(arr + i));
	}
}
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	Print(arr, sz);
	return 0;
}

3.3 结论

1、数组传参的本质是传递了数组首元素的地址

2、数组传参时,形参可以写为数组形式,但本质仍是指针变量,而不是完整的数组,故而建议将形参写为指针形式而非数组形式:

cpp 复制代码
void Print(int* arr, int sz) {
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(arr + i));
	}
}

3、数组传参时,形参的数组不会单独再创建数组空间,故形参的数组可省略数组大小,即形参可写为以下形式:

cpp 复制代码
void Print(int arr[], int sz) {  
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(arr + i));
	}
}

4、数组传参时,数组大小sz必须在函数体外计算并作为参数传递给函数

4. 指针数组

类比整型数组,即存放整型变量的数组;字符数组,即存放字符变量的数组;

指针数组即存放指针变量的数组;

现利用指针数组模拟二维数组:

cpp 复制代码
int main() {
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* arr[3] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 5; j++) {
			/*printf("%d ",*(arr[i]+j));*/
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

注:(1)对于模拟实现的二维数组的元素访问,arr [ i ] [ j ]等价于 * ( arr [ i ] + j),编译时编译器会将数组元素的访问解析为指针运算;

(2)真正的二维数组是在内存中逐行连续存放的,本例中模拟实现的二维数组仅实现了每一个子一维数组的连续存放,并不是真正的二维数组;

相关推荐
软件黑马王子1 小时前
C#初级教程(4)——流程控制:从基础到实践
开发语言·c#
闲猫1 小时前
go orm GORM
开发语言·后端·golang
李白同学3 小时前
【C语言】结构体内存对齐问题
c语言·开发语言
楼台的春风4 小时前
【MCU驱动开发概述】
c语言·驱动开发·单片机·嵌入式硬件·mcu·自动驾驶·嵌入式
黑子哥呢?4 小时前
安装Bash completion解决tab不能补全问题
开发语言·bash
青龙小码农4 小时前
yum报错:bash: /usr/bin/yum: /usr/bin/python: 坏的解释器:没有那个文件或目录
开发语言·python·bash·liunx
大数据追光猿4 小时前
Python应用算法之贪心算法理解和实践
大数据·开发语言·人工智能·python·深度学习·算法·贪心算法
彳卸风5 小时前
Unable to parse timestamp value: “20250220135445“, expected format is
开发语言
dorabighead5 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
风与沙的较量丶6 小时前
Java中的局部变量和成员变量在内存中的位置
java·开发语言