数组和指针是C语言中两个很重要的知识点,也是两个难点。对于很多C/C++的开发者来说,虽然经常会看到和使用它们,但是会对这二者的关系有些搞不清楚。
如果说让我来形容它们关系的话,我可以说:它们之间在本质上是没有任何关系的!
打一个形象的比喻来说,我觉得它们就像是在一个部门工作的两个很要好的同事,而且两个同事长得还很像而已。所以我们经常会弄混它们。
为了便于我们理解这两个“同事”之间的关系,我们还是用程序来说明:
#include <stdio.h>
//主函数
int main()
{
//定义一个数组
int a[5] = { 1, 2, 3, 4, 5 };
//定义一些指针
int* p1 = a;
int* p2 = a + 1;
int* p3 = a + 2;
int* p4 = a + 3;
int* p5 = a + 4;
//定义一些变量
int i1 = *p1;
int i2 = *p2;
int i3 = *p3;
int i4 = *p4;
int i5 = *p5;
int i6 = *a;
int i7 = *(a + 1);
int i8 = *(a + 2);
int i9 = *(a + 3);
int i10 = *(a + 4);
//返回
return 0;
}
编译结果如下(编译环境是Visual Studio 2013):
可以看到,里面是没有任何内容的,当然这是肯定的,因为程序里面没有调用printf函数打印任何内容,因为这次我们需要通过Visual Studio 2013的监视窗口来查看它们的
值和指针(因为不同的人使用printf打印的风格不尽相同,便于统一,通过查看值和指针
的方法是最直接和方便的)。
我们可以使用Visual Studio 2013的调试功能(F10键),并逐行运行完成后,我们来查看一下监视窗口的内容:
虽然这段程序看起来不复杂,不过我们还是可以总结出很多东西的。
1.我们可以看到,数组a的地址是0x0088f920,指针p1的值也是0x0088f920,而i1和i6的值也是数组a的第一个元素值(1),我们可以总结出第一条规律: 数组名就是数组第
一个元素的指针,通过数组名,我们就可以获取数组第一个元素的值(*a),以及第一个元素的指针(a)。
2.变量i2的值是数组a的第二个元素(2),i2 = *p2,而p2 = a + 1,这样我们就可以知道:如果a是数组名的话,那么a + 1就是获取数组的第二个元素的指针;通过i3到i5的
值,我们可以继续得到一个更广泛的结论: 如果如果a是数组名的话,那么a + n就是获取数组的第n个元素的指针。指针的加操作并不是地址值加1,而是加sizeof(<变量类型>),
对于这个例子的int类型而言,sizeof(int)等于4,所以我们看到a(0x0088f920)和a + 1(0x0088f924),a + 1(0x0088f924)和a + 2(0x0088f928),a + 2(0x0088f928)和a + 3
(0x0088f92c),a + 3(0x0088f92c)和a + 4(0x0088f930)的地址差都是4。
3.变量i7的值也是数组a的第二个元素(2),不过它的得到方法是*(a + 1),这相当于将变量i2得到的方法简化了(先得到a + 1的地址,再取得a + 1地址里面的值),这说明: 如果如果a是数组名的话,*(a + n)就是数组第n个元素的值。
当然,我们还可以得到更多的结论,这里只是总结了三个最重要的结论。
总之,数组和指针就是两个关系很好的“同事”,它们没有什么“血缘“和“亲戚“关系,只是经常在一起共同工作和相互协作罢了。
如果说让我来形容它们关系的话,我可以说:它们之间在本质上是没有任何关系的!
打一个形象的比喻来说,我觉得它们就像是在一个部门工作的两个很要好的同事,而且两个同事长得还很像而已。所以我们经常会弄混它们。
为了便于我们理解这两个“同事”之间的关系,我们还是用程序来说明:
#include <stdio.h>
//主函数
int main()
{
//定义一个数组
int a[5] = { 1, 2, 3, 4, 5 };
//定义一些指针
int* p1 = a;
int* p2 = a + 1;
int* p3 = a + 2;
int* p4 = a + 3;
int* p5 = a + 4;
//定义一些变量
int i1 = *p1;
int i2 = *p2;
int i3 = *p3;
int i4 = *p4;
int i5 = *p5;
int i6 = *a;
int i7 = *(a + 1);
int i8 = *(a + 2);
int i9 = *(a + 3);
int i10 = *(a + 4);
//返回
return 0;
}
编译结果如下(编译环境是Visual Studio 2013):
可以看到,里面是没有任何内容的,当然这是肯定的,因为程序里面没有调用printf函数打印任何内容,因为这次我们需要通过Visual Studio 2013的监视窗口来查看它们的
值和指针(因为不同的人使用printf打印的风格不尽相同,便于统一,通过查看值和指针
的方法是最直接和方便的)。
我们可以使用Visual Studio 2013的调试功能(F10键),并逐行运行完成后,我们来查看一下监视窗口的内容:
虽然这段程序看起来不复杂,不过我们还是可以总结出很多东西的。
1.我们可以看到,数组a的地址是0x0088f920,指针p1的值也是0x0088f920,而i1和i6的值也是数组a的第一个元素值(1),我们可以总结出第一条规律: 数组名就是数组第
一个元素的指针,通过数组名,我们就可以获取数组第一个元素的值(*a),以及第一个元素的指针(a)。
2.变量i2的值是数组a的第二个元素(2),i2 = *p2,而p2 = a + 1,这样我们就可以知道:如果a是数组名的话,那么a + 1就是获取数组的第二个元素的指针;通过i3到i5的
值,我们可以继续得到一个更广泛的结论: 如果如果a是数组名的话,那么a + n就是获取数组的第n个元素的指针。指针的加操作并不是地址值加1,而是加sizeof(<变量类型>),
对于这个例子的int类型而言,sizeof(int)等于4,所以我们看到a(0x0088f920)和a + 1(0x0088f924),a + 1(0x0088f924)和a + 2(0x0088f928),a + 2(0x0088f928)和a + 3
(0x0088f92c),a + 3(0x0088f92c)和a + 4(0x0088f930)的地址差都是4。
3.变量i7的值也是数组a的第二个元素(2),不过它的得到方法是*(a + 1),这相当于将变量i2得到的方法简化了(先得到a + 1的地址,再取得a + 1地址里面的值),这说明: 如果如果a是数组名的话,*(a + n)就是数组第n个元素的值。
当然,我们还可以得到更多的结论,这里只是总结了三个最重要的结论。
总之,数组和指针就是两个关系很好的“同事”,它们没有什么“血缘“和“亲戚“关系,只是经常在一起共同工作和相互协作罢了。