c语言指针作为函数参数(C语言学习指针变量作为函数参数)(1)

一、定义某一变量的指针

为了在一个变量中存放另一变量的地址,这个变量必须定义为指针类型的变量,与其他类型一样,指针变量也要遵循“先定义后使用”的原则,定义指针变量的一般形式如下:

类型关键字 *变量名;

其中,变量名前面的*是指针类型说明符,表示这里定义的是一个指针类型的变量,最前面的类型关键字用于指定该指针变量可以指向哪一种类型的变量,或者说用哪一种类型的变量的地址对其进行初始化,我们称其为指针的基类型。例如,下面语句定义了两个可以指向整型数据的指针变量p1和p2;

int *p1,*p2;

如果将这条变量定义语句写成:

int *p1,p2;

那么它绝不是定义了两个整型指针,这里的*只对变量定义的p1起作用。用来声明指针变量的*,不会对一个变量定义语句中的所有变量都起作用。因此,为了防止像上面这样在一个变量定义语句中同时定义指针变量和非指针变量而带来的混淆,最好是在一个变量定义语句中只定义一个指针变量。

此外还需要注意的是,前面第一条变量定义语句仅仅定义了两个“可以”指向整型数据的指针变量p1和p2,但此时p1和p2究竟指向哪里,我们并不知道,因为未初始化的指针变量的值是随机不确定的,即乱码。

若要指针变量确定指向某个变量,则需要对其初始化。例如:

int a, b;

int *p1=&a,*p2=&b;

注意,上面语句中的符号*只是指针类型说明符,不是指针运算符。所以上面这条语句不能理解为将&a和&b的值分别赋值给p1和p2所指向的变量。事实上,它相当于·

int *p1,*p2;

int a,b;

p1=&a;

p2=&b;

其含义为:先定义两个可以指向整型数据的指针变量p1和p2,然后将整型变量a和b的地址分别赋值给指针变量p1和p2。这样,p1和p2就分别指向了整型变量a和b。指针变量p1与之间a的关系举个例子如图:

c语言指针作为函数参数(C语言学习指针变量作为函数参数)(2)

小提示:指针变量尽量在定义时进行初始化,以避免后面因为忘记为其赋值而造成对未知内存非法访问

因为表达式*p1引用的是p1所指向的变量a,所以语句:

*p1=10;

实际上相当于将整型常量10赋值给了变量a,而语句:

b=*p1;

相当于将p1所指向的变量a的内容赋值给b。

注意:*p1类型是p1所指向的变量的类型。

再如,表达式&(*p1)的值代表的是变量a的地址,而*(&a)引用的则是变量a的值。

二、指针变量的特点

指针变量也是一种变量,它与其它类型变量有相同处,如都在内存中用一定的存储空间,都需“先定义,后使用”等。但指针变量又有其特殊性,有以下四点:

1.指针变量的内容不能是变量的值,只能是变量的地址,而且必须用相同基类型的变量的地址对指针变量进行赋值。例如:

int x,*p;

float y;

则下面两种用于指针初始化的赋值语句都是错误的;

p=x;//错误,指针p的内容应为地址值

p=&y;//错误,必须用相同基类型的变量的地址对指针赋值

正确的初始化语句应该为:

p=&x;

2.指针变量必须初始化后才能使用,不要使用未初始化的指针,否则指针所指向的存储单元是未确定的。使用未初始化的指针是非常严重的错误。

如下面的程序所示,在不知道指针变量究竟指向哪里的情况下,如果直接对它所指向的存储单元进行写操作,则会因为非法内存访问而使程序异常终止。指针的强大在于它可以指向内存的任何地方,并且可以通过指针修改那个地方的值,但是其危险性也源于此。

注意:这是个错误的程序:

#Include<stdio.h>

int main()

{

int x=10,*p;//指针变量p未初始化,不知道p指向哪里造成的错误

*p=x;//使用未初始化的指针p,引起非法内存访问错误

printf("%d\n",*p);

return 0;

}

在Visual C 6.0下编译这个程序时,编译器会给出如下警告信息,指出用户企图使用未初始化的指针:

warn c4700:local variable 'p' used without having been initialized

此时,如果忽略警告信息而运行程序,会弹出一个对话框,使程序异常终止。通常,如果程序运行时弹出类似的对话框,大多都是错误使用指针而造成。

可见,指针变量在解引用前必须进行初始化,对指针变量进行初始化,可以防止产生意想不到的结果。如果一个指针变量没有被正确初始化使其指向内存中某个确定的存储单元,就对这个指针变量进行解引,将引发一个致命的运行时错误。

指针变量的初始化可以在定义指针时进行,也可以通过一条赋值语句来完成。如果在定义指针变量时不能确定指针变量究竟指向哪里,那么可以将指针初始化为NULL。值为NULL的指针表示它不指向任何对象。

NULL是一个在头文件<stdio.h>中定义的符号常量,通常被定义为0,此时将指针初始化为0等价于初始化NULL,但是初始化为NULL更好,因为这样显式强调了该变量是一个指针变量。注意,0是可以直接赋值给指针变量的唯一整数值。

3.由于指针变量的值是一个地址值,指针运实质上就是对地址值得运算,因此指针只能参与赋值运算、算术运算和关系运算,并且指针可以参与的算术运算只有有限的两种:

加和减,即加、减一个整数或增1、减1运算。

指针的赋值运算用于改变指针变量当前的指向。例如,执行下面语句:

p1=&a;

p2=&b;

的结果是,使p1指向变量a,使p2指向变量b。而执行下面语句:

p1=p2;

相当于执行:

p1=&b;

其结果是,修改p1的指向,使p1也指向变量b。

4.指针算术运算的规则不同于一般的算术运算,指针每次增1,并非存储地址简单地增加1字节,具体增加几字节取决于指针所指向的变量的类型(即指针的基类型)。

若指针的基类型是字节型,则指针变量的增1运算相当于指针的值增加1字节;

若指针的基类型是整型,则指针变量的增1运算相当于指针的值增加sizeof(int)字节。

一般而言,指针作为操作数加上或减去一个整数n,其指针的值的变化:加上或减去n*sizeof(基类型)字节。再来看下面的语句是如何执行的:

a=*p ;

因为运算符 和*是同级,是右结合,所以a=*p 相当于a=(p ),又因为运算符 位于变量p后面,是后缀运算符,所以这条语句等价于下面两条语句:

a=*p;

p=p 1;

其含义:先取出变量p所指向的单元中的内容赋值给a,在使p指向下一个地址单元。这里,指针变量p的指向发生了变化,而p所指向的存储单元中的内容并未发生变化。

而下面的语句:

a=(*p) ;

则相当于下面的两条语句:

a=*p;

*p=*p 1;

其含义:先取出变量p所指向的单元中的内容赋值给a,再使p所指向的单元中的内容加1。

注意:这里p所指向的存储单元中的内容发生了变化,而指针变量p的指向并未发生变化。

c语言指针作为函数参数(C语言学习指针变量作为函数参数)(3)

指针的算术运算和关系运算通常用于与数组有关的程序设计中。对指向数组元素的指针变量执行增1(或减1)运算,意味着指向下一个(或者前一个)数组元素。同理,对指向同一个数组中的不同元素的两个具有相同基类型的指针变量进行关系运算,也才是有意义,它的实际上是对它们所指向的数组元素在内存中的前后位置关系进行比较。

三、指针变量作为函数参数

从键盘任意输入两个整数,用指针变量作为函数的参数,编程实现两个数互换功能,然后将交换后的数据重新输出。

编写的程序如下:

#include<stdio.h>

void swap(int *x,int *y)//函数作用:交换整型指针x和y指向的两个整型数的值

{

int temp;

temp=*x;

*x=*y;

*y=temp;

}

int main()

{

int a,b;

printf("请输入a和b的值:");

scanf("%d %d",&a,&b);

printf("交换前a=%d , b=%d\n",a,b);

swap(&a,&b);

printf("交换后a=%d , b=%d\n",a,b);

return 0;

}

程序的运行结果如下:

请输入a和b的值:12 36(按回车)

交换前a=12 , b=36

交换后a=36 , b=12

程序运行过程如下图:

c语言指针作为函数参数(C语言学习指针变量作为函数参数)(4)

,