一.指针与数组名
两条关系:
- pa[i] = *(pa+i)
- pa = &pa[0]
- 数组名是该数组的首地址,是个地址常量,也及指针变量的一个值,不是指针。本质上是变量和常量的区别。
- 因此指针可以自增加,而数组名不可以自增加。由此可以看出指针与数组名的区别。
二.int *pi指针指向const int i常量的情况
/* begin */
const int i1 = 40;
int *pi;
pi = &i1;
//VC下报错,因为不能通过指向整型变量的指针来修改常整形的值。
pi = (int *) &i1;
/* VC下编译通过,但是仍不能通过 *pi = 80来修改 i1 的值。去试试 吧!看看具体的怎样。*/
//我认为强制转换只是在该条语句中起作用,后续通过该指针改变i,i仍然是常整形。
/* end */
三.函数参数的传递
1.值传递
void Exchg1(int x, int y)
{
int x = a; /* ← */
int y = b; /* ← 注意这里,头两行是调用函数时的隐含操作 */
int tmp;
tmp = x;
x = y;
y = tmp;
printf("x = %d, y = %d\n", x, y);
}
main()
{
int a = 4,b = 6; Exchg1(a, b);
printf("a = %d, b = %d\n", a, b);
return(0);
}
其实函数在调用时是隐含地把实参 a、b 的值分别赋值给了 x、y, 之后在你写的 Exchg1 函数体内再也没有对 a、b 进行任何的操作了。交换的只 是 x、y 变量。并不是 a、b。当然 a、b 的值没有改变啦!函数只是把 a、b 的 值通过赋值传递给了 x、y,函数里头操作的只是 x、y 的值并不是 a、b 的值。这就是所谓的参数的值传递了。
2.地址传递
void Exchg1(int x, int y)
{
px = &a;
py = &b;//隐含操作
int tmp;
tmp = x;
x = y;
y = tmp;
printf("x = %d, y = %d\n", x, y);
}
main()
{
int a = 4,
b = 6;
Exchg1(a, b);
printf("a = %d, b = %d\n", a, b);
return(0);
}
3.引用传递
void Exchg3(int &x, int &y)
{
int tmp = x;
x = y;
y = tmp;
printf("x = %d,y = %d\n", x, y);
}
main()
{
int a = 4;
int b = 6;
Exchg3(a, b);
printf("a = %d, b = %d\n", a, b); return(0);
}
引用传递 Exchg3(a, b)函数里是用 a、b 分别代替了 x、y。函数里操作 的就是 a、b 变量的本身,因此 a、b 的值可在函数里被修改的。
四.指向另一指针的指针
1.内存实质
2.应用实例
错误示例:
void find1(char array[], char search, char *pa)
{
int i;
for (i = 0; *(array + i) != 0; i++)
{
if ( *(array+i) == search)
{
pa = array + i;//错误,如果误以为函数外部的pa的值为该值,则会导致出错。
break;
}
else if (*(array+i) == 0)
{
pa = 0;
break;
}
}
}
参数 pa 与参数 search 的传递并没有什么不同,都是值传递嘛(小语:地 址传递其实就是地址值传递嘛)!所以对形参变量 pa 值(当然值是一个地址值) 的修改并不会改变实参变量 p 值,因此 p 的值并没有改变(即 p 的指向并没有被改变)。
修正:
void find2(char array[], char search, char **ppa)//利用二级指针可以实现
{
int i;
for (i=0; *(array + i) != 0; i++)
{
if(*(array + i) == search)
{
*ppa = array + i;
break;
}
else if(*(array + i) == 0)
{
*ppa = 0;
break;
}
}
}
我的理解:假设int a=10,int b =20,当我们想利用函数对a b进行互换,
则传入的是a b 两个变量的地址
,所以函数的形参写为:(int *,int *),实参为(&a, &b)。假设int *c = &a, int *d = &b,当我们想利用函数对c d交换地址,
则传入的是c d 两个指针变量的地址
,所以指针的地址的用形参表示为(int * *, int * *),实参为(&c,&d )。总结:当我们想用函数实现对某数据的改变,则应传入该数据的所在的地址。
五.函数名与函数指针
1.函数指针变量的声明
void (*FunP)(int)//函数名加括号,并在之前加*号
2.通过函数指针变量调用函数
MyFun(10); /* 这是直接调用 MyFun 函数 */
FunP = &MyFun; /* 将 MyFun 函数的地址赋给 FunP 变量 */
(*FunP)(20);//第一种方式
MyFun(10); /* 这里是调用 MyFun(10)函数 */
FunP = MyFun; /* 将 MyFun 函数的地址赋给 FunP 变量 */
FunP(20);//第二种方式
MyFun(10); /* 这里是调用 MyFun(10)函数 */
FunP = &MyFun; /* 将 MyFun 函数的地址赋给 FunP 变量 */
FunP(20);//第三种方式
MyFun(10); /* 这里是调用 MyFun(10)函数 */
FunP = MyFun; /* 将 MyFun 函数的地址赋给 FunP 变量 */
(*FunP)(20);//第四种方式
//1)其实,MyFun 的函数名与 FunP 函数指针都是一样的,即都是函数指针。 MyFun 函数名是一个函数指针常量,而 FunP 是一个函数数指针变量,这是它 们的关系。
//2)但函数名调用如果都得如(*MyFun)(10)这样,那书写与读起来都是不 方便和不习惯的。所以 C 语言的设计者们才会设计成又可允许 MyFun(10)这种 形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
//3)为统一起见,FunP 函数指针变量也可以 FunP(10)的形式来调用。
//4)赋值时,即可FunP = &MyFun形式,也可FunP = MyFun。
3.给函数指针类型起别名
typedef void (*FunType)(int);//给这一函数指针类型起了FunType这一别名
FunType FunP;//因此void(*FunP)(int) == FunType FunP;