• C语言的指针


    目录

    指针的基本介绍

    什么是指针

    指针的算术运算

     指针的比较

    指针数组

    定义一个指向字符的指针数组来存储字符串列表

     多重指针

    基本介绍

    练习

    传递指针(地址)给函数

    传数组给指针变量

    返回指针的函数

     指针函数的注意事项和细节

     编写一个函数,生成10个随机数,并使用表示指针的数组名(一个数组元素的地址)来返回它们

    函数指针(指向函数的指针)

    基本介绍

    函数指针定义

    回调函数

    指针的注意事项和细节

    指针的基本介绍

    • 指针是C语言的精华,也是C语言的难点
    • 指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量
    • 获取变量的地址,用&
    • 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值
    • 获取指针类型所指向的值,使用*ptr

    什么是指针

    • 指针是一个变量,其值为另一个变量的地址,即内存地址的直接地址。在使用指针存储其他变量地址之前,对其进行声明

    指针的算术运算

    • 数组在内存中是连续分布的
    • 当对指针进行++时,指针会按照它指向的数据类型字节数大小增加,每++,就增加4个字节
    1. #include
    2. const int MAX = 3;//常量
    3. int main () {
    4. int var[] = {10, 100, 200}; // int 数组
    5. int i, *ptr; // ptr 是一个 int* 指针
    6. ptr = var; // ptr 指向了 var 数组的首地址
    7. for ( i = 0; i < MAX; i++) {
    8. printf("var[%d]的地址= %p \n", i, ptr );
    9. printf("存储值:var[%d] = %d\n\n", i, *ptr );
    10. ptr++;// ptr = ptr + 1(1个int字节数); ptr 存放值+4字节(int)
    11. }
    12. getchar();
    13. return 0;
    14. }

    1. int main () {
    2. int var[] = {10, 100, 200};
    3. int i, *ptr;
    4. ptr = var; // 将 var 的首地址 赋给 ptr
    5. ptr += 2; // ptr 的存储的地址 + 2个int的字节 (8个字节)
    6. printf("var[2]=%d \nvar[2]的地址=%p \nptr存储的地址=%p \nptr指向的值=%d",
    7. var[2], &var[2], ptr, *ptr);
    8. getchar();
    9. return 0;
    10. }

    1. const int MAX = 3;//常量
    2. int main () {
    3. int var[] = {10, 100, 200}; // int 数组
    4. int i, *ptr; // ptr 是一个 int* 指针
    5. /* 指针中最后一个元素的地址 */
    6. ptr = &var[MAX-1]; // &var[2]
    7. for ( i = MAX; i > 0; i--) {// 反向遍历
    8. printf("ptr存放的地址=%p\tvar[%d]的地址=%p\n", ptr,i,&var[i-1]);
    9. printf("存储值:var[%d] = %d\n\n", i-1, *ptr );
    10. ptr--; // ptr = ptr - 1(1个int的字节数 [4个字节])
    11. }
    12. getchar();
    13. return 0;
    14. }

     指针的比较

    1. #include
    2. int main () {
    3. int var[] = {10, 100, 200};
    4. int *ptr;
    5. ptr = var;//ptr 指向var 首地址(第一个元素)
    6. if(ptr == var[0]) {//错误,类型不一样 (int *) 和 (int )
    7. printf("ok1");//不输出
    8. }
    9. if(ptr == &var[0]) { // 可以
    10. printf("\nok2"); //输出
    11. }
    12. if(ptr == var) { //可以
    13. printf("\nok3"); //输出
    14. }
    15. if(ptr >= &var[1]) { //可以比较,但是返回false
    16. printf("\nok4");//不会输出
    17. }
    18. getchar();
    19. return 0;
    20. }
    1. const int MAX = 3;
    2. int main () {
    3. int var[] = {10, 100, 200};
    4. int i, *ptr;
    5. ptr = var;
    6. i = 0;
    7. while ( ptr <= &var[MAX - 2] ){//&var[1]
    8. printf("Address of var[%d] = %x\n", i, ptr );//16进制输出
    9. printf("Value of var[%d] = %d\n", i, *ptr );
    10. ptr++;
    11. i++;
    12. } //会输出 10 , 100
    13. getchar();
    14. return 0;
    15. }

    指针数组

    1. #include
    2. const int MAX=3;
    3. int main (){
    4. //指针数组
    5. int var[] = {10, 100, 200};
    6. int i, *ptr[3];
    7. for ( i = 0; i < MAX; i++){
    8. ptr[i] = &var[i]; /* 赋的值为整数的地址 */
    9. }
    10. for ( i = 0; i < MAX; i++){ //指针数组来获取各个值
    11. printf("Value of var[%d] = %d \tptr[%d]本身的地址=%p\t ptr[%d]保存的地址=%p\t var[%d]的地址=%p\n",
    12. i, *ptr[i] , i, &ptr[i],i,ptr[i],i,&var[i]); // 10, 100, 200
    13. }
    14. getchar();
    15. return 0;
    16. }

    定义一个指向字符的指针数组来存储字符串列表

    (定义一个指针数组,该数组的每个元素,指向的是一个字符串)

    1. #include
    2. void main() {
    3. //定义一个指针数组,该数组的每个元素,指向的是一个字符串
    4. //当输出数组所指字符串直接是books[i]
    5. char *books[] = {
    6. "三国演义",
    7. "西游记",
    8. "红楼梦",
    9. "水浒传"
    10. };
    11. char * pStr = "abc";
    12. int strLen = sizeof(pStr);
    13. //遍历
    14. int i, len = 4;
    15. for(i = 0; i < len; i++) {
    16. printf("\nbooks[%d] 指向字符串 = %s,books[%d]的地址 = %p\n", i, books[i],i,&books[i]);
    17. printf("pStr指向的内容 = %s 地址 = %p\n\n",pStr,pStr);
    18. printf("pStr指针的长度为%d\n\n", strLen);
    19. }
    20. getchar();
    21. }

    •  为什么books[]指针字符数组每一个地址长度都为4?

            这是指针的长度,不是数组的长度 

    • 若求的是books[]的长度,则可以使用sizeof()函数中的形参为books.即sizeof(books)

     多重指针

    基本介绍

            指向指针的指针是一种多级间接寻址的方式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

    • 一个指向指针的指针变量在变量名前放置两个星号。
    int **ptr//ptr的类型是int **

    练习

    1. #include
    2. void main() {
    3. int var;
    4. int* ptr;
    5. int** pptr;
    6. var = 3000;
    7. ptr = &var;//var变量的地址赋给ptr
    8. pptr = &ptr;//将ptr的地址,赋给pptr
    9. printf("var的地址 = %p, var = %d\n\n", &var, var);
    10. printf("ptr的本身地址 = %p, ptr存放的地址 = %p, *ptr = %d\n\n", &ptr, ptr, *ptr);
    11. printf("pptr的本身地址 = %p, pptr存放的地址 = %p, **pptr = %d\n", &pptr, pptr, **pptr);
    12. getchar();
    13. }

    传递指针(地址)给函数

    1. //指针传递给函数
    2. //数组默认也是指针传递
    3. #include
    4. void test2(int *p); //函数声明,接收int *
    5. void main() {
    6. int num=90;
    7. int *p = # //将 num 的地址赋给 p
    8. test2(&num); //传地址
    9. printf("\nmain() 中的num=%d", num); // num = 91
    10. test2(p); //传指针
    11. printf("\nmain() 中的num=%d", num);// num = 92
    12. getchar();
    13. }
    14. void test2(int *p) {
    15. *p += 1; //*p 就访问 num的值
    16. }

    传数组给指针变量

            数组名本身就代表该数组首地址,因此传数组的本质就是传地址

    1. #include
    2. /* 函数声明 */
    3. double getAverage(int *arr, int size); //函数声明
    4. double getAverage2(int *arr, int size); //函数声明
    5. int main ()
    6. {
    7. /* 带有 5 个元素的整型数组 */
    8. int balance[5] = {1000, 2, 3, 17, 50};
    9. double avg;
    10. /* 传递一个指向数组的指针作为参数 */
    11. avg = getAverage( balance, 5 ) ;
    12. /* 输出返回值 */
    13. printf("Average value is: %f\n", avg );
    14. getchar();
    15. return 0;
    16. }
    17. //说明
    18. //1. arr 是一个指针,
    19. double getAverage(int *arr, int size){
    20. int i, sum = 0;
    21. double avg;
    22. for (i = 0; i < size; ++i){
    23. // arr[0] = arr + 0
    24. // arr[1] = arr + 1个int字节(4)
    25. // arr[2] = arr + 2个int字节(8)
    26. //...
    27. sum += arr[i];// arr[0] =>数组第一个元素的地址
    28. //此时函数的指针指向的地址(保存的地址)并不会改变,而是直接通过下标取值
    29. printf("\narr存放的地址=%p ", arr);
    30. }
    31. avg = (double)sum / size;
    32. return avg;
    33. }

    1. #include
    2. /* 函数声明 */
    3. double getAverage(int *arr, int size); //函数声明
    4. double getAverage2(int *arr, int size); //函数声明
    5. int main ()
    6. {
    7. /* 带有 5 个元素的整型数组 */
    8. int balance[5] = {1000, 2, 3, 17, 50};
    9. double avg;
    10. /* 传递一个指向数组的指针作为参数 */
    11. avg = getAverage2( balance, 5 ) ;
    12. /* 输出返回值 */
    13. printf("Average value is: %f\n", avg );
    14. getchar();
    15. return 0;
    16. }
    17. //说明
    18. //1. arr 是一个指针,
    19. double getAverage2(int *arr, int size){
    20. int i, sum = 0;
    21. double avg;
    22. for (i = 0; i < size; ++i){
    23. sum += *arr;
    24. printf("\narr存放的地址=%p ", arr);
    25. arr++; // 指针的++运算, 会对arr 存放的地址做修改
    26. }
    27. avg = (double)sum / size;
    28. return avg;
    29. }

     如果在getVerage()函数中,通过指针修改了数组的值,那么main函数的balance数组也会发生相应变化。getVerage函数中的指针,指向的就是main函数的数组

    返回指针的函数

            C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数

    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include
    3. #include
    4. char *strlong(char *str1, char *str2){ //函数返回的char * (指针)
    5. printf("\nstr1的长度%d str2的长度%d", strlen(str1), strlen(str2));
    6. if(strlen(str1) >= strlen(str2)){
    7. return str1;
    8. }else{
    9. return str2;
    10. }
    11. }
    12. int main(){
    13. char str1[30], str2[30], *str; // str 是一个指针类型,指向一个字符串
    14. printf("\n请输入第1个字符串");
    15. //gets(str1);//和scanf()函数的功能一致
    16. scanf("%s", str1);
    17. getchar();
    18. printf("\n请输入第2个字符串");
    19. gets(str2);
    20. str = strlong(str1, str2);
    21. printf("\nLonger string: %s \n", str);
    22. printf("\nstr指针本身的地址为: %p \n", &str);
    23. printf("\nstr指针指向的地址为: %p \n", str1);
    24. printf("\nstr1数组的地址为: %p \n", str1);
    25. printf("\nstr2数组的地址为: %p \n", str2);
    26. getchar();
    27. return 0;
    28. }

     指针函数的注意事项和细节

    • 用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据。
    • 函数运行结束后会销毁该函数所有的局部数据,但是这所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存。
    • C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为static变量
    1. #include
    2. int *func(){
    3. //int n = 100;//局部变量, 在func 返回时,就会销毁
    4. static int n = 100; // 如果这个局部变量是 static 性质的,那么n 存放数据的空间在静态数据区
    5. return &n;
    6. }
    7. int main(){
    8. int *p = func(); //func 返回指针
    9. printf("当前返回的指针指向的值为%d", *p);
    10. int n;
    11. //printf("okoook~~"); //可能是使用到 局部变量 int n = 100 占用空间
    12. //printf("okoook~~");
    13. //printf("okoook~~");
    14. n = *p;
    15. printf("\nvalue = %d\n", n);// 思考,当直接int n = 100时是否能够输出100? 不一定
    16. //当用static修饰时,就能直接输出100
    17. getchar();
    18. return 0;
    19. }

     编写一个函数,生成10个随机数,并使用表示指针的数组名(一个数组元素的地址)来返回它们

    1. #include
    2. #include
    3. #include
    4. //编写一个函数,返回一个一维数组
    5. int * f1() {
    6. static int arr[10]; //必须加上static ,让arr 的空间在静态数据区分配
    7. int i = 0;
    8. srand((unsigned)time(NULL));//随机数需要的,同时还有头文件
    9. for (i = 0; i < 10;i++) {
    10. //arr[i] = rand();//伪随机数
    11. arr[i] = rand();//真正随机数
    12. }
    13. return arr;
    14. }
    15. void main() {
    16. int *p;
    17. int i;
    18. p = f1(); //p 指向是在 f1 生成的数组的首地址(即第一个元素的地址)
    19. for ( i = 0; i <10; i++) {
    20. printf("\n%d", *(p+i));
    21. }
    22. getchar();
    23. }

    函数指针(指向函数的指针)

    基本介绍

    • 一个函数总是占用一段连续的内存区域,函数名在表达式中有时会被转换为函数所在内存区域的首地址,这和数组名非常类似
    • 把函数的这个首地址(入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数,就叫做函数指针

    函数指针定义

    returnType (*pointerName)(param list);
    • returnType为函数返回值类型
    • pointerName为指针名称
    • param list为函数参数列表
    • 参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称
    • 注意()的优先级高于*,第一个括号不能省略,如果写作
    returnType *pointerName (param list);

    就成了函数原型,它表明函数的返回值类型为returnType *

    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include
    3. //说明
    4. //1. max 函数
    5. //2. 接收两个int ,返回较大数
    6. int max(int a, int b){
    7. return a>b ? a : b;
    8. }
    9. int main(){
    10. int x, y, maxVal;
    11. //说明 函数指针
    12. //1. 函数指针的名字 pmax
    13. //2. 第一个int 表示 该函数指针指向的函数是返回int 类型
    14. //3. (int, int) 表示 该函数指针指向的函数形参是接收两个 int
    15. //4. 在定义函数指针时,也可以写上形参名 int (*pmax)(int x, int y) = max;
    16. int (*pmax)(int x, int y) = max; //pmax函数指针指向的就是max函数
    17. printf("Input two numbers:");
    18. scanf("%d %d", &x, &y);
    19. // (*pmax)(x, y) 通过函数指针去调用 函数max
    20. // 调用方式 2 pmax(x,y)
    21. //maxVal = pmax(x,y);同样可以输出正确
    22. maxVal = (*pmax)(x, y);
    23. printf("\nmax函数的首地址为%p\n", &max);
    24. printf("\nMax value: %d pmax保存的地址为%p pmax本身的地址=%p\n", maxVal, pmax, &pmax);
    25. getchar();
    26. getchar();
    27. return 0;
    28. }

    回调函数

    • 函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数

    • 回调函数就是由别人的函数执行时调用自己传入的函数

    1. #include
    2. #include
    3. // 回调函数
    4. //1. int (*f)()
    5. //2. f 就是 函数指针 , 它可以接收的函数是 (返回 int ,没有形参的函数)
    6. //3. f 在这里被 initArray 调用,充当了回调函数角色
    7. void initArray(int *array, int arraySize, int (*f)()) {
    8. int i ;
    9. //循环10
    10. for ( i=0; i
    11. array[i] = f(); //通过 函数指针调用了 getNextRandomValue 函数
    12. }
    13. }
    14. // 获取随机值
    15. int getNextRandomValue() {
    16. return rand();//rand 系统函数, 会返回一个随机整数
    17. }
    18. int main() {
    19. int myarray[10],i; //定义一个数组和 int
    20. //说明
    21. //1. 调用 initArray 函数
    22. //2. 传入了一个函数名 getNextRandomValue (地址), 需要使用函数指针接收
    23. initArray(myarray, 10, getNextRandomValue);
    24. //输出赋值后的数组
    25. for(i = 0; i < 10; i++) {
    26. printf("%d\n", myarray[i]);
    27. }
    28. printf("\n");
    29. getchar();
    30. return 0;
    31. }

     输出值为10个整数,也就是一个数组的形式

    指针的注意事项和细节

    •  指针变量存放的是地址,指针的本质就是地址
    • 变量声明的时候,如果没有确切的地址赋值,为指针变量赋一个NULL值
    • 赋为NULL值得指针被称为空指针,NULL指针是一个定义在标准库中值为零的常量
    int *p = NULL; // p 空指针
  • 相关阅读:
    android studio 的 adb配置
    纯电动汽车杂谈
    初入职场必备丨二进制面试问题汇总
    线性代数:向量、张量、矩阵和标量
    [Java反序列化]—CommonsCollections6
    ssm好乐买超市管理系统毕业设计-附源码111743
    Python基于django的图书商城管理系统毕业设计源码110938
    都说Redux不支持非序列化数据 简单讲解非序列化数据概念 并举例说明
    探索工业4.0:数字孪生如何重塑工业生产流程?
    HTML页面
  • 原文地址:https://blog.csdn.net/qq_47896523/article/details/126415044