C语言 指针*

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll long long
#define MAXN 1000
#define ENTER printf("\n")
//传递二维数组,形参是二维指针
void print(int** ptr)
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", *(ptr+4*i+j));
		}
		ENTER;
	}
}
//传递二维数组,形参是数组指针(指向数组的指针)
void print1(int a[][4])
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", *(*(a+i)+ j));
		}
		ENTER;
	}
}
void print2(int* ptr)
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", *(ptr + 4 * i + j));
		}
		ENTER;
	}
}
int main()
{ 
	int test0[] = { 0,1,2,3 };
	int test1[][4] = { {0,1,2,3}, {4,5,6,7}, {8,9,10,11}, {12,13,14,15} };
	int(*p)[4];
	p = &test0;
	/*
	printf("指针p本身的地址%d\n", &p);                     //相当于开辟一个叫p的内存单元,里面存放一个地址
	printf("指针p里的内容%d\n", p);                        //这个和下面相等,但是意义不同,p是个指向数组的指针,即p指向一个元素,所以p的内容应该是
														   //这个元素的地址,这里a不再是数组首元素的地址,而是代表这个数组本身,把整个数组当成一个元素
														   //例如int * b =a 这里a是首地址,但是sizeof(a)这里a就代表里整个数组,总不可能对计算一个地址的大小吧
														   //要知道一个元素的地址,自然就是&a,结果也得到p里的内容是个地址。
	printf("对指针p里的内容进行解引用%d\n", *p);           //代表对p这个内存单元里的内容进行解引用,自然会得到一个元素,而这个元素是数组,自然不能访问整个数组
														   //所以只能是,数组的首地址

														   //只要记住p是数组的首地址应该就可以了,p+i是第i个数组的首地址
	

	printf("test0的地址%d\n", &test0);                     //由于上面的p是我们声明的,内存会实实在在的分配变量,但是test0只是在内存上分配连续的四个空间填上
	                                                       //0,1,2,3。所以并没有为test0分配实际变量。对他取地址就是test0的值
	printf("test0+1的地址%d\n", &test0 + 1);               //&test0+1于数组首地址的值相差16,可以“看作”是test0声明了一个变量为“数组”,它的地址是&test0,
	                                                       //则下一个地址就是+16
	printf("test0的内容%d\n", test0);
	printf("test0的解引用%d\n", *test0);


	printf("test1的地址%d\n", &test1);                     //同上
	printf("test1的地址+1%d\n", &test1+1);               //+64
	//printf("test1+1的地址%d\n", &(test1 + 1));
	printf("test1的内容%d\n", test1);
	printf("test1的解引用%d\n", *test1);
	printf("test1的两次解引用%d\n", **test1);        
	*/

	int* q = (int*)test1;
	printf("q的内容%d\n", q);
	printf("q的解引用%d\n", *q);
	//printf("q的两次解引用%d\n", **q);
	print2((int *)test1);


		return 0;
}

关于&,*的个人理解
为什么&test1+1代表的就是整个二维数组呢,而 * test1 却代表的是地址呢?
可以这样理解
当使用&运算的时候,作用的一定是个对象,比如一个int类型的a,一个double 类型的b,但是在这里 可以看成作用的是一个二维的数组“对象”,这个二维数组对象大小是64个字节,所以当用&test1可以获得这个二维数组“对象“的地址,比如收说是x,则&test1+1,可以获得这个”对象“的”末地址“,也就是x+64,这个也可以在上面的test0一维数组中得到验证。而如果想要输出&(test1+1),则会提示报错,因为test1是指向第一行数组的数组指针是个常量,不能加1,同样的也可以按照上面的来理解,test1可以看成是一个数组”对象“,但是test1+1就不知道是什么了。

而当使用* 运算时,运算的对象一定是个地址,所以对test1进行* 运算时,由于编译器把二维数组看成是多个数组指针(可以向函数里传递一个二维数组,会显示test1的类型为int(*)[]),则对test1进行解引用是看成是指向一个数组元素的指针,结果就是”一个数组元素“,自然”我无法访问“一个数组元素,于是就被隐式转换成了该数组的首地址(等同于一维数组a[]的a)

那为什么通过(int **)把test1,从数组指针强制转换成了二维数组指针就不行呢,因为就如同在上面代码中的那样,直接对test1进行两次解引用,能够获得test1[0][0]处的值,但是强制转换后就不行了。为什么?
可以这样理解,首先二维数组本质上还是一维数组,像上面的有16个元素,每个元素有个地址,a[0][0]是第一个元素,他有一个地址t,而这个地址t是不能改变的。经过上面分析可知,&test1,test1,*test1值是一样的,都是这个地址t。所以如果强制转换了,int ** q=(int **) test1 则对其进行第一次解引用获得t地址下单元里的内容m,再进行一次解引用获得m地址下单元的内容k,而这个k就是我们所希望的二维指针所最终指向的内容。而实际上,这个被转换成了二维指针,脱离了二维数组这个概念,就只能是个单纯的”二维指针“它所代表的就是上述t,m,k这个含义,所以回到二维数组中,通过地址t获得元素0,但是地址0是被包含起来的,不能被访问的。所以就不行了

同时在记录一下二维数组传递给函数的方法
1.void f(int array[][5])
这种情况实际上就是指明了有5个数组指针
在这种情况下函数内可用array[i][j]访问数组
也可用*((array+i)+j)访问
主函数内可以通过
f(array)调用
2.void f(int *array)
这种情况下就要知道列,因为编译器访问a[i][j]实际上执行的是
(a+i
col+j),必须要知道列,
所以需要手动访问*(array+i*col+j)
3.甚至可以用一维指针
void f(int * array)
情况同2
因为本质上二维数组就是一个一维数组,只要知道第0号元素的地址,就行了

二维指针先分配空间再进行操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值