《C专家编程》第四章——令人震惊的事实:数组和指针并不相同
数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点。
首先来看指针与数组在声明上的区别:
int a[10];
int *p;
很明显的,第一个是数组a,第二个是指针p。下一个问题是a的类型是什么?p的类型是什么?a[0]的类型是int,而a是个数组名,它是否表示整个数组呢?事实并非如此,a是一个指针常量,是一个指向int的指针常量,而p是一个指向int的指针,是一个变量。这是它们的第一个区别:一个是常量,一个是变量。那么常量和变量之间存在的区别,它们都存在。比方说,变量可以相互赋值,而常量则不行,如下面这个例子:
int a[10];
int b[10];
int *c;
a与b均是数组名,为常量指针,而c是一个变量,所以你可以对c进行赋值,比如说c = &a[0],或者c = a,都没有问题,可是考虑一下b = a吧,一下子就会发现这条语句是非法的,如果你要复制一个数组,只能一个变量一个变量的用循环赋值。
第二个重要的区别是两者的访问方式不同,尽管它们的访问形式看起来都可以用下标引用。
char a[] = "helloworld";
char *p = "helloworld";
比如在上面两个定义中,如果你查看a[3]的值,那么肯定是 'l',但是同样的你也可以查看p[3],答案同样也是 ‘l’,不过访问的方式稍有区别:
首先看到a[3],它等同于*(a + 3),我们假定a这个常量指针的地址为1000,那么数组在内存中可能是这样子的:
直接代入a的地址看看,那么就是*(1000 + 3),就是1003号地址,显而易见的就是字符 ’l‘。再来看看指针访问的方式:
这里面的“Helloworld”是一个字符串常量,系统会为它分配一片内存,2345这个地址是随机分配的,指针的地址1032也是随机的,让指针p指向一个常量字符串就是将地址1032的内容改为字符串常量的首地址,那么现在来看p[3]的意思,首先要取p的地址中的内容也就是2345,然后将下标所表示的偏移量加上指针的值,产生一个地址,那么就是2345+3,这个地址就是2348,之后在再对2348这个地址进行访问,从而得到字符 'l'.
用char说明可能比较简单,在这里用char作为类型会让你忽略一个细节,如果使用int的话,这个细节将会被你注意到,那就是它们计算地址的方式,比方说还是以上面的例子,不过这次换成int作为类型。
int a[10];
int *p;
同样考虑a[3]与p[3],这下子问题就来了,char在内存中刚好占一个字节,而int,一般来说,在32位机器上占了4个字节,那么a[3]是否还等于*( a + 3)呢?那明显不对,因为假定a[0]的地址为1000,那么a[1]的地址就是1004了,a + 3的值为1003的话应该取多少呢?答案是,在这里应该这么计算,它等于:
*( a + 3 * int)
要乘以一个类型长度,所以它实际上就是*( a + 3 * 4),取的地址是1012,。
那么对于p[3]呢?也是一样的,由于指针的类型不同,指针也会要乘以一个int的长度值。
经过对两种访问方式的探讨,我们可以发现,如果你把声明和定义搞混了,那是要出问题的,比如说,你定义了一个数组char p[10],但是你在其他文件使用它的时候对它进行了这样的声明extern char *p,那么你就有大麻烦了,由于数组和指针的访问方式不同,你的数组中的那个值可能要被作为一个地址,把一个数值当成一个地址,再去取里面的值,它是一个垃圾值。
当然,除了访问方式的不同,它们保存数据的方法也有区别:
仍然如上例,当你声明定义一个数组时,它会实际为你分配一片区域;而你声明定义一个指针时,它只为你分配指针变量的内存,指针所指向的值并不分配内存,并且这种情况只对字符有用,比如说你可以这样:char *p = "helloworld",但是,你却不能这样:float *p = 3.14,这条语句是非法的。
主要区别就在于上面的几点,另外下面来看看它们之间的相同之处:体现得最明显的一点就在于当数组名作为函数的参数传递时:
int function(char a[]);
像上面这条语句肯定是没有问题的,它说明一个字符数组被作为参数传递给函数function,不过它居然没有说明数组的大小呢?仔细想想,你就会觉得这个用法相当机智,有下面几点原因:首先这说明所有的数组无论大小,都可以作为同一个函数的参数,你想想,如果处理只有两个元素的数组和只有三个元素的数组都要编写不同的函数,那将会是一个相当恐怖的事情;
后面的优点则要基于它的传递行为,上面那个语句等价于下面这个:
int function(char *a);
这能够很好的应用于函数的一个性质:所有的参数传递均为传值调用,也就是说形参都只是实参的一份拷贝,在function这个函数里,由于传递的参数是指向原数组a的一份指针拷贝,所以你可以放心的使用a++之类的语句,而不用担心编译器会警告你修改数组名。
从上面的比较可以看出,指针和数组之间关系密切,两者既相似又有很大的区别,所以大家使用时一定要小心,上面只是列出它们之间的主要区别和主要共同点,至于有其它的一些关系,大家可以参考其他资料。
《C专家编程》第四章——令人震惊的事实:数组和指针并不相同的更多相关文章
- [转]Windows Shell 编程 第四章 【来源 http://blog.csdn.net/wangqiulin123456/article/details/7987933】
第四章 文件的本质 以前,所有文件和目录都有一个确定的属性集:时间,日期,尺寸,以及表示‘只读的’,‘隐藏的,‘存档的’,或‘系统的’状态标志.然而,Windos95(及后来的WindowsNT4.0 ...
- 《C专家编程》第二章——这不是Bug,而是语言特性
无论一门语言有多么流行或多么优秀,它总是存在一些问题,C语言也不例外.本章讨论的重点是C语言本身存在的问题,作者煞费苦心的用一个太空任务和软件的故事开头,也用另一个太空任务和软件的故事结尾,引人入胜. ...
- 读高性能JavaScript编程 第四章 Duff's Device
又要开始罗里吧嗦的 第四章 Summary 了. 这一次我尽量精简语言. 如果你认为 重复调用一个方法数次有点辣眼睛的话 比如: function test(i){ process(i++); pr ...
- 读高性能JavaScript编程 第四章 Conditionals
if else 和 switch && 递归 if else 和 switch 一般来说,if-else 适用于判断两个离散的值或者判断几个不同的值域.如果判断多于两个离散 ...
- Windows核心编程 第四章 进程(下)
4.3 终止进程的运行 若要终止进程的运行,可以使用下面四种方法: • 主线程的进入点函数返回(最好使用这个方法) . • 进程中的一个线程调用E x i t P r o c e s s函数(应该避免 ...
- Windows核心编程 第四章 进程(上)
第4章 进 程 本章介绍系统如何管理所有正在运行的应用程序.首先讲述什么是进程,以及系统如何创建进程内核对象,以便管理每个进程.然后将说明如何使用相关的内核对象来对进程进行操作.接着,要介绍进 ...
- 【读书笔记】C#高级编程 第四章 继承
(一)继承的类型 1.实现继承和接口继承 在面向对象的编程中,有两种截然不同的继承类型:实现继承和接口继承. 实现继承:表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数.在实现继承中, ...
- 《C专家编程》第一章——C:穿越时空的迷雾
这一章讲的是C语言的发展史,包括它是多么不经意的诞生,而后又经历了早期C.K&C.ANSI C的各种阶段,直到它现在形成的这个样子.C语言从来不是一门完美的语言,所以它一直在发展,直到今日,它 ...
- windows核心编程---第四章 进程
上一章介绍了内核对象,这一节开始就要不断接触各种内核对象了.首先要给大家介绍的是进程内核对象.进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象就是与进程相关联的一个数据结构.操作系统内核通过 ...
随机推荐
- curl方法post一个数组
$r = $this->curl_post($url, $data);$list = json_decode($r,true); function curl_post($url = '', ...
- OpenCV中的矩阵操作
函数 Description 说明 cvAdd Elementwise addition of two arrays 两个数组对应元素的和 cvAddS Elementwise addition of ...
- iOS 8 以后 MKMapView 代理不执行问题
因为ios8上,如果你使用了mapView,必须设置mapView的大小,必须把它添加显示在视图上. 可以将frame设为(0,0,0.1,0.1)
- 2014优秀的好用的20款免费jQuery插件推荐
2013 年最好的 20 款免费 jQuery 插件,世界买家网 www.buyerinfo.biz在这里分享给大家 这些插件对设计者和开发者都有很大的帮助,希望大家能够喜欢:) 现 今互联网市场上提 ...
- jQueryNotes仿QQ空间添加标记
jquery-notes有以下特点: 支持添加备注图像 丰富的API 支持标记伸缩 支持更改主题 支持图片标记添加链接 不需要数据库 HTML 首先在页面上放置一张添加标志的图片 <div cl ...
- C++的STL
今天,看一段代码的时候发现只一句话就做了个排序,是这样的: sort(rotateArray.begin(),rotateArray.end()); 很震惊,后来查了一下sort的用法, sort函数 ...
- gantt甘特图的制作过程
甘特图主要是用来做项目管理的,可以清楚的看到任务间的逻辑关系,任务与时间关系和任务间并行关系. 在甘特图中,横轴方向表示时间,纵轴方向并列着活动列表.图表内可以用线条.数字.文字代号等来表示计划(实际 ...
- htop基本使用
一.什么是htop? top是所有类unix系统的必备工具,能直观方便的查看到系统负载.内存及进程等信息. 而htop具有top工具的全部功能且还新增了一些额外的功能和使用体验改进.与top相比,其具 ...
- linux命令:chmod
1.命令介绍: chmod用来改变系统文件或目录的相关权限,可读,可写,可执行等. 2.命令格式: chmod [选项] 模式 文件 3.命令参数: 必要参数:-c 当发生改变时,报告处理信息-f 错 ...
- Asp.net Web.Config - 配置元素 httpCookies
1.属性 属性 说明 domain 可选的 String 属性. 设置 Cookie 域名. httpOnlyCookies 可选的 Boolean 属性. 脚本(javascript之类)能够访 ...