# 目录 - [目录](#目录) - [指针](#指针) - [变量与地址](#变量与地址) - [指针与指针变量](#指针与指针变量) - [直接访问与间接访问](#直接访问与间接访问) - [空指针与野指针](#空指针与野指针) - [空类型](#空类型) - [定义与初始化的书写规则](#定义与初始化的书写规则) - [指针运算](#指针运算) - [指针与数组](#指针与数组) - [指针与一维数组](#指针与一维数组) - [指针与二维数组](#指针与二维数组) - [指针与字符数组](#指针与字符数组) - [const 与指针](#const-与指针) - [指针数组与数组指针](#指针数组与数组指针) - [多级指针](#多级指针) # 指针 ## 变量与地址 `int i=100;` `i`是变量名,也就是地址。 `100`是存放在这个地址的变量的值。 ## 指针与指针变量 ```c int i = 100; int *p = &i; int **q = &p; ``` p是一个指针变量,他所存放的值是指针,指向另一个变量`i`的地址。 ## 直接访问与间接访问 同样对于上面那个存放值为`100`的变量,既可以通过`i`直接访问,也可以通过`*p, **q`来间接访问。 ```c #include #include int main() { int i = 1; int *p = &i; // 相当于 // int *p; // p = &i; int **q = &p; printf("i = %d\n", i); printf("&i = %d\n", &i); printf("p = %d\n", p); printf("&p = %d\n", &p); printf("*p = %d\n", *p); printf("q = %d\n", q); printf("*q = %d\n", *q); printf("**q = %d\n", **q); // i = 1 // &i = 1175115956 // p = 1175115956 // &p = 1175115944 // *p = 1 // q = 1175115944 // *q = 1175115956 // **q = 1 #if 0 float *q; double *d; char *c; printf("%d\n", sizeof(i)); printf("%d\n", sizeof(p)); printf("%d\n", sizeof(q)); printf("%d\n", sizeof(d)); printf("%d\n", sizeof(c)); // out: // 4 // 8 // 8 // 8 // 8 /* 指针在某一个平台所占的大小是一样的 */ /* 其指向的类型的空间的大小,由指针的类型决定 */ #endif #if 0 printf("i = %d\n", i); printf("&i = %d\n", &i); printf("p = %d\n", p); printf("&p = %d\n", &p); printf("*p = %d\n", *p); // out: // i = 1 // &i = 1343894748 // p = 1343894748 // &p = 1343894736 // *p = 1 #endif exit(0); } ``` ## 空指针与野指针 - 野指针:`int *p = 0x14532534`,不确定这个地址的情况,盲目的指过去,那么读或者写都是非法的。 - 空指针:`int *p = NULL`,避免野指针。 ```c #include #include int main() { int *p; *p = 1; printf("%p --> %d\n", p, *p); // 有的编译器报段错误,有的编译器能输出 // 0x7feb1910dad0 -- > 420327424 exit(0); } ``` ## 空类型 `void *q` 万能类型。 ## 定义与初始化的书写规则 ```c int *p; int* p; ``` ## 指针运算 `&` ` *` 关系运算 `++` `--` ## 指针与数组 ### 指针与一维数组 ```c #include #include // TYPE NAME = VALUE; // a[i]value: a[i] = *(a+i) = *(p+i) = p[i] // &a[i]: &a[i] = a+i = p+i = &p[i] int main() { int a[] = {5, 1, 7, 2, 8, 3}; int y; int *p = &a[1]; y = (*--p)++; printf("y = %d\n", y); // out: y = 5 printf("a[0] = %d\n", a[0]); // out: a[0] = 6 #if 0 int *p = (int[3]){1, 2, 3}; // 只是不要数组名了 for (int i = 0; i < 3; i++) { printf("%p --> %d\n", &p[i], p[i]); } // 0x7ffd12681964 --> 1 // 0x7ffd12681968 --> 2 // 0x7ffd1268196c --> 3 #endif #if 0 int a[3]; int *p = a; int i; for (i = 0; i < sizeof(a) / sizeof(*a); i++) { printf("%p --> %d\n", &a[i], a[i]); } for (i = 0; i < sizeof(a) / sizeof(*a); i++) { scanf("%d", p++); } p = a; for (i = 0; i < sizeof(a) / sizeof(*a); i++, p++) { // printf("%d\n", *p++); printf("%p --> %d\n", p, *p); } // out: // // 0x7ffdad36c478 --> -563487456 // 0x7ffdad36c47c --> 32710 // 0x7ffdad36c480 --> 0 // 1 // 2 // 3 // 0x7ffdad36c478 --> 1 // 0x7ffdad36c47c --> 2 // 0x7ffdad36c480 --> 3 #endif #if 0 int a[3] = {1, 2, 3}; int i; int *p = a; p++; // p从a[0]挪到a[1] printf("%p,%p\n", p, p + 1); // *a相当于*(a+0)相当于a[0] // for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) for (i = 0; i < sizeof(a) / sizeof(*a); i++) { // printf("%p--> %d\n", &a[i], a[i]); // printf("%p--> %d\n", a + i, a[i]); printf("%p--> %d\n", p + i, *(p + i)); } printf("\n"); #endif exit(0); } ``` ### 指针与二维数组 ```c #include #include int main() { int a[2][3] = {1, 2, 3, 4, 5, 6}; int i, j; int *p = *a; int(*q)[3] = a; #if 0 //(W) int *p = a; // 报错,不兼容 // 因为a是行指针,不是int类型指针 // int *p = *a; *p = &a[0][0]; // for (i = 0; i < 6; i++, p++) for (i = 0; i < 6; i++) { // printf("%d ", *p); printf("%d ", p[i]); } printf("\n"); #endif #if 1 printf("%p %p\n", a, a + 1); printf("%p %p\n\n", q, q + 1); for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { // printf("%p --> %d ", &a[i][j], a[i][j]); // printf("%p --> %d ", *(a + i) + j, *(*(a + i) + j)); printf("%p --> %d ", *(q + i) + j, *(*(q + i) + j)); } printf("\n"); } #endif exit(0); } ``` ### 指针与字符数组 ```c #include #include #include int main() { char *str = "hello"; printf("%d %d\n", sizeof(str), strlen(str)); // out: 8 5 //(F) strcpy(str, "world"); // 这里企图把 "world" 覆盖 "hello"这个串常量 str = "world"; puts(str); // char str[] = "hello"; // printf("%d %d\n", sizeof(str), strlen(str)); // out :6 5 #if 0 char str[] = "hello"; // str[6] //(F) str = "world"; strcpy(str, "world"); puts(str); #endif #if 0 char str[] = "I love china!"; char *p = str + 7; puts(str); puts(p); // I love china! // china! #endif exit(0); } ``` ## const 与指针 - 常量指针:**指向常量的指针** const修饰*p,指针指向的值不能变 - 指针常量:**这个指针是一个常量** const修饰指针,指针指向不能变 ```c /** * const int a; * int const a; * * 常量指针 * const int *p; * int const *p; * * 指针常量 * int *const p; * * const int *const p; * */ #include #include #define PI 3.14 int main() { int i = 1; int j = 100; const int *const p = &i; //(F) p = &j; //(F) *p = 10; #if 0 int *const p = &i; //(T) *p = 10; //(F) p = &j; // [main][~/workspace/Linux-C-Notes/Chapter6]$ make const // cc const.c -o const // const.c: In function ‘main’: // const.c:29:7: error: assignment of read-only variable ‘p’ // 29 | p = &j; // | ^ // make: *** [: const] Error 1 printf("i = %d\n", *p); #endif #if 0 int i = 1; int j = 2; // 常量指针 const int *p = &i; //(T) i = 2; // 直接用i改还是可以改成功的 //(F) *p = 2; //(T) p = &j; printf("i = %d\n", i); #endif #if 0 const float pi = 3.14; // const 将变量常量化,相比于宏,多了检查语法 //(F) pi = 3.14159; // float *p = π const float *p = π // *p = 3.14159; // [main][~/workspace/Linux-C-Notes/Chapter6]$ make const // cc const.c -o const // const.c: In function ‘main’: // const.c:28:16: warning: initialization discards ‘const’ qualifier from // pointer target type [-Wdiscarded-qualifiers] // 28 | float *p = π // | #endif exit(0); } ``` ## 指针数组与数组指针 - 数组指针:指向数组的**指针**。 【存储类型】 数据类型 (*指针名)【下标】 = 值 如:`int (*p)[3];` 看成 `int[3] *p;` - 指针数组: 【存储类型】 数据类型 * 数组名【长度】 如:`int *arr[3];` 看成 `int *[3] arr;` ```c #include #include #include int main() { char *name[5] = {"Follow me", "Basic", "Great", "Fortran", "Computer"}; // 指针数组 int i, j, k; char *tmp; for (i = 0; i < 5 - 1; i++) { k = i; for (j = i + 1; j < 5; j++) { if (strcmp(name[k], name[j]) > 0) k = j; } if (k != j) { tmp = name[i]; name[i] = name[k]; name[k] = tmp; } } for (int i = 0; i < 5; i++) { puts(name[i]); } exit(0); } ``` ## 多级指针