一维数组和指针

声明:1. 文章如有不妥的地方,请您指正,谢谢.

          2.另外文中有些细节可能引用您的内容却未给出参考,请原谅我的疏忽,你的共享我不会忘记.

          3. Email:lizhiguo0532@163.com  李枝果/lizgo

          4. 转载请保留该部分信息

 

对一维数组的指针的理解很关键,这个清楚之后,会对多维数组和指针的理解,复杂指针声明分析都有很大的帮助。

里面涉及的代码都很简单,这里不外乎就是要打印出相关的地址来验证。

 

#include <stdio.h>

int main(void)
{
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    int *p;
    int **pp;
    int ***ppp;
    int (*ap)[10];
    int i;

    p=&a[1];
    pp=&p;
    ppp=&pp;
    ap=&a;
   
    printf("/n");
    printf("p=&a[1];/npp=&p;/nppp=&pp;/nap=&a;/n");
    printf("/n");

    printf("&ap_%%p=%p, &ap_%%x=%x, ap_%%p=%p, ap_%%x=%x/n",&ap,(int)(&ap),ap,(int)ap);
    printf("/n");
    printf("&a_%%p=%p, &a_%%x=%x, a_%%p=%p, a_%%x=%x/n",&a,(int)(&a),a,(int)a);
    printf("/n");
    printf("&a[0]_%%x=%x, &a[0]_%%p=%p, a[0]_%%d=%d/n",(int)(&a[0]),&a[0],a[0]);
    printf("/n");
    printf("&a[1]_%%x=%x, &a[1]_%%p=%p, a[1]_%%d=%d/n",(int)(&a[1]),&a[1],a[1]);
    printf("/n");
    //1.%p是指打印出指针变量的内容,而不是打印某个变量在内存中的地址,如果强行用%p去打印一个普通变量的内存地址时,编译器会报错。
    //2.从利用typedef来定义数组的角度看,数组名就如同一个普通变量名一样,可以对变量名求地址运算,运算结果就是该特殊类型(数组类型)所占据的内存
    //  空间的首字节地址,其基类型很容易看出来,就是对应的数组类型(占据40字节空间)
    //3.单单看数组名的话,c语言里有这样的两句很重要的话:数组名的内容就是其首元素的首地址;数组名的类型就是其元素类型的指针。从这两句话,是不是又可以看出
    //  数组类型和普通类型的差别来吧!至少在名字上就有这两个很大的差别
    //  从2.3点说明可以看出为什么在printf("&a_%%x=%x, a_%%p=%p, a_%%x=%x/n",(int)(&a),a,(int)a);的语句执行后,打印的结果都一样来吧!
    //4.还需要说明一点的是:为什么我们给printf传参数时,传送的明明是一个用&运算得到的地址值,然后用%p打印的时候就可以原样打出地址来呢?%p不是打印出
    //  指针变量的内容吗?这个就是和printf这个函数的实现有关了,简单的说,在printf函数体内部,会创建同类型的指针变量,然后将求地址运算的结果赋值给该指针变量
    //  最后打印
    //
    //
    printf("&p_%%p=%p, &p_%%x=%x, p_%%p=%p, p_%%x=%x/n",&p,(int)(&p),p,(int)p);
    printf("/n");
    printf("&pp_%%p=%p, &pp_%%x=%x, pp_%%p=%p, pp_%%x=%x/n",&pp,(int)(&pp),pp,(int)pp);
    printf("/n");
    printf("&ppp_%%p=%p, &ppp_%%x=%x, ppp_%%p=%p, ppp_%%x=%x/n",&ppp,(int)(&ppp),ppp,(int)ppp);
    printf("/n");

    printf("p+1=%x/npp+1=%x/nppp+1=%x/nap+1=%x/n",(int)(p+1),(int)(pp+1),(int)(ppp+1),(int)(ap+1));

    p=&a[0];
    printf("/n");
    for(i=0;i<10;i++)
    {
        printf("a[%d]=%d/n",i,*((*pp)+i));//利用二级指针引用数组内容,也可以三级指针引用*((*(*ppp))+i) 一级指针引用
    }

    printf("/n");
    for(i=0;i<10;i++)
    {
        printf("a[%d]=%d/n",i,(*ap)[i]);//利用数组指针引用数组内容(*ap)<---->a数组名,甚至可以这样写*((*ap)+i)<--->*(a+i)
    }
    return 0;
}

/*
一个int型变量b ,假设在内存中占据一块空间,4个字节,内容位100,这个变量的内存地址是0xbffff300。
于是&b=0xbffff300,b=100.
这个是理所当然的,每个c人都很熟悉,但是

如果我现在定义了一个数组:int a[10];
这个你怎么看呢?其实c语言中,a也是有其类型的,和int char float double 等类型定义的变量一样,只不过这种类型比较特殊
是数组类型,这里数组类型其实是一个统称了,其实再细分还有一维数组类型,二维数组类型,三维数组类型等,之后还可以细分,有
一维int型数组,一维char型数组,一维float型数组,二维int型数组,二维char型数组等等,在之后呢?这样就可以来吗?no,比如在
一维int型数组之上,还有更细的类型可以区分,你的一维int型数组具有几个元素呢?这个也应该说明。所以到最后,上面数组a就可以说成是
这样的类型:具有10个int型元素的一维数组类型,可以这样表示int [10](在c语言中,从语法的角度来说,在一个定义变量的表达式中,只要
将变量名去掉,剩下的部分就是这个变量的类型来)。

数组类型在内存中也是占据来一段连续的地址空间的,上面的a就是占据来40个自己的空间,我们就可以说a这种数组类型的特殊变量具有40个字节的
空间,那现在来和我们的b比较看看呢?是不是本质是一样的?我们同样可以像对普通变量b一样对其取地址,然后赋值给某个指针变量,对于b来说我
们需要定义一个int *pb=&b就可以来,但是对于数组,我们的数组指针变量该如何定义呢?其实方法一样。
我这里就引出一个很的关键字:typedef
这个关键字以前只知道是类型定义,就是不知道怎么去用,用在那里。不过现在知道了

typedef  int A[10];
A  a;
这两句等价于什么呢?int a[10];
这里看似简单,可后面它的作用就大了
从A  a;从形式上看起来是不是和 int b;一样了
现在就可以通过A这种类型来定义数组指针了
A  *p;
那这一句等价于什么呢?int (*p)[10];
这就是定义了一个数组指针,有的人可能会疑惑,问这里为什么要加括号呢?
这个就涉及到了[]和*的优先级的问题,[]的优先级高于*。如果是这样 int *p[10];那么[]先与p结合组成一个数组,然后数组p
中的每个元素再和*结合组成指针,什么指针呢?当然就是int *(int指针)了。
而在这里我们的本意是想定义一个指针,而不是一个数组,所以必须得让*与p先结合,这里就只有用括号了,之后再和[]结合来说明
该指针所指向的类型。所以加上()就是必须的了。
p=&a;这个表达式就成立了,因为他们的类型(基类型)一样。
说具体一点,&a就相当于对数组这种特殊的变量取地址运算(和对普通变量取地址运算是一样的),那么在赋值之后p就成了一个指向具有
10个int型元素的数组的指针变量。(通常在不影响语法的情况下,地址,指针,指针变量我们都不区分)
*/

//执行结果如下:
lzg@lzg-desktop:~/array_test$ vim array_1.c
lzg@lzg-desktop:~/array_test$ gcc -g -o array_1 array_1.c
lzg@lzg-desktop:~/array_test$ ./array_1

p=&a[1];
pp=&p;
ppp=&pp;
ap=&a;

&ap_%p=0xbf86c9f0, &ap_%x=bf86c9f0, ap_%p=0xbf86c9c4, ap_%x=bf86c9c4
//&ap是ap数组指针变量的地址,ap是数组指针的内容(指向的空间首地址)
&a_%p=0xbf86c9c4, &a_%x=bf86c9c4, a_%p=0xbf86c9c4, a_%x=bf86c9c4
//&a是取数组a这个特殊类型变量的地址,和普通变量取址一样,后面两者就要考虑数组名的特殊性了。上面程序注释里的两句重要的话
&a[0]_%x=bf86c9c4, &a[0]_%p=0xbf86c9c4, a[0]_%d=1
//证实了数组名的内容是其首元素的首地址,数组名是一个指针的事实,可以通过a_%p=0xbf86c9c4里面的%d得到证实
&a[1]_%x=bf86c9c8, &a[1]_%p=0xbf86c9c8, a[1]_%d=2

&p_%p=0xbf86c9fc, &p_%x=bf86c9fc, p_%p=0xbf86c9c8, p_%x=bf86c9c8

&pp_%p=0xbf86c9f8, &pp_%x=bf86c9f8, pp_%p=0xbf86c9fc, pp_%x=bf86c9fc

&ppp_%p=0xbf86c9f4, &ppp_%x=bf86c9f4, ppp_%p=0xbf86c9f8, ppp_%x=bf86c9f8
//多级指针的关系
p+1=bf86c9cc    //
pp+1=bf86ca00    //pp的基类型是int *,arm平台下占据4字节,所以+1操作,就只加上4
ppp+1=bf86c9fc
ap+1=bf86c9ec   //ap的基类型是具有10个int型元素的数组,这个+1操作就是加上了40
//指针的操作,p+/-n,这个在数值上实际上是增加或者减少了  n*sizeof(p的基类型) 这么多。
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
a[5]=6
a[6]=7
a[7]=8
a[8]=9
a[9]=10

a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
a[5]=6
a[6]=7
a[7]=8
a[8]=9
a[9]=10
lzg@lzg-desktop:~/array_test$
//无论是定义数组指针int (*)[10]还是数组元素类型的指针int *(int **或者int ***),对数组的引用操作上都没什么区别,只是在对所定义的指针操作上面存在差异

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值