之前在写C的时候,没怎么留意数组。就这么定义一个。然后颠来倒去的使用即可了。只是后来碰到了点问题。解决后决定写这么一篇博客,数组离不开指针。索性就放一起好了。

如今我定义了一个数组:int cc[10];

环绕这个数组有好几种指针:cc, cc+1, &cc[0], &cc, &cc+1等等。你知道它们都是什么含义吗?试试执行下面带代码:

#include <stdio.h>

int main()
{
    int cc[10];     printf("%x\n", cc);
    printf("%x\n", cc+1);
    printf("%x\n", &cc[0]);
    printf("%x\n", &cc);
    printf("%x\n", &cc+1);     getchar();
    return 0;
}

cc,这是学数组时第一个接触的"指针",最为熟悉,它是数组的首个元素。

cc+1,这是指向数字第二个位置的指针。

&cc[0],这个事实上就是cc,指向数组的首个元素。

&cc。这是什么玩意儿?指向指针的指针?

&cc+1,假设上面的意思是指向指针的指针。那这个岂不是指向野地址了?

如果执行环境是32位机。而且数组首地址为0x28ff00。那么:

cc的结果为0x28ff00,这点毫无疑问。

cc+1的地址是0x28ff04而不是0x28ff01,由于一个int占用了4个字节的空间。cc+1事实上是当成cc+1*sizeof(int)来看待。

&cc[0]的结果是0x28ff00,cc[0]表示的是数组的首个元素。那么&cc[0]自然就是首个元素的地址了。&cc[0] == cc。

&cc,这个就难说了。指针cc的值是0x28ff00,&cc表示这个指针本身的地址,我们怎么可能会知道这个地址?输出是个随机地址吗?随机数的话这个输出全然没有意义啊。假设不是随机地址的话,难不成还是0x28ff00?这种话a不就等于&a了?明显不正确吧。

对于基本类型的指针,如int *tt; 那么*tt是其值,&tt是指针的地址,&tt != tt

可是上述的cc是个数组。实际上。&cc被编译成了&cc[0],可是其含义不同,&cc指向的是整个数组的开头。

&cc与cc的指向能够用下图来形象表示:

上图能够看出,&cc事实上代表的是int(*)[10],那么&cc+1就能够理解为cc + sizeof(cc)/4,之所以除以4是由于int型指针++事实上是移动了4个字节。

又或者说%cc == cc + sizeof(cc)/4 == cc + 10。所以&cc+1的值为0x28ff28。

可见我们寻常使用的数组名,并不能单纯的当成指针看待。数组名的本质是代表数组对象的变量名,是一个左值,是一个不能被改变的左值。

可是因为在程序中不保存数组的大小,所以通过数组名仅仅能訪问数组的左值。不能訪问数组的右值。

因为这个原因,数组名在作为右值使用的时候被赋予另外一个新的意义——指向数组第一个元素的指针。这就是array-to-pointer转换规则。

依据标准规定。仅仅有当数组名作为sizeof、&运算符的操作数的时候。它是一个左值,其类型为数组类型。除此之外的全部情况,数组名都是一个右值,被编译器自己主动转换为指针类型,这样的情况下我们就说数组名是一个指针。而且是一个指针常量。

接下来是另外一些有趣的东西,我们结合sizeof与数组输出各类值。

下面程序的输出结果是什么?建议思考后再执行程序来验证答案。

#include <stdio.h>

int main()
{
int cc[10];
printf("%d\n", sizeof(cc[0]));
printf("%d\n", sizeof(cc));
printf("%d\n", sizeof(&cc));
printf("%d\n", sizeof(int(*)[10])); getchar();
return 0;
}

sizeof(cc[0]),一个int的大小。输出4,没问题。

sizeof(cc),注意不要和上面搞混。这不是数组首地址的指针,cc在这里是左值。其为数组类型。所以结果为40。

sizeof(&cc),这个的答案应该是多少呢?注意了。cc在这里还是左值,其为数组类型,但&cc不同于cc,无论数组怎么复杂它始终是个指针,32位机上指针大小始终是4个字节。所以结果为4。

sizeof(int(*)[10]),这个事实上和上面的&cc是一个样的,代表了整个数组,但还是指针,所以结果相同为4。

练习:以下的程序输出结果是什么(如果32位机):

#include <stdio.h>

int main()
{
int *p[2];
printf("%d\n", *p);
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(&p)); getchar();
return 0;
}

答案是:随机数、8、4

再来一个练习:下列程序的输出是什么?

#include <stdio.h>

int main()
{
char str[]="hactrox";
char *p = str;
printf("%d %d\n", sizeof(str), sizeof(p)); getchar();
return 0;
}

答案:8、4(不要忘了'\0')

C语言事实上不简单:数组与指针的更多相关文章

  1. C语言核心之数组和指针详解

    指针 相信大家对下面的代码不陌生: int i=2; int *p; p=&i;这是最简单的指针应用,也是最基本的用法.再来熟悉一下什么是指针:首先指针是一个变量,它保存的并不是平常的数据,而 ...

  2. C语言学习004:数组与指针

    在C语言中,字符串实际上就是字符数组,在内存中字符串"Shatner"存储的形式是这样的

  3. 关于c语言二维数组与指针的个人理解及处理办法。

    相信大家在学习C语言时,对一维数组和指针的理解应该是自信的,但是,我在学习过程中,看到网上一些博文,发现即便是参加工作的一些专业编程人员,突然碰到二维数组和指针的问题时,也可能会遇到难以处理的诡异问题 ...

  4. C语言多维数组的指针传递

    在C语言中为了节省空间,提高运行速度经常使用指针来完成数组的传递. 对于一维数组而言可以直接传递首地址 而对于二维数组必须在传递时声明是二维数组的指针,并且调用时也要经过一些运算 首先是定义形参: 函 ...

  5. C语言 二维数组(指针)动态分配和释放(转)

    C 二维数组(指针)动态分配和释放 先明确下概念: 所谓32位处理器就是一次只能处理32位,也就是4个字节的数据,而64位处理器一次就能处理64位,即8个字节的数据.如果我们将总长128位的指令分别按 ...

  6. C语言之一维数组与指针

    一维数组: 假如有一维数组如下: ]; 该数组有3个元素,数据类型为char型,地址空间如下. 如果想访问数据,直接使用a[0].a[1].a[2]取出相应地址空间的值即可 一级指针: 指针即地址,c ...

  7. C语言中的数组和指针以及字符串

    数组名同时也是该数组首元素的地址,而指针提供了一种用来使用地址的符号方法,因此指针能够很有效地处理数组. 将一个整数加给指针,这个整数会和指针所指类型的字节数相乘,然后所得的结果会加到初始地址上 da ...

  8. C语言事实上不简单:sizeof

    问:C语言中一共同拥有多少个keyword? 答:32个. 答不上来的没关系.非常正常.我们玩的是程序的艺术.而不是背数字. 只是这个特殊的数字1<<5也是非常好记的-.-. 问:size ...

  9. C语言中的数组与指针

    1. 数组的初始化 数组的初始化方法有很多,常用的方法有 定义时初始化 ]={,,}; 或 ]={}; //未初始化的元素全为0: 定义后遍历赋值初始化 int arr[3]; for(int i=0 ...

随机推荐

  1. HDU_1237_简单计算器

    运算符为+,-,*,/:操作数为整数:且没有括号 设定符号优先级,先在栈底压运算符0 #include<iostream> #include<cstdio> #include& ...

  2. linux 后台进程

    1.进程放入后台 ctrl+z 进程放入后台 暂停执行 2.进程放入后台执行 bg % n 或者 bg n 进程放入后台执行 3.进程取出前台执行 fg % n 或者 fg n 进程取出前台执行 4. ...

  3. 谷歌全屏脚本 start chrome.exe --kiosk http://www.baidu.com

    start chrome.exe --kiosk http://www.baidu.com

  4. slover层解读

    void Solver<Dtype>::UpdateSmoothedLoss(Dtype loss, int start_iter, int average_loss) { if (los ...

  5. [SQL]数据分析SQL笔试题

    SQL笔试题 1.请简单写出left join和join的用法区别(可举例说明): 2.求出订单表(order表)中每个客户(custid)的最近一次购买日期(要求:按custid降序排列,trans ...

  6. 第3节 mapreduce高级:7、自定义outputformat实现输出到不同的文件夹下面

    2.1 需求 现在有一些订单的评论数据,需求,将订单的好评与差评进行区分开来,将最终的数据分开到不同的文件夹下面去,数据内容参见资料文件夹,其中数据第九个字段表示好评,中评,差评.0:好评,1:中评, ...

  7. group 和 gshadow

    group组文件 位置:/etc/group 作用:存放用户的分组信息 使用 /etc/group 命令查看时,得到的数据如下: 分析上图,可以得到以下结果 第1个字段:组名 默认组名与用户名名称一样 ...

  8. Spring 实现 IoC

    理解 “ 控制反转(IoC)”   控制反转(IoC):用白话来讲,就是由 Spring 容器控制程序中类与类之间的关系,而非传统实现中,由程序代码直接操控.这也就是所谓 “控制反转” 的概念所在:控 ...

  9. 零基础入门学习Python(13)--元组:戴上了枷锁的列表

    前言 这节课我们讨论主题是元祖:我们有个小标题戴上了枷锁的列表 我们都知道早在300多年前,孟德斯鸠在变法的时候说过,一切拥有权力的人都容易被滥用权力,这是万古不变的一条经验.但是呢,凡是拥有大权利的 ...

  10. 腾讯ISUX网站的一个小问题

    腾讯isux网站的一个小问题. 它的网站:http://isux.tencent.com/?variant=zh-hans     优秀的网站和差的网站的距离往往就在于细节.   浏览环境:谷歌.   ...