1. 一维数组
c语言中, 数组占用的空间是一组连续的物理内存空间.
所以对于1维数组来讲,只需要知道数组头1个元素的地址, 就可以逐个地得出各个元素的值了.
例子:
首先定义1个1维数字型数组, 并且赋值.
int A[10]={10,1,2,3,4,5,6,7,8,9};
这里说明一下, c语言中大部分情况下如上面这个语句这样, 定义1个数组的同时就对它赋值了, 这是最方便的.
也可以先定义再赋值.
如
int A[10]; A[0]=10; A[1]=2;........
但是不能用A={10,1,2,3,4,5,6,7,8,9}; 会编译失败~
跟住定义1个指针.
int *p;
这时这个指针p只是1个空指针, 所以下一步就是给这个指针赋值.
p=A; //意思就是将数组A的头部地址赋值给指针p
注意 : 对于一般的变量, 要把它的地址赋值给指针,必须使用寻址符"&", 例如p=&a, 但是对于数组变量来讲, 是不需要寻址符的, 直接p=A就ok了.
原因是A这个数组名字本身是1个指针. 它指向数组A第1个元素A[0]的地址, 下面会详细讲解的啦~
这时指针p的值是A的头1个元素的地址.
printf("A[0] is %d\n", *p); //这时去访问p指针指向的值, 就是指向数组A的第1个元素啊, 所以就是A[0], *p==10了
如果想访问下1个元素的值A[1], 则指针必须指向一下元素的地址:
p+=1; //意思并不是指针的值加1, 而是指指针所指向的地址指向下数组1个元素的地址.同理p+i, 就是指向下i个元素的地址了.
//实际上, 假如p是1个指针, 那么p+i 等价与p+ (i 乘以 p所指向元素的字节长度)
这时再对指针p取值, 就得到A[1]的地值了.
printf("A[1] is %d\n", *p); //同理再执行p+1;, 就得到A[2]的地址...... 一直类推
这时要返回具体某个元素的地址, 则先执行
p=A; //重置p的地址指向A第1个元素.
p=p+i-1; //指向数组A第i个元素A[i]
或者执行p=&A[i] ,因为用取址符号能将数字型变量的地址取出来, 这时p存放的同样是A[i]的地址.
而不能直接p=A[i] 因为A[i]已经是1个数字型变量了.
如果 当p指向数组A最后1个地址时, 例如p当前的值已经是&A[9], 如果再执行p+1, 那么p继续会已相同幅度指向下1个内存地址, 但是那个地址是不属于数组A的, 要注意啊.
写了1个小测试函数:

执行结果如下:

其实, 好明显可以看出只要给p赋值,如上图红色框框
p=A;
要获取任何1个元素A[i]的值,
只要取值*(p+i)就ok
也就是
*(p+i) = A[i]
这个特性会在下面二维数组上讲解的更加深刻
2. 二维数组
二维数组跟1维数组小许区别, 指针的用法也是不同的.
其实二维数组也可以看成1个特殊的一维数组. 只不过1一般的一位数组里面的元素是数字,或者字符串...等一般的类型值. 而这个特殊的一维数组里面的元素是若干个长度相当的一维数组.
也就是说二维数组是1个由相同长度的若干个一维数组组成的数组.
例如1个2维数组B[3][4] 可以看成是由如下3个长度为4的一维数组组成.
B0[0] B0[1] B0[2] B0[3] //1维数组B0
B1[0] B1[1] B1[2] B1[3] //1维数组B1
B2[0] B2[1] B2[2] B2[3] //1为数组B2
2.1 一般的指针
也做个例子:
首先定义1个二位数组并赋值:
int B[3][4] = {{ 0, 1, 2, 3},
{10,11,12,13},
{20,21,22,23}};
跟住定义1个指针:
int *p2;
同样,将指针指向整个数组的第1个元素的地址.
p2=B[0]; //或者p2=&B[0][0]
===================================================================================================
这里有点奇怪, 为何不像之前一位数组那样直接用B来赋值给指针(p2=B;), 而用B[0]呢?
下面解析一下:
a1) 对于一维数组A[4]来讲, 有4个元素, 他们分别是A[0],A[1],A[2],A[3], 数组每1个元素都存放着对应的值.
a1-1) 对于二位数组B[3][4]来讲, 我们可以将它看成1个特殊的1维数组, 这个数组有3个元素, 他们分别是 B[0],B[1],B[2]. 这3个元素分别是3个长度为4的子1维数组 .
a2-2) 对于每1个子数组B[x]来讲, 它包含4个元素, 分别是B[x][0],B[x][1],B[x][2],B[x][3], 同样, 这个4个孙元素都存放这对应的值.
a2-3) 但是! 对于父数组B来讲, 他说包含的3个数组,只是存放了3个子数组的头部地址. 并不是这3个子数组的值啊.
b1) 所以对于一维数组A来讲, p=A; 指针p就指向A的首个元素的地址, 也就是A[0]地址啊. 对p进行取值*p 就是后A[0]的值了.
A的头部地址p 取值后*p与A[0]是等价的
b2) 对于二维数组B来讲, p=B;指针p同样就是指向B的首个元素的地址, 也就是B[0]的地址. 而B[0]的值并不是第1个元素B[0][0]的值, 而是B[0]这个子1维数组的首个元素的地址, 也就是B[0][0]的地址.
也就是说p=B; 的话 ===> *p=B[0] 而*B[0]才是和B[0][0]等价的
所以二位数组, 指针p想获得第1个元素的地址的话, 要执行
P=B[0], 这样*p==*B[0]==B[0][0]
但是个人亲测, 在64位linux系统上, 用P=B; 语句(B是二维数组), 一样可以获得B[0][0]的地址, 不过在编译时会有如下的警告信息.

===================================================================================================
获得二维数组首个元素地址后,就可以取得对应元素的值了.
printf("B[0][0] is %d\n", *p2);
获得下1个元素的地址, 就是B[0][1]的地址
p++;
获得下B[1][2]的地址,当前就地址是B[0][1], 对于B[i][j]来讲, 则i+1, j+1, 而这个二维数组有4个列, 则j+4相当于i+1,所以:
p+=1*4+1; //p+=5;
下面以这个例子编写了1个函数:

执行结果如下图:

2.2 用二维数组名作地址表示数组元素
对于2维数组B[3][4]来讲, 数组名字B其实算是1个指针, 它指向了子一维数组B[0]的地址, 也就是说*B 与B[0]是等价的, 都是1个地址,指向元素B[0][0]的地址.
B----> B[0] ----->B[0][0]
既然B是1个指针, 指向B[0]的地址, 那么指针(B+1) 就指向B[1]的地址了, *(B+1)
也就是说, (B+i )是1个 行数组指针, 指向 子一维数组B[i]的地址, 每当这个指针+1, 那么它就指向下1个子一维数组.
而*(B+i) 就是B[i]的值的了. 而B[i]的值仍然是1个地址, 指向B[i][0]的地址, 所以*(B+i) 与 B[i]一样 仍然是1个指针啊. 它指向B[i][0].
(B+i) --> B[i] -->B[i][0];
*(B+i)
也可以容易看出, 既然B[i] 是1个指针, 那么B[i]也可与进行指针移动运算, 即B[i]+j 也是1个指针, 它指向B[i][j]的地址.
即:
B[i]+j --> B[i][j]
而上面提过 B[i] 就是 *(B+i)啊
so:
*(B+i)+j --> B[i][j]
也就是说 *(B+1)+j 是1个指针, 它指向二位数组B的1个具体元素B[i][j]的地址
所以对这个指针取值 *(*(B+1)+j) (也就是 *(B[i]+j) 因为 *(B+i) == B[i]嘛), 就是元素B[i][j]
亦即系讲加如我们想获取二位数组B里面的1个具体的元素B[i][j]的值, 只需要利用指针*(B+i)+j 就ok啦.
下面就是例子:
首先定义1个二位数组并赋值:
int B[3][4] = {{ 0, 1, 2, 3},
{10,11,12,13},
{20,21,22,23}};
跟住定义1个指针:
int *p3;
这时我们想获取B[1][2]的值, 只需要给指针p3 赋值成 指针*(B+1)+2
p3 = *(B+1)+2
接下来就很方便地获取B[1][2]的值啦~
printf("B[1][2] is %d\n", *p3); //B[1][2] = 12
这时我们又想获取B[2][1]的值, 只需要给指针p3 赋值成 指针*(B+2)+1
p3 = *(B+2)+1
printf("B[2][1] is %d\n", *p3);
感觉很方便啦, 避免了无谓的行列换算啦.
也写了1个函数:

执行结果:

2.3 行数组指针.
上面已经提过,对于二维数组B来讲, 数组名(B+i)是1个指针, 但是(B+i)指向的B[i](也就是*(B+i)) 也是1个指针.
而当你定义1个普通指针p时, p指向的*p 一般是一个值
也就是说指针(B+i) 与 普通指针p 所指向的对象性质不同, 1个是指针, 而另1个是值.
也可以说它们是两个级别不同的指针.
所以说一般我们定义1个指针p 后.
不能直接用(B+i) 给p赋值
例如:
p=B; //B是个二位数组, 如果B是1维数组的话 p=B;似乎正确的, 因为他们是同级别的指针.
p=(B+1);
都是错误的.
======================================================================================
而有一种指针, 它的的级别比一般的指针高一级. 这就是行数组指针.
定义1个行数组指针:
int (*p)[4]; // []里面是4啊
********************************************************************************************
特别说明:
这里的int (*p)[4], 并不是这个行数组指针有4个元素, 这个4代表的是行数组指针p指向的行数组(也就是B的子一维数组里有4个元素)
也就是说这个行指针数组p 是专门for有4个列的二位数组的指针
如果有个二维数组B[3][5], 则对应定义的行指针数组为 int (*p)[5]
就如一维数组的指针*p 一样, 这个(*p)[4] 不是1个数组, 而是1个指针 而这个指针是指向二位数组B的1个子一维数组的.
********************************************************************************************
所以这个高一级别的指针p 跟二位数组B 名字指针是同级别的/
也就是说可以直接用B对其赋值.
p=B; 是正确的.
那么:
p==B --> *B == B[0]
*p --> *B[0] == B[0][0]
*(*p)== *(*(p+0)) == *p[0] == B[0][0] // *(p+i) = p[i] 见文2.2节
*(*(p+0) +1) == *(p[0]+1) == B[0][1]
p+1 = B+1 --> *(B+1) == B[1] //同上 见此文2.2 节
*(p+1) --> *B[1] == B[1][0]
*(*(p+1) ) == *p[1] == B[1][0]
*(*(p+1)+1) == *(p[1]+1) == B[1][1]
通用化:
p+i == B+i --> *(B+i) == B[i]
*(p+i) == *(B+i) --> *B[i] == B[i][0]
*(*(p+i)) == *(*(B+i)) == *p[i] == *B[i] == B[i][0]
*(*(p+i)+j) == *(*(B+i)+j) == *(p[i]+j) == *(B[i]+j) == B[i][j]
由上面红色语句得出
*(p+i)+j == *(B+i)+j --> B[i][j]
就是1个指向 1个具体元素B[i][j] 的指针
而 *(B+i)+j 就是2.2 节那个以行数组名作的指针吗?
而且可以看出p 与 B是等价的 (p=B;)嘛
没错, 二位数组B的名字B 本身就是i个行数组指针...
艹. 我承认自己也很难理解, 我快吐血了!
总之就是:
p[i]--> B[i][0]
p[i]+j --> B[i][j]
所以: *(p[i]+j) = B[i][j] //注意这里的P是1个行数组指针啊
下面又是一个例子:
首先定义1个二位数组并赋值:
int B[3][4] = {{ 0, 1, 2, 3},
{10,11,12,13},
{20,21,22,23}};
跟住定义1个行数组指针:
int (*p4)[3]; //p4是1个指针名字啦,数字4毫无其他意义 注意不要写成 int *p4[3]啊
直接用数组名字给指针赋值, 就如一维数组一样:
p4=B;
接下来就很方便地获取B[1][2]的值啦~
printf("B[1][2] is %d\n", *(p4[1]+2); //B[1][2] = 12
这时我们又想获取B[2][1]的值,
printf("B[2][1] is %d\n", *(p4[2]+1); //B[2][1] = 21
国际惯例又写了1个函数如下:

执行结果:

2.4 指针数组
上面2.2 2.3 节讲的实质上都是行数组指针, 但是c 还有一种叫指针数组的数组, 可以用于二维数组, 真的要吐了!
假如有个二维数组B[3][4]
定义1个指针数组:
int *p[3] //注意跟 行数组指针 int(*p)[3]的区别 定义的语句只差1个括号, 意义就大大不同了.
这就定义了1个指针数组了, 它是1个数组, 成员是3个指针(p[0],p[1],p[2]), 他们可以分别用于指向二维数组B的3个子一维数组.
p[0] = B[0];
那么p[0] 就指向了B[0]这个子一维数组的首元素地址(B[0][0])
所以
*p[0] = *B[0] = B[0][0];
*(p[0]+1) = *(B[0]+1) = B[0][1];
也就是:
*(p[i]+j) = *(B[i]+j) = B[i][j];
p[i]+j --> B[i][j]
又吐一口, 这不是跟行指针数组一样吗?
========================================================================================
而因为我们上面刚刚对 指针数组p执行了赋值 令
p[i] = B[i]; //i=0
首先p[i] 是1个指针, 而B[0]是1个子一维数组,实际上也是1个指针, 它指向B[i][0]的地址
也就是说对于 数组指针p来讲:
p[i]是1个实质上的对象, 他是p数组的1个成员. 他是1个指针
而对于 行数组指针P来讲,
当执行P=B;时
得P+i == B+i ==> *(P+i) = *(B+i)
因为对于行指针数组(B+i)来讲
(B+i)--> B[i](子一维数组)的地址 所以*(B+i)== B[i]的
而又因为p与B是等价的,都是行数组指针
所以 *(P+i)== P[i]
也就是说 P[i] 其实就是 B[i], 也是1个子一维数组,也是1个指针, 指向B[i][0]的地址,
又因为 p[i] = B[i]; //见前15行
所以
P[i] = p[i]
也即是说 对于行数组指针p 和 指针数组P来讲,
虽然它么你的定义方法不同,
但是他们的p[i](P[i])是等价的.
对于指针数组p来讲 p[i] 数组p内的一个元素,是1个普通指针.
而对于行指针数组P来将, P[i] 是通过对行数组指针P+i 执行*(P+i) 取值得出的.也就是P+i 指向地址的内容. 也就是B[i]啦, 而B[i]是1个数组名(子一维数组B[i], 也相当与1个普通指针啦) 所以B[i]与P[i]与p[i]都是等价的. 前提是已经赋值P=B; p[i]=B[i];哦


=======================================================================
最后也写了1个函数

执行结果:
