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);
     }

    未完,待续

     

  • 相关阅读:
    poj 2312 Battle City
    poj 2002 Squares
    poj 3641 Pseudoprime numbers
    poj 3580 SuperMemo
    poj 3281 Dining
    poj 3259 Wormholes
    poj 3080 Blue Jeans
    poj 3070 Fibonacci
    poj 2887 Big String
    poj 2631 Roads in the North
  • 原文地址:https://www.cnblogs.com/xutopia/p/10833374.html
Copyright © 2011-2022 走看看