C语言学习 指针(2)

2023.08.14

通过指针引用数组元素

当指针变量指向数组元素时,数组元素的指针就是数组元素的地址。

int main()
{
    int arr[3]={1,2,3};
    int *parr=&arr[0];//定义一个指向整型变量的指针变量,把arr[0]的地址赋给指针变量parr
    return 0;
}

而在C语言中,数组名代表数组中首元素(即序号为0的元素)的地址,因此在赋值时这两句话等价。

int *parr=&arr[0];  p的值是a[0]的地址

int *parr=arr;        p的值是数组arr首元素(即a[0])的地址

除了这两种情况:

sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的长度

&(数组名),这里数组名表示整个数组的地址

引用数组元素

下标法:a[i]

指针法:*(parr+i) 或 *(arr+i)

指针的运算

指针就是地址,对地址没有必要进行乘除运算,那对于加减运算,意义与步骤该如何理解?

在什么情况下需要对地址进行加减运算呢?

我们知道,数组元素在内存中是连续存放的,当需要对一个数组中的所有元素进行地址使用时,可以依照内存地址的连续存放,对地址进行加减运算从而得到其他数组元素的地址。

在指针已经指向一个数组元素时:

加一个整数(+或者+=) p+1  指向同一数组的下个元素

减一个整数(- 或者 -=) p-1  指向同一数组的上个元素

自加运算 p++

自减运算 p--

两个指针相减(只有p1和p2都指向同一个数组中的元素时才有意义) p1-p2

注意:在执行指针加减运算时,不是单纯的将指针的值(地址)加上/减去1,而是加上一个指针变量所指向变量的类型占用的字节数,即p+1*d(d为一个所指变量类型占用的字节数)。

如果p=&a[0],那么 (p+i) 和 (a+i) 都为 a[i] 的地址,*(p+i) 和 *(a+i) 都为 a[i] 。(等价)

指针相减:p2-p1 的结果并不为单纯的地址相减,而是两个地址之差除以数组元素的长度(一个数组元素所占的字节个数),结果为p2所指的元素和p1所指的元素之间相差的元素个数。

有一个整形数组a,其中有10个元素,要求输出数组中的全部元素。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* parr = arr;
	//等价 int* parr = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
		printf("%d ", *(parr + i));
		printf("%d ", *(arr + i));
		//引用数组中元素有三种方法:下标法,数组名计算数组首元素地址,指针变量指向数组元素
	}
	return 0;
}

//for循环也可以这样写:
//for(p=arr;p<(arr+sz);p++)
//{
//    printf("%d ",*p);
//}
//不可以写成arr++; 因为数组名代表的是数组首元素的地址,是一个指针型常量。
//注意在执行循环之前,注意p的指向要为&arr[0],否则会导致输出错误。

下标法直观,但是运行效率没有直接使用指针变量指向数组元素效率高。

指向数组元素的指针变量也可以带下标(p[i])。

程序编译时,对下标处理是这样的:将下标转换为地址,将p[i]处理成*(p+i)。

使用数组名作为函数参数

在前面也学习有将数组名(数组首元素的地址)作为函数实参传递给调用函数,这里阅读书本,进行一个更加清晰的总结。

实参类型变量名数组名
要求形参的类型变量名数组名或指针变量
传递的信息变量的值

实参数组首元素的地址

通过函数调用能否改变实参的值不能改变实参变量的值能改变实参变量的值

常使用这种方法通过调用函数来改变实参数组的值。

将数组a中的10个整数按相反顺序存放。

void change(int* arr)//指针变量作为形参接收地址,int arr[]也可以
{
	int j = 0;
	int temp = 0;
	for (j = 0; j < 5; j++)
	{
		temp = *(arr + j);
		*(arr + j) = *(arr + 9 - j);
		*(arr + 9 - j) = temp;
	}
}

//下面这个是另一种交换
//void inv(int* arr, int n)
//{
//	int temp = 0;
//	int i =0,j=0 ;
//	int m = (n - 1) / 2;
//	for (i = 0; i <= m; i++)
//	{
//		j = n - 1 - i;
//		temp = arr[i];
//		arr[i] = arr[j];
//		arr[j] = temp;
//	}
//	return;
//}


int main()
{
	int array[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)
		scanf("%d", &array[i]);
	change(array);//将数组名(数组首元素地址)传递过去
	i = 0;
	for (i = 0; i < 10; i++)
		printf("%d ", array[i]);
	return 0;
}

定义实参数组时必须指定数组大小,因为内存要开辟相对应的储存空间,

定义形参数组时可以不指定数组大小,因为形参数组名实际上是一个指针变量。

用指针方法对10个整数按由大到小顺序排序。

void change(int arr[])
{
	int j = 0;
	int k = 0;
	for (j = 0; j < 9; j++)
	{
		for (k = 9; k >=j; k--)
		{
			if (arr[k] < arr[k + 1])
			{
				int temp = arr[k];
				arr[k] = arr[k + 1];
				arr[k + 1] = temp;
			}
		}
	}
}

int main()
{
	int array[10] = { 0 };
	int* p = array;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d", p++);
	}
	change(array);
	for (i = 0,p=array; i < 10; i++)
	{
		printf("%d ", *p++);
	}
	return 0;
}

使用数组名(地址)作为实参变量,在输入与打印时使用指针变量。

另外注意的是,这里按由大到小的顺序输出整型数组元素,在写循环时使用p--比较合适。

指针数组与数组指针

指针数组

顾名思义,指针数组是存放指针的数组,每个数组元素都是指针变量。

数组的命名格式为: 类型说明符 数组名[常量表达式]

指针数组当然也按照这个格式: 类型说服符 * 数组名[常量表达式]

例如:int *p[10]   char *p[2]

使用指针数组可以模拟多维数组

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* parr[3] = { arr1,arr2,arr3 };//指针数组存放的是每个数组的首元素地址
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ",*(*(parr+i)+j));
		}
		printf("\n");
	}
	return 0;
}

如上面的代码,定义三个一维数组,然后定义一个指针数组,利用指针数组可以将这三个一维数组变成一个二维数组。

指针数组里面的数组元素都为数组名(数组首元素地址),在打印的时候,对于%d的指向内容,可以有这两种写法:

下标型:parr[ i ][ j ] 这是引用数组元素时最基本的写法。

指针型:parr是地址

              parr+i 是指针数组中某数组元素的地址 

              *(parr+i) 是指针数组中某元素,这是个地址

              *(parr+i)+j 是某数组中某整型元素的地址

              *(*parr+i)+j) 是某数组中某整形元素

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值