#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+icol+j),必须要知道列,
所以需要手动访问*(array+i*col+j)
3.甚至可以用一维指针
void f(int * array)
情况同2
因为本质上二维数组就是一个一维数组,只要知道第0号元素的地址,就行了
二维指针先分配空间再进行操作