一、指针
(1)指针是内存的地址;指针变量是保存了内存地址的变量。
(2)在声明指针变量时,如果没有确切的地址赋值,则声明为空指针:int *ptr = NULL。
(2)获取变量的地址用 &,比如: int num = 10,获取num的地址:&num
(3)指针变量存的是一个地址,这个地址指向的空间存的才是值,比如: int *ptr = # ptr 就是指向int类型的指针变量,即 ptr 是 int* 类型,ptr存的是num的地址,该地址存的是num的值。
(4)获取指针变量所指向的值使用 *,比如: int *ptr = &num,使用*ptr获取ptr指向的值,即num的值。
(5)指针是一个变量,其值为另一个变量的地址。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为: ① int *ip:一个整型的指针;(2) double *dp:一个 double 型的指针;③ float *fp:一个浮点型的指针;(4)char *ch:一个字符型的指针。
#include<stdio.h> int main(){int num = 2;// 获取变量的值printf("num的地址为: %p \n", &num); // num的地址为: 000000000061FE1C// int* 表示类型为 整数指针类型// ptr是一个 int* 类型,指向了int类型变量 num 的地址int *ptr = #// 获取指针变量的地址(&ptr):指针变量本身也有地址printf("ptr的地址为: %p \n", &ptr); // ptr的地址为: 000000000061FE10// 获取指针变量存储的内容(ptr):指针变量存储的是 num 的地址printf("ptr存储的内容为: %p \n", ptr); // ptr存储的内容为: 000000000061FE1C// 获取指针变量指向的值(*ptr):指针变量指向的值是 num 的值printf("ptr指向的值为: %d \n", *ptr); // ptr指向的值为: 2return 0;
}
二、指针的算数运算
1. 自增和自减
#include<stdio.h> int main(){int arr[] = {10, 12, 15};int *ptr = arr; // ptr是一个 int* 类型,指向了数组的首地址,即arr[0]的地址// 通过指针来获取数组中每一个元素的值for(int i = 0; i < 3; i++){printf("arr[%d]的地址: %p \n", i, ptr); // ptr 指向数组元素的地址printf("arr[%d] = %d \n", i, *ptr); // *ptr 是 ptr 指向地址对应的值 ptr++; // ptr = ptr + 1(1个 int 长度的字节数),ptr指向了数组的下一个地址}return 0;
}
#include<stdio.h> int main(){int arr[] = {10, 12, 15};int *ptr = &arr[2]; // ptr是一个 int* 类型,指向了数组 arr[2] 的地址for(int i = 2; i >= 0; i--){printf("arr[%d]的地址: %p \n", i, ptr);printf("arr[%d] = %d \n", i, *ptr); ptr--; // ptr = ptr - 1(1个 int 长度的字节数),ptr指向了数组的前一个地址}return 0;
}
2. 加法和减法
#include<stdio.h> int main(){int arr[] = {10, 12, 15};int *ptr = arr; // ptr是一个 int* 类型,指向了数组的首地址,即arr[0]的地址// 通过指针来获取数组元素的值ptr += 2; // ptr = ptr + 2(2个int长度的字节数),ptr指向了当前地址 +2 后的地址printf("%d \n", *ptr); // 15ptr -= 1;printf("%d \n", *ptr); // 12return 0;
}
三、指针函数
当函数的形参类型是指针类型时,需要传递指针(变量的地址 或 数组)。
1. 函数形参是指针
#include<stdio.h> void add(int *num){ // num 等于 变量 的地址(*num)++; // *num 指向 变量 的值
}int main(){int n = 3;int *ptr = &n;add(&n);printf("n = %d \n", n); // n = 4add(ptr);printf("n = %d \n", n); // n = 5return 0;
}
// 数组#include<stdio.h> // 通过下标的方式:该方式不会使 arr 的值发生变化
int getSum1(int *arr, int size){ // arr 指向 数组 的首地址int sum = 0;for(int i = 0; i < size; i++){sum += arr[i];}return sum;
}// 通过自增修改地址的方式:该方式会使 arr 的值发生变化
int getSum2(int *arr, int size){ // arr 指向 数组 的首地址int sum = 0;for(int i = 0; i < size; i++){sum += *arr; // *arr 为当前地址的值arr++; // arr++ 指向 数组的下一个地址}return sum;
}// 通过移动地址的方式:该方式不会使 arr 的值发生变化
int getSum3(int *arr, int size){ // arr 指向 数组 的首地址int sum = 0;for(int i = 0; i < size; i++){sum += *(arr + i); // *(arr + i) 为 arr[i] 地址的值}return sum;
}int main(){int nums[] = {1, 2, 3, 4};int sum;sum = getSum1(nums, 4);printf("sum = %d \n", sum); // sum = 10sum = getSum2(nums, 4);printf("sum = %d \n", sum); // sum = 10sum = getSum3(nums, 4);printf("sum = %d \n", sum); // sum = 10return 0;
}
2. 函数返回值是指针
指针函数:函数的返回值是一个指针(地址)
#include<stdio.h> int *add(int *arr1, int len, int *arr){ for(int i = 0; i < len; i++){arr[i] = arr1[i] + 2;}return arr;
}int main(){int nums1[] = {1, 2, 3};int *nums;int *result;result = add(nums1, 3, nums);for(int i = 0; i < 3; i++){printf("%d ", result[i]); // 3 4 5}return 0;
}
#include<stdio.h>
#include<string.h> char *getLong(char *string1, char *string2){ if(strlen(string1) > strlen(string2)){return string1;}else{return string2;}
}int main(){char str1[] = "ABCD";char str2[] = "EF";char *str;str = getLong(str1, str2);printf("%s", str); // ABCDreturn 0;
}
(1)用指针作为函数返回值时,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形参,函数返回的指针不能指向这些数据;(2)函数运行结束后会销毁所有的局部数据,这里所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存;
// 错误示例#include<stdio.h> int *getSum(int *arr, int size){ int sum = 0; // 局部变量,在getSum返回时就会销毁for(int i = 0; i < size; i++){sum += arr[i];}return ∑ // 错误:不能返回局部变量的地址,因为该地址在调用完函数时会销毁
}int main(){int nums[] = {1, 2, 3, 4};int *ptr;int sum;ptr = getSum(nums, 4);sum = *ptr;printf("sum = %d \n", sum); return 0;
}
(3)C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为 static变量。
#include<stdio.h> int *getSum(int *arr, int size){ static int sum = 0; // 静态变量存储在静态存储区,该区域的不会由于函数执行情况被回收for(int i = 0; i < size; i++){sum += arr[i];}return ∑
}int main(){int nums[] = {1, 2, 3, 4};int *ptr;int sum;ptr = getSum(nums, 4);sum = *ptr;printf("sum = %d \n", sum); return 0;
}
#include<stdio.h> int *add(int *arr1){static int arr[3];for(int i = 0; i < 3; i++){arr[i] = arr1[i] + 2;}return arr;
}int main(){int nums1[] = {1, 2, 3};int *result;result = add(nums1);for(int i = 0; i < 3; i++){printf("%d ", result[i]); // 3 4 5}return 0;
}
四、函数指针
一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。
函数指针定义:函数返回值类型 (*指针名称) (函数参数列表);
注意:(1)参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称;(2)第一个括号不能省略,如果写作函数返回值类型 *指针名称 (函数参数列表)就成了函数原型。
#include<stdio.h> int getMax(int x, int y){return x > y ? x : y;
}int getMin(int x, int y){return x > y ? y : x;
}int main(){int a = 2;int b = 3;int maxValue;int minValue;// ptrFun是函数指针的名称,int表示函数返回值为int,(int, int)表示该函数指针指向的函数形参是两个intint (*ptrFun)(int num1, int num2);ptrFun = getMax; // ptrFun指向函数getMax的地址maxValue = ptrFun(a, b); // 调用函数getMaxprintf("%d", maxValue); // 3ptrFun = getMin; // ptrFun指向函数getMin的地址 minValue = ptrFun(a, b); // 调用getMinprintf("%d", minValue); // 2return 0;
}
五、回调函数(函数挂钩)
函数指针变量可以作为某个函数的形参,回调函数就是一个通过函数指针调用的函数。
将自己编写的钩子函数挂在已经声明的函数指针上
#include<stdio.h> int getMax(int x, int y){return x > y ? x : y;
}int getMin(int x, int y){return x > y ? y : x;
}// ptrFun是回调函数,进行函数挂钩
int function(int (*ptrFun)(int num1, int num2), int x, int y){int result = ptrFun(x, y);return result;
}int main(){int a = 2;int b = 3;int maxValue;int minValue;maxValue = function(getMax, a, b); // 挂getMax的钩子函数,调用getMaxprintf("%d \n", maxValue); // 3minValue = function(getMin, a, b); // 挂getMin的钩子函数,调用getMinprintf("%d \n", minValue); // 2return 0;
}
六、多重指针
多重指针:定义一个指向指针的指针,第一个指针包含了第二个指针的地址,第二个指针指向实际值的地址。
数据类型 **指针名
#include<stdio.h> int main(){int num = 2;int *ptr = # // 一级指针,指向 num值 的地址int **pptr = &ptr; // 二级指针,指向 ptr 的地址printf("num = %d, num的地址: %p \n", num, &num);printf("num = %d, num的地址: %p, ptr的地址: %p \n", *ptr, ptr, &ptr);printf("num = %d, num的地址: %p, ptr的地址: %p, pptr的地址: %p \n", **pptr, *pptr, pptr, &pptr);
}
七、指针数组
指针数组:数组中的元素 是 基本数据类型 的地址(指针)
数据类型 *指针数组名[大小]
int *ptr[3]:ptr是一个指针数组,包含3个整数指针,其中每个元素都是一个指向int类型值 的 指针
#include<stdio.h> int main(){int arr[] = {10, 12, 15};int *ptr[3]; // ptr是一个指针数组// 给指针数组赋值,指针数组的每个元素都是一个指向值的指针for(int i = 0; i < 3; i++){ptr[i] = &arr[i]; // ptr 的每一个元素都是 数组arr每一个元素的地址,该地址(指针)指向了arr每个元素的值}// 通过指针数组来获取 数组元素的值for(int i = 0; i < 3; i++){printf("%d \n", *ptr[i]); // *ptr[i] 是 ptr[i] 指针 对应的值 }return 0;
}
// 字符串指针数组#include<stdio.h> int main(){char *ptr[] = {"ABC", "DEF", "GHI"}; // 通过指针数组来获取 数组元素的值for(int i = 0; i < 3; i++){printf("%s \n", ptr[i]); }return 0;
}