程序设计基石与实践之C语言指针和数组基础

英文出处:Dennis Kubes:  《Basics of Pointers and Arrays in C》。

关于C语言中指针和数组的争论就像是一场恶战。一方面,有些人觉得,所有人都必须承认指针与数组是不同的。而另一些人则认为数组被当成指针来处理,因此它们不应该有什么区别。这种现象让人迷惑。然而,这两种说法其实都是正确的。

数组不是指针,指针也不能说是数组。在C语言中,指针仅在内存中代表一个地址,而数组是许多连续的内存块,多个类型相似的元素存储在其中。更深入的解释,请参考我之前的博文《C语言内存地址》。在C语言的绝大多数情况下,数组被当作指针来处理,这也是使人困惑的地方。

数组表示法vs指针表示法

数组被当作指针来处理,具体指的下面两条:

  • 数组名变量代表了数组中第一个元素的地址。它并不是一个指针,但却表现得像一个不能被修改的常指针一样。
  • 程序在与数组交互的时候,用指针表示法代替数组表示法。

我们来看点代码吧。

// initialize an array of ints
int numbers[5] = {1,2,3,4,5};

// standard array notation
int *ptr1 = numbers;
int val1 = numbers[0];

// address of array notation
int *ptr2 = &numbers[0];
int val2 = *(&numbers[0]);

// pointer + increment notation
int *ptr3 = numbers + 0;
int val3 = *(numbers + 0);

// print out the address stored in the pointers
printf("*ptr1 = %p\n", (void *)ptr1);
printf("*ptr2 = %p\n", (void *)ptr2);
printf("*ptr3 = %p\n", (void *)ptr3);

// print out the value at the pointer addresses
printf("val1 = %d\n", val1);
printf("val2 = %d\n", val1);
printf("val3 = %d\n", val1);

我们声明了一个包含5个int的数组,并将数组名变量numbers赋给了一个int指针,ptr1。numbers代表了这个数组第一个元素的地址,将其赋给ptr1正是把它当成了指针来使用。接着我们用数组表示法访问了第一个元素的值。

第二个例子中,我们用数组表示法取了数组中第一个元素的地址,之后我们用解引用第一个元素所在地址的方法访问了它。

第三个例子中,我们用指针运算将数组中第一个元素的地址赋值给ptr3,之后我们解引用相同的地址来得到它的值。

最后我们将所有存储在指针中的地址和所有在这些地址的int值输出到屏幕上。运行这段代码,你会得到类似下面的输出:

*ptr1 = 0x7fff6be1de60
*ptr2 = 0x7fff6be1de60
*ptr3 = 0x7fff6be1de60
val1 = 1
val2 = 1
val3 = 1

所有值都是相同的。接下来再看看下面的代码:

// initialize an array of ints
int numbers[5] = {1,2,3,4,5};
int i = 0;

// print out elements using array notation
for (i = 0; i < 5; i++ ) {
  int value = numbers[i];
  printf("numbers[%d] = %d\n", i, value);
}

// print out elements using pointer math + array indexing (yuck!)
for (i = 0; i < 5; i++ ) {
  int value = *(numbers + i);
  printf("*(numbers + %d) = %d\n", i, value);
}

// print out elements using a single pointer
int *ptr = numbers;
for (i = 0; i < 5; i++ ) {
  int value = *ptr++;
  printf("%d, *ptr++ = %d\n", i, value);
}

运行它,你会得到以下输出:

numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
numbers[3] = 4
numbers[4] = 5
*(numbers + 0) = 1
*(numbers + 1) = 2
*(numbers + 2) = 3
*(numbers + 3) = 4
*(numbers + 4) = 5
0, *ptr++ = 1
1, *ptr++ = 2
2, *ptr++ = 3
3, *ptr++ = 4
4, *ptr++ = 5

就像你看到的那样,所有过程得到了相同的结果。

数组表示法实际上就是指针运算。C语言标准只是将numbers[0]定义为*(numbers + 0)的语法糖。(译者注:语法糖,它意指那些没有给计算机语言添加新功能,而只是对人类来说更容易理解的语法。)无论何时,你写下一个数组表示法,比方说numbers[2],都会被编译器转换为*(numbers + 2)。这里,numbers表示数组中第一个元素的地址,+2则表示用于指针运算的偏移量。

数组变量

我们已经展示了,数组常被当作指针来处理,而且对于C编译器而言数组表示法就是指针运算。一些人自然而然地就做出了这样的假设:既然数组能被当成指针,指针也应该能赋值给数组。这是不对的,数组名变量不能被改变。我们看看下面的代码吧。

// initialize an array of ints
int numbers[5] = {1,2,3,4,5};
int numbers2[5] = {6,7,8,9,0};
int *ptr = numbers2;

// this won‘t compile
numbers = numbers2;
numbers = &numbers2;
numbers = ptr;

这段代码不能通过编译。试一试,你会得到以下输出。

incompatible types when assigning to type ‘int[5]’ from type ‘int *’
incompatible types when assigning to type ‘int[5]’ from type ‘int (*)[5]’
incompatible types when assigning to type ‘int[5]’ from type ‘int *’

虽然数组名变量代表了数组第一个元素的地址,它却表现得像一个不能被更改的常指针一样。它不能接受一个别的数组名变量或是指向另一个数组的指针的赋值。思考一下,如果你有一个数组名变量A代表一个数组,而且你能够改变A的地址,那被A指向的内存将会发生生么?

接下来看看这段能够编译的代码。

// initialize an array of ints
int numbers[5] = {1,2,3,4,5};
int numbers2[5] = {6,7,8,9,0};
int *ptr1 = numbers;
int *ptr2 = numbers2;

// this will compile
ptr1 = ptr2;

// print the addresses
printf("numbers = %p\n", (void *)numbers);
printf("numbers2 = %p\n", (void *)numbers2);
printf("ptr1 = %p\n", (void *)ptr1);
printf("ptr2 = %p\n", (void *)ptr2);

它会输出这样的结果:

numbers = 0x7fff5ea3d230
numbers2 = 0x7fff5ea3d250
ptr1 = 0x7fff5ea3d250
ptr2 = 0x7fff5ea3d250

虽然不能直接改变数组名变量,我们仍然改变一个指向这个数组的指针。代码中,我们创建了两个数组,两个int指针。我们将numbers赋给了ptr1,将numbers2赋给了ptr2。接着我们将ptr2赋给了ptr1,最后输出结果。可以看到,ptr1和ptr2都指向了numbers2数组的第一个元素。

总结

我希望你们能够喜欢这篇对C语言中数组和指针的概述。我们没有囊括关于指针和数组的一切知识,但足以作为一个开始。跟往常一样,我非常愿意接受大家的评论和建议.

关于程序设计基石与实践更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.

时间: 2024-10-26 20:38:55

程序设计基石与实践之C语言指针和数组基础的相关文章

程序设计基石与实践系列之C语言程序员必读的5本书

英文出处:fromdev:best-c-programming-books 你正计划着通过看书来学习C语言吗?"书籍是人类最忠诚的朋友".海明威一定知道书籍对一个人一生的重要性.书籍是知识的丰富来源.你可以从书中学到各种知识.书籍可以毫无歧视地向读者传达作者的本意.C语言是由 Dennis Ritchie在1969年到1973年在贝尔实验室研发的.C语言可以把程序简单地编译为机器指令,使得它成为了最高效的语言. 为什么在程序员中,C语言如此流行呢?这背后有很多原因.首先,它独立于平台,

程序设计基石与实践系列之成为一名Top的C语言程序员

英文出处:Fabien Sanglard -To become a good C programmer 问题的提出 每过一段时间我总会收到一些程序员发来的电子邮件,他们会问我是用什么编程语言来编写自己的游戏的,以及我是如何学习这种编程语言的.因此,我认为在这篇博文里列出一些有关C语言的最佳读物应该能帮到不少人.如果你知道其它的优秀读物,请给我发邮件或者直接在评论栏中告诉我吧. 问题的解答 我在之前的一篇博文中已经提到过了,目前为止,所有我所编写的商业3D引擎95%都是C89(也称作标准C,或AN

程序设计基石与实践系列之类型提升、内存分配,数组转指针、打桩和矢量变换

英文出处:Peter Fa?ka: Guide to Advanced Programming in C C语言可用于系统编程.嵌入式系统中,同时也是其他应用程序可能的实现工具之一. 当你对计算机编程怀有强烈兴趣的时候,却对C语言不感冒,这种可能性不大.想全方位地理解C语言是一件极具挑战性的事. Peter Fa?ka 在2014年1月份写下了这篇长文,内容包括:类型提升.内存分配,数组转指针.显式内联.打桩(interpositioning)和矢量变换. 整型溢出和类型提升 多数C程序员以为,

程序设计基石与实践系列之失落的C语言结构体封装艺术

英文来源于 Eric S. Raymond-- The Lost Art of C Structure Packing 谁该阅读这篇文章 本文是关于削减C语言程序内存占用空间的一项技术--为了减小内存大小而手工重新封装C结构体声明.你需要C语言的基本知识来读懂本文. 如果你要为内存有限制的嵌入式系统.或者操作系统内核写代码,那么你需要懂这项技术.如果你在处理极大的应用程序数据集,以至于你的程序常常达到内存的界限时,这项技术是有帮助的.在任何你真的真的需要关注将高速缓存行未命中降到最低的应用程序里

程序设计基石与实践系列之能让你成为Top程序员的十个C语言资源

英文出处:mycplus ---top-ten-c-language-resources 一些人觉得编程无聊,一些人觉得它很好玩.但每个程序员都必须紧跟编程语言的潮流.大多数程序员都是从C开始学习编程的,因为C是用来写操作系统.应用程序最常用的语言. C编程笔记 : 这些是华盛顿实验学院C编程入门课的部分笔记.它们是以<c程序设计语言>的补充笔记(从1995年春开始)为基础修改的,而这本书的作者就是大名鼎鼎的Brian Kernighan和Dennis Ritchie了,人们亲昵地称呼他们为K

程序设计基石与实践系列之编写高效的C程序与C代码优化

原文出处: codeproject:Writing Efficient C and C Code Optimization 虽然对于优化C代码有很多有效的指导方针,但是对于彻底地了解编译器和你工作的机器依然无法取代,通常,加快程序的速度也会加大代码量.这些增加的代码也会影响一个程序的复杂度和可读性,这是不可接受的,比如你在一些小型的设备上编程,例如:移动设备.PDA--,这些有着严格的内存限制,于是,在优化的座右铭是:写代码在内存和速度都应该优化. 整型数 / Integers 在我们知道使用的

c/c++(疑3) C语言指针与数组之间关系

c/c++ (疑1)数组和指针 c/c++(疑2) const extern 有了前面 两篇 基础,下面我们可以更深入的来介绍c/c++(疑3) C语言指针与数组之间关系 1 概述(C语言指针与数组之间关系) 指针就是指针,指针变量在32 位系统下,永远占4 个byte,其值为某一个内存的地址.指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到. 数组就是数组,其大小与元素的类型和个数有关.定义数组时必须指定其元素的类型和个数.数组可以存任何类型的数据,但不能存函数. 2    

C语言指针和数组

C语言指针和数组 binsearch else-if  shellsort  insertsort 指针和地址 指针是编程语言中的一类数据类型及其对象或变量,用来表示或存储一个内存地址,这个地址的值直接指向(points to)存在该地址的对象的值. 取值运算*p返回保存在内存地址为p的内存空间中的值.取地址&p运算则返回操作数p的内存地址 C语言是以传值的方式将参数值传递给被调用函数,被调用函数不能直接修改主调函数中的值,需要修改主调函数中的值就需要利用指针 指针和数组 数组名代表的是该数组的

C语言指针与数组易混淆知识点(一)

一指针与数组 二指针与函数 三指针数组数组指针指向指针的指针 四程序陷阱 一.指针与数组 指针:指针本身也是一个变量,它的内容是指向的内容的地址.指针同样有类型的区分,char 的指针只能指向char型数据,int 指针指向int型数据.但是指针所占内存单元的大小(即其内容)是跟操作系统的地址位数有关,比如32位地址的系统,那么指针所占的内存单元就是4个字节,16位就是2个字节,因此,指针的类型只是限定它所指向的变量的类型,其内容所占的单元大小是与操作系统的地址位数相关,与变量类型无关. 在32