0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

所有C语言数组和指针的知识都在这里了!|周立功手把手教你学C语言编程

AGk5_ZLG_zhiyua 来源:未知 作者:电子大兵 2017-09-01 09:28 次阅读

第一章为程序设计基础,本文为1.6.1数组与指针。

>>>1.6.1数组与指针

1.数组

(1)数组的声明

我们知道,一个基本数据类型的变量只能存储一个数据,比如:

int data = 0x64;

如果需要存储一组int型数据呢?比如,1、2、3,则至少需要3个变量data0、data1、data2。比如:

int data0 = 1, data1 = 2, data2 = 3;

由于数据的表现形式多种多样,还有字符型和其它的数值类型,因此仅有基本数据类型是不够的。是否可以通过基本数据类型的组合抽象构造其它的数据类型呢?答案是可以的,构造数据类型数组就是这样产生的。

从概念的视角来看,int型整数1、2和3都是相同的数据类型,data0、data1和data2三个变量的共性是data,其差异性是下标不一样。因此可以将data0、data1和data2抽象为一个名字,然后用下标区分这些变量的集合——data[0]、data[1]和data[2]。如果有以下声明:

int data[3]; //解读为data是int数组(元素个数3)

那么data[3]就成了存放3个int型数据1、2、3的data[0]、data[1]和data[2]所组成的数组,即可分别对data[0]、data[1]和data[2]赋值:

data[0] = 1, data[1] = 2, data[2] = 3;

当然,也可以按照以下方式声明一个数组并进行初始化:

int data[3] = {1, 2, 3};

通常将data称为数组(变量)名,data[0]、data[1]和data[2]被称为变量。因而可以说,数组是将相同类型数据的若干变量按有序的形式组织起来,用一个名字命名,然后用下标区分这些变量的集合。

由于数组是建立在其它类型的基础上,因此C将数组看作构造类型,在声明数组时必须说明其元素的类型。比如,int类型的数组、float类型的数组或其它类型的数组。而其它类型也可以是数组类型,在这种情况下,创建的是数组类型的数组,简称数组的数组。

(2)下标与变量的值

在这里,定义了一个名为data的数组类型变量,它是由存放3个int型数据1、2、3的变量data[0]、data[1]和data[2]组成的。通常又将数组的各个变量称为数组的元素,而数组的元素是按照顺序编号的,这些元素的编号又称为数组元素的下标。

由于有了下标,因此数组元素在内存中的位置就被唯一确定下来了。下标总是从0开始的,最后一个元素的下标为元素的个数减1(即2),data[0]叫第1个元素,data[1]叫第2个元素,data[2]叫第3个元素,也就意味着所有的元素在内存中都是连续存储的。

直观上,数组是由下标(或称为索引)和值所组成的序对集合,对于每个有定义的下标都存在一个与其关联的值,在数学上称为映射。除了创建新数组外,大多数语言对数组只提供两种标准操作:一个操作是检索一个值,另一个操作是存储一个值。

函数Create(data, size)创建一个新的具有适当大小的空数组,初始时数组的每一项都没有定义。Retrieve操作接受一个数组data和一个下标index,如果下标合法,则该操作返回与下标关联的值,否则产生一个错误。Store操作接受一个数组data、一个下标index和一个项item的集合,即项是value值的集合,有时也将值(value)称为项(item),返回在原来数组中增加新的序对后的数组。

显然,int系的任何常量表达式都可以作为数组元素的下标。比如:

int array[3+5]; //合法

int array['a']; //表示int array[97];

上述定义之所以合法,因为表示元素个数的常量表达式在编译时就具有确定的意义,与变量的定义一样明确地分配了固定大小的空间。

虽然使用符号常量增强了数组的灵活性,但如果定义采用了以下的形式:

int n = 5;

int array[n]; //非法

因为标准C认为数组元素的个数n不是常量,虽然编译器似乎已经“看到”了n的值,但int array[n]要在运行时才能读取变量n的值,所以在编译期无法确定其空间大小。使用符号常量定义数组长度的正确形式如下:

#define N 10

int array[N];

即可根据实际的需要修改常量N的值。

由于数组元素下标的有效范围为0~N-1,因此data[N]是不存在的,但C语言并不检查下标是否越界。如果访问了数组末端之后的元素,访问的就是与数组不相关的内存。它不是数组的一部分,使用它肯定会出问题。C为何允许这种情况发生呢?这要归功于C信任程序员,因为不检查越界可以使运行速度更快,所以编译器没有必要检查所有的下标错误。因为在程序运行之前,数组的下标可能尚未确定,所以为了安全起见,编译器必须在运行时添加额外代码检查数组的每个下标值,但这样会降低程序的运行速度。C相信程序员能编写正确的代码,这样的程序运行速度更快。但并不是所有的程序员都能做到这一点,越界恰恰是初学者最容易犯的错误,因此要特别注意下标的范围不能超出合理的界限。

(3)变量的地址与类型

当将变量data[0]、data[1]和data[2]作为&的操作数时,&data[0]是指向变量data[0]的指针,&data[1]是指向变量data[1]的指针,&data[2]是指向变量data[2]的指针。data[0]、data[1]和data[2]变量的类型为int,&data[0]、&data[1]和&data[2]指针的类型为int *const,即指向常量的指针,简称常量指针,其指向的值不可修改。比如:

int a;

int * const ptr = &a;

ptr = NULL; //试图修改,则编译报警

&a = NULL; //试图修改,则编译报警

同理,&data是指向变量data的指针,那么data是什么类型?

按照声明变量的规约,将标识符data取出后,剩下的“int [3]”就是data的类型,通常将其解释为由3个int组成的数组类型,简称数组类型。其目的是告诉编译器需要分配多少内存?3个元素的整数数组,data类型测试程序详见程序清单 1.20。

程序清单1.20data类型测试程序

1 #include

2

3 void f(int x);

4 int main(int argc, char *argv[])

5 {

6 int data[3];

7 f(data);

8 return 0;

9 }

通过编译器提示的警告,“funtion: 'int' differ in levels of indirection from 'int [3]'”,说明数组变量data的类型为不是int而是int [3]数组类型。由于在设计C语言时,过多地考虑了开发编译器的便利。虽然设计编译器更方便了,却因为概念的模糊给初学者造成了理解上的困难。实际上数组应该这样定义:

int [3] data;

即int是与[3]结合的。&data到底是什么类型?

当data作为&的操作数时,则&data是指向data的指针。由于data的类型为int [3],因此&data是指向“int [3]数组类型”变量data的指针,简称数组指针。其类型为int (*)[3],即指向int [2]的指针类型。为何要用“()”将“*”括起来?

如果不用括号将星号括起来,那么“int (*)[3]”就变成了“int *[3]”,而int *[3]类型名为指向int的指针的数组(元素个数3)类型,这是设计编译器时约定的语法规则。

&data的类型到底是不是“int (*)[3]”?其验证程序范例详见程序清单 1.21。

程序清单1.21&data类型测试程序

1 #include

2 int main(int argc, char *argv[])

3 {

4 int data[3];

5 int b = &data;

6 return 0;

7 }

通过编译器提示的警告,“'int' differ in levels of indirection from 'int (*)[3]'”,说明&data的类型为int (*)[3]。

(4)sizeof(data)

当data作为sizeof的操作数时,其返回的是整个数组的长度。在这里,sizeof(data)的大小为12,即3个元素占用的字节数为4×3=12,系统会认为&data+1中的“1”,偏移了一个数组的大小,因此&data +1是下一个未知的存储空间的地址(即越界)。在小端模式下,数组在内存中的存储方式详见图 1.10。

1.10数组的存储

将如何寻找相应的数组元素呢?常用的方法是通过“数组的基地址+偏移量”算出数组元素的地址。在这里,第一个元素&data[0]的地址称为基地址,其偏移量就是下标值和每个元素的大小sizeof(int)相乘。假设数组元素&data[0] 的地址为A,且在内存中的实际地址为0x22FF74,那么&data[1]的值为:

A + 1×sizeof(int) = (unsigned int)data + 4 = 0x22FF74 + 4 = 0x22FF78

&data[2]的值为:

A + 2×sizeof(int) = (unsigned int)data + 8 = 0x22FF74 + 8 = 0x22FF7C

实际上,当在C语言中书写data[i]时,C将它翻译为一个指向int的指针。Data是指向data[0]的指针,data+i是指向data[i]的,因此不管data数组是什么类型,总有data+i等于data[i],于是*(data+i)等于data[i],其相应的测试范例程序详见程序清单 1.22。

程序清单1.22变量的地址测试程序

1 #include

2 int main(int argc, char *argv[])

3 {

4 int data[3]= {1, 2, 3};

5 printf("%x, %x, %x, %x, %x", &data[0], &data[1], &data[2], &data, &data+1);

6 return 0;

7 }

实践证明,虽然&data[0]与&data的类型不一样,但它们的值相等。同时也可以看出,数组的元素是连续存储的。如果将数组变量占用内存的大小除以数组变量中一个元素所占用空间的大小,便可得到数组元素的个数。即:

int numData = sizeof(data) / sizeof(data[0]);

当然,也可以使用宏定义计算数组元素的个数:

#define NELEMS(data) (sizeof(data) /sizeof(data[0])

当数组作为函数的参数时,C语言函数的所有参数必须在函数内部声明。但是,由于在函数内部并没有给数组分配新的存储空间,因此一维数组的容量只在主程序中定义。显然,如果函数需要得到一维数组的大小,则必须将它以函数参数的形式传入函数中,或将它作为全局变量访问。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 指针
    +关注

    关注

    1

    文章

    465

    浏览量

    70354
  • C语言编程
    +关注

    关注

    6

    文章

    90

    浏览量

    20976
  • 周立功
    +关注

    关注

    38

    文章

    130

    浏览量

    37036
  • 数组
    +关注

    关注

    1

    文章

    405

    浏览量

    25579

原文标题:周立功:C语言数组与指针详解

文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    手把手教你单片机之AVR入门视频教程

    第02讲 AVR硬件电路设计教程_手把手教你单片机之AVR入门篇第03讲 AVR开发基础知识_手把手
    发表于 03-02 11:04

    手把手教你单片机c语言》视频教程

    `《手把手教你单片机C语言》视频教程电驴、优酷、迅雷等下载地址《手把手
    发表于 10-26 14:14

    力天手把手教你单片机视频全集下载

    下--力天手把手教你单片机之入门篇.rarhttp://115.com/file/an9yqmx3#07.第四讲.C语言基础
    发表于 02-14 17:06

    手把手教你单片机c语言 郭天祥

    手把手教你单片机c语言》视频教程 https://bbs.elecfans.com/forum.php?mod=viewthread&t
    发表于 06-01 22:46

    期待宋老师的《手把手教你51单片机-C语言版》

    看过宋老师的《手把手教你51单片机-C语言版》,感觉挺好的,如果有书的教材那就更完美了!
    发表于 07-17 15:43

    今天收到关于《手把手教你51单片机-C语言版》抢楼送书

    ``今天收到宋老师《手把手教你51单片机-C语言版》这本书,感觉书还不错啊,{:12:}最近
    发表于 08-01 15:14

    手把手教你51单片机-C语言版》公布获奖楼层

    学过其他人的教程,也可以通过这个教程来提高一下眼界,收益肯定不会少。小编推荐:《手把手教你51单片机(C语言版)》的创作,目的就是改变当前
    发表于 08-04 16:51

    手把手教你CPLD/FPGA与单片机联合设计》-兴华

    。此外,为了帮助读者掌握单片机与CPLD/FPGA的联合设计,还介绍51单片机的基本知识及单片机c语言编程的基础
    发表于 12-29 17:10

    手把手教你CPLD/FPGA与单片机联合设计》-兴华

    。此外,为了帮助读者掌握单片机与CPLD/FPGA的联合设计,还介绍51单片机的基本知识及单片机c语言编程的基础
    发表于 01-06 17:21

    【下载】手把手教你DSP:基于TMS320C55x

    过程,所有代码都标注有详细的中文注释,为读者快速熟悉并掌握DSP的开发方法和技巧提供方便。《手把手教你DSP:基于TMS320X281x
    发表于 06-02 16:40

    手把手教你FPGA 编程规范篇

    手把手教你FPGA 编程规范篇
    发表于 02-02 11:32

    手把手教你CAN总线 来清民

    手把手教你CAN总线》以CAN总线的通信实例和基本实验为主线,以单片机、数据通信和工业控制网络的基础知识为出发点,介绍CAN现场总线的
    发表于 04-08 21:17

    手把手教你PIC单片机C语言

    手把手教你PIC单片机C语言教程第1课- PICHL-K18- PICHL-K18慧净电子--做人人都买得起的PPIICC单片机开发板真诚
    发表于 07-15 09:23

    手把手教你如何开始DSP编程

    手把手教你如何开始DSP编程
    发表于 04-09 11:54 10次下载
    <b class='flag-5'>手把手</b><b class='flag-5'>教你</b>如何开始DSP<b class='flag-5'>编程</b>

    手把手教你学LabVIEW视觉设计

    手把手教你学LabVIEW视觉设计手把手教你学LabVIEW视觉设计手把手教你学LabVIEW视
    发表于 03-06 01:41 2720次阅读