總結了一些指針易出錯的常見問題(一)


  簡介:計算機是如何從內存單元中存取數據的呢?從程序設計的角度看,有兩種辦法:一是通過變量名;二是通過地址。程序中聲明的變量是要占據一定的內存空間的,例如,C語言中整型變量占2字節,實型變量占4字節。程序中定義的變量在程序運行時被分配內存空間。在變量分配內存空間的同時,變量名也就成為了相應內存空間的名稱,在程序中可以用這個名字訪問該內存空間,表現在程序語句中就是通過變量名存取變量內容(這就是程序中定義變量的用途,即程序中通過定義變量來實現數據在內存中的存取)。但是,有時使用變量名不夠方便或者根本沒有變量名可用,這時就可以直接用地址來訪問內存單元。例如,學生公寓中每個學生住一間房,每個學生就相當於一個變量的內容,變量名指定為學生姓名,房間是存儲單元,房號就是存儲單元地址。如果知道了學生姓名,可以通過這個名字來訪問該學生,這相當於使用變量名訪問數據。如果知道了房號,同樣也可以訪問該學生,這相當於通過地址訪問數據。   

  由於通過地址能訪問指定的內存存儲單元,因此可以說,地址“指向”該內存存儲單元(如同說,房間號“指向”某一房間一樣)。故將地址形象化地稱為“指針”,意思是通過它能找到以它為地址的內存單元。一個變量的地址稱為該變量的“指針”。如果有一個變量專門用來存放另一個變量的地址(即指針),則它稱為“指針變量”。在許多高級程序設計語言中有專門用來存放內存單元地址的變量類型,這就是指針類型。指針變量就是具有指針類型的變量,它是用於存放內存單元地址的。通過變量名訪問一個變量是直接的,而通過指針訪問一個變量是間接的。就好像要在學生公寓中找一位學生,不知道他的姓名,也不知道他住哪一間房,但是知道101房間里有他的地址,走進101房間后看到一張字條:“ 找我請到302”,這時按照字條上的地址到302去,便順利地找到了他。這個101房間,就相當於一個指針變量,字條上的字便是指針變量中存放的內容(另一個內存單元的地址),而住在302房間的學生便是指針所指向的內容。

1.認識指針

  1.1 指針和內存

    C程序在編譯后,會以三種形式使用內存:

    1)靜態/全局內存

      靜態/全局聲明的變量在這里。這些變量從程序開始運行時分配,直到程序終止才消失。

      所有函數都能訪問全局變量,靜態變量的作用域則局限在定義它們的函數內部。

    2)自動內存

      這些變量在函數內部聲明,並且在函數調用時才創建,他們的作用域局限於函數內部,

      而且聲明周期限制在函數的執行時間內。

    3)動態內存

      內存分配在堆上,可以根據需要釋放,而且直到釋放才消失。指針引用分配的內存,作用域

      局限於引用內存的指針。

    數組和指針實現員工鏈表時情況:

1.創建數組時需要知道數組的長度,這樣就會限制鏈表所容納的元素數量。

2.指針沒有上面的限制,新節點可以根據需要動態分配。malloc和free函數分配和釋放動態內存。

Tips:

1.如何閱讀指針聲明,那就是倒過來讀

const int *pci     // pci 是一個指向整數常量的指針變量

2.在不同平台上用一致的方式顯示指針的值比較困難。一種方法就是把指針(強制)轉化為void指針,然后用%p/%o/%x格式說明符來顯示。

 printf("Value of pi: %p\n", (void*)pi);

void指針是通用指針,用來存放任何數據類型的引用。任何指針都可以被賦給void指針,它可以被轉換為原來的指針類型。

void只用作數據指針,不能用作函數指針。

3.指針被聲明為全局/靜態。就會在程序啟動時被初始化為NULL。

   1.2 指針操作符

     

    直接間接引用

      指針可以用不同的間接引用層級。雙重指針**。

char *titles[]={"ajj","bbs","klc"};         char **bestbooks[2];         char **english[1];           bestbooks[0]=&titles[0];           bestbooks[1]=&titles[1];           english[2]=&titles[2];

    常量和指針

2. C的動內存管理 malloc/realloc/calloc/free

  malloc分配的是字節數/  sizeof(字節數)  

      內存泄漏:丟失內存地址,應該調用free函數沒有調用

 

    迷途指針

3.指針和函數     指針函數/函數指針/程序棧/堆

  程序棧(程序棧通常占據區域下部,堆則上部,棧向上生長,堆向下生長)

用指針傳遞數據的一個主要原因就是函數可以修改數據。

   

 4. 函數指針和指針函數

 

 

為函數指針聲明一個類型定義會比較方便。類型定義的如下:

typedef int (*funcptr)(int);

funcptr fptr2;

fptr2=square;

......

#include<iostream> 
using namespace std; int main() { int square(int num); int (*fptr1)(int); //類似於定義變量 
    typedef int (*funcptr)(int); funcptr fptr2; fptr2=square; int n=5; fptr1=&square;//此時也可以將其替換為 fptr1=square, 函數名表示函數入口地址
    cout<<"5 square is "<<fptr1(n)<<endl; cout<<"5 square is "<<fptr2(n)<<endl; return 0; } int square(int num) { return num*num; } 

傳遞函數指針,只要把函數指針聲明作為函數參數(形參)即可。

#include<iostream>
using namespace std; int add (int num1,int num2) { return num1+num2; } int sub(int num1,int num2) { return num1-num2; } typedef int (*fptr)(int,int); int compute(fptr operation, int num1,int num2) { return operation(num1,num2); } int main() { cout<<compute(add,5,8)<<endl; cout<<compute(sub,5,8)<<endl; return 0; } 

返回函數指針需要把函數的返回類型聲明為函數指針。(注意注釋部分////)

#include<iostream>
using namespace std; int add (int num1,int num2) { return num1+num2; } int sub(int num1,int num2) { return num1-num2; } typedef int (*fptr)(int,int); int compute(fptr operation, int num1,int num2) { return operation(num1,num2); } //////////////////////////////////////////////
    fptr select(char opcode) { switch(opcode) { case '+':return add; case '-':return sub; } } int evalute(char opcode,int num1,int num2) { fptr operation=select(opcode); return operation(num1,num2); } ///////////////////////////////////////////////
    int main() { cout<<evalute('+',5,8)<<endl; cout<<evalute('-',5,8)<<endl; return 0; } 

使用函數指針數組可以基於某些條件選擇要執行的函數,只要把這些函數指針聲明為數組類型即可。定義如下:

#include<iostream>
using namespace std; int add (int num1,int num2) { return num1+num2; } int sub(int num1,int num2) { return num1-num2; } typedef int (*fptr)(int,int); int compute(fptr operation, int num1,int num2) { return operation(num1,num2); } //////////////////////返回函數指針////////////////////////     fptr select(char opcode) { switch(opcode) { case '+':return add; case '-':return sub; } } int evalute(char opcode,int num1,int num2) { fptr operation=select(opcode); return operation(num1,num2); } /////////////////////使用函數指針數組//////////////////////////
typedef int (*operation)(int,int); operation operations[128]={NULL}; void initializeopArray() { operations['+']=add; operations['-']=sub; } int evaArray(char opcode,int num1,int num2) { fptr operation; operation=operations[opcode]; return operation(num1,num2); } /////////////////////////////////////////////////////
    int main() { initializeopArray(); cout<<evalute('+',5,8)<<endl; cout<<evalute('-',5,8)<<endl; cout<<evaArray('+',5,8)<<endl; cout<<evaArray('-',5,8)<<endl; return 0; } 

小結:理解程序棧和堆有助於更深入徹底理解程序的工作方式以及指針的行為。

備注:本文代碼均為Dev C++編譯器編譯


免責聲明!

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



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