1,指针:
可以看成存储地址的变量,地址可以指向是函数和基本类型。
数据都要通过地址来查找获取横好理解,不同编程语言的区别就是在代码层面有没有可以主动使用还是交给编译器完成。
2,C语言中如何来表达指针:
变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址
指针变量声明的一般形式为:
type *var-name;
type 是指针的基类型(即指向变量的类型),var-name 是指针变量的名称,星号是用来指定一个变量是指针
int *ip; /* 一个整型的指针 */ double *dp; /* 一个 double 型的指针 */ float *fp; /* 一个浮点型的指针 */ char *ch; /* 一个字符型的指针 */
例子:
#include <stdio.h> int main () { int var_runoob = 10; int *p; // 定义指针变量 p = &var_runoob; printf("var_runoob 变量的地址: %p ", p); return 0; } 执行后: var_runoob 变量的地址: 0x7ffeeaae08d8
一般指针操作的过程:
定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。通过 * 来获取指定地址的变量的值。
实例:
#include <stdio.h> int main () { int var = 20; /* 实际变量的声明 */ int *ip; /* 指针变量的声明 */ ip = &var; /* 在指针变量中存储 var 的地址 */ printf("var 变量的地址: %p ", &var ); /* 在指针变量中存储的地址 */ printf("ip 变量存储的地址: %p ", ip ); /* 使用指针访问值 */ printf("*ip 变量的值: %d ", *ip ); return 0; }
执行结果:
var 变量的地址: 0x7ffeeef168d8 ip 变量存储的地址: 0x7ffeeef168d8 *ip 变量的值: 20
关于空指针:
赋为 NULL 值的指针被称为空指针, 空指针并非没有指向的地址, 而是这个值是0. 如果没有确切的地址可以赋值, 为指针变量赋一个 NULL 值是一个良好的编程习惯。 大多数的操作系统上,程序不允许访问地址为 0 的内存, 因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义, 它表明该指针不指向一个可访问的内存位置。 但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
例子:
#include <stdio.h> int main () { int *ptr = NULL; printf("ptr 的地址是 %p ", ptr ); return 0; } 执行结果: ptr 的地址是 0x0
实际上空指针说的是指针变量没有指向具体的一个基本类型。而是指针没有值(默认0)。
因此指针可以直接参与逻辑运算:
if(ptr) /* 如果 p 非空,则完成 */ if(!ptr) /* 如果 p 为空,则完成 */
3,指向数组的指针
数组的声明:
double balance[50];
数组的名称可以作为一个指针,是指向数组第一个位置的指针。
等价与:balance ==&balance[0]
一个简单例子:
double *p; double balance[10]; p = balance; 可以使用指针来获取元素: *(balance + 4) 等同与 balance[4] 也可以: *p、*(p+1)、*(p+2) 等来访问数组元素
完整的例子:
#include <stdio.h> int main () { /* 带有 5 个元素的整型数组 */ double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0}; double *p; int i; p = balance; /* 输出数组中每个元素的值 */ printf( "使用指针的数组值 "); for ( i = 0; i < 5; i++ ) { printf("*(p + %d) : %f ", i, *(p + i) ); } printf( "使用 balance 作为地址的数组值 "); for ( i = 0; i < 5; i++ ) { printf("*(balance + %d) : %f ", i, *(balance + i) ); } return 0; } 结果: 使用指针的数组值 *(p + 0) : 1000.000000 *(p + 1) : 2.000000 *(p + 2) : 3.400000 *(p + 3) : 17.000000 *(p + 4) : 50.000000 使用 balance 作为地址的数组值 *(balance + 0) : 1000.000000 *(balance + 1) : 2.000000 *(balance + 2) : 3.400000 *(balance + 3) : 17.000000 *(balance + 4) : 50.000000
指向数组的指针和指向数组第一个元素的指针是等价的:*p==p[0]
#include <stdio.h> int main () { /* 带有 5 个元素的整型数组 */ double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0}; double *p; int i; p = balance; /* 输出数组中每个元素的值 */ printf( "使用指针的数组值 "); printf( "使用 p[i] 作为地址的数组值 "); for ( i = 0; i < 5; i++ ) { printf("p[%d]: %f ", i, p[i] ); } return 0; }
4,指向函数指针即函数指针:
函数指针可以像函数一样使用:
typedef int (*fun_ptr)(int,int); // 类似函数声明一样,声明一个指定参数和返回值类型的函数指针
一个实例:函数名可以看成指向函数的指针
#include <stdio.h> int max(int x, int y) { return x > y ? x : y; } int main(void) { /* p 是函数指针 */ int (* p)(int, int) = & max; // &可以省略 int a, b, c, d; printf("请输入三个数字:"); scanf("%d %d %d", & a, & b, & c); /* 与直接调用函数等价,d = max(max(a, b), c) */ d = p(p(a, b), c); printf("最大的数字是: %d ", d); return 0; }
执行结果:
请输入三个数字:1 2 3 最大的数字是: 3
函数回调:函数指针作为另一个函数的参数:
即通过预先设置函数间的调用流程,后续自定义函数具体内容
#include <stdlib.h> #include <stdio.h> // 回调函数 void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)) { for (size_t i=0; i<arraySize; i++) array[i] = getNextValue(); } // 获取随机值 int getNextRandomValue(void) { return rand(); } int main(void) { int myarray[10]; populate_array(myarray, 10, getNextRandomValue); for(int i = 0; i < 10; i++) { printf("%d ", myarray[i]); } printf(" "); return 0; } 执行,输出结果如下: 16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709
总结:
数组指针和其他基本类型的指针书写类似,函数指针和函数书写类似。
特别注意数组指针函数指针他们的名称,函数名称和数组名称代表指向各自的指针。