天天看點

指針不過如此,看完後我差點飄了(二)

函數的指針

一個函數總是占用一段連續的記憶體區域,函數名在表達式中有時也會被轉換為該函數所在記憶體區域的首位址。我們可以把函數的這個首位址賦予一個指針變量,使指針變量指向函數所在的記憶體區域,然後通過指針變量就可以找到并調用該函數。這種指針就是函數指針。

函數指針的定義形式為:

returnType (*pointerName)(param list);

returnType 為函數傳回值類型,pointerNmae 為指針名稱,param list 為函數參數清單。參數清單中可以同時給出參數的類型和名稱,也可以隻給出參數的類型,省略參數的名稱,這一點和函數原型非常類似。

用指針來實作對函數的調用:

#include <stdio.h>
//傳回兩個數中較大的一個
int max(int a, int b)
{
    return a>b ? a : b;
}
int main()
{
    int x, y, maxval;
    //定義函數指針
    int (*pmax)(int, int) = max;  //也可以寫作int (*pmax)(int a, int b)
    printf("Input two numbers:");
    scanf("%d %d", &x, &y);
    maxval = (*pmax)(x, y);
    printf("Max value: %d\n", maxval);
    return 0;
}      

結構體和指針

結構體指針有特殊的文法: -> 符号

如果p是一個結構體指針,則可以使用 p ->【成員】 的方法通路結構體的成員

typedef struct
{
    char name[31];
    int age;
    float score;
}Student;
int main(void)
{
    Student stu = {"Bob" , 19, 98.0};
    Student*ps = &stu;
    ps->age = 20;
    ps->score = 99.0;
    printf("name:%s age:%d
",ps->name,ps->age);
    return 0;
}      

const 和 指針

指向常量的指針,值不能改變,指向可改變

常指針值能改變,指向不可改變

指向常量的常指針,都不能改變

#include <stdio.h>
int main()
{
  // 1 可改變指針
  const int a = 10;
  int *p = &a;
  *p = 1000;
  printf("*p = %d\n", *p);
  // 2 可改變指針
  const b = 10;
  int *pb = &b;
  pb = p;
  printf("*pb = %d\n", *pb);
  // 3
  const c = 10;
  int * const pc = &c;
  *pc = 1000;
  //pc = pb;不能改變
  //4
  const d = 10;
  const * int const pd = &d;
  //*pd = 1000; 不能改變
  printf("\n");
  return 0;
}      

深拷貝和淺拷貝

如果2個程式單元(例如2個函數)是通過拷貝 他們所共享的資料的 指針來工作的,這就是淺拷貝,因為真正要通路的資料并沒有被拷貝。如果被通路的資料被拷貝了,在每個單元中都有自己的一份,對目标資料的操作互相 不受影響,則叫做深拷貝。

#include <iostream>
using namespace std;
 
class CopyDemo
{
public:
  CopyDemo(int pa,char *cstr)  //構造函數,兩個參數
  {
     this->a = pa;
     this->str = new char[1024]; //指針數組,動态的用new在堆上配置設定存儲空間
     strcpy(this->str,cstr);    //拷貝過來
  }
 
//沒寫,C++會自動幫忙寫一個複制構造函數,淺拷貝隻複制指針,如下注釋部分
  //CopyDemo(CopyDemo& obj)  
  //{
  //   this->a = obj.a;
  //  this->str = obj.str; //這裡是淺複制會出問題,要深複制
  //}
 
  CopyDemo(CopyDemo& obj)  //一般資料成員有指針要自己寫複制構造函數,如下
  {
     this->a = obj.a;
    // this->str = obj.str; //這裡是淺複制會出問題,要深複制
     this->str = new char[1024];//應該這樣寫
     if(str != 0)
        strcpy(this->str,obj.str); //如果成功,把内容複制過來
  }
 
  ~CopyDemo()  //析構函數
  {
     delete str;
  }
 
public:
     int a;  //定義一個整型的資料成員
     char *str; //字元串指針
};
 
int main()
{
  CopyDemo A(100,"hello!!!");
 
  CopyDemo B = A;  //複制構造函數,把A的10和hello!!!複制給B
  cout <<"A:"<< A.a << "," <<A.str << endl;
  //輸出A:100,hello!!!
  cout <<"B:"<< B.a << "," <<B.str << endl;
  //輸出B:100,hello!!!
 
  //修改後,發現A,B都被改變,原因就是淺複制,A,B指針指向同一地方,修改後都改變
  B.a = 80;
  B.str[0] = 'k';
 
  cout <<"A:"<< A.a << "," <<A.str << endl;
  //輸出A:100,kello!!!
  cout <<"B:"<< B.a << "," <<B.str << endl;
  //輸出B:80,kello!!!
 
  return 0;
}

      

根據上面執行個體可以看到,淺複制僅複制對象本身(其中包括是指針的成員),這樣不同被複制對象的成員中的對應非空指針會指向同一對象,被成員指針引用的對象成為共享的,無法直接通過指針成員安全地删除(因為若直接删除,另外對象中的指針就會無效,形成所謂的野指針,而通路無效指針是危險的;

除非這些指針有引用計數或者其它手段確定被指對象的所有權);而深複制在淺複制的基礎上,連同指針指向的對象也一起複制,代價比較高,但是相對容易管理。

繼續閱讀