《彻底搞定C指针》文档整理
一.变量存储
#include <stdio.h> int main(void) { int i = 39; printf(“%d\n”, i); printf(“%d\n”, &i); //十进制打印i的地址 printf(“%x\n”, &i); //十六进制打印i的地址 return(0); } /*得到结果: ray@ray-Lenovo:~/Documents/Exec_important/Daily$ ./0128-2 39 23300652 1638a2c ray@ray-Lenovo:~/Documents/Exec_important/Daily$ ./0128-2 39 1367552540 5183321c ray@ray-Lenovo:~/Documents/Exec_important/Daily$ ./0128-2 39 1684339276 6464fa4c 每次得到的地址都不同*/
二. 什么是指针
#include <stdio.h> int main(void) { char a, *pa; a = 10; pa = &a; /* pa指向变量a的地址 */ *pa = 20; /* 给变量a的地址的内容赋值为20, 即a=20*/ printf("%d\n", a); } /* 得到结果: 20 */
三. 指针与数组名
#include <stdio.h> int main(void) { int i, *pa, a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //int * const pa = a; //不行, pa被定义为常量指针了 pa = a; printf("array mode:\n"); for(i = 0; i < 10; i++) printf("%d ", a[i]); printf("\npointer mode:\n"); for(i = 0; i < 10; i++) printf("%d ", *(a+i)); printf("\n\n"); for(i = 0; i < 10; i++) printf("%d ", pa[i]); printf("\n"); for(i = 0; i < 10; i++) printf("%d ", *(pa+i)); printf("\n"); for(i = 0; i < 10; i++) { printf("%d ", *pa); pa++; } printf("\n"); return 0; } /*执行后显示: array mode: 0 1 2 3 4 5 6 7 8 9 pointer mode: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 */
四. const int *pi与int *const pi的区别
#include <stdio.h> int main(void) { int i1 = 30, i2 = 40; const int *pi = &i1; pi = &i2; /* 注意这里, pi 可以在任意时候重新赋值一个新内存地址*/ i2 = 80; /* 这里能用*pi = 80 来代替吗?当然不能! */ printf("%d\n", *pi); /* 输出是 80 */ return 0; } /*pi 的值是可以被修改的。即它可以重新指向另一个地址的,但是,不能通过*pi 来修改 i2 的值*/
#include <stdio.h> int main(void) { int i1 = 30, i2 = 40; int *const pi = &i1; /* pi = &i2; 注意这里, pi 不能再这样重新赋值了,即不能再指向另一个新地址 */ i1 = 80; /* 这里能用 *pi = 80; 来代替吗?可以,这里可以通过*pi 修改 i1 的 */ printf("%d\n", *pi); /* 输出是 80 */ return 0; } /*pi 的值是可以被修改的。即它可以重新指向另一个地址的,但是,不能通过*pi 来修改 i2 的值*/
总结:
1) 如果 const 修饰在*pi 前,则不能改的是*pi(即不能类似这样: *pi=50;赋值)而不是指 pi。
2) 如果 const 是直接写在 pi 前,则 pi 不能改(即不能类似这样:pi=&i; 赋值)。
五. 函数参数的传递
Q1. 值传递
#include <stdio.h> void Exchg1(int x, int y) { int tmp; tmp = x; x = y; y = tmp; printf("x = %d, y = %d\n", x, y); } int main(void) { int a = 4, b = 6; Exchg1(a, b); printf("a = %d, b = %d\n", a, b); return 0; } /*编译执行,输出: x = 6, y = 4 a = 4, b = 6 */
Q2. 地址传递
#include <stdio.h> void Exchg2(int *px, int *py) { int tmp = *px; *px = *py; *py = tmp; printf("*px = %d, *py = %d\n", *px, *py); } int main(void) { int a = 4, b = 6; Exchg2(&a, &b); printf("a = %d, b = %d.\n", a, b); return 0; } /*编译执行,输出: *px = 6, *py = 4 a = 6, b = 4 */
Q3. 引用传递
#include <stdio.h> 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); } /*编译报错,因为C没有引用的用法,C++才*/
六. 指向另一指针的指针
#include <stdio.h> void find1(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 main(void) { char str[] = {"afsdfsdfdf\0"}; char a = 'd'; char *p = 0; find1(str, a, &p); if(0 == p) printf("没找到!\n"); else printf("找到了,p = %x\n", p); return 0; } /*编译执行,输出: 找到了,p = 6393eb53 */
七. 函数名与函数指针
1.
#include <stdio.h> void MyFun(int x); //也可写成void MyFun(int) void (*FunP)(int); //也可写成void (*FunP)(int x) int main(int argc, char* argv[]) { MyFun(10); FunP = &MyFun; (*FunP)(20); } void MyFun(int x) { printf("%d\n", x); } /*编译执行,输出: 10 20 */
2.
#include <stdio.h> void MyFun(int x); //也可写成void MyFun(int) void (*FunP)(int); //也可写成void (*FunP)(int x) int main(int argc, char* argv[]) { MyFun(10); FunP = MyFun; FunP(20); return 0; } void MyFun(int x) { printf("%d\n", x); } /*编译执行,输出: 10 20 */
3.
#include <stdio.h> void MyFun(int x); //也可写成void MyFun(int) void (*FunP)(int); //也可写成void (*FunP)(int x) int main(int argc, char* argv[]) { MyFun(10); FunP = &MyFun; FunP(20); } void MyFun(int x) { printf("%d\n", x); } /*编译执行,输出: 10 20
*/
4.
#include <stdio.h> void MyFun(int x); //也可写成void MyFun(int) void (*FunP)(int); //也可写成void (*FunP)(int x) int main(int argc, char* argv[]) { MyFun(10); FunP = MyFun; (*FunP)(20); } void MyFun(int x) { printf("%d\n", x); } /*编译执行,输出 10 20 */
5.
#include <stdio.h> void MyFun(int x); //也可写成void MyFun(int) void (*FunP)(int); //也可写成void (*FunP)(int x) int main(int argc, char* argv[]) { (*MyFun)(10); FunP = &MyFun; (*FunP)(20); } void MyFun(int x) { printf("%d\n", x); } /*编译执行,输出: 10 20 */
总结上面: 1) 其实, MyFun 的函数名与 FunP 函数指针都是一样的, 即都是函数指针。 MyFun 函数名是一个函数指针常量,而 FunP 是一个函数数指针变量,这是它们的关系。
2) 但函数名调用如果都得如(*MyFun)(10)这样,那书写与读起来都是不方便和不习惯的。所以 C 语言的设计者们才会设计成又可允许 MyFun(10)这种形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
3) 为统一起见, FunP 函数指针变量也可以 FunP(10)的形式来调用。
4) 赋值时,即可 FunP = &MyFun 形式,也可 FunP = MyFun。
6.
#include <stdio.h> void MyFun(int x); //也可写成void MyFun(int) typedef void (*FunType)(int); FunType FunP; int main(int argc, char* argv[]) { //FuncType FunP;/*函数指针变量也可以是局部的*/ MyFun(10); FunP = &MyFun; FunP(20); return 0; } void MyFun(int x) { printf("%d\n", x); } /*编译执行,输出: 10 20 */
7.
#include <stdio.h> void MyFun1(int x); void MyFun2(int x); void MyFun3(int x); typedef void (*FunType)(int); void CallMyFun(FunType fp, int x); int main(int argc, char* argv[]) { CallMyFun(MyFun1, 10); CallMyFun(MyFun2, 20); CallMyFun(MyFun3, 30); } void CallMyFun(FunType fp, int x) { fp(x); } void MyFun1(int x) { printf("函数MyFun1中输出:%d\n", x); } void MyFun2(int x) { printf("函数MyFun2中输出:%d\n", x); } void MyFun3(int x) { printf("函数MyFun3中输出:%d\n", x); } /* 函数MyFun1中输出:10 函数MyFun2中输出:20 函数MyFun3中输出:30 */