C語言指針詳解


 以下講解是按照如下這個程序的執行順序來講解的

一,程序中的c語言指針

 int a,b; //這是一個普通的整型變量
     int *p;//這是一個整形的指針
     a = 3;
     b = 4;
     6
     printf("   a的地址:%d;\r\n", &a);
     printf("   b的地址:%d;\r\n", &b);
     printf("   p的地址:%d;\r\n", &p);
     printf("   p的值:%d,現在p的值是不確定的,目前只是為p申請了地址,還沒有為它賦值;\r\n", p);
     
     p = &a;//取址運算/* p現在指向a */
     printf("   利用取址操作p = &a;,把a的地址賦值給p,現在p的值是%d,也就是a的地址;\r\n", p);
     printf("   p的地址沒有變化,p的地址仍然是%d,在這個地址上存儲的是變量a的地址;\r\n", &p);
     
     printf("   利用*運算符得到指針p指向地址中的數值為%d,在剛才p已經指向變量a的地址了,所以指針p指向地址中的值是3,但是p的值仍然是a的地址;\r\n", *p);
     
     b = *p;/* b現在為a的值 */
     printf("   b = *p;,現在b的值就是p指向地址中的值,也就是a的值:%d;\r\n", b);
 
     *p = 5;/* a現在為5 */
     printf("   現在利用*p為p指向地址中存儲的值進行賦值:%d,這時a的值也已經改變了:%d;\r\n", *p,a);

int a,b; //這是一個普通的整型變量 ​ int *p;//這是一個整形的指針 ​ a = 3; ​ b = 4;

 printf("   a的地址:%d;\r\n", &a);
 printf("   b的地址:%d;\r\n", &b);
 printf("   p的地址:%d;\r\n", &p);
 printf("   p的值:%d,現在p的值是不確定的,目前只是為p申請了地址,還沒有為它賦值;\r\n", 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的地址;\r\n", p); printf(" p的地址沒有變化,p的地址仍然是%d,在這個地址上存儲的是變量a的地址;\r\n", &p);

 p = &a;//取址運算/* p現在指向a */
     printf("   利用取址操作p = &a;,把a的地址賦值給p,現在p的值是%d,也就是a的地址;\r\n", p);
     printf("   p的地址沒有變化,p的地址仍然是%d,在這個地址上存儲的是變量a的地址;\r\n", &p);
     
     printf("   利用*運算符得到指針p指向地址中的數值為%d,在剛才p已經指向變量a的地址了,所以指針p指向地址中的值是3,但是p的值仍然是a的地址;\r\n", *p);

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

  b = *p;/* b現在為a的值 */
  printf("   b = *p;,現在b的值就是p指向地址中的值,也就是a的值:%d;\r\n", b);

對*p進行賦值后,也就是對p指針指向地址中的數值進行賦值,這是a的值也就變為了5

 *p = 5;/* a現在為5 */
 printf("   現在利用*p為p指向地址中存儲的值進行賦值:%d,這時a的值也已經改變了:%d;\r\n", *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\r\n", a);
     printf("b=%d\r\n", b);
     Swap_pointer(&a, &b);
     printf("\r\n");
     printf("a=%d\r\n", a);
     printf("b=%d\r\n", 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("\r\n");
 }
 
 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\r\n", maxval);
  p = &min;
  minval = p(1, 2);
  printf("min=%d\r\n", minval);
  //結構體,指針與函數
  s.fun = &max;
  maxval = s.fun(3, 4);
  printf("\r\nmax=%d\r\n", maxval);
 
  s.funP = &maxP;
  pval = s.funP(9, pval);
  printf("max=%d\r\n", 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 != '\0'; 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\r\n", i,&arr[i]);
    }
     printf("將指針p指向數組arr的第0個元素\r\n");
     //將指針p指向數組arr的第0個元素
     p = &arr[0];
     p = arr;//也可以寫成這種形式,因為數組名所代表的就是該數組最開始的一個元素的地址
     for (i = 0; i < 10; i++)
    {
         printf("p[%d]=%d\r\n", i, *(p++));
    }
 
     printf("將指針p指向數組arr的第3個元素\r\n");
     //將指針p指向數組arr的第3個元素
     //打印出來的結果可以看到,最后面3個地址的數值是不確定的
     p = &arr[3];
     for (i = 0; i < 10; i++)
         printf("p[%d]=%d\r\n", i, *(p++));
 
     printf("安全的做法應該是\r\n");
     //安全的做法應該是
     p = &arr[3];
     for (i = 0; i < 10-3; i++)
         printf("p[%d]=%d\r\n", i, *(p++));
 
     printf("對數組也可以進行如下方式的使用\r\n");
     //對數組也可以進行如下方式的使用
     //printf("arr[%d]=%d\r\n", i, *(arr++));//數組名不是變量,因此,類似於arr=p和arr++形式的語句是非法的
     for (i = 0; i < 10; i++)
         printf("arr[%d]=%d\r\n", i, *(arr + i));
 
     int n = 0;
     char *str = "hello,word";
     n = StrLen(str);
     printf("n=%d\r\n", n);
     n = StrLen(&str[2]);//當然,這里可以只傳入數組的一部分
     printf("n=%d\r\n", 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\r\n", allocp);
     allAddr = alloc(100);
     printf("空閑地址:%d\r\n", allocp);
     afree(allAddr);
     printf("空閑地址:%d\r\n", allocp);
 }

未完,待續

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM