C51学习笔记,数组和指针的程序设计

发布者:落霞与孤鹜最新更新时间:2018-01-03 来源: eefocus关键字:C51  数组  指针 手机看文章 扫描二维码
随时随地手机看文章

终于说到了指针。指针是C语言的精华部分,如果没有指针,c语言对底层的许多操作将无法完成。也是因为指针的存在,使得c语言看起来并不那么高级,因为指针操作的对象的是内存地址,想要熟练地进行指针操作,必须考虑到内存等偏硬件方面的东西。当然,也不需要了解过多。但是,数据结构这一关还是要过的。我对数据结构方面了解尚浅,就不多说了。数组与指针的关系如此复杂,让我不得不照着书来写这一篇笔记了。

一、数组不等于指针

C语言中,对数组的操作,是仿照指针的模式进行的。但是需要记住一点,数组不等于指针。对于一维数组a[],指向数组的指针p=a来说,他们之间最大的区别在于,数组方式使用数组名a(同时也是数组的首地址)对数组进行直接的访问和操作,而指针方式使用指针名p对数组进行的是间接的访问和操作。在多数情况下,他们操作结果是相同的,但是也有例外。

如果我们在文件外定义了一个指针p,int *p=a(a是一个整型数组)。在文件内用到p时,需要用extern声明一下,表明p是个外部变量,在外部定义好了。如果我们声明为指针 extern int * p,然后去使用,肯定是没有问题的。但是如果我们声明为数组extern int p[] ,问题就出现了,编译系统处理的时候会使用数组方式对指针进行操作。也就是说,当我们想得到*p,也就是*a,a[0]的值的时候,因为系统把p当作了数组首地址,所以*p并不能得到a[0],得到的是p所存放的地址值,也就是a[0]的地址。这种情况下,一定要使extern声明与定义相匹配。

数组的直接访问数据模式与指针的间接访问数据模式,是两者之间最根本的不同。数组不等于指针。数组通常用于存储固定数目且数据类型相同的元素,数组所占用的内存是隐式分配和删除的,数组中保存数据,并且数组中的每个元素都有唯一且明确的变量名来标识数据,使用数组可以直接访问数据也就是说a[i]只是简单地以a+i为地址取得数据。而指针通常用于动态数据结构,指针变量保存的是数据的地址(其中包括变量的地址,也包括不匿名数据),使用指针访问数据采用的是间接访问模式,即首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果指针有下标,p[i]就是先去的指针p的内容,然后把指针p的内容加上i作为地址,从中提取数据。指针可以指向匿名数据,所以要学会用指针操作匿名内存,c语言中与内存空间相关的函数为malLOC(), free()。

在定义指针时,编译器并不为指针所指的对象分配内存空间,只是分配指针本身的空间,除非在定义字符指针(必须是指向字符型的)的同时用字符串常量进行初始化。其实就算是这种情况,也可当作是编译器为此字符串常量分配了内存空间后,在为字符指针本身分配了空间,并使字符指针指向字符串常量的首地址。在ANSI C中,初始化指针所创建的字符串常量被定义为只读(?这一点,我用turbo C试了试,好像可以修改)。数组也可以用字符串常量进行初始化,但与指针襄樊,由字符串常量初始化的数组是可以修改的,原因很简单,由字符串常量初始化的数组本来就是一个字符数组,每个字符都有确定的变量名与之对应,所以当然可以修改单个字符了。而初始化指针所创建的字符串常量,其实是匿名的数据,如果你把指向字符串常量的字符指针赋予了其他地址,这个字符串常量显然再也找不到了。

由此可见,数组和指针在编译器处理时是不同的,在运行时的表示形式也是不一样的,并且可能产生不同的代码。对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。所以,在外部数组的声明时,在数组的定义(因为数组的定义必然要分配内存空间)时,不能用指针来替代数组。

还有,在下列情况下,对数组的引用不能用指向该数组第一个元素的指针来替代:

1. 数组名作为sizeof()的操作数,因为此时需要的是整个数组的大小,而不是指针所指向的第一个元素的大小;

2. 使用&操作符取数组的地址。&操作符的主要用途是实现传址调用。指针本身就是地址,所以对指针使用&意义不大。

3. 数组是一个字符串常量初始值。这一点上面已经提到,不多说了。字符串常量初始化数组必须一气呵成,不能分成两了语句。也就是说,字符串常量只能对数组进行声明初始化,不能用字符串常量对数组赋值。这点是因为C语言中只能够在数组声明时对它进行初始化(这点没啥原因,也许是因为数组名是不可修改的左值吧),不能对数组名赋值,只能对数组元素逐个赋值。

4. 数组名是不可修改的左值,它的值是数组第一个元素的地址,不可以改变。

二、什么时候数组与指针相同

大多数的时候,数组和指针可以互换。

1. 除了个别情况,表达式中的数组名(数组在使用中,而不是声明中)就是指针

假如我们声明:int a[10], *p, i=2;

就可以通过以下方法来访问a[i] :

a[i];

*(a+i);

p=a ; p[i] ;

p=a ; *(p+i) ;

p=a+i ; *p ;

实际上,对数组的引用如a[i]在编译时总是被编译器改写成*(a+i)的形式。所以如加法一样,取下标操作符的操作数是可以交换的(a[i] 与i[a] 都是正确的,等价的)。

编译器自动把下标值得步长调整到数组元素的大小。对起始地址执行加法操作之前,编译器会负责计算每次增加的步长。这就是为什么指针总是有类型限制,每个指针只能指向一种类型的原因所在。

2. 数组下标总是与指针的偏移量相同

数组下标总是与指针的偏移量相同,所以程序员完全可以使用指针来访问数组,从而绕过下标操作符。在这种情况下,对数组下标范围检查并不能检测所有对数组的访问情况。因此,C语言并不进行下标范围检测。但是我们编写程序的时候,可要小心,不要越界。

在处理以为数组时,指针并不比数组更快。C语言吧数组下标改写成指针偏移量的根本原因是指针和偏移量是底层硬件所使用的基本模型。

3. 作为函数参数的数组名等同于指针

吧作为形参的数组和指针等同起来是出于效率原因的考虑。在函数参数的声明中,数组名被编译器当中指向该数据第一个元素的指针。编译器只向函数传递数组的地址,而不是整个数字的拷贝。隐性转换意味着下面三种函数定义形式是完全相同的:

fun(int *p){...}

fun(int p[]) {...}

fun(int p[10]) {...}

在函数的声明和调用上,使用数组或者指向数组第一个元素的指针都是合法的。

注意,这里第一个元素的含义,这第一个元素可以是数值变量、字符变量、数组、结构体、指针。

三、总结

数组不等于指针,但是数组可以用指针等效;指针始终是指针,指针绝对不可以改写为数组。你可以使用下标方式访问指针,一般是指针作为函数参数时,一般是指针指向的是数组元素时。

在外部声明和定义时,数组不能拿指针来等效,除了数组作为函数参数之外,定义和声明必须匹配。

在使用时,数组基本可以与指针互换。a[i]总是被编译器解释为*(a+i)。

在作为函数参数时,数组可以与指针互换。编译器总是把函数参数的数组修改为指向数组第一个元素的指针。在函数内部获得的事实上都是一个指针。

c语言实际上没有多维数组。只有数组元素是数组的数组。多维数组作为函数参数,传递的指针类型是指向数组的指针。


关键字:C51  数组  指针 引用地址:C51学习笔记,数组和指针的程序设计

上一篇:C51程序设计中的运算符应用
下一篇:C51程序设计中的数组和指针关系

推荐阅读最新更新时间:2024-03-16 15:51

深入理解C51对标准ANSIC的扩展
深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一。因为大多数扩展功能都是直接针对8051系列CPU硬件的。大致有以下8类: 8051存储类型及存储区域 存储模式 存储器类型声明 变量类型声明 位变量与位寻址 特殊功能寄存器(SFR) C51指针 函数属性   具体说明如下(8031为缺省CPU)。 第一节 Keil C51扩展关键字 C51 V4.0版本有以下扩展关键字(共19个): _at_ idata sfr16 alien interrupt small bdata large _task_ Code bit pdata using reentrant xdata compact sbi
[单片机]
C51编译器-语言扩展(1)-存储器模型和存储类型
Cx51对ANSI标准C进行了扩展。这些扩展的大部分是为了支持8051系统的构架。这些扩展有: 8051上的存储器类型和区域 l 存储器模型 l 存储器类型指示 l 变量类型指示 l 位变量和可位寻址数据 l 特殊功能寄存器 l 指针 l 函数属性 关键字 _at_ alien bdata bit code compact data idata interrupt large pdata _priority_ reentrant sbit sfr sfr16 small _task_ using xdata 你可以通过N
[单片机]
指针式万用表有什么特点
  指针式万用表的品种、型号较多,但其工作原理却基本相同或大同小异,它的最大显示特点就是由表头指针指示所测量的数值。   (1)测量电压准确度方面。万用表一般可测量从几百毫伏至几百伏甚至几千伏的直流和交流电压,其测量准确度直流为±2.5%,交流为±4.0%。它的频率范围通常为40~1000Hz,如果准确度要求不高,还可用以测试高达10kHz的正弦波和非正弦波信号。由于万用表的交流刻度是根据正弦波的有效值来定值的,因此,它对方波电压的指示值偏大,而对锯齿波、脉冲波的指示值偏小。   (2)输入阻抗方面。万用表各电压挡级的输入阻抗是不一样的,其阻值等于相应挡级的电压满度值Uo(即量程)和万用表灵敏度S(Ω/v)值的乘积,即 R
[测试测量]
如何实现简单的位数组(bit array)
在 comp.lang.c 上面看到一则不错的 FAQ,《 How can I implement sets or arrays of bits? 》感觉很实用,仅仅使用了几个简单的宏就实现了一个基本的位数组(bitset)。 #include limits.h #define BITMASK(b) (1 ((b) % CHAR_BIT)) #define BITSLOT(b) ((b) / CHAR_BIT) #define BITSET(a, b) ((a) |= BITMASK(b)) #define BITCLEAR(a, b) ((a) &= ~BITMASK(b)) #define BITTEST(a, b)
[单片机]
如何实现简单的位<font color='red'>数组</font>(bit array)
C51—小知识点
1-write总线:和IIC总线类似,是一种通信方式,主机对1-Wire总线的基本操作分为复位、读和写三种。总线上接上拉电阻。对时序的要求比较严格 ROM指令、操作协议: 晶振: CCH:控制信道(CCH)是用于传送信令信息和短的分组数据的信道。 占空比:占空比是指一个脉冲循环内,通电(高电平)时间相对于总时间所占的比例。 PWM:脉冲宽度调试技术,通过对一系列脉冲宽度进行调节来改变来等效获得所需波形 上、下拉电阻:如果一个线路中的电压处于不确定的状态(例如一个引脚不和任何其它回路连通时),那我们就说它的电压是浮动的,他会随着时间不断变化,跳动,而且很容易受到外界环境的影响。处于这种不确定状态的电路会被随机解释为高
[单片机]
指针万用表测电容器的几种方法
在家电维修过程中,因电容漏电或容量变化而引发的故障可谓屡见不鲜且故障现象各异。一般的指针万用表和部分数字万用表都无法测量电容,特别是那些小电容,给维修造成很大的不便。在此,我给大家介绍几种小容量电容的测量方法,供参考。 方法l:找一个β≥250的晶体三极管(要求穿透电流要小),如一时找不到,可用两只同型号的三极管复合成达林顿形式,见图1。将被测电容并接在三极管的c-e结(若为有极性电容则电容正极接三极管c极),然后用万用表R×10k挡,黑表笔接c极,红笔接e极,见图2,观察表针瞬时摆动程度。照此法用几个已知容量的正常(高精确度)的电容反复测试,记录下表针每次的瞬时最大摆动幅值,l进行处理计算,算出表盘上每小格应代表的电容值,备日后
[测试测量]
用<font color='red'>指针</font>万用表测电容器的几种方法
Keil C51 Startup.a51我的理解
$NOMOD51 ;------------------------------------------------------------------------------ ; This file is part of the C51 Compiler package ; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc. ;------------------------------------------------------------------------------ ; STARTUP.A51: This code is exe
[单片机]
C51单片机 写一个外部中断(入门单片机)
代码部分 void main() { /*---------------EA,IT,EX必须写-------------*/ EA=1; //开启总中断 IT1=1; //中断触发模式 //=0为低电平触发,=1为下降沿触发 EX1=1; //外部中断允许位 while(1) { led1=0; } } void int1() interrupt 0 { led=~led1; } 解释: 1.外部中断(按键中断)最最重要的部分就是EA,IT,EX三条语句,这三条是必不可少的。 2.EA是中断总开关,类似家里电闸的总闸,总闸
[单片机]
<font color='red'>C51</font>单片机 写一个外部中断(入门单片机)
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved