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

未完,待续

 

原文地址:https://www.cnblogs.com/xutopia/p/10833374.html