C语言中指针的用法

2024-09-22

C语言中指针的用法(精选10篇)

C语言中指针的用法 篇1

1 引言

C语言是使用最广泛的程序设计语言之一,它以功能强大,语言简练,编程灵活等特点为广大程序设计人员所青睐,但对初学者是有一定难度的。尤其是指针,它既是C语言的一个重要特色,又是C语言的重点和难点。正确而灵活的使用指针,可有效的表示复杂的数据结构、动态分配空间、方便引用字符串和数组、在函数调用中获得多个返回值、并且可以直接处理内存地址等等。正是因为指针太灵活,一旦指针使用不当,将会导致程序出错,甚至造成系统崩溃。因此,要编写出正确高效的程序,正确理解和使用指针,以及了解指针使用的常见错误是很有必要的。

2 指针的理解和使用

2.1 指针的理解

理解语言指针的基础就是牢固树立指针就是地址的概念。明确地说,指针常量就是地址常量,指针变量就是地址变量。

我们先来看看指针常量:

1)若a是整型常量,则&a表示是a的地址,因为常量的地址在内存中是固定的,因此,是&a指针常量。

2)对于数组来说,只写数组名表示数组的首地址,而数组在内存中的位置是不能改变的,所以它也是指针常量。

3)对于字符串常量来说,直接写出某一字符串就是表示是该字符串的首地址,这是许多教科书上未能提到的。字符串常量在内存中有固定的位置,字符串常量的地址也属于指针常量。

4)对于某个函数来说,它在内存中的地址也是固定的。这样,函数的地址也属指针常量,在C语言中,函数名可以表示函数的地址。

再来看看指针变量:

指针变量就是地址变量,就是说,它是用来存放某一类变量地址的变量。有此概念以后,我们就容易理解指针数组的元素是用来存放变量地址的。同样,在处理链表、二叉树这类递归数据结构时,相应的结构中应存放其它结点的地址从而形成链接的成员变量,即该成员变量应是地址变量,必须用指针变量来刻画。

对于指针变量来说,C语言提供了“*”运算符,它表示取该变量的内容,而当指针变量是指向某一函数时,这时的“取内容”,应广义的理解,即执行相应的函数。

2.2 指针的使用

在程序中定义一个变量,C语言编译系统就会根据该变量的数据类型,为其分配相应的存储单元,类型不同所分配的存储单元的字节数也是不同的。指针变量是一种特殊类型的变量,定义一个指针类型后,C语言编译系统便会为该指针变量分配一个存储单元,用于存放相应变量的地址。使用该指针变量时,必须保证该指针变量指向一个明确的存储单元,即被赋值。例如:

使用指针时,常常涉及到两个运算符:

1)&:获取存储单元的地址

2)*:获取指针变量所指向的存储单元的内容

例如,&i表示变量i的地址,*p表示指针变量p所指向的存储单元的内容,即变量i的内容为2。

2.3 指针变量的类型

指针变量与一般变量一样,也有类型。指针变量的类型是指针所指向的数据的类型,我们也称指针变量类型为指针变量的基类型。指针变量的基类型不仅仅是指所定义指针变量的类型修饰符,同时还包含该指针变量是对多大的存储空间进行解释。例如:

int*p1,(*p2)[3],(*p3)[2][3];

其中,p1可指向1个int型的存储单元,p2可指向3个int型的存储单元,p3可指向6个

int型的存储单元。因此,p1+1是p1指针向下移动2个字节(基本整型占用2个字节),p2+1是p2指针向下移动6个字节(p2指向3个int型的存储单元,每个存储单元占用2个字节,即3×2),p3+1是p3指针向下移动12个字节(p3指向6个int型的存储单元,每个存储单元占用2个字节,即6×2)。

我们知道可以有下列语句定义指针与数组:

那么把两者结合起来会怎么样呢?

int*pa[3];

是定义成数组呢?还是定义成指针呢?回答这个问题的关键在于运算符的优先级。因[]的优先级高于*,加上括弧后定义变成

int(*(pa[3]));

所以pa究竟定义成什么,只要从内层依次往外层看就可以了。即pa是数组,由三个元素组成,每个元素是指针,是指向int型的指针。综合起来,pa是由三个指向int型的指针所构成的数组。

所以对于一个复杂的定义,我们可以按以下步骤来理解它:

1)根据优先次序,加上必要的括弧,使变量名在最内层。

2)从最内层开始依次往外层看,写上所看到的内容。

如:*—→指向×××的指针

[n]—→有n个元素的数组

()—→返回×××的函数等等

例如,对于int**pp;

1)加括弧int(*(*pp));

2)pp是指针,是指向指针的指针,该指针是指向int型的。

3)所以,pp是指向int型指针的指针。

注意,这时不要被两个*所困惑。pp也只不过是一个指针变量而已。请看下面的操作:如图1:

int i;

int*p;

int**pp;

pp=&p;/*把指向int型的指针变量p的地址放入pp所在的内存单元*/

p=&i;/*把int型变量i的地址放入p所在的内存单元*/

**pp=2;/*把整数2放入变量i所在的内存单元*/

现在我们知道,编绎系统不能区分简单指针和指向数组元素的指针。换句话说,对指针进行操作的时候,对指针指向的具体对象不能加以区分。如上面的pp是指向一个int型的指针变量的,而在下面的例子中,pp就指向了一个指针数组。如图2:

当然还有pp指向其它对象的可能。所以定义了1个指针以后,要记住它究竟是指向一个简单的对象呢?还是指向数组?这就要由编程序者自己掌握了。

再看下面的例子:

int(*pa)[3]:

按我们先前介绍的方法去理解,可知pa是一个指向由三个整型元素组成的数组的指针,通过下面的语句可以对它进行初始化。如图3:

3 指针使用中常见的错误

指针类型有其独特的特点,正确合理的使用可以提高程序的功能,但错误地理解和使用指针,将导致无法想象的结果。下面分析讨论指针使用中常犯的错误。

3.1 指针变量未赋值

指针变量在使用之前必须赋初值,这个初值应该是一个地址量。如果只是定义了一个指针变量而没有为它赋值,此时,指针变量指向的是一个不明确的存储单元。例如:

int*p;

*p=8;/*错误*/

该例中,将10赋值给p指向的存储单元,而p指向哪个存储单元是不确定的。应该改为:

这样指针p就指向了变量a,*p=10,实际上是将10赋值给变量a。或者也可以先调用C语言中的内存分配函数得到相应的存储空间,再将函数返回的首地址赋值给指针变量。即:

因此,对应指针变量一定要保证:先给指针变量赋值,然后再使用。

3.2 指针类型不匹配

在给指针变量赋值时,只能将与指针变量基类型相同的变量的地址赋值给改指针变量。例如:

例子中,指针pa的基类型为int型,指针pb的基类型为float型,只能将int型变量的地址赋值给pa,将float型变量的地址赋值给pb。应改为:

另外,基类型相同不仅仅是指类型相同,还包含系统分配给这种基类型的存储单元的容量也相同。例如:

此例中,p1=a是错误的,原因是p1与a的基类型是不同的,p1的基类型是含有1个int型的存储单元,a是二维数组名,是地址常量,它总共有2行,每行3个元素,因此它的基类型是含有3个int型的存储单元。其中,p2的基类型是含有3个int型的存储单元,p2与a的基类型是相同的,因此可以把a赋值给p2,这样就正确了。所以,在给指针变量赋值时,要考虑基类型是否匹配。

3.3 指针变量所指示的存储单元越界

当指针变量对所指示的存储单元进行操作时,应保证所操作的存储单元应该是已分配给程序的存储单元,不应该越界,否则将会导致严重的后果。例如:

第一个for循环用于给数组a的5个元素赋值,第二个for循环用于把数组a的5个元素输出,看上去好像没错,但输出的结果却是出现了问题,程序中存在严重的错误,原因是程序开始p指向数组a的首地址,当第一个for循环执行结束时,p指向数组a的末尾,因此第二个for循环开始执行时,p指针的值并不是&a[0],而是a+5,其值超过了系统给数组分配的空间,应该在第二个for循环前面加上一条语句:

p=a;

使p指针再重新指向&a[0],这样结果就正确了。

因此,在使用指针变量时,一定要注意指针变量应在系统所分配存储空间使用,不要超过系统分配的空间范围,否则结果是无法预料的。

3.4 指针函数带回的指针不存在

函数的返回值不仅可以是一般的数据类型,还可以为指针类型,这种带回的类型为指针的函数称为指针函数。对于函数带回的指针,这个指针所指示的存储单元应该是存在的,不可以不存在。例如:

max是指针函数,编写该函数的本意是由指针P带回x、y中较大数的地址,但函数max执行完毕,返回主函数main时x、y所占用的存储单元已被释放,而p带回的将是一个不存在的存储单元,这样做显然是不正确的。因此,程序应该改为:

这样修改后,max函数中的指针p带回的则是主函数main中a、b中较大数的地址,就可以正确输出了。可见指针函数中带回的指针应该是主调函数中变量的地址,不能是已释放空间的地址。

4 小结

指针是C语言的重要概念,是C语言的重要特色,它简洁、灵活、高效,但又有风险。正确理解和使用指针,避免常见错误发生,不仅要小心谨慎,还有多编程,多上机调试,弄清细节,发现错误,积累经验。

摘要:指针是C语言的一个重要概念,文章对指针基本概念和使用作了简单介绍,总结了指针在使用过程中常犯的错误,讨论了出错的原因,指出正确使用指针的方法,并阐述了C语言中的指针与数组、函数等结合起来的指针及其应用。

关键词:C语言,指针,数组,函数

参考文献

[1]谭浩强.C程序设计[M].北京:清华大学出版社,1999.

[2]冯博琴等.C语言学习指南[M].北京:北京机械工业出版社,1996.

[3]田淑清.全国计算机等级考试二级考试教程-C语言程序设计[M].北京:高等教育出版社,1998.

C语言中指针的用法 篇2

考虑数组的指针的时候我们要同时考虑类型和维数这两个属性,换一句话,就是说一个数组排除在其中存储的数值,那么可以用类型和维数来位置表示他的种类。

A)一维数组

在c和c++中数组的指针就是数组的起始地址(也就第一个元素的地址),而且标准文档规定数组名代表数组的地址(这是地址数值层面的数组表示)。例如:

int a[10]; int *p;

p=&a[0]//和p=a是等价的:

因为a是数组名,所以他是该数组的地址,同时因为第一个元素为a[0],那么&a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&运算是一回事呢?在一维的时候当时是的,但是在高维的时候,我们要考虑到维数给数组带来的影响。

a[10]是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c++中从int[]到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。

所以我们要注意以下问题:

(1) p[i]和a[i]都是代表该数组的第i+1个元素;

(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;

(3)p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;

B)多维数组

对于二维数组a[4][6];由于数组名代表数组的起始地址,所以a(第一层)和第一个元素a[0][0]地址的数字是相同的,但是意义却是不同的。对于该数组我们可以理解为:a的一维数组(第一层),它有四个元素a[0]、a[1]、a[2]、a[3](第二层),而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[0][5](第三层),…到此我们终于访问到了每个元素了,这个过程我们经历了:a->a[0]->a[0][0];

整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址&a[0]),同时a[0]指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a[0][0]))。所以从数字角度说:a、a[0]、&a[0][0]是相同的,但是他们所处的层次是不同的。

既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中

*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用 *(*(a+i)+j)(也就是a[i][j])。

他们的示意图为(虚线代表不是实际存在的):

对照这个图,如下的一些说法都是正确的(对于a[4][6]):

a是一个数组类型,*a指向一个数组;

a+i指向一个数组;

a、*a和&a[0][0]数值相同;

a[i]+j和*(a+i)+j是同一个概念;

总结一下就是:我们对于二维指针a,他指向数组a[0,1,2,3],使用*,可以使他降级到第二层次,这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式,对于其他维数和类型的数组我们可以采用相类似的思想,

说到指向数组的指针,我们还可以声明一个指针变量让它指向一个数组。例如:

int (*p)[5];

这时p就是一个指针,要指向一个含有5个int类型元素的数组,指向其他的就会出现问题。

这个时候我们可以使用上面的什么东西来初始化呢?

我们可以使用*a,*(a+1),a[2]等。

原因很简单:我们在一个二维的数组中,那么表达方式有上面的相互类似的意义呢?只有 *a,*(a+1),a[2]等,

C)指针数组

一个指针数组是指一个数组中的每个元素都是一个指针,例如:

int *p[10];//而不能是int (*p)[10]

或者

char *p[10];

此时p是一个指针(数值上和&p[0]一样);

在前面有int t[10];

int * pt=t;//使用pt指向t

那么这里我们用什么指向int *t[10]中的t呢?我们要使用一个指针的指针:

int **pt=t;

这是因为:在int *t[10]中,每个元素是指针,那么同时t又指向这个数组,数组上和&t[0]相同,也就是指向t[0],指向一个指针变量,可以说是一个指针的指针了,所以自然要用

int **pt;

D)指针的指针

一个指针变量内部可以存储一个值,这个值是另外一个对象的地址,所以我们说一个指针变量可以指向一个普通变量,同样这个指针变量也有一个地址,也就是说有一个东西可以指向这个指针变量,然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢?由于指针变量本身已经是一个指针了(右值),那么我们这里就不能用一般的指针了,需要在指针上体现出来这些特点,我们需要定义指针的指针(二重指针)。

int *p1=&i; int**p2=&p1;

综合以上的所有点,下面是我们常常看到一些匹配(也是经常出错的地方):

int a[3],b[2][3],c,*d[3]; void fun1(int *p); void fun2(int (*p)[3]); void fun3(int **p); void fun4(int p[3]); void fun5(int p[]); void fun6(int p[2][3]); void fun7(int (&p)[3]);

函数 不会产生编译时刻的可能值(但逻辑上不一定都对)

函数

不会产生编译时刻的可能值(但逻辑上不一定都对)

fun1

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun2

b,b+i,

fun3

d

fun4

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun5

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun6

C语言中指针的用法 篇3

关键词:存储空间图;间接寻址;一级指针;二级指针;存储单元

中图分类号:TP311文献标识码:A文章编号:1009-3044(2007)16-31093-03

A Discussion about the Relation of C Language Pointer and Assemble Language Indirect Addressing——Giving Analysis from the Storage Space Digraph

WANG Hai-yan

(Computer Science Department of Suqian College, Suqian 223800, China)

Abstract: C language is a user-oriented procedural language. Pointer is the most flexible part in this language. Assemble language is a language which is processor-oriented and there is no definite conception of pointer, but the similar conception of pointer emergences everywhere. In this paper, it present the application of pointer in assemble language through indirect addressing mode. Depending on the storage space digraph and the relation between pointer of C language and indirect addressing of assemble language, it makes it easier to understand the application and relation between the two languages.

Keywords: Storage Space Digraph; Indirect Addressing; Pointer; Second Rank Pointer; Storage Unit

1 引言

作为最基本的编程语言之一,汇编语言的重要性勿庸置疑,即使是 Linux 程序员有时也需要使用汇编语言解决实际问题,理由很简单:精简、高效和 libc 无关性。假设要移植 Linux 到某一特定的嵌入式硬件环境下,首先必然面临如何减少系统大小、提高执行效率等问题,此时或许只有汇编语言能帮上忙了。我们常说汇编语言的抽象是C语言, 而C语言中最灵活是C语言中拥有“指针”这个数据类型。那么汇编语言和C语言中的指针有什么样的关系呢?我们也知道在汇编语言中,几乎所有对内存的操作都是对给定地址的内存进行访问来完成的,那么在汇编语言中,绝大多数操作也必然和地址(即指针)产生或多或少的联系。汇编语言和CPU以及内存,端口等硬件知识是连在一起的。汇编语言中存储数据的地方不仅有寄存器而且还有存储单元,更多的数据则保存在存储单元中。因此,对编程人员而言,他肯定迫切地希望访问内存,以保存

更多的数据,本文将重点阐述访问数据的方式。

2 存储空间图的概念

画一个图形,或者观察一个图形,能帮助我们把复杂的程序设计具体化、形象化,有利于掌握程序之间的内在联系,从而更好的理解程序,激发大家对程序设计语言的兴趣。本文用形象的图形解释C语言中指针以及汇编语言中间接寻址方式之间的相互关系。文中运用类似于存储空间图的方法对两门语言进行解释。说它类似于存储空间图是因为这种图形没有用真正二进制描述空间的值和空间的地址,姑且我把这种图形叫做存储空间图。

3 两门语言之间的内在联系

我们知道在计算机中,所有的数据都是存放在存储器中的,访问数据的方式并不是唯一的。在C语言中访问单元数据有两种常见方式,一是通过名字直接访问单元数据;二是通过指针访问单元数据。直接通过单元名字访问数据,两种语言都很简单,我在这篇文章中不加以阐述。接下来我就分一级指针和二级指针分别阐述汇编语言和C语言是如何间接访问内存单元数据,又是以怎样的形式加以表现的。

3.1 C语言中的一级指针和汇编语言中一次间接寻址的联系

3.1.1 C语言中一级指针定义

指针定义形如:数据类型 *指针变量,它的存储空间图表示如下:

图1中X就是一级指针,Y是一个整形变量,X、Y的本质区别在于X单元的值是用于存放内存单元的地址,Y单元是存放任意类型的数据。X之所以指向Y,是因为Y单元的地址赋予了X,同时X所指向的类型和Y同类型。

图1

3.1.2 汇编语言中的一次间接寻址是指通过寄存器或存储单元一次间接寻找操作数。

它的存储空间图表示如下:

图2中si是cpu中的一个寄存器,si中存放着Y所在单元的地址,Y是一个单元的名称,Y单元存放着普通操作数,si之所以指向Y,是因为Y单元的地址赋予了si。

图2

图1、图2中X和si都是用于存放内存单元的地址,我们从中可以得到启迪,汇编语言中也存在着类似C语言的指针。

3.1.3 以下从编程角度深入探悉二者之间的联系。

例1 用C语言指针的方法输出a单元中的数据:

main()

{int a=3,*p;

p=&a

printf("%d",*p); 3

}

例2 用间接寻址方法把a单元中的数据送往al寄存器中:

data segment

a db 3

data ends

……

lea si, a

mov al, [si]

……

以上程序均是操作存储单元中的数据,但都不是通过单元名字操作的。例1用C语言中一级指针的方法,例2用汇编语言中的寄存器间接寻址的方法。那么二者有怎样的联系呢?为了讲清楚这个问题,我把这两个程序分别用以下存储空间图表示:

图3 C语言编程的存储空间图图4 汇编语言编程的存储空间图

上图中p和si其实质都是一级指针,在C语言中是用指针指向的内容,即用*p的形式来取得单元的数据,而汇编语言是通过寄存器一次间接寻址方式来取得单元内容,当然这都要求指针p和寄存器si首先获得单元的地址。(p=&a/lea si, a)。

3.2 C语言中的二级指针和汇编语言中二次间接寻址的联系

3.2.1 C语言中二级指针的定义。

指针定义形如:数据类型 **指针变量,它的存储空间图表示如下:

图5中X相对与Z这个变量是二级指针,它和一级指针的区别在于X这个指针所指向的内容仍然是某个单元的地址(图中是Z单元的地址)。要想输出Z单元的内容,则需要通过两次指向才能完成。

图5

3.2.2 汇编语言中二次间接寻址是指通过寄存器或存储单元两次间接寻找操作数。

它的存储空间图表示如下:

图6中si其实就等价于图5中定义的X,都是二级指针。但在汇编语言中si是一个指针寄存器,无论是作为一级指针还是作为二级指针,我们在使用的时候都是不需要定义的。事实上汇编语言中根本没有一级指针和二级指针的定义,也就是说汇编语言中没有C语言中定义上的繁琐,但你仍可以用C语言的思想来解释汇编语言中的指针问题。

图6

3.2.3 以下从编程角度深入探悉二者之间的联系。

例3 输出以下字符串

main()

{char *p[] = {"ab", "cd", "ef"};

char **sp = p;

int i;

for(i=0; i<3; i++)

printf("%s", **(sp+i));

}

图7 C语言编程的存储空间图

例4 累加数组array中元素和的程序段

data segment

array dw 10,20,30,40,50,60,70,80,90,100

count dw 10

sum dw?

table dw 3 dup(?);地址表

data ends

code segment

main proc far

assume cs:code, ds:data

start:push ds

……

mov table, offset ary

mov table+2, offset count

mov table+4, offset sum

mov bx, offset table

call proadd

ret

main endp

proadd procnear

……

mov si, [bx]

mov di, [bx+2]

mov cx, [di]

mov di, [bx+4]

xor ax, ax

next: add ax, [si]

add si, 2

loop next

mov [di], ax

……

proadd endp

code ends

end start

图8 汇编语言编程的存储空间图

以上程序均是通过两次操作才找出单元中的数据,前者是用C语言中二级指针二次指向的方法,后者是用汇编语言中的两次间接寻址的方法寻找操作数。从上图可知这两门语言的内在联系,即两门语言最终都是通过指针两次寻址来访问操作数。所不同的是:前者是二级指针的概念,后者是两次间接寻址。这种本质相同,只是语言描述上有所不同的概念,从存储空间图的视角可以帮助我们更好的理解他们之间的联系。

依此类推,对于多级指针的学习,我们也可以借助存储空间图来帮助我们更好的分析程序,理解程序,对于一些复杂的问题给予简化,对于不同语言之间的关系我们也能进行更好的深入思考。

4 结束语

本文通过存储空间图深入浅出的探悉了两门语言在寻找数据方面的联系。可以说C语言的指针和汇编语言的寻址方式一直困扰着很多编程者,尤其对二级指针、二次间接寻址的理解及运用更加困难。文中的例题均以最简单的题型来说明这些深奥的道理,而且从存储空间图的视角分析了二者之间的联系。相信这篇文章,不仅能帮助我们运用存储空间图解释问题、分析问题,而且能帮助我们更好的思考不同语言之间的联系,更好的去思考程序设计。

参考文献:

[1] 裘宗燕. C++程序设计语言(特别版). 北京机械工业出版社, 2002.7.

[2] Standley B.lippman. C++ Primer中文版. 人民邮电出版社,20006.3.

[3] 谭浩强. C程序设计(第二版). 清华大学出版社出版,2005.6.

[4] 钱能. C++程序设计教程. 清华大学出版社, 2005.5.

[5] 沈美明. IBM PC汇编语言程序设计. 清华大学出版社,1993.9.

[6] 钱晓捷. 汇编语言程序设计. 电子工业出版社,2003.

C语言中的函数与指针 篇4

随着计算机技术的飞速发展及应用领域的扩大, 熟练掌握一门语言已变的尤为关键。C语言这门课程在计算机的基础教学中一直占有比较重要的地位, 然而要想突破C语言的学习, 对函数和指针的掌握是非常重要的, 本文将具体针对函数和指针的关系做详尽的介绍。

一、函数的有关概念

为了使程序的编写更加清晰、直观且易于修改, C语言中引用了函数。所谓函数, 就是一个程序模块, 该模块用来完成一个特定的程序功能。引用一个函数时, 需要包括对函数的定义、声明, 继而调用。在掌握函数相关概念的同时, 有以下几点需要注意:

(1) 调用函数和被调用函数

由上例可以看出, 函数A在执行的过程中包括了对函数B的调用, 则函数A称为调用函数 (调用函数B) , 而函数B被函数A调用, 称为被调用函数。

(2) 实参和形参

调用函数中定义的变量是实参, 被调用函数中定义的变量是形参。如上例, 函数A中的变量a是实参, 函数B中的变量b是形参。

(3) 实参变量和形参变量之间的独立性

实参变量和形参变量之间只存在值的传递过程, 实参变量的存储空间在调用函数中分配, 而形参变量的存储空间在被调用函数中分配, 被调用函数执行完毕后, 其所分配的存储空间被释放, 即形参变量的存储空间被释放, 它不会返回值给实参变量, 也不会参与调用函数的继续执行。例如 (实现两个数的交换) :

显然, 函数main是调用函数 (调用函数swap) , 函数swap是被调用函数。main函数中的a, b由main函数分配存储空间, 而swap函数中的a, b由swap函数分配存储空间。main函数执行到swap函数时, 调用swap函数, swap函数为其变量分配存储空间, 然后实现了swap函数中变量a, b的值交换, 执行完毕后即释放其分配变量的存储空间。继而, main函数继续执行, 但其变量a, b没有做任何改变, 即main函数不能实现a, b的交换。由上例可以看出, 若单纯的使用变量, 则被调用函数无法改变调用函数中的变量值, 即swap函数无法实现main函数中变量a, b的交换。

二、指针的有关概念

指针是C语言中功能最强大, 使用最广泛的一种数据类型, 主要用于描述存储单元的地址。通过使用指针, 可以在函数中进行传址调用。

(1) 指针变量的定义

定义指针变量的一般形式:类型标识符*变量名;其中, 变量名前的符号“*”表示将要定义的变量, 类型说明符表示该指针变量所指向数据的类型。例如:int*p1;char*p2;float*p3;

(2) 指针变量的引用

&为取地址运算符, 其一般形式为:&变量名, 例如:

int x=3, *p;p=&x;指针变量p指向变量x。

在使用x的值时, 可以直接使用x, 也可以用*p来代替使用x。此外, 指针变量一定是和它所对应的变量相互引用, 即指针变量在使用时一定要有明确的指向, 必须赋予具体的值, 否则将可能导致错误。

三、指针与函数的关系

在函数的编写过程中, 若单纯的只用变量参数, 则无法实现被调用函数改变调用函数中变量值的目的。而为了实现这一目的, 就需要函数和指针之间的结合使用。

(1) 引用指针, 可以实现调用函数和被调用函数中的指针变量共同指向调用函数中的存储单元, 从而实现被调用函数改变调用函数中变量值的目的。例如:

由上例可以看出, 在调用函数 (main函数) 中定义了变量a和指针变量p1, 被调用函数 (change函数) 中定义了指针变量p2。程序首先从main函数开始执行, 分配变量a和指针变量p1的存储单元, 此时指针变量p1指向变量a (p1=&a) 。当程序执行到change函数时, 程序跳转到change函数执行其函数体, change函数为其指针变量p2分配存储单元, 同时p2也得到了p1传过来的值 (变量a的地址) , 此时p2也指向了调用函数中的变量a, 即实现了p1和p2共同指向了调用函数中的存储单元 (变量a的存储单元) , change函数执行其函数体 (*p2=3) , 即使a的值变为3。change函数执行完毕后, 释放其变量的存储空间, 转而继续执行main函数, 此时a的值已经发生改变 (由2变为3) , 实现了被调用函数改变调用函数中变量值的目的。

(2) 调用函数和被调用函数中实参和形参之间的关系图

被调用函数执行完毕后, 释放它所分配的存储单元, 而调用函数分配的存储单元仍继续使用。此外, 只有当调用函数中传指针值 (即实参是指针值) , 而被调用函数中引用变量 (即形参收到指针值后, 在函数体内引用变量值) , 才能达到改变的目的。

例如, 实现变量a和b的交换, 程序如下:

例1虽然传的是指针值, 但在函数体的执行过程中引用的仍然是指针值 (引用x和y) , 所以不能实现a和b的交换;例2程序传指针值后, 引用变量 (*x和*y, 即a和b) , 所以能实现a和b的交换。

四、结束语

在以后的编程过程中, 若遇到想通过被调用函数改变调用函数中变量值的目的, 则可以把该变量的地址值传给被调用函数, 从而达到改变的目的。鉴于文章篇幅及个人能力有限, 本文肯定还存在许多不足之处, 仅供大家学习和参考。

参考文献

[1]谭浩强.C程序设计[M].二版.清华大学出版社, 2004.

[2]杜友福.C语言程序设计[M].二版.科学出版社, 2007.

深度理解C语言的指针与数组 篇5

Section 1 左值与右值

编译器为每个变量分配一个地址(左值),该地址在编译时可知,且变量在运行时一直存于该地址。存于该地址的变量的值(右值)只有在运行时可知。因此,编译器如果需要一个地址来执行某种操作,它可以直接进行操作,如果需要一个变量的值,它需要发出指令从指定地址中读入变量值并存于寄存器中。到这里,可以理解作为一个指针变量,它本身的地址是左值,它变量的值(即指向的地址值)为右值。所以指针首先需要在运行时取得它的当前值,然后才能对它进行解除引用操作。

数组名是一个左值,即内存中的位置。但数组名是一个不可修改的左值,即不可被赋值。

int main()

{

int a[3] = {0};

int b = 1;

a = &b; //ERROR: “=” : 左操作数必须为 l 值。

return 0;

}

Section 2 数组与指针的不同

一个例子:

int main()

{

char arr[4] = “abc”; // Note 1

//char arr[4] = {'a', 'b', 'c', '�'}; // Note 2

char *ptr = “ABC”; // Note 3

//ptr+1 = &arr[2]; // Note 4

printf(“arr: %x, %x, %x %x ”, &arr, &arr[0], &arr[1]); //Note 5

printf(“ptr: %x, %x, %x %x ”, &ptr, &ptr[0], &ptr[1]);

return 0;

}

Note 1&2等价定义,其结构如下:

a b c �

[__] [__] [__] [__]

12fed4 +1 +2 +3

Note 3结构如下

42703c A B C �

[__] [__] [__] [__] [__]

12fec8 42703c +1 +2 +3

Note 4复习一下Section 1.显然的错误,因为p+1首先需要知道p的值(右值),只有在运行时刻才能得到,编译时刻就希望对其所在的地址进行赋值显然错误,

Note 5验证Note1和3,运行结果如下:

arr: 12fed4, 12fed4, 12fed5

ptr: 12fec8, 42703c, 42703d

可以发现,arr的地址(左值)的结果与数组中首元素的地址一致,而ptr的变量值(右值)与数组的首元素地址一致。

因此对一个数组中的元素进行引用,c=arr[i]和c=ptr[i]都能够取出相应数组中的第i个元素。但要注意这两个操作的过程完全不同:

c = arr[i]; c = ptr[i];

1:取地址12fec8的内容,即42703c

1 取出i的值与12fed4相加 2:取出i的值与42703c相加

2 取地址(12fed4+ i)的内容 3:取地址(42703c+i)的内容

得到结论:尽管c=arr[i]和c=ptr[i]用同样的形式完成了同样的功能,但绝不可以混用。注意数组原始的声明方式,如果原始声明为数组式的,那么对其元素的引用要使用数组形式,反之亦然。

文件1中:

C语言教学中指针浅析 篇6

一、使用电子教室配合多媒体教学

C语言程序设计是一门实践性极强的课程, 传统的课堂教学往往使学生在听课的过程中感到极为吃力, 几乎完全不能理解教师所讲解的内容。而C语言中的指针又是C程序设计中的一个重点也是一个难点, 概念抽象, 学生更是难以理解。在我们的教学中采用将学生分组带进实验室进行实际的多媒体配合电子教室进行和教师同步教与学的方法。让教师一边演示一边讲解, 使指针的教学由枯燥、晦涩变得生动活泼起来, 加深学生对指针的理解和掌握, 比如说针对以下两个问题的教学。

1、什么是指针

指针实质是代表某一个内存单元的地址, 是内存单元的编号;在C语言的译系统中, 对于变量的访问形式之一, 就是先求出变量的地址, 然后再过地址对它进行访问, 这就是指针, 变量的指针就是指该变量所占用的内存单元的首地址。

2、什么是指针变量

指针变量是存放内存单元编号的变量, 或者说指针变量就是存放地址的变量。比如说有这样的定义:

int*i_point;;

在这里point是变量的名字, int*代表i_point这个变量存放的是int型变量的地址。再作如下定义:

int i=10

i_point=&i;

为了让学生彻底理解指针变量和指针是两个不同的概念, 教师可以制作出有趣的PPT动画进行演示。

二、指针教学中学生容易犯的错误

1、指针变量未初始化

学生在学习的过程中定义好一个指针变量后, 常常会忘记对其进行初始化操作。在C语言中, C语言的变量, 都是先定义后使用, 而C中的指针变量却有所不同, 指针变量在使用前, 不仅要先定义, 还要进行初始化也就是要对对指针进行赋值。如果没有对指针变量进行初始化, 那定义的指针变量就不具有具体的指, 将会随机指向内存单元中的某个地址, 如果不幸指向系统区中, 就会造成灾难性的后果。所以必须对指针变量进行初始化。

在这个程序中, 指针变量*point没有经过赋值操作, 就很容易造成计算机系统的灾难性崩溃!

2、对指针变量直接进行赋整数值

运行结果为:10, 20

10, 20

本程序定义了两个指针变量g1和g2, “g1=&i;和g2=&j;”语句是将i与j的地址分别赋给g1和g2, 不能写成“*g 1=&i;和*g 2=

&j;”。

与指针相关的两个运符:

(1) &:取地址运算符。

(2) *:指针运算符或称间接访问运算符, 取指针所指向的目标值。

“&”与“*”运算符的优先级别相同, 按自右而左的方向结合, 如“g1=&i;”语句, 若&*g1, 先进行的是*g 1的运算, 再执行&运算。

如:*&i的含义是什么?当然先进行&i运算, 得到i的地址, 再进行*运算。

三、指针与数组的安全性

C语言中指针的用法 篇7

在C/C++程序中,对象(变量)存储在程序的内存空间中,并对应着惟一的地址。确切地讲,这个地址还跟对象(变量)的类型(auto,static,global,etc)有关。

同样,函数与对象(变量)相似,它也有自己惟一的地址与之对应。那么,就可以定义一个指针指向该地址(函数),这个指向函数的指针就叫做函数指针。与函数指针相似的一个术语是指针函数,表示其返回值是指针类型的函数。

2 定义

所有的C/C++中的对象(变量)必须经过定义才能够使用,函数指针也不例外。函数指针的定义与对象(变量)类似,略有不同。函数指针定义的一般形式为:

Return_Type(*Function_Fointer)(Argulist)。

示例(一):pointer to object pointer to function

注意函数指针定义中括号的使用保证了*和func_ptr的优先结合,否则func_ptr将变成一个返回值为指针的普通函数。函数指针中一个非常重要的问题是要保证函数指针的类型与它所指向的函数的一致性,比如示例一中func_ptr与f的返回类型和参数列表都是一致的,这样才不会报错。

函数指针的初始化和赋值有二种方式,第一种方式如示例一所示,对函数名使用取址运算符&。由于函数名本身就代表着入口地址,所以也可以直接把函数名赋给函数指针,那么示例一中可改为:int(*func_ptr)(int arg)=f。

示例(二)

运用typedef可以使函数指针的定义和使用更加方便和简洁。在示例二中,typedef的使用是程序更加明朗和可读。

示例(二)

typedef的功能是定义新的类型。示例二中第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。

3 使用

通过函数指针去调用函数有两种方法,包括隐性(explicit)和显性(implicit)调用。显性调用如示例三所示。隐性调用只需要将(*funcptr)(3,2)改为funcptr(3,2)即可。这两种方法完全是等同的,具体由个人爱好所决定。

4 应用

4.1 应用技巧

函数指针(function pointer)是一类特殊的指针,它在C/C++编程中有着广泛的应用。函数指针主要有两个方面的用途:调用函数和做函数的参数。利用函数指针作为参数和调用函数,就开创了许多程序设计中的可能和技巧,主要体现在以下3个方面:

(1)多态(polymorphism)。多态实现的机制十分复杂,其中虚函数表起着重要的作用。一般来讲,编译器会为数据结构加入一项成员,是一个指向虚函数表的指针(经常被称为vptr),而虚函数表中存放的是由函数指针组成的数组,函数指针指向实际所调用的函数。

(2)回调函数(call-back)。实现回调函数的关键是函数指针作为函数的参数。回调函数应用的非常广泛,例如Win32的Win Proc其实就是一种回调,用来处理窗口的信息。

(3)取代switch–case语句。switch-case语句在选择项数上和程序维护上有一定的限制,使用函数指针可以解决这一问题。

4.2 应用实例

4.2.1 消息映射雏形

Windows程序是以消息为基础的,其消息映射极其复杂,函数指针发挥着重要的作用。下面用一个简单的消息映射雏形来说明函数指针的使用方法和技巧。

定义一个MSGMAP结构和一个cou宏:

MSGMAP结构中第二个元素pfn是一个函数指针,以此指针所指的函数处理n Message消息。

设计一个数组_message Eneries[]把程序中预处理的消息以及消息处理函数的关联性建立起来。

可以看出,Wnd Proc函数永远不必改变,每当有新的要处理的消息时,只需在_message Entries[]数组中加入新的元素即可。而函数指针在整个消息映射中扮演十分关键的角色。

4.2.2 回调函数

回调函数是一个通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来用它所指向的函数时,就说这是回调函数。回调函数使用的场合很多,下面就通过一个简单回调函数实例来阐述函数指针在回调函数中的应用。

假设Barrel Volume是库函数中用来计算柱体体积的一个函数,定义如下:

由于不同类型柱体底面积的计算方法不一样,用户可以根据需要编写自己的面积计算函数,通过函数指针传递进去,增加了程序的维护性和简洁性。

如果用户计算圆柱的体积,可以这样编写:

其中Area0(),Area1()为回调函数。

5 结语

函数指针由于定义复杂,抽象难懂,使用技巧性强,是C/C++程序设计学习中的难点。,在程序设计的过程中,要认真思考,巧妙应用,克服函数指针的致命缺点(函数指针无法对参数和返回值的类型进行检查),利用函数指针设计出优秀的程序代码

参考文献

[1]汪明光.C++语言中函数指针的分析与应用.巢湖学院学报,2006,(3):30-33.

[2]李永祥,陈意云.基于函数指针数组的代码迷惑技术.计算机学报,2004,(12):1707-1708.

[3]侯俊杰.深入浅出MFC.武汉:华中科技大学出版社,2001.

全面解析C语言中的指针变量 篇8

1 内存分配

计算机中的内存都是编址的,就象家的地址一样。在程序编译或者运行的时候,每遇到一次声明语句(包括函数的传入参数的声明),编译系统就为已定义的变量分配相应的内存单元,也就是说,每个变量在内存会有固定的位置,变量的数据类型不同,它所占的内存单元数也不相同。若我们在程序有如下定义:

如果将变量的地址保存在内存的特定区域,用变量来存放这些地址,这样的变量就是指针变量,通过指针对所指向变量的访问,也就是一种对变量的“间接访问”。

设有:

现在我们访问变量a、b、x、y、m、ch1、ch2对应的内存单元时,不仅可以通过变量名(直接访问),也可以通过指向这些变量的指针(间接访问)。

二者的关系如图2所示。

此时访问a变量对应的内存空间有:a、*pa二种方式。

2 指针就是一个整数

指针,是一个无符号整数(unsigned int),它是一个以当前系统寻址范围为取值范围的整数。32位系统下寻址能力(地址空间)是4G-byte(0~2^32-1)二进制表示长度为32bit(也就是4B)int类型也正好如此取值。

例如:程序1

程序1得到的答案和程序2的答案一致。(不同机器可能需要调整一下pt的取值。)

3 C的按值传递

C中函数调用是按值传递的,传入参数在子函数中只是一个初值相等的副本,无法对传入参数作任何改动。但实际编程中,经常要改动传入参数的值。这一点我们可以用传入参数的地址而不是原参数本身,当对传入参数(地址)取(*)运算时,就可以直接在内存中修改,从而改动原想作为传入参数的参数值。

在调用函数语句inc(&a)执行时,a的地址传给了函数inc的形参(*val)并在函数体内执行了(*val)++;就等于改变了变量a的值。执行完inc函数后返回到main函数输出a的值为4,操作*val,即是在操作a了。

4 双重指针(指向指针的指针)

设有如下程序:

5 指针数组、数组指针和指向函数的指针

指针数组是一组有序的指针集合,它所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。

设有:int a,b,c;int*p[3]={&a,&b,&c};

上述语句定义了一个名称为p的指针数组,其中有三个元素p[0]、p[1]、p[2]分别指向三个整型变量a、b、c。此时可以通过*p[0]、*p[1]、*p[2]这三种方式来访问a、b、c。

数组指针是指向数组的指针。数组在计算机内存中占据一片连续的存储空间。数组名存放着这一连续空间的首地址(注意这是一个常数)。数组指针指向这一片连续空间的起始地址。

设有:

int a[10]{1,2,3,4,5,6,7,8,9,10},*p=a;/*p指向了起始地址*/

此时访问数组a中第三个元素有三种方法:a[2]、*(a+2)、*(p+2)

设有:

int b[3][3]={{1,2,3},{4,5,6},{7,8,9}},*p=&[0][0];/*此时p这是一个列指针*/

此时访问数组b中第一行的第2个元素有:a[0][1]、*(p+1)、*(*a+1)(注:a是一个行指针)三种方法。

指向函数的指针,从二进制角度考虑,数组名是该数组数据段首地址,函数名就是该代码段的首地址。

设有如下程序:

程序运行的结果与上面第4个问题完全相同。丰富了访问函数的方法。

6 结束语

指针可以指向任何类型的变量用,有效地表示出复杂的数据结构;指针可以指向函数;用于函数参数传递,达到更加灵活使用函数的目的,有了指针在编程时丰富了访问内存单元的手段和方法,使C语言程序的设计更加灵活、实用、高效。

参考文献

[1]陈强.C语言核心开发技术从入门到精通[M].北京:电子工业出版社,2009.

[2]匡松.C语言程序设计[M].北京:中国铁道出版社,2008.

[3]李俊杰.C语言复习指南与题解[M].北京:清华大学出版社,2003.

C语言中数组与指针关系的探讨 篇9

C语言是计算机程序设计语言中专业性较强的语言之一,以其功能丰富,表达能力强、使用方便灵活、目标代码运行效率高、可移植性好而受到青睐。数组和指针是C语言教学中的重点也是难点,多维数组与指针的关系更是让学生迷惑不解。如何使学生轻松理解并掌握数组与指针的对应关系成为教学中的难题。下面通过对一维﹑二维数组与指针的关系详细的探讨,总结C语言中多维数组与多级指针的对应关系。

1 一维数组与指针

数组是一组类型完全相同的有序数据的集合。用一个统一的数组名和下标来唯一确定数组中的元素。通过对数组一章的学习,定义一个一维数组并不难。例如:

即表示定义了一个整型的一维数组。数组名为a,数组长度为5。

数组一旦定义,其数组名代表的不是变量,而是一个描述该数组起始地址的常量,也就是该数组第0个元素的地址&a[0]。在C语言中,地址又称为指针,如果常量或变量描述的是另一个变量的地址,我们称该常量或变量指向另一个变量。所以a作为第0个元素的指针,可称a指向a[0]。通过数组名加地址偏移量的方式可以描述数组中其它元素的指针。a+1指向a[1],a+2指向a[2],以此类推。引用数组元素时可以采用下标法,也可以采用指针法。例如:

这种方式不能用于数组名,因为数组名是常量,不能使用自加自减运算。

2 二维数组与指针

二维数组是由行和列所组成的平面结构的数组。例如:

上例中,若把每一行看成一个大的元素,那么可以把x看成是一个特殊的一维数组,它有3个大的元素:x[0],x[1],x[2],每个大的元素又是一个包含4个具体的元素。如下表。

相当于定义了三个一维数组x[0],x[1],x[2]。x[0],x[1],x[2]看成了一维数组的数组名。

二维数组的数组名代表的是其特殊元素的指针,x指向的是其特殊元素x[0]。那么x+1指向x[1],x+2指向x[2],以此类推。要描述具体元素,则可以通过前述一维数组的方法,一维数组的数组名加下标描述的是其元素。那么第0行的元素即可为x[0][0],x[0][1]等。x[i]相当于一维数组的数组名。也可以采用指针法,因为x指向x[0],则x+i指向x[i],所以*(x+i)即表示x[i]。x[i]作为一维数组的数组名指向其内的第0个元素x[i][0],那么x[i]+j即指向第i行的第j个元素x[i][j],*(x[i]+j)表示的为x[i][j],将x[i]换成*(x+i),所以*(*(x+i)+j)表示的也是x[i][j]。从以上的分析可以看到,通过二维数组的数组名对元素的引用并非通过一次间接应用完成的,而是通过了两级指向。这说明二维数组的数组名代表的不是具体元素的地址,而是特殊元素的地址,即行地址。所以二维数组的数组名也升级成了二级指针常量。而每一个特殊元素则相当于一维数组的数组名来使用。

引用二维数组的元素可以用下列语句描述:

在第一个输出语句就会出现差错。因为p1=a将该数组的首地址给p1。p1指向*p1。*p1是一级指针。所以在该数组首地址取1个指针类型(4字节)的值,在32位环境下是第0列元素a[0][0]的值**p1指向*p1,数据类型是该指针的基本类型int,所以在第0行第0列元素指向的内存位置处取一个整型值,访问了不可以访问的内存区域。所以会出现‘xxxxxx指令引用的xxxxxx内存该内存不能为read’的错误。这就说明指针变量p1虽然也是二级指针变量,但并非与二级指针常量a的使用方式等价。p1只是一般的二级指针,不能正确描述二维数组中的行列值。指针变量p2的定义是C语言中行指针的定义方式。p2先后*相结合说明p2是指针变量,再与后面的[5]相结合,说明它指向的是具有5个元素的行。二维数组的每行都包含5个元素所以p2可以指向a中的行。a作为二维数组的数组名指向的是第0行。p2=a;使p2与a具有相同的指向。在引用数值元素时,p2就可以代替a来使用了。那么数组元素a[i][j]也可描述成*((*p2+i)+j)。在此还需说明的是虽然在定义p2时只用了一个*符号,但却将p2定义成了特殊的二级指针——行指针。从上面的分析可以看到,二维数组的数组名和行指针变量指向的并不是整个二维数组,而是二维数组中的行。

3 三维及多维数组与指针

二维数组可以看成由一维数组所组成,把一维数组当成了特殊元素。二维数组同样可以作为一个整体。

类型和长度完全相同的若干一维数组可以组成二维数组。那么同样可以把类型和长度相同的二维数组作为一个特殊的元素放于一个数组中,这就构成了三维数组。因为它的每一个特殊元素都是平面结构,那么三维数组则是由若干平面组成的立体结构。例如:

int m[3][5][10];

则定义了一个三维数组,其中包含了3个5行10列的二维数组。要想指向每一个平面,则可以定义指向平面的指针,int(*p3)[5][10]。由此可以推及n维数组与指针,定义一个n维数组的格式为:

类型标识符数组名[N1][N2][……][Nn];(其中N1,N2,……,Nn均为常量表达式)可以把n维数组看成由N1个n-1维数组组成的一维数组,要指向其中的n-1维数组,则应定义指向n-1维数组的指针,方式为:

类型标识符(*变量名)[N2][N3][……][Nn];

这里的指针变量和n维数组的数组名都是n级指针,都可以指向的是n-1维数组。

4 结束语

数组与指针在C语言教学中很容易引起学生的误解,希望能通过本文的探讨对读者理解数组与指针的关系带来方便。不当之处敬请同行不吝赐正。

参考文献

[1]谭浩强.C程序设计[M].北京:清华大学出版社,1997.

[2]谭浩强,张基温,唐永炎.C语言程序设计教程(第二版)[M].北京:高等教育出版社,1998.

[3]谭浩强.C程序设计题解与上机指导(第二版)[M].北京:清华大学出版社,2002.

[4]杨立影,高爱华,李晖.C语言程序设计教学方法的探索与实践[J].中国科技信息,2010,(15).

[5]彭召意,朱艳辉,周玉.C++面向对象程序设计课程的实践教学研究[J].计算机教育,2010,(05).

[6]夏承遗,李文杰,孙世温.问题驱动的“C++程序设计”教学方法研究[J].计算机教育,2010,(01).

[7]马晓亭.“C++高级编程”教学方法探索与实践[J].计算机教育,2010,(04).

[8]张明川,孙士保.面向工程实训的“C++程序设计”教学改革研究与实践[J].计算机教育,2010,(03).

C语言中指针的用法 篇10

关键词:C语言,指针,函数调用,形式参数

1 引言

目前, 在普通高等院校中, 都开设有计算机程序设计的课程, 以培养学生利用计算机编制程序、解决实际问题的能力。在众多的程序设计语言中, C语言以其高效、简洁、结构性强、功能强大而成为首选的教学语言。C语言的一个重要特点就是支持功能强大且类型丰富的指针, 强大的指针给C语言带来巨大的灵活和便利的同时, 也给学生的学习增添了难度, 尤其在函数调用时, 用指针做函数参数时很容易出错。本文将就这一在教学和学习中, 困扰师生的问题进行较深入的讨论。

2 C语言中的函数调用

通常, 在各类程序设计语言中, 都是以函数作为程序的基本组成单位的。一个函数实现一个功能, 函数之间通过参数调用和return语句来互相传递消息, 当然, 我们还能通过全局变量来实现数据在函数之间的传送。但是, 我们从“高内聚、低耦合”的程序设计基本原则出发, 要尽量限制使用全部变量, 只使用函数参数和return语句来实现函数之间的数据交换, 以符合“单入口、单出口”的程序设计基本原则。很多语言中的参数调用有两种形式, 即“传值调用”和“引用调用”。“传值调用”是单向的, 只能将数据由主调函数 (实参) 传递给被调函数 (形参) ;而“引用调用”是双向的, 主调函数 (实参) 将变量名传递给被调函数 (形参) , 被调函数中结束调用后, 再将形参的值回传给实参, 因而被调函数能改变主调函数中实参的值。C语言中的函数调用严格来说都是传值调用的, 任何情况下都只能将主调函数 (实参) 的值传递给被调函数 (形参) , 那么要想让被调函数改变主调函数中实参的值, 就只能采用地址传递的方法。即在主调函数中用地址 (指针) 作参数, 将变量的地址传送给被调函数 (形参) , 这种情况下, 要求被调函数的形参类型必须是与主调函数实参相同类型的指针型变量。

3 指针做函数参数时

3.1 指针在函数调用时的不同表现形式

下面, 我们以一个简单的数据交换的程序来展示5种不同的参数形式在函数调用中实现的不同效果。

我们对上述5中不同形式的函数调用情况进行分析。

先分析swap1函数。swap1传的是值的副本, main () 函数中, 实参a和b的值分别是1和2, 函数调用时, 将实参值1和2传递给了形参p和q, 在函数swap1内部, p, q的值确实是交换了, 但是他们是局部变量, 不会影响到主函数的a, b。当swap1返回时, p、q也就被删除了。所以从主函数的角度来看, swap1是不能完成a、b的值的交换的。

我们再来讨论函数swap2。主调函数中的实参是&a和&b, 是两个地址值, 被调函数中的形参是两个指向int型数据的指针变量, 指针变量接受地址值, 这是合法的。我们在swap2函数内部定义了一个指向整型数据的指针变量temp, 但是我们却没有给temp初始化, 所以指针变量temp是没有值的, 它没有指向一个确定的内存单元, 在C语言中, 像temp这样定义了基类型, 但没有初始化的指针变量, 称之为“野指针”。正因为temp没有确定值, 所以*temp也是不存在的, 故*temp=*p是不合法的。特别的, 如果将swap2函数体内的第一句话改成int x, temp=&x;则符合C语言的语法规定。x是定义好的一个整型变量, 操作系统会给它分配一个内存空间, 然后把该内存空间的地址赋值给temp, 那么以后*temp就相当于x。

函数swap3同样用两个指向整型的指针变量p和q做形参, 主调函数同样是传送地址过去。这样, p指向a的地址, q指向b的地址, 在swap3函数内, 将p和q的值交换了, 也就是说, swap3结束调用以后, p指向a的地址, q指向b的地址。在swap3函数内, 看似p、q的地址交换了, *p、*q的值也确实交换了, 但当返回主函数以后, 由于p和q的值不能返回给实参, 所以, 在主函数中, a、b并没有任何变化。这种函数调用方式同样也不能对主函数实现两个数的交换。

函数swap4可以对主函数实现值的交换。我们来具体分析一下, swap4中, 用两个指向整型的指针变量p和q作形参。主调函数将a和b的地址传递给p和q以后, p和q指向的是a和b这两个变量值。swap4函数体内, 将*p和*q交换, 实际上就是将a和b的值交换, 因为p存储的是&a, *p实际上就是a, 同理, *q就等同于b。简单地说, swap4调用结束后, 由于p和a共用内存地址, p和b共用内存地址, 所以, 对*p操作就是对*&a操作, 也就是对a操作。

最后我们来分析下swap5函数, 我们看到swap5函数的形参定义为int&p, int&q, 这种定义方式不符合C的语法, 本身就是错误的。

3.2 指针做函数参数的用法小结

通过上述程序的分析, 我们对指针做函数参数的用法已经有了初步理解, 指针是C语言中的重要组成部分, 利用指针变量作函数参数时, 我们务必注意以下问题:首先, 被调函数用指针变量作形参时, 主调函数必须用指向同一基类型的地址值作实参。实参既可以用相同基类型的指针变量, 也可以用“&变量”获得相同基类型变量的地址值。其次, 注意到实参将地址值传递给形参后, 形参和实参实际上共用同一内存空间, 即“同址不同名”。最后, 在使用指针变量时, 要注意避免出现“野指针”, 所谓“野指针”, 是指定义过的指针变量, 但对它没有初始化, 也没有赋给相同基类型的地址值, 则该指针没有指向特定的内存单元, 对它的指向操作 (*指针变量名) 是没有意义的。

4 总结

C语言是一门程序设计语言, 其最终目的是编程实现特定的算法和数据结构。而指针作为C语言的灵魂, 在教学中是我们的重中之重。然而在教学中, 由于指针的复杂性, 很多学生在学习时往往感到迷惑, 而有些老师在教学时也只注意强调指针的用法, 而忽略了指针作用机理的分析。本文对指针在函数调用中的作用机理作了较全面的探讨, 以期对C语言学习者进一步理解指针机制、熟练使用指针、避免出现地址错误起到有益的帮助。

参考文献

上一篇:套种竹荪下一篇:基于项目招投标