C语言数组分析

发布者:清新微笑最新更新时间:2015-05-04 来源: 51hei关键字:C语言  数组分析 手机看文章 扫描二维码
随时随地手机看文章
以前学习C语言的时候觉得数组和指针结合在一起的时候真的是地狱,很容易就搞混淆了,最近看了C语言深度解剖有了一点理解,好好的总结一下吧。其中很多的知识都是因为我们在学习的过程中没有仔细的去分析导致的。同时我体会到了我们在写代码的过程中应该更多的注重代码的调试,而不是换新的代码,只有不断的调试才能知道其中问题所在。
 
数组和指针之间本来没有什么关系,数组就是数组,指针就是指针,之间并没有关系,只是因为某些相似特性使得我们在分析的过程中存在较大的迷惑。
 
数组就是一个连续存储空间的存储的数值。指针就是指针,指针变量所在内存中存储的值都是地址。
 
C 语言中数组的大小必须是一个常数,但是不能认为采用const限定的变量就能作为数组大小的值,在C语言中const并不是定义一个常数,只是定义了一个只读类型的数据,并不是常数。在C语言中通常采用:
#define N  5
 
int Array[N];
const int M = 5;
int Array[M];//这是一种错误的定义方式,注意const并不是定义常量,但是C++中可以这样定义。
 
在一维数组中使用下标来访问数组,Array是整个分配存储空间的名字,单个的存储空间并没有名字。这个存储空间存储的值为数组的元素,主要是 Array[0],Array[1],...,Array[N-1],数组元素并没有名字。我们对数组的访问主要是采用下标的方式进行访问。Array与这块存储空间已经密切的关联起来,不能改变。但是需要主要的是Array作为右值时,表示的是该数组首个元素(Array[0])的地址,而不是代表整个数组的地址起始地址,虽然两个起始地址是相同的,但是需要理清其中的概念。整个数组的起始地址可以通过对这块存储区域取地址,也就是采用&a,这时得到的值才是整个数组的起始地址,虽然两个值是相同的,但是需要搞清楚其中的道理。
 
在数组中位置的变化也是非常重要的。
int *p = NULL;
p = Array + 1;
是指在数组Array的首元素的首地址上增加一个元素的宽度,使得p指向Array[1]。因为Array表示的是数组首个元素的首地址,那么操作的最小单位就是元素,Array + 1就是访问下一个元素。
p = &Array + 1;
由于&Array是表示数组的起始地址,操作的最小单位是一个数组,而不是元素,因此Array+1就是下一个数组的起始地址,也就是将p指向了Array的下一个数组,而不是元素。
 
因此需要注意一维数组名在作为右值时是表示数组首个元素的首地址,并不表示整个数组的首地址。
 
对于二维数组(多维数组)也存在类似的问题。
 
int *p = NULL;
#define N 5
#define M 5
int A[N][M];
 
数组A存在5个元素,每一个元素是一个数组,每个数组中存在5个元素。数组名A表示首个元素的首地址,因此A表示A[0]的首地址,A的元素为数组,A + 1表示下一个元素(小数组)也就是A[1]的首地址;&A表示整个数组的首地址。&A+1表示下一个数组的起始地址。
 
A作为右值时,表示数组首元素的首地址,也就是A[0]的首地址,A[i]作为右值时是表示第i个元素(也是数组)的首元素(A[i][0])的首地址。由于A表示地址,这与指针存在很多的相似性,因此可采用指针的方式进行访问。具体的实现过程如下:
A                                                     %%第0行的元素的首地址,不是第0行第0个元素的地址
A[i]        <--->    *(A+i);                   %%第i行第0个元素的地址
A[i][j]     <--->    *(*(A+i)+j);           %%第i行第j个元素
&A[i][j]  <--->    (*(A+i)+j);             %%第i行第j个元素的地址
A[i]+j     <--->    (*(A+i)+j);             %%第i行第j个元素的地址
&A+1                                              %%下一组数组的起始地址
A+i                                                  %%第i行数组的起始地址
 
在多维数组中存储方式是线性的存储方式,可以通过指针快速的访问。首先A表示首元素的首地址,将指针指向这个首地址就能快速的实现访问。
 
为了说明这些相互关系的,采用GDB对数组进行调试:
 
int main()
{
        int  i = 0,j = 0;
        int a[5][5];
        int b[5]={4,5,7,8,9};
        for(;i < 5; ++i)
                for(;j<5;++j)
                        a[i][j] = (i-j)+15;
}
 
编译调试:
(gdb) p a    
$1 = {{15, 14, 13, 12, 11}, {32768, 8736756, 8729060, -1073744844, 
    -1073745080}, {0, -1073744928, 134518436, -1073745064, 134513340}, {
    7298965, 134518436, -1073745016, 134513785, 134513194}, {8740000, 8740000, 
    8736756, 134513760, 134513408}}
(gdb) p &a    
$2 = (int (*)[5][5]) 0xbffff314  
(gdb) p &a+1  
$3 = (int (*)[5][5]) 0xbffff378   
(gdb) p a+1
$4 = (int (*)[5]) 0xbffff328    
(gdb) p *(a+1)
$5 = {32768, 8736756, 8729060, -1073744844, -1073745080}
(gdb) p *(a+1)+1
$6 = (int *) 0xbffff32c
(gdb) p *(*(a+1)+1)
$7 = 8736756
(gdb) p *a
$8 = {15, 14, 13, 12, 11}
(gdb) p **a
$9 = 15
(gdb) p **(a+1)
$10 = 32768
(gdb) p a[1]
$11 = {32768, 8736756, 8729060, -1073744844, -1073745080}
(gdb) p *a[1]
$12 = 32768
(gdb) p a[1]+1
$13 = (int *) 0xbffff32c
(gdb) p *(a[1]+1)
$14 = 8736756
 
根据$2 = (int (*)[5][5]) 0xbffff314中的[5][5]可知&a的大小是5*5的空间,刚好是一个数组的大小,说明&a表示整个数组的起始地址。(gdb) p &a+1  $3 = (int (*)[5][5]) 0xbffff378  中的[5][5]可知&a+1也是一个数组的起始地址,这也说明了&a是整个数组的初始地址。
 
(gdb) p a+1,$4 = (int (*)[5]) 0xbffff328中的[5]说明a+1是元素a[1](数组5个元素)的起始地址,而不是某一个数值(二维元素)的起始地址。
 
(gdb) p *a,$8 = {15, 14, 13, 12, 11},(gdb) p **a,$9 = 15,$8,$9说明a表示的是元素a[0](一个小数组)的起始地址,而不是某一个值的起始地址。*a是一个值(二维元素)的起始地址。
 
(gdb) p a[1]+1 $13 = (int *) 0xbffff32c 说明a[1]+1是一个值的地址,而不是一个数组的起始地址,因此a[i]+j是一个值(二维元素)的地址。
 
(gdb) p *(a+1)+1  $6 = (int *) 0xbffff32c   (gdb) p *(*(a+1)+1) $7 = 8736756,根据调试结果可知*(a+1)+1是一个值的地址。因此*(a+i)+j是一个值(二维元素)的地址。同时可知*(*(a+i)+j)是一个值(二维元素)。a[i][j]也是一个值。
 
 
综合上面的分析可知数组名A在作为右值时是数组首个元素(可能是一个值也可能是一个数组)的起始地址,而&A表示整个数组的起始地址。二维数组具体问题建议多去调试,根据调试分析其中的意义。多调试,多观察,多理解。
关键字:C语言  数组分析 引用地址:C语言数组分析

上一篇:fork函数的写时拷贝
下一篇:驱动学习1

推荐阅读最新更新时间:2024-03-16 14:00

STM32——C语言数据类型
在编程过程中,不同的CPU,其数据类型的意义各不相同,所以一定要注意相应变量数据类型的定义和转换,否则在计算中可能会出现不确定的错误。 (一)C语言中的种类数据 整型:int short long 实型:float double STM32中的数据类型非常的多,常用的变量,文件中的定义如下: /* exact-width signed integer types */ typedef signed char int8_t; typedef signed short int int16_t; typedef signed int int32_t; typedef signed __int
[单片机]
Keil C51对C语言的关键词扩展之十八:using
在8051系列单片机中,内部ram的前32个字节被分为4组,每组8个寄存器。每组的8个寄存器名字都为R0-R7。通过设置PSW寄存器的两个位,可以选择使用4组寄存器中的哪一组。 寄存器组在处理中断或者使用实时操作系统时非常有用,可以在进入中断或者切换任务时使用不同寄存器组,而不用把8个寄存器的内容保存到堆栈。在退出中断或返回原任务时,只需切换回原来的寄存器组即可。 指定一个函数使用的寄存器组: void rb_function (void) using 3 { . . . } using后跟数字0-3,不允许使用符号表达式指定代替该数字常量。只有定义函数时才可使用using关键词,在做函数原型声明时不允许使用usin
[单片机]
超声波模块测距51程序_单片机超声波测距c语言
超声波检测原理 超声波测距的程序流程图 程序如下: //超声波模块程序 //超声波模块程序 //Trig = P2^0 //Echo = P3^2 #include #define uchar unsigned char #define uint unsigned int // void delay(uint z) { uint x,y; for(x=z;x 0;x--) for(y=110;y 0;y--); } // void delay_20us() { uchar a ; for(a=0;a } //**********************************************************
[单片机]
超声波模块测距51程序_单片机超声波测距<font color='red'>c语言</font>
AVR控制TEA5767 C语言程序
#include iom32v.h #include macros.h #define uchar unsigned char #define uint unsigned int #define ulong unsigned long void delay() { uint i=0; for (i=0;i 2000;i++) ; } unsigned char radio_write_data ={0x69,0x28,0xa0,0x13,0x00}; //要写入TEA5767的数据 void inputbyte(uchar CD) //写入一字节数据 { uchar i,mid=0; for (
[单片机]
C语言一百例第十六例
代码: /* C语言第十六例 题目:输入两个正整数m和n,求其最大公约数和最小公倍数。 程序分析: (1)最小公倍数=输入的两个数之积除于它们的最大公约数,关键是求出最大公约数; (2)求最大公约数用辗转相除法(又名欧几里德算法) 1)证明:设c是a和b的最大公约数,记为c=gcd(a,b),a =b, 令r=a mod b 设a=kc,b=jc,则k,j互素,否则c不是最大公约数 据上,r=a-mb=kc-mjc=(k-mj)c 可知r也是c的倍数,且k-mj与j互素,否则与前述k,j互素矛盾, 由此可知,b与r的最大公约数也是c,即gcd(a,b)=gcd(b,a mod b),得证。 2)算法描述: 第一步:a ÷ b,
[单片机]
<font color='red'>C语言</font>一百例第十六例
C语言实现CRC校验计算
在编写数据传输程序时,数据容错是一个非常重要的问题。循环冗余位校验(Cyclicl Redundncy Check英文简称CRC)是目前运用非常广泛的一种数据容错方法,在数据传输,数据压缩等领域运用极其广泛。CRC的实现分为硬件和软件两种方法,其中软件实现的关键在于计算速度。如果单纯模拟硬件实现方法,则计算速度较慢。笔者在编制一个数据通讯软件中,运用了一种新颖的查表法计算CRC,速度很快,效果极佳。 首先介绍其原理,如果每次参与CRC计算的信息为一个字节,该信息字节加到16位的累加器中去时,只有累加器的高8位或低8位与信息字节相互作用(异或),相互作用(异或)的结果记为组合值,那么累加器中的新值等于组合值加上(按模2异或)累加器
[单片机]
单片机的C语言中位操作用法
在对单处机进行编程的过程中,对位的操作是经常遇到的。C51对位的操控能力是非常强大的。从这一点上,就可以看出C不光具有高级语言的灵活性,又有低级语言贴近硬件的特点。这也是在各个领域中都可以看到C的重要原因。在这一节中将详细讲解C51中的位操作及其应用。 1、位运算符 C51提供了几种位操作符,如下表所示: 运算符 含义 运算符 含义 & 按位与 ~ 取反 | 按位或 左移 ^ 按位异或 右移
[单片机]
基于固态存储器的ECC算法分析及实现
  评价存储器的一个重要指标就是它的可靠性,在一般的数据存储中,几个位的错误可能不是很关键的问题,如果但是发生在某个敏感的数据上,这个小小的故障可能会导致严重的后果。因此,必须采取一些措施来及时检出并纠正出错的数据。目前常用的方法有:奇偶校验、CRC校验、重复码校验等。   ECC校验是在奇偶校验的基础上发展而来的,它将数据块看作一个矩阵,利用矩阵的行、列奇偶信息生成ECC校验码。它能够检测并纠正单比特错误和检测双比特错误,但对双比特以上的错误不能保证检测。它克服了传统奇偶校验只能检出奇数位出错、校验码冗长、不能纠错的局限性。文中在高速大容量固态存储器的硬件结构基础上,详细介绍了ECC校验码的生成规则以及ECC校验流程,并用C
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

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