zoukankan      html  css  js  c++  java
  • C语言指针详解

     以下讲解是按照如下这个程序的执行顺序来讲解的

    一,程序中的c语言指针

     int a,b; //这是一个普通的整型变量
         int *p;//这是一个整形的指针
         a = 3;
         b = 4;
         6
         printf("   a的地址:%d; ", &a);
         printf("   b的地址:%d; ", &b);
         printf("   p的地址:%d; ", &p);
         printf("   p的值:%d,现在p的值是不确定的,目前只是为p申请了地址,还没有为它赋值; ", p);
         
         p = &a;//取址运算/* p现在指向a */
         printf("   利用取址操作p = &a;,把a的地址赋值给p,现在p的值是%d,也就是a的地址; ", p);
         printf("   p的地址没有变化,p的地址仍然是%d,在这个地址上存储的是变量a的地址; ", &p);
         
         printf("   利用*运算符得到指针p指向地址中的数值为%d,在刚才p已经指向变量a的地址了,所以指针p指向地址中的值是3,但是p的值仍然是a的地址; ", *p);
         
         b = *p;/* b现在为a的值 */
         printf("   b = *p;,现在b的值就是p指向地址中的值,也就是a的值:%d; ", b);
     
         *p = 5;/* a现在为5 */
         printf("   现在利用*p为p指向地址中存储的值进行赋值:%d,这时a的值也已经改变了:%d; ", *p,a);

    int a,b; //这是一个普通的整型变量 ​ int *p;//这是一个整形的指针 ​ a = 3; ​ b = 4;

     printf("    a的地址:%d;
    ", &a);
     printf("   b的地址:%d; ", &b);
     printf("   p的地址:%d; ", &p);
     printf("   p的值:%d,现在p的值是不确定的,目前只是为p申请了地址,还没有为它赋值; ", p);

    指针p定义的时候没有进行初始化,所以在这里,p的初始值是不确定的。  当然也可以在p定义的时候赋初值,这样p的初始值就是确定的了。

     p = 1;

    一元运算符&可用于取一个对象的地址,如下,这时p为指向a的指针。地址运算符&只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式、常量或register类型的变量。

    这时p的值是3930420,即变量a的地址;

    利用&取出p的地址仍然为3930396,没有变;

    利用间接寻址p可以得到指针p指向地址中的值为3,即a的值。

    p = &a;//取址运算/* p现在指向a */ printf(" 利用取址操作p = &a;,把a的地址赋值给p,现在p的值是%d,也就是a的地址; ", p); printf(" p的地址没有变化,p的地址仍然是%d,在这个地址上存储的是变量a的地址; ", &p);

     p = &a;//取址运算/* p现在指向a */
         printf("   利用取址操作p = &a;,把a的地址赋值给p,现在p的值是%d,也就是a的地址; ", p);
         printf("   p的地址没有变化,p的地址仍然是%d,在这个地址上存储的是变量a的地址; ", &p);
         
         printf("   利用*运算符得到指针p指向地址中的数值为%d,在刚才p已经指向变量a的地址了,所以指针p指向地址中的值是3,但是p的值仍然是a的地址; ", *p);

    利用*可以得到p地址中的数值,这里b的值就是3,即a的值。

      b = *p;/* b现在为a的值 */
      printf("   b = *p;,现在b的值就是p指向地址中的值,也就是a的值:%d; ", b);

    对*p进行赋值后,也就是对p指针指向地址中的数值进行赋值,这是a的值也就变为了5

     *p = 5;/* a现在为5 */
     printf("   现在利用*p为p指向地址中存储的值进行赋值:%d,这时a的值也已经改变了:%d; ", *p,a);

    二,图解C语言指针

    定义两个变量

     int a=3,b; //这是一个普通的整型变量
     int *p;//这是一个整形的指针

    定义后,a的地址是0x2000,p的地址是0x3000; 在定义的时候a赋的初始值是3,p没有赋初始值,所以p的值是不确定的。

    现在进行运算:

     p = &a;//取址运算/* p现在指向a */

    这时内存图就变成了这样,p的地址没有变化,但是p的值变化了,此时,*p=3;

    三,指针与函数

    指针作为函数的形参

    在程序设计中,指针作为函数形参往往会带来意想不到的效果,下面用一个例程来讲解指针作为函数形参的特性。 例子:用一个函数交换两个变量的值:

     void Swap(int x, int y)
     {
         int temp = 0;
         temp = x;
         x = y;
         y = temp;
     }
     
     void Swap_pointer(int *x, int *y)
     {
         int temp = 0;
         temp = *x;
         *x = *y;
         *y = temp;
     }
     
     /*
     指针与函数
     2019-05-09
     */
     void Test2()
     {
         int a = 1, b = 2;
         Swap(a, b);
         printf("a=%d ", a);
         printf("b=%d ", b);
         Swap_pointer(&a, &b);
         printf(" ");
         printf("a=%d ", a);
         printf("b=%d ", b);
     }

    执行结果如下图,可以明显的看出指针作为函数形参的特性。

    具体讲解详见《C语言程序设计》的5.2章节。

    指针,结构体与函数

    在C语言中,函数本身不是变量,但是也可以定义指向函数的指针。这种指针的使用方法、特性与变量指针的使用方法、特性大同小异。

    下面的介绍都是围绕这段函数来讲解的

     /*
     xutopia
     */
     #include "stdio.h"
     
     int max(int x, int y)
     {
      return x > y ? x : y;
     }
     
     int min(int x, int y)
     {
      return x < y ? x : y;
     }
     
     int* maxP(int x, int* y)
     {
      return x > *y ? x : *y;
     }
     
     void CallbackFun(int x, int(*f)(int, int b))
     {
      int i = 0, a = 0;
      for (i = x;i < 10;i++)
      {
      a = f(i, 0);
      printf("%d, ",a);
      }
      printf(" ");
     }
     
     typedef struct _dat
     {
      int(*fun)(int, int);
      int* (*funP)(int, int*);
      void(*funFun)(int, int(*f)(int, int));
     
     }datTypedef;
     
     datTypedef s;
     
     int main()
     {
      int maxval = 0, minval = 0;
      int a = 10;
      int *pval;
      pval = &a;
      //指针与函数
      //int(*p)(int a, int b);//right
      int(*p)(int, int);
      p = &max;
      maxval = p(1, 2);
      printf("max=%d ", maxval);
      p = &min;
      minval = p(1, 2);
      printf("min=%d ", minval);
      //结构体,指针与函数
      s.fun = &max;
      maxval = s.fun(3, 4);
      printf(" max=%d ", maxval);
     
      s.funP = &maxP;
      pval = s.funP(9, pval);
      printf("max=%d ", pval);
      //回调函数
      s.funFun = &CallbackFun;
      s.funFun(4, max);
      system("pause");
     }

    1,指针指向函数。在某些场景下,我们需要用到指针指向函数。

    2,在结构体中定义函数指针。利用这样的技巧,可以实现高级语言的特性,例如类的方法,属性等,可以使得代码更加安全规范。

    3,函数指针作为函数的参数。

    四,指针与数组

    使用数组时,需要明白的一些事项:   1,申请的数组存储在相邻的内存区域中;   2,数组名所代表的就是该数组最开始的一个元素的地址;   3,对数组元素a[i]的引用也可以写成*(a+i)这种形式;   4,&a[i]和a+i的含义是相同的,简而言之,一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现。;   5,数组名不是变量,因此,类似于a=pa和a++形式的语句是非法的;   6,当把数组名传递给一个函数时,实际上传递的是该数组第一个元索的地址;   下面通过一些例程来解释指针与数组的运用:

     /*
     统计字符串长度
     2019-05-09
     */
     //int StrLen(char *s)//数组作为形参有两种写法“char s[]”、“char *s”,效果一样的
     int StrLen(char s[])
     {
         int n;
         for (n = 0; *s != ''; s++)
             n++;
         return n;
     }
     
     /*
     2019-05-09
     */
     void Array_pointer()
     {
         int arr[10];
         int *p;
         int i = 0;
     
         for (i = 0; i < 10; i++)
        {
             arr[i] = i;
             printf("数组[%2d]的地址:%d ", i,&arr[i]);
        }
         printf("将指针p指向数组arr的第0个元素 ");
         //将指针p指向数组arr的第0个元素
         p = &arr[0];
         p = arr;//也可以写成这种形式,因为数组名所代表的就是该数组最开始的一个元素的地址
         for (i = 0; i < 10; i++)
        {
             printf("p[%d]=%d ", i, *(p++));
        }
     
         printf("将指针p指向数组arr的第3个元素 ");
         //将指针p指向数组arr的第3个元素
         //打印出来的结果可以看到,最后面3个地址的数值是不确定的
         p = &arr[3];
         for (i = 0; i < 10; i++)
             printf("p[%d]=%d ", i, *(p++));
     
         printf("安全的做法应该是 ");
         //安全的做法应该是
         p = &arr[3];
         for (i = 0; i < 10-3; i++)
             printf("p[%d]=%d ", i, *(p++));
     
         printf("对数组也可以进行如下方式的使用 ");
         //对数组也可以进行如下方式的使用
         //printf("arr[%d]=%d ", i, *(arr++));//数组名不是变量,因此,类似于arr=p和arr++形式的语句是非法的
         for (i = 0; i < 10; i++)
             printf("arr[%d]=%d ", i, *(arr + i));
     
         int n = 0;
         char *str = "hello,word";
         n = StrLen(str);
         printf("n=%d ", n);
         n = StrLen(&str[2]);//当然,这里可以只传入数组的一部分
         printf("n=%d ", n);
     }

    五,地址算术运算

    通过以下这个例子讲解地址算术运算   这是一个不完善的存储分配程序。它由两个函数组成。第一个函数alloc(n)返回一个指向n个连续字符存储单元的指针,alloc函数的调用者可利用该指针存储字符序列。第二个函数afree(p)释放已分配的存储空间,以便以后重用。之所以说这两个函数是“不完善的”,是因为对afree函数的调用次序必须与调用alloc函数的次序相反。换句话说,alloc与afree以栈的方式(即后进先出的列表)进行存储空间的管理。标准库中提供了具有类似功能的函数malloc和free,它们没有上述限制。

     #define ALLOCSIZE 10000 /* 可用空间大小 */
     
     static char allocbuf[ALLOCSIZE];/* alloc使用的存储区 */
     static char *allocp = allocbuf; /* 下一个空闲位置 */
     
     char *alloc(int n) /* 返回指向n个字符的指针 */
     {
         if (allocbuf + ALLOCSIZE - allocp >= n) { /* 有足够的空闲空间 */
             allocp += n;
             return allocp - n; /* 分配前的指针 */
        }
         else /* 空间不够 */
             return 0;
     }
     
     void afree(char *p) /* 释放p指向的存储区 */
     {
         if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
             allocp = p;
     }
     
     /*
     指针运算
     2019-05-09
     */
     void Pointer_cal()
     {
         char *allAddr;
         printf("空闲地址:%d ", allocp);
         allAddr = alloc(100);
         printf("空闲地址:%d ", allocp);
         afree(allAddr);
         printf("空闲地址:%d ", allocp);
     }

    未完,待续

     

  • 相关阅读:
    [hihocoder][Offer收割]编程练习赛62
    [laravel]用户异地登录后踢掉之前的登录
    [hihocoder][Offer收割]编程练习赛57
    [hihocoder][Offer收割]编程练习赛58
    线性基
    数组墙 最详细的解题报告
    MySQL中EXPLAIN命令详细解析
    MySQL性能优化
    MySQL中的事务隔离级别
    Java NIO的理解和应用
  • 原文地址:https://www.cnblogs.com/xutopia/p/10833374.html
Copyright © 2011-2022 走看看