(3)指针和数组

在c中指针和数组似乎有着千丝万缕的关系。事实上它们不是一回事:指针是指针,数组是数组。两者不同样。

说它们有关系,只是是由于常见这种代码:

int main()
{
int array[] = {1,2,3,4,5};
int n = sizeof(array) / sizeof(int);
int *p = array;
int i;
for (i = 0; i < n; i++)
printf("p[%d]...%d\n", i, p[i]);
system("pause");
return 0;
}

执行

在上面的代码中,指针和下标运算符的结合使用,给人一种指针和数组是一样的感觉。

本质是:数组名是一个指向数组起始元素的常量指针。这也是数组和指针的唯一联系!

之所以能够使用 p[i] 来訪问数组中的元素,是由于在编译器中 p[i] 被解释为 *(p+i),这仍然是指针的功能。

对编译器而言,用p[i]表达*(p+i)的含义是没有意义的。它仅仅是为了让人看着舒服。用着方便。这是语法糖

p[i]是*(p+i)的简单写法,实际上。至少对于编译器来说,[]这种运算符全然能够不存在。

但是对于人类来说。*(p+i)的写法在解读上比較困难,写起来也麻烦(键入量大)。因此,c语言引入[]运算符。

就像这样。这些不过为了让人类easy理解而引入的功能,的确能够让我们感受到编程语言的甜蜜味道(easy着手),有时我们称这些功能为语法糖(syntax sugar 或者 syntactic sugar)。

以上摘自《征服c指针》。借此推荐这本书。书中一针见血地指出:仅仅有在声明语句中。[]才表达数组的含义。在表达式中,[]与数组无关!

总结起来就是。看似数组的使用方法:p[i],事实上是*(p+i)的语法糖。p仍然是指针,与数组并无关系。

指针和数组的不同之处,还能够从以下的样例看出

void fun(int array[5])
{
printf(" sizeof(array)...%d\n", sizeof(array));
}
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
printf(" sizeof(array)...%d\n", sizeof(array));
fun(array);
return 0;
}

执行

从执行结果看,函数形參尽管用数组的方式进行了声明,但仍然被当做指针。这揭示了c语言中传递数组时的规则:传递过去的是地址,是指向数组起始元素的地址。之所以这样,基于两点;

  1. 从效率上考虑。若是把整个数组赋值过去,太耗时,也耗空间。还不如传地址过去,使用同一份内容。
  2. 在c语言设计之初,赋值操作就仅限于基本类型(char、int、float……),而数组是聚合类型。
这给我们的编程启发是:传递数组时,不要忘了把数组大小也传递过去。否则,函数那边因为不知道数组大小,极易越界。

应这样设计函数 void fun(int *array, int n),n是数组大小。

另一点须要指出。即使函数被设计成void fun(int array[5], int n),array依旧被看成是指针。

也就是说即使数组带了长度。该长度也会被编译器忽略掉。一句话:形參中的数组统统看成指针。

既然如此,还不如直接写成void fun(int *array, int n)。指针的形式。更能表达本意。


再思考:假设p[i]是*(p+i)的意思。因为加法具有交换律:p+i=i+p,那么i[p]相同能够表达p[i]的意思,是这种吗?实验验证:
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
int n = sizeof(array) / sizeof(int);
int *p = array;
int i;
for (i = 0; i < n; i++)
printf(" %d[p]...%d\n", i, i[p]);
return 0;
}

执行


实验证明,我们的猜想是正确的:p[i]确实是*(p+i)的语法糖。i[p]这种写法是否非常逆天呢!

总结:仅仅有在函数形參中,仅有这一种情况,声明的数组。如 int array[]会被看作是指针。其他情况下,指针与数组并无联系。

&array的含义
另一点,对于 int array[5];array表示指向数组起始元素的指针,那么&array又是什么呢?实验下:
int main()
{
int array[] = { 1, 2};
printf(" array...%p\n", array);
printf(" &array...%p\n", &array);
printf("&array+1...%p\n", &array+1);
return 0;
}

执行


分析实验结果:0031FCEC与0031FCE4相差8。而sizeof(array)就是8。
结论就是:array和&array都是指针。但类型不同。array的类型是int*。而&array的类型是int(*)[2]。

array是指向普通int类型的指针;&array是数组指针,该数组元素是int类型的,且数组大小是2。

至于array和&array两者的值是一样的,应该非常好理解。

补充
标量(scalar):简单讲,标量就是指char、int、double和枚举类型等数值类型,再加上指针。

至于数组、结构体和共用体这样将多个标量进行组合的类型,我们称之为聚合类型(aggregate)。

那么为什么int(*)[2]表示的是数组指针呢?这须要透彻理解c的声明语法。又比方。二维数组(更甚者,多维数组)的数组名又是什么类型的指针呢?这须要了解c中数组的实际含义,后序解说。

很多其它指针和数组的内容见(5)c数组本质

专栏文件夹:

娓娓道来c指针 (3)指针和数组的更多相关文章

  1. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  2. 图解c/c++多级指针与“多维”数组

    声明:本文为原创博文,如有转载,请注明出处.若本文有编辑错误.概念错误或者逻辑错误,请予以指正,谢谢. 指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的.其中,多级指针与“多维”数组更 ...

  3. C++多级指针与多维数组详细介绍

    多级指针的概念 多级指针可对应于多维数组,这种指针变量中存的是另一个指针变量的地址,其说明如下:    int val=10;    int *ptr=&val;    int **pptr= ...

  4. 指针的指针&指向指针数组的指针

    一.指针的指针    指针的指针看上去有些令人费解.它们的声明有两个星号.例如:        char ** cp;    如果有三个星号,那就是指针的指针的指针,四个星号就是指针的指针的指针的指针 ...

  5. C/C++ 一段代码区分数组指针|指针数组|函数指针|函数指针数组

    #include<stdio.h> #include<stdlib.h> #include<windows.h> /* 举列子说明什么是函数指针 */ //以一个加 ...

  6. o4.数组指针和指针数组的区别

    ------- android培训.iOS培训.期待与您交流! ---------- 我们看一下数组指针和指针数组: 数组指针(也称行指针)定义 int (*p)[n];()优先级高,首先说明p是一个 ...

  7. Linux C 程序指针和指针数组(NIGH)

    指针和指针数组 #include<stdio.h> int main() { , b = ; int *p1 = &a , *p2 = &b ; printf(" ...

  8. 通过数组初始化链表的两种方法:指向指针的引用node *&tail和指向指针的指针(二维指针)node **tail

    面试高频题:单链表的逆置操作/链表逆序相关文章 点击打开 void init_node(node *tail,char *init_array) 这样声明函数是不正确的,函数的原意是通过数组初始化链表 ...

  9. c - 对数组进行排序(通过指针的指针)

    通过指针的指针,以及一个指针数组,对实际数组元素进行排序,有一个优点,就是排序过程交换的只有指针数组中的值,而不是实际的数组的元素.当实际元素中的对象很大,特别是结构体等类型时,这样做是很有好处. 下 ...

随机推荐

  1. [CSS]overflow内容溢出

      定义和用法 overflow 属性规定当内容溢出元素框时发生的事情. 说明 这个属性定义溢出元素内容区的内容会如何处理.如果值为 scroll,不论是否需要,用户代理都会提供一种滚动机制.因此,有 ...

  2. QQ登录接口(第三方登录接口)

    CI框架 QQ接口(第三方登录接口PHP版) 本帖内容较多,大部分都是源码,要修改的地方只有一个,其他只要复制过去,就可以完美运行.本帖主要针对CI框架,不用下载SDK,按我下面的步骤,建文件,复制代 ...

  3. coder

    #include <iostream>#include <GL/glut.h>using std::cout;using std::endl;float windowWidth ...

  4. mongoengine教程1

    mongoengine安装过程,建议先安装好pip,pip是不Python不错的安装包管理器,安装命令:pip install mongoengine. mongoengine是mongodb的pyt ...

  5. C#中获得汉字的首拼音(简化版)

    利用汉字在计算机里面的编码来得到汉字的首拼音: static public string GetChineseSpell(string strText) { int len = strText.Len ...

  6. 2016022613 - redis连接命令集合

    redis连接命令 1.ping 用途:检查服务器是否正在运行 返回数据pong,表示服务器在运行. 2.quit 用途:关掉当前服务器连接 3.auth password 用途:服务器验证密码 没有 ...

  7. Lambda表达式, 可以让我们的代码更优雅.

    在C#中, 适当地使用Lambda表达式, 可以让我们的代码更优雅. 通过lambda表达式, 我们可以很方便地创建一个delegate: 下面两个语句是等价的 Code highlighting p ...

  8. (Step1-500题)UVaOJ+算法竞赛入门经典+挑战编程+USACO

    http://www.cnblogs.com/sxiszero/p/3618737.html 下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年 ...

  9. MVC自学系列之一(MVC入门篇)

      MVC是如何适用于ASP.NET中的 自从2002年ASP.NET 1.0版本的首次发布,就知道ASP.NET和Web Forms是同样一回事.ASP.NET由抽象两层类所支持: -->Sy ...

  10. 如何从 Xcode 控制台输出 JavaScript 的 log?

    调试 UIWebView 中的 JavaScript 一直以来都是很痛苦的一件事.通常我们会通过下面的方法调试 HTML 和 JavaScript. 1.第一种,使用桌面浏览器调试.大多数现代浏览器都 ...