数组名与指针变量的区别
请看下面的代码:
int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
pa = a;
for (i = 0; i <= 9; i++)
{
printf("%d\n", *pa);
pa++; /*注意这里,指针值被修改*/
}
可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的 pa
12
改成 a 试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。
其实上面的指针是指针变量,而 数组名只是一个指针常量。这个代码与上面的代
码不同的是,指针 pa 在整个循环中,其值是不断递增的,即指针值被修改了。
数组名是指针常量,其值是不能修改的,因此不能类似这样操作:a++。
前面 4、5 节中 pa[i],*(pa+i)处,指针 pa 的值是使终没有改变。所以
变量指针 pa 与数组名 a 可以互换
int i, a[] = {3,4,5,6,7,3,7,4,4,6};
int *const pa = a; /* 注意 const 的位置:不是 const int *pa */
//pa为所指内容的地址
for (i = 0; i <= 9; i++)
{
printf("%d\n", *pa);
pa++ ; /*注意这里,指针值被修改*/
}
这时候的代码能成功编译吗?不能。因为 pa 指针被定义为常量指针了
const int ic = 20;
与
int const ic = 20;没有区别
那
const int *pi
与
int const *pi
也没有区别
值传递、地址传递、引用传递
void Exchg1(int x, int y) /* 定义中的x,y变量被称为Exchg1
函数的形式参数 */
{
int tmp;
tmp = x;
x = y;
y = tmp;
printf("x = %d, y = %d.\n", x, y);
}
问:你认为这个函数是在做什么呀?
答:好像是对参数 x、y 的值对调吧?
请往下看,我想利用这个函数来完成对 a,b 两个变量值的对调,程序如
下:
main()
{
int a = 4,b = 6;
Exchg1(a, b); /*a,b 变量为 Exchg1 函数的实际参数。*/
printf("a = %d, b = %d.\n”, a, b);
return(0);
}
Exchg1(a, b)时所完成的操作代码如下所示。
int x = a; /* ← */
int y = b; /* ← 注意这里,头两行是调用函数时的隐含操作 */
int tmp;
tmp = x;
x = y;
y = tmp;
请注意在调用执行 Exchg1 函数的操作中我人为地加上了头两句:
int x = a;
int y = b;
这是调用函数时的两个隐含动作。它确实存在,现在我只不过把它 显式地
写了出来而已。问题一下就清晰起来啦。(看到这里,现在你认为函数里面交换
操作的是 a、b 变量或者只是 x、y 变量呢?)
原来 ,其实函数在调用时是隐含地把实参 a 、b 的值分别赋值给了 x 、y,
之后在你写的
,
之后在你写的 Exchg1 函数体内再也没有对 a 、b 进行任何的操作了。交换的只
是
进行任何的操作了。交换的只
是 x 、y 变量。并不是 a 、b 。当然 a 、b 的值没有改变啦!函数只是把 a 、b 的
值通过赋值传递给了
的
值通过赋值传递给了 x 、y ,函数里头操作的只是 x 、y 的值并不是 a 、b 的值。
这就是所谓的参数的值传递了。
的值。
这就是所谓的参数的值传递了。
地址传递
再看调用处:Exchg2(&a, &b);
它将 a 的地址(&a)代入到 px,b 的地址(&b)代入到 py。同上面的值
传递一样,函数调用时作了两个隐含的操作:将&a,&b 的值赋值给了 px、py。
px = &a;
py = &b;
呵呵!我们发现,其实它与值传递并没有什么不同,只不过这里是将 a、b
的地址值传递给了 px、py,而不是传递的 a、b 的内容,而(请好好地在比较
比较啦)整个 Exchg2 函数调用是如下执行的:
px = &a; /* ← */
py = &b; /* ← 请注意这两行,它是调用 Exchg2 的隐含动作。*/
int tmp = *px;
*px = *py;
*py = tmp;
printf("*px =%d, *py = %d.\n", *px, *py);
针 这样,有了头两行的隐含赋值操作。我们现在已经可以看出,指针 px 、py
是 的值已经分别是 a 、b 变量的地址值了。接下来,对*px 、*py 的操作当然也就
是对
的操作当然也就
是对 a 、b 变量本身的操作了
同理 引用传递
复杂问题简单化
展开函数,注意拷贝的副本使用
指向指针的指针
内存地址→ 5 6 7 8 9 10 11 12 13
-------------------------------------------------------------------------------------------------------
… | 50 | | | 5| | 9 |
-------------------------------------------------------------------------------------------------------
|short int i|char a| |short int * pi|short int ** ppi|
从图中看出,指针变量 ppi 的内容就是指针变量 pi 的起始地址。于是……
ppi 的值是多少呢?—— 9。
*ppi 的值是多少呢?—— 5,即 pi 的值。
**ppi 的值是多少呢?——50,即 i 的值,也是*pi 的值
函数指针
就象某一数据变量的内存地址可以存储在相应的指针变量中一样,函数的
首地址也以存储在某个函数指针变量里的
指针是个变量,指不同的位置,调用不同的函数
MyFun 的函数名与 FunP 函数指针都是一样的,即都是函数指针。
MyFun 函数名是一个函数指针常量,而 FunP 是一个函数数指针变量,这是它
们的关系。
是一个函数数指针变量,这是它
们的关系。
2 )但函数名调用如果都得如(*MyFun)(10)这样,那书写与读起来都是不
方便和不习惯的。所以
这样,那书写与读起来都是不
方便和不习惯的。所以 C 语言的设计者们才会设计成又可允许 MyFun(10)这种
形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
这种
形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
3 )为统一起见,FunP 函数指针变量也可以 FunP(10) 的形式来调用。
4 )赋值时,即可 FunP = &MyFun 形式,也可 FunP = MyFun 。
用途:
函数指针通常用来实现回调,也可以用来对模块调用以函数表的形式进行优化。
使用方法:
1、定义函数指针类型
使用typedef更直观更方便
// 定义一个原型为int Fun( int a );的函数指针
typedef int (*PTRFUN) ( int aPara );
typedef的功能是定义新的类型。这里定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。后面就可以像使用int,char一样使用PTRFUN了。
2、函数指针变量的定义
PTRFUN pFun; // pFun 为函数指针变量名
int (*pFun2) ( int a ); // pFun2也是函数指针变量名
3、函数指针作为函数的参数传递
// 定义回调函数
int CallBack( int a )
{
return ++a;
}
// 定义回调者函数
void Caller( PTRFUN cb )
// void Caller( int (*cb) ( int ) ) // 也可这样申明
{
int nPara = 1;
int nRet = cb( nPara );
}
// 使用回调
void Test()
{
Caller( CallBack ); // 直接使用回调函数
PTRFUN cb = CallBack; // int (*cb) ( int ); cb = CallBack;
int nRet1 = cb( 99 ); // nRet1 = 100;
}