C Primer Plus学习笔记(三)数组和指针

#include <stdio.h>

int main(){
    int a[3][3][3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                         11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
                         21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
                         31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
                         41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
                         51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
                         61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
                         71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
                         81};
    for(int i1=0;i1<3;i1++){
        for(int i2=0;i2<3;i2++){
            for(int i3=0;i3<3;i3++){
                for(int i4=0;i4<3;i4++){
                    printf("%p a[%d][%d][%d][%d] %d\n",&a[i1][i2][i3][i4],i1,i2,i3,i4,a[i1][i2][i3][i4]);
                }
            }
        }
    }
    return 0;
}
0x16f877614 a[0][0][0][0] 1
0x16f877618 a[0][0][0][1] 2
0x16f87761c a[0][0][0][2] 3
0x16f877620 a[0][0][1][0] 4
0x16f877624 a[0][0][1][1] 5
0x16f877628 a[0][0][1][2] 6
0x16f87762c a[0][0][2][0] 7
0x16f877630 a[0][0][2][1] 8
0x16f877634 a[0][0][2][2] 9
0x16f877638 a[0][1][0][0] 10
0x16f87763c a[0][1][0][1] 11
0x16f877640 a[0][1][0][2] 12
0x16f877644 a[0][1][1][0] 13
0x16f877648 a[0][1][1][1] 14
0x16f87764c a[0][1][1][2] 15
0x16f877650 a[0][1][2][0] 16
0x16f877654 a[0][1][2][1] 17
0x16f877658 a[0][1][2][2] 18
0x16f87765c a[0][2][0][0] 19
0x16f877660 a[0][2][0][1] 20
0x16f877664 a[0][2][0][2] 21
0x16f877668 a[0][2][1][0] 22
0x16f87766c a[0][2][1][1] 23
0x16f877670 a[0][2][1][2] 24
0x16f877674 a[0][2][2][0] 25
0x16f877678 a[0][2][2][1] 26
0x16f87767c a[0][2][2][2] 27
0x16f877680 a[1][0][0][0] 28........

​ 上面这组输出告诉了我们高维数组的变量地址是怎样变化的。arr[1][0]的前一个地址是arr[0][n](n的意思是最后一维也就是arr[0][0][0]这一层的时候,它的最后一个数据arr[0][0][0][n]的后一个实际上是arr[0][0][1][0]).

​ 那么就是说,每一层维度的数组的最后一个元素的后继元素(如果还在数组里面的话),是下一层数组的第一个元素。

​ 这些也其实说明了,在C内存中,所有的数组其实都是,无论是否高维,在内存中都是一维数组。

这是一个多维数组的指针

#include <stdio.h>
#define SIZE 4


int main(){
    int arr[SIZE][SIZE][SIZE] = {{{1,2,3,4},
                                 {1,2,3,4},
                                 {1,2,3,4},
                                 {1,2,3,4}},

                                 {{1,2,3,4},
                                  {1,2,3,4},
                                  {1,2,3,4},
                                  {1,2,3,4}},

                                 {{1,2,3,4},
                                  {1,2,3,4},
                                  {1,2,3,4},
                                  {1,2,3,4}},

                                 {{1,2,3,4},
                                  {1,2,3,4},
                                  {1,2,3,4},
                                  {1,2,3,4}}};
    int (*p1)[4][4]=arr;
    printf("arr[0]=%p,  arr[1]=%p,  arr[2]=%p,  arr[3]=%p\n",arr[0],arr[1],arr[2],arr[3]);
    printf("p1=%p,      p1+1=%p,    p1+2=%p,    p1+3=%p\n",p1,p1+1,p1+2,p1+3);
    printf("*p1=%p,     (*p1)+1=%p, (*p1)+2=%p, (*p1)+3=%p\n",*p1,(*p1)+1,(*p1)+2,(*p1)+3);
    printf("    %p              %p          %p          %p",arr[0][0],arr[0][1],arr[0][2],arr[0][3]);
}
arr[0]=0x16b4db658,  arr[1]=0x16b4db698,  arr[2]=0x16b4db6d8,  arr[3]=0x16b4db718
p1=0x16b4db658,      p1+1=0x16b4db698,    p1+2=0x16b4db6d8,    p1+3=0x16b4db718
*p1=0x16b4db658,     (*p1)+1=0x16b4db668, (*p1)+2=0x16b4db678, (*p1)+3=0x16b4db688
    0x16b4db658              0x16b4db668          0x16b4db678          0x16b4db688

​ 首先可以看到使用int (*p1)[4][4]=arr指向了这个三位数组的第一层,也就是说这个指针所指向的了arr[0], 并且此时对p1使用加法的话,那么p1+1 就相当于arr[1]。可以从上面的输出中看出这一点。

​ 然后之所以要使用int (*p1)[4][4]=arr这样的方式去定义是为了告诉编译器p1这个指针在进行比如加法,减法之类的操作的时候,应该移动几个自己大小,在这里可以看出它移动了64,因为arr[1]与arr[0]之间相差了64个字节,更加细节的讲,就是arr[1]这个二维数组中储存了16个int数据,每个int类型数据占4个字节所以占用了 16 ∗ 4 = 64 16*4=64 164=64个比特。

下面是一些骚操作

假设arr[4][4]

arr 相当于&arr[0]
arr+2 相当于&arr[2]
*(arr+2) 相当于 arr[2] 相当于 &arr[2][0]
*(arr+2)+1相当于 &arr[2][1]
*(*(arr+2)+1) 相当于arr[2][1]

下面是示例

#include <stdio.h>


int main(){
    int zippo[4][2] = {{2,4},{1,3},{6,8},{5,7}};
    int(*pi)[2] = zippo;
    printf("pi=%p zippo=%p\n\n",pi,zippo);
    printf("*(pi+2)=%p,*(pi+2)+1=%p\n",*(pi+2),*(pi+2)+1);
    printf("%p %p\n\n",&zippo[2][0],&zippo[2][1]);
    printf("*(*(pi+2))=%d,*(*(pi+2)+1)=%d\n",*(*(pi+2)),*(*(pi+2)+1));
    printf("%d %d\n",zippo[2][0],zippo[2][1]);
}
pi=0x16f593740 zippo=0x16f593740

*(pi+2)=0x16f593750,*(pi+2)+1=0x16f593754
0x16f593750 0x16f593754

*(*(pi+2))=6,*(*(pi+2)+1)=8
6 8

下面这个历史是关于一个指向int的指针和一个指向指向的指针

#include <stdio.h>


int main(){
    int zippo[3][2] = {{1,2},{3,4},{5,6}};
    int a=2;
    int *p1 = &a;
    printf("%p\n",p1);
    int **p2 = &p1;
    *p2 = zippo[0];
    printf("%d\n",**p2); //**p2 == zippo[0][0]
    printf("p1=%p,zippo[0]=%p\n",p1,zippo[0]);
}
0x16d2ff74c
1
p1=0x16d2ff750,zippo[0]=0x16d2ff750

可以看到*p2实际上是p1的内存的内容,现在修改有*p2 = zippo[0]相当于p1=zippo[0].因此p1的指向发生了改变

void somefunction(int arr[][n])这样声明的函数说明,它的形参二维矩阵,且它的的内存的那个一维数组有n个int 类型。

当然第一个中括号中也可以填元素,但是编译器会忽。填上的好处是有助于告诉阅读代码的人,这里的输入的要求。

第一个方括号用于告诉编译器这里是一个指针,后面的方括号及其内容是在告诉编译器这个指针的类型。

复合字面量

#include <stdio.h>


int main(){
    int *ptl1;
    ptl1 = (int [2]){10,20}; //这是一个复合字面量(int [2]){10,20}
    printf("%p",ptl1);
    printf("\n%d, %d, %d",*ptl1,*(ptl1+1),ptl1[1]);
}
0x16f3cf760
10, 20, 20

之所以可以用ptl1[1]是因为数组实际上就是指针。在这里你可以理解为ptl1相当于一个数组,其指向了int类型,所以也是指针。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值