自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理
探索Android软键盘的疑难杂症
深入探讨Android异步精髓Handler
详解Android主流框架不可或缺的基石
站在源码的肩膀上全解Scroller工作机制
Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南
讲给Android程序员看的前端系列教程(图文版)
讲给Android程序员看的前端系列教程(视频版)
Android程序员C语言自学完备手册
数组和指针虽然是不同的东西,但是两者有着非常紧密的关系。在C语言中:数组名原则上会被解释为指向该数组起始元素的指针。 通俗地来讲:数组名是一个指针,它指向了数组的起始元素。例如:有一个数组a[5],那么数组名a就是一个指针,它指向了该数组的起始元素。
请看如下示例:
#include
#include
int main()
{
int a[5]={1,2,3,4,5};
printf("a=%p\n",a);
printf("&a=%p\n",&a);
printf("&a[0]=%p\n",&a[0]);
return 0;
}
结果如下:
a=0060FEFC
&a=0060FEFC
&a[0]=0060FEFC
从该示例中也可以看:a和&a以及&a[0]的值是一致的,它们均代表了数组起始元素a[0]的地址。
明白了这一点,我们再来看如下示例:
#include
#include
int main()
{
int a[5]={1,2,3,4,5};
int *p;
p=&a[0];
printf("&a=%p\n",&a);
printf("&a[0]=%p\n",&a[0]);
printf("p=%p\n",p);
printf("*p=%d\n",*p);
printf("a[0]=%d\n",a[0]);
return 0;
}
在示例中声明了数组a和指针p,并将&a[0]的值存入p。也就是说:指针p会被初始化为指向数组a的起始元素a[0],即*p=a[0]。
结果如下:
&a=0060FEF8
&a[0]=0060FEF8
p=0060FEF8
*p=1
a[0]=1
之前我们讲解并验证了:a和&a以及&a[0]的值是一致的,它们均代表了数组起始元素a[0]的地址。 所以,我们可以优化刚才的示例:
#include
#include
int main()
{
int a[5]={1,2,3,4,5};
int *p=a;
printf("&a=%p\n",&a);
printf("&a[0]=%p\n",&a[0]);
printf("p=%p\n",p);
printf("*p=%d\n",*p);
printf("a[0]=%d\n",a[0]);
return 0;
}
对比可知:将如下两句代码
int *p;
p=&a[0];
替换成了:
int *p=a;
从这里我们可以看出: 数组名a会被解释为&a[0],存入p的值为&a[0]的值,即p=&a[0]。
至此,我们对指针与数组的基础知识做一个总结。
核心小结:
假设指针p指向数组a,我们知道:
1、数组名原则上会被解释为指向该数组起始元素的指针。
2、a和&a以及&a[0]的值是一致的,它们均代表了数组起始元素a[0]的地址。
3、指针p会被初始化为指向数组a的起始元素a[0],即*p=a[0]
4、数组名a会被解释为&a[0],存入p的值为&a[0]的值,即p=&a[0]
指针p指向数组的元素e时,遵循以下规则:
- p+i 为指向元素e后第i个元素的指针
- p-i 为指向元素e前第i个元素的指针
之前,我们已经总结过:数组名原则上会被解释为指向该数组起始元素的指针,所以该规则也可以如下描述:
- a+i 为指向元素e后第i个元素的指针
- a-i 为指向元素e前第i个元素的指针
也就是说:指针p指向数组a时p+i与a+i和&a[i]是等价的。 当i=0时,则p与a和a[0]是等价的;这又回到了之前已经讲过的内容了,不再赘述。
请看如下验证示例:
#include
#include
int main()
{
int i;
int a[5]={1,2,3,4,5};
int *p=a;
for(i=0;i<5;i++){
printf("&a[%d]=%p,p+%d=%p,a+%d=%p\n",i,&a[i],i,p+i,i,a+i);
}
return 0;
}
结果如下:
&a[0]=0060FEF4,p+0=0060FEF4,a+0=0060FEF4
&a[1]=0060FEF8,p+1=0060FEF8,a+1=0060FEF8
&a[2]=0060FEFC,p+2=0060FEFC,a+2=0060FEFC
&a[3]=0060FF00,p+3=0060FF00,a+3=0060FF00
&a[4]=0060FF04,p+4=0060FF04,a+4=0060FF04
既然p+i与a+i和&a[i]是等价的,这三者均表示地址,那么我们再取出它们各自中保存的值看看:
#include
#include
int main()
{
int i;
int a[5]={1,2,3,4,5};
int *p=a;
for(i=0;i<5;i++){
printf("&a[%d]=%p,p+%d=%p,a+%d=%p\n",i,&a[i],i,p+i,i,a+i);
}
puts("----------line------------");
for(i=0;i<5;i++){
printf("a[%d]=%d,*&a[%d]=%d,*(p+%d)=%d,*(a+%d)=%d\n",i,a[i],i,*&a[i],i,*(p+i),i,*(a+i));
}
return 0;
}
结果如下:
&a[0]=0060FEF4,p+0=0060FEF4,a+0=0060FEF4
&a[1]=0060FEF8,p+1=0060FEF8,a+1=0060FEF8
&a[2]=0060FEFC,p+2=0060FEFC,a+2=0060FEFC
&a[3]=0060FF00,p+3=0060FF00,a+3=0060FF00
&a[4]=0060FF04,p+4=0060FF04,a+4=0060FF04
----------line------------
a[0]=1,*&a[0]=1,*(p+0)=1,*(a+0)=1
a[1]=2,*&a[1]=2,*(p+1)=2,*(a+1)=2
a[2]=3,*&a[2]=3,*(p+2)=3,*(a+2)=3
a[3]=4,*&a[3]=4,*(p+3)=4,*(a+3)=4
a[4]=5,*&a[4]=5,*(p+4)=5,*(a+4)=5
我们发现:指针p指向数组a时*(p+i)与a[i]和*&a[i]以及*(a+i)是等价的!
接下来,我们继续来看另外一个规则:指针p指向数组的元素e时,遵循以下规则:
- 指向元素e后第i个元素的*(p+i),可以写为p[i]
- 指向元素e前第i个元素的*(p-i),可以写为p[-i]
既然*(p+i)可以写为p[i] 那么可得知:指针p指向数组a时*(p+i)和p[i]与a[i]和*&a[i]以及*(a+i)是等价的! 刚才的代码可以继续完善:
#include
#include
int main()
{
int i;
int a[5]={1,2,3,4,5};
int *p=a;
for(i=0;i<5;i++){
printf("&a[%d]=%p,p+%d=%p,a+%d=%p\n",i,&a[i],i,p+i,i,a+i);
}
puts("----------line------------");
for(i=0;i<5;i++){
printf("a[%d]=%d,*&a[%d]=%d,*(p+%d)=%d,*(a+%d)=%d,p[%d]=%d\n",i,a[i],i,*&a[i],i,*(p+i),i,*(a+i),i,p[i]);
}
return 0;
}
结果如下:
&a[0]=0060FEF4,p+0=0060FEF4,a+0=0060FEF4
&a[1]=0060FEF8,p+1=0060FEF8,a+1=0060FEF8
&a[2]=0060FEFC,p+2=0060FEFC,a+2=0060FEFC
&a[3]=0060FF00,p+3=0060FF00,a+3=0060FF00
&a[4]=0060FF04,p+4=0060FF04,a+4=0060FF04
----------line------------
a[0]=1,*&a[0]=1,*(p+0)=1,*(a+0)=1,p[0]=1
a[1]=2,*&a[1]=2,*(p+1)=2,*(a+1)=2,p[1]=2
a[2]=3,*&a[2]=3,*(p+2)=3,*(a+2)=3,p[2]=3
a[3]=4,*&a[3]=4,*(p+3)=4,*(a+3)=4,p[3]=4
a[4]=5,*&a[4]=5,*(p+4)=5,*(a+4)=5,p[4]=5
指针p指向数组a时:
(1) &a[i]、a+i、&p[i]、p+i均表示指向数组中第i个元素的指针
(2) a[i]、*(a+i)、p[i]、*(p+i)均表示对数组中第i个元素的访问
验证代码:
#include
#include
int main()
{
int i;
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
for (i = 0; i < 5; i++){
printf("&a[%d] = %p a+%d = %p &p[%d] = %p p+%d = %p\n",i, &a[i], i, (a + i), i, &p[i], i, (p + i));
}
for (i = 0; i < 5; i++){
printf("a[%d] = %d *(a+%d) = %d p[%d] = %d *(p+%d) = %d\n",i, a[i], i, *(a + i), i, p[i], i, *(p + i));
}
return 0;
}
结果如下:
&a[0] = 0060FEF4 a+0 = 0060FEF4 &p[0] = 0060FEF4 p+0 = 0060FEF4
&a[1] = 0060FEF8 a+1 = 0060FEF8 &p[1] = 0060FEF8 p+1 = 0060FEF8
&a[2] = 0060FEFC a+2 = 0060FEFC &p[2] = 0060FEFC p+2 = 0060FEFC
&a[3] = 0060FF00 a+3 = 0060FF00 &p[3] = 0060FF00 p+3 = 0060FF00
&a[4] = 0060FF04 a+4 = 0060FF04 &p[4] = 0060FF04 p+4 = 0060FF04
a[0] = 1 *(a+0) = 1 p[0] = 1 *(p+0) = 1
a[1] = 2 *(a+1) = 2 p[1] = 2 *(p+1) = 2
a[2] = 3 *(a+2) = 3 p[2] = 3 *(p+2) = 3
a[3] = 4 *(a+3) = 4 p[3] = 4 *(p+3) = 4
a[4] = 5 *(a+4) = 5 p[4] = 5 *(p+4) = 5
通过之前的学习,给我们一种感觉:数组名和指针是同一回事,两者是等价的;而且有的书上明确指出数组名就是指针数组的指针。其实,这类的说话有失偏颇,是不够准确、不够严谨的。
先来看四小东西:数组,指针,指针常量,指针变量。
其实,我们也能通过非常简单的方式证明数组名和指针不是等价的;代码如下:
#include
#include
int main()
{
int arraySize,pointerSize;
int a[5]={1,2,3,4,5};
int *p=a;
arraySize=sizeof(a);
pointerSize=sizeof(p);
printf("arraySize=%d,pointerSize=%d",arraySize,pointerSize);
return 0;
}
在该示例中分别将数组名a和指针p传递给sizeof( ),结果如下:
arraySize=20,pointerSize=4
嗯哼,看到了吧;两者所占内存空间并不一样!压根就不是同一个东西。
之前,我们学过赋值运算符=
,现在我们来看如下示例:
#include
#include
int main()
{
int a[5]={1,2,3,4,5};
int b[5]={6,7,8,9,0};
a=b;
return 0;
}
从表面来看该示例代码没有任何错误,但事实上第8行代码a=b;
是错误的!虽然数组名a会被解释为执行数组首元素的指针,但是不可改写其值。如果可以这样赋值,那么数组的地址就会被改变!所以,赋值表达式的左侧不可为数组名!
在学习了指针之后,我们再回过头来看之前的代码,如下所示:
#include
#include
//获取数组中的最大值
int getMax(int a[],int length){
int i,max;
max=a[0];
for(i=0;imax){
max=a[i];
}
}
return max;
}
int main()
{
int a[]={3,4,5,1,2};
int length=sizeof(a)/sizeof(a[0]);
int max=getMax(a,length);
printf("数组的最大值是:%d",max);
return 0;
}
函数int getMax( )
的第一个参数a,本质上而言是一个指针!该函数为什么要设计两个输入参数呢?有指针不就够了么?假若函数getMax( )只接收一个指向数组的指针,那么是无法知道该数组的长度的!所以,不但需要指向数组的指针还需要知晓数组的长度。故,一般而言在函数调用过程中传递数组时会同时传递数组长度。
如果,不这么做会怎么样呢?请看如下示例:
#include
#include
void printArray(int a[]){
int i;
for(i=0;i<10;i++){
printf("a[%d]=%d\n",i,a[i]);
}
}
int main()
{
int a[5]={1,2,3,4,5};
printArray(a);
return 0;
}
在本示例中:数组长度为5,但是for循环的条件是i<10
;假若在Java中那么避免会报数据越界异常ArrayIndexOutOfBoundsException
,那么,在这里也会么?
结果如下:
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
a[5]=56
a[6]=2
a[7]=6356884
a[8]=4198653
a[9]=1
Process returned 0 (0x0) execution time : 0.669 s
Press any key to continue.
嗯哼,我们可以看到程序没有报错,而且a[5]—a[10]的值依然可以打印出来!!
程序修改如下:
#include
#include
void printArray(int a[],int len){
int i;
for(i=0;i
所以,在C语言中操作数组时需要我们自己明确数组的长度并作出相应的限制从而避免越界等异常情况。