基本介紹
在日常生活中指針是儀器或鐘錶上的,用來指示測量的數據的裝置。在信息工程中指針是一個用來指示一個記憶體地址的計算機語言的變數或中央處理器(CPU)中暫存器。
指針介紹
指針
在信息工程中指針是一個用來指示一個記憶體地址的計算機語言的變數或中央處理器(CPU)中暫存器(Register)。指針一般出現在比較近機器語言的語言,如彙編語言或C語言。面向對象的語言如Java一般避免用指針。指針一般指向一個函式或一個變數。在使用一個指針時,一個程式既可以直接使用這個指針所儲存的記憶體地址,又可以使用這個地址里儲存的變數或函式的值。
指針與C語言
大家都認為,c語言之所以強大,以及其自由性,很大部分體現在其靈活的指針運用上。因此,說指針是c語言的靈魂,一點都不為過。同時,這種說法也讓很多人產生誤解,似乎只有C語言的指針才能算指針。basic不支持指針,在此不論。其實,pascal語言本身也是支持指針的。從最初的pascal發展至今的object pascal,可以說在指針運用上,絲毫不會遜色於c語言的指針。
記憶體分配表
計算機中的記憶體都是編址的,就像你家的地址一樣。在程式編譯或者運行的時候,系統(可以不關心具體是什麼,可能是編譯器,也可能是作業系統)開闢了一張表。每遇到一次聲明語句(包括函式的傳入參數的聲明)都會開闢一個記憶體空間,並在表中增加一行紀錄。記載著一些對應關係。(如圖1所示)
----------------------------------------------------
Declaration | ID Name Address Length
----------------------------------------------------
int nP; | 1 nP 2000 2B
char myChar; | 2 myChar 2002 1B
int *myPointer; | 3 myPointer 2003 2B
char *myPointer2; | 4 myPointer2 2005 2B
----------------------------------------------------
是一個整數
指針,是一個無符號整數(unsigned int),它是一個以當前系統定址範圍為取值範圍的整數。32位系統下定址能力(地址空間)是4G-byte(0~2^32-1)二進制表示長度為32bit(也就是4B)。
int類型也正好如此取值。
例證(一)
例證就是程式1得到的答案和程式2的答案一致。(不同機器可能需要調整一下pT的取值。)
----------------------------------------------------
程式1
#include
main()
{
char *pT;
char t="h";
pT=&t;
putchar(*pT);
}
----------------------------------------------------
程式2
#include
main()
{
char *pT;
char t="h";
pT=(char *)1245048;
putchar(*pT);
}
----------------------------------------------------
加上(char *)是因為畢竟int 和char *不是一回事,需要強制轉換,否則會有個警告。因為char *聲明過的類型,一次訪問1個sizeof(char)長度,double *聲明過的類型,一次訪問1個sizeof(double)長度。
在彙編里int 類型和指針就是一回事了。因為不論是整數還是指針,執行自增的時候,都是其值加一。如果上文聲明char *pT;,彙編語言中pT自增之後值為1245049,可是C語言中pT++之後pT值為1245049。如果32 位系統中, s 上文聲明int *pT;,彙編語言中pT 自增之後值為1245049,可是C 語言中pT++之後pT值為1245052。
為什麼DOS下面的Turbo C,和Windows下VC的int類型不一樣長。因為DOS是16位的,Windows是32位的,可以預見,在64位Windows 中編譯,上文聲明int *pT;,pT++之後pT值為1245056。
例證(二)
那么,複雜的結構怎么分配空間呢?C語言的結構體(彙編語言對應為Record類型)按順序分配空間。(如圖2所示)
----------------------------------------------------
int a;
----------------------------------------------------
typedef struct st
{
double val;
char c;
struct st *next;
} pst;
----------------------------------------------------
pst pT;
----------------------------------------------------
在32 位系統下,記憶體裡面做如下分配(單位:H,16 進制);(如圖3所示)
----------------------------------------------------
變數 2000 2001 2002 2003 2004 2005 2006 … 204C 204D 204E 204F
地址 a a … a
----------------------------------------------------
變數 2050 2051 … 2057 2058 2059 205A 205B 205C 205D 205E 205F
地址 pst.val pst.c pst.next 無效 無效 無效
----------------------------------------------------
這就說明了為什麼sizeof(pst)=16而不是8。編譯器把結構體的大小規定為結構體成員中大小最大的那個類型的整數倍。
至於pT的存儲,可以依例推得。總長為160,此不贅述。
有個問題,如果執行pT++,答案是什麼?是自增16,還是160?別忘了,pT 是常量,不能加減。
所以,我們就可以聲明:
----------------------------------------------------
typedef struct BinTree
{
int value;
struct BinTree *LeftChild;
struct BinTree *RightChild;
} BTree;
----------------------------------------------------
用一個整數,代表一棵樹的結點。把它賦給某個結點的LeftChild/RightChild 值,就形成了上下級關係。只要無法找到一個路徑,使得A->LC/RC->LC/RC...->LC/RC==A,這就構成了一棵二叉樹。反之就成了圖。
指針的作用
指針可以用來有效地表示複雜的數據結構,可以用於函式參數傳遞並達到更加靈活使用函式的目的.使C語言程式的設計具有靈活、實用、高效的特點。
指針不僅僅是C語言的靈魂,運用得好更是事半功倍,讓你寫出的程式更簡潔!
指針網
一個專門用於圖書搜尋的網站,指針圖書網。自稱全球最大中文圖書檢索網站。
鐘錶的指針
在日常生活中指針是儀器或鐘錶上的,可動的,一般狹長的,往往在一段尖的用來指示測量的數據的裝置。
指針法
指針是以手指按壓或爪切某些穴位,代替針刺治病的一種治療方法,具有疏通經絡、行氣活血、調和臟腑功能、開竅醒神、止痛等作用。常用於突發性病症,如虛脫、中暑、癔病及多種痛症。亦可用於一些內傷外感雜病的治療。
按值傳遞
C中函式調用是按值傳遞的,傳入參數在子函式中只是一個初值相等的副本,無法對傳入參數作任何改動。但實際編程中,經常要改動傳入參數的值。這一點我們可以用傳入參數的地址而不是原參數本身,當對傳入參數(地址)取(*)運算時,就可以直接在記憶體中修改,從而改動原想作為傳入參數的參數值。
編程參數值
#include
voidinc(int*val)
{
(*val)++;
}
main()
{
inta=3;
inc(&a);
printf("%d",a);
}
在執行inc(&a);時,系統在記憶體分配表里增加了一行“inc中的val”,其地址為新地址,值為&a。操作*val,即是在操作a了。
*和&運算
(*p)操作是這樣一種運算,返回p 的值作為地址的那個空間的取值。(&p)則是這樣一種運算,返回當時聲明p 時開闢的地址。顯然可以用賦值語句對記憶體地址賦值。我們假設有這么兩段記憶體地址空間,他們取值如下:(單位:H,16 進制)(如圖4所示)
假設有這么一段代碼:(假設開闢空間時p 被分配給了3001H、3002H 兩個位置)
int *p;
p=2003H;
*p=3000H
**p的值為多少?
**p=*(*(p))=*(*(2003H))=*(3000H)=3000H。
那么&&p、*(&p)和&(*p)又等於多少?
&&p=&(&(p))=&(3001H),此時出錯了,3001H 是個常數怎么可能有地址呢?
*&p=*(&(p))=*(3001H)=2003H,也就是*&p=p。
&*p=&(*p)=&(3000H)=2003H,之前有人認為這個是不成立的,實際上&(3000H)是求存儲3000H這個變數所在的記憶體地址,仍然是p的值。下面的代碼是個很簡單的例子:
輸出的結果為5
另類*和&
兩個地方要注意: 在程式聲明變數的時候的*,只是表明“它是一個無符號整數,這個整數指向某個記憶體地址,一次訪問sizeof(type)長度”。這點不要和(*)操作符混淆;
在C++程式聲明變數的時候的&,只是表明“它是一個引用,這個引用聲明時不開闢新空間,它在記憶體分配表加入新的一行,該行記憶體地址等於和調用時傳入的對應參數記憶體地址”。
這點不要和(*)聲明符,(&)操作符混淆。
雙級指針
對於一棵樹,我們通常用它的根節點地址來表示這棵樹。這就是“擒賊先擒王”。找到了樹的根,其每個節點都可以找到。但是有時候我們需要對樹進行刪除節點,增加節點操作,往往考慮到刪除根節點,增加的節點取代原來的根節點作為新根節點的情況。為了修改根節點這個“整數”,我們需要退一步,使用這個“整數”的記憶體地址,也就是指向這個“整數”的指針。在聲明時,我們用2個*號,聲明指向指針的指針。它的意思是“它是一個整數,這個整數指向某個記憶體地址,一次訪問sizeof(int)長度,其值是一個整數,那個整數值指向某個記憶體地址,一次訪問sizeof(BTree)長度。”。詳見<數據結構>有關“樹”的程式代碼。由於存放的指針變數的地址,因此是指向指針變數的指針變數,或稱二級指針變數。
指針的初始化
對指針進行初始化或賦值只能使用以下四種類型的值
:
1. 0 值常量表達式,例如,在編譯時可獲得 0 值的整型 const對象或字面值常量 0。
2. 類型匹配的對象的地址。
3. 另一對象末的下一地址。
4. 同類型的另一個有效指針。
把 int 型變數賦給指針是非法的,儘管此 int 型變數的值可能為 0。但允
許把數值 0 或在編譯時可獲得 0 值的 const 量賦給指針:
int ival;
int zero = 0;
const int c_ival = 0;
int *pi = ival; // error: pi initialized from int value of ival
pi = zero;// error: pi assigned int value of zero
pi = c_ival;// ok: c_ival is a const with compile-time value of 0
pi = 0;// ok: directly initialize to literal constant 0
除了使用數值 0 或在編譯時值為 0 的 const 量外,還可以使用 C++ 語言從 C 語言中繼承下來的預處理器變數 NULL,該變數在 cstdlib頭檔案中定義,其值為 0。如果在代碼中使用了這個預處理器變數,則編譯時會自動被數值 0 替換。因此,把指針初始化為 NULL 等效於初始化為 0 值
:
// cstdlib #defines NULL to 0
int *pi = NULL; // ok: equivalent to int *pi = 0;
與數組關係
指針數組:就是一個由指針組成的數組,那個數組的各個元素都是指針,指向某個記憶體地址。char *p;//p是一個指針數組
數組指針:數組名本身就是一個指針,指向數組的首地址。注意這是一個常數。
example:
char (*p)//p是一個數組指針
函式指針:本身是一個指針,指向一個函式入口地址,通過該指針可調用其指向的函式,使用函式指針可實現回調函式。
example:
#include
void inc(int *val)
{
(*val)++;
}
main()
{
void (*fun)(int *);
int a=3;
fun=inc;//fun是一個函式指針
(*fun)(&a);
printf("%d" , a);
}
指針函式:本身是一個函式,其返回值是一個指針。
example:
void * fun(void);//fun是一個指針函式
這個問題大家應該都碰到過,指針數組和數組指針,剛開始看時覺得還是能看懂,但是過些時又搞混了,最後發現還是沒有真正理解。
下面就簡單說說這兩個概念:一:指針數組,顧名思義,就是說的首先是一個數組吧,然後數組的元素是指針而已。說明形式為:type *pointer_array[constant1][constant2]...[constantn]; 例如:int *pai; 由於‘*’是自右向左結合,因此從右向左看,首先看到說明是一個數組,是一個包含4個元素的數組,然後看到‘*’,顯然是指針類型,由此可以看出數組中存放的是指針而不是一般的類型。同理,char *pac是包含有6個元素,每一個元素都是一個字元型指針。再來說說他們的初始化: int *pai;既然是一個包含4個整形指針的數組那么其對應的將是一個二維整形數組,因為一個整形指針對應一個一維整形數組。那我就用一個二維整形數組來初始化它,事實上一般也都是這么做的,這裡有一個二維數組,int arr={{1,2},{3,4},{5,6}},一個三行兩列的整形數組,注意這裡的行必須和你的指針數組的維數一致,否則是不允許的,不信你可以試試。這個初始化有很多種選擇,以下只列舉常見的兩種:第一種也是很好理解的: for(int i=0;i<3;i++) pai[i]=arr[i]; 顯然arr[i]是每一行的首地址,相當於一個一維數組的數組名,如是把它送給一個整形指針pai[i]是理所當然的了。
第二種方法:在說明數組指針時就初始化:int (*ap)={{1,2},{3,4},{5,6}};
注意:不能將二維數組的數組名賦給指針數組的數組名,pai=arr(錯),因為兩者的類型不一致,二維數組名的類型是指向int[][]型的指針,而指針數組的的數組名是指向int *[]類型的指針。
在c/c++語言中,指針數組最常用的場合就是說明一個字元串數組。即說明一個數組,它的每個元素都是一個字元串。
二:數組指針:指向一個數組的指針。說明形式為:type (*pointer_array)[constant1][constant2]...[constantn]; 注意這裡的圓括弧是必須就將這是由於方括弧[],較指針說明符“*”的優先權高,若無此圓括弧,編譯器將把上述說明解釋成成了一個數組指針。例如:int (*ap); 這樣就說明了一個指向包含有2個元素的整形數組的數組指針,聽起來確實有點彆扭。不過仔細分析應該還是能理解的,就是說ap是一個指針,而它指向的對象是一個指針,注意不要將它和一個指向一個整形變數的指針搞混了。同樣以一個二維數組來說明其初始化問題, int arr={{1,2},{3,4},{5,6}};注意這裡的列數必須和數組指針所指的數組的列數相同。第一種方法: ap=arr; 為什麼這裡能這樣將二維數組名送給ap呢,你可以這樣理解,二維數組不就可以看成是一維數組的數組嗎,而一個數組指針它指向的內容就是一個一維數組,那么你就可以把這個數組指針當做是一個數組名,只不過這個數組裡的元素不是像int,char之類型的,而是一個數組,這樣你就可以把它和二維數組的數組名聯繫在一起了。
第二種方法: ap=&arr; 這裡arr其實就是一維數組的數組名,將它的地址給ap是很自然的,因為ap本來就是指向一個一維數組的。注意這裡不能這樣初始化:int (*a)={{1,2},{3,4},{5,6}};大家可以想想為什麼。當然他們也可以動態賦值,由於篇幅就不探討了。
與“引用”的區別
C++編程中指針與引用的區別
一、指針和引用的區別
(1)引用總是指向一個對象,沒有所謂的 null reference .所有當有可能指向一個對象也有可能不指向對象則必須使用 指針.
由於C++ 要求 reference 總是指向一個對象所以 reference要求有初值.
String & rs = string1;
由於沒有所謂的 null reference 所以在使用前不需要進行測試其是否有值,而使用指針則需要測試其的有效性.
(2)指針可以被重新賦值而reference則總是指向最初或地的對象.
(3)必須使用reference的場合. Operator[] 操作符 由於該操作符很特別地必須返回 [能夠被當做assignment 賦值對象] 的東西,所以需要給他返回一個 reference.
(4)其實引用在函式的參數中使用很經常.
void Get***(const int& a) //這樣使用了引用又可以保證不修改被引用的值
{
}
★ 相同點:
1. 都是地址的概念;
指針指向一塊記憶體,它的內容是所指記憶體的地址;引用是某塊記憶體的別名。
★ 區別:
1. 指針是一個實體,而引用僅是個別名;
2. 引用使用時無需解引用(*),指針需要解引用;
3. 引用只能在定義時被初始化一次,之後不可變;指針可變;
引用“從一而終”
4. 引用沒有 const,指針有 const,const 的指針不可變;
5. 引用不能為空,指針可以為空;
6. “sizeof 引用”得到的是所指向的變數(對象)的大小,而“sizeof 指針”得到的是指針本身(所指向的變數或對象的地址)的大小;
typeid(T) == typeid(T&) 恆為真,sizeof(T) == sizeof(T&) 恆為真,
但是當引用作為成員時,其占用空間與指針相同(沒找到標準的規定)。
7. 指針和引用的自增(++)運算意義不一樣;
二、C++中指針傳遞與引用傳遞(進一步整理)
從概念上講。指針從本質上講就是存放變數地址的一個變數,在邏輯上是獨立的,它可以被改變,包括其所指向的地址的改變和其指向的地址中所存放的數據的改變。
而引用是一個別名,它在邏輯上不是獨立的,它的存在具有依附性,所以引用必須在一開始就被初始化,而且其引用的對象在其整個生命周期中是不能被改變的(自始至終只能依附於同一個變數)。
在C++中,指針和引用經常用於函式的參數傳遞,然而,指針傳遞參數和引用傳遞參數是有本質上的不同的:
指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程中,被調函式的形式參數作為被調函式的局部變數處理,即在棧中開闢了記憶體空間以存放由主調函式放進來的實參的值,從而成為了實參的一個副本。值傳遞的特點是被調函式對形式參數的任何操作都是作為局部變數進行,不會影響主調函式的實參變數的值。(這裡是在說實參指針本身的地址值不會變)
而在引用傳遞過程中,被調函式的形式參數雖然也作為局部變數在棧中開闢了記憶體空間,但是這時存放的是由主調函式放進來的實參變數的地址。被調函式對形參的任何操作都被處理成間接定址,即通過棧中存放的地址訪問主調函式中的實參變數。正因為如此,被調函式對形參做的任何操作都影響了主調函式中的實參變數。
引用傳遞和指針傳遞是不同的,雖然它們都是在被調函式棧空間上的一個局部變數,但是任何對於引用參數的處理都會通過一個間接定址的方式操作到主調函式中的相關變數。而對於指針傳遞的參數,如果改變被調函式中的指針地址,它將影響不到主調函式的相關變數。如果想通過指針參數傳遞來改變主調函式中的相關變數,那就得使用指向指針的指針,或者指針引用。
為了進一步加深大家對指針和引用的區別,下面我從編譯的角度來闡述它們之間的區別:
程式在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變數名及變數所對應地址。指針變數在符號表上對應的地址值為指針變數的地址值,而引用在符號表上對應的地址值為引用對象的地址值。符號表生成後就不會再改,因此指針可以改變其指向的對象(指針變數中的值可以改),而引用對象則不能修改。
最後,總結一下指針和引用的相同點和不同點:
★相同點:
●都是地址的概念;
指針指向一塊記憶體,它的內容是所指記憶體的地址;而引用則是某塊記憶體的別名。
★不同點:
●指針是一個實體,而引用僅是個別名;
●引用只能在定義時被初始化一次,之後不可變;指針可變;引用“從一而終”,指針可以“見異思遷”;
●引用沒有const,指針有const,const的指針不可變;(具體指沒有int& const a這種形式,而const int& a是有 的, 前者指引用本身即別名不可以改變,這是當然的,所以不需要這種形式,後者指引用所指的值不可以改變)
●引用不能為空,指針可以為空;
●“sizeof 引用”得到的是所指向的變數(對象)的大小,而“sizeof 指針”得到的是指針本身的大小;
●指針和引用的自增(++)運算意義不一樣;
●引用是類型安全的,而指針不是(引用比指針多了類型檢查)
操作方法
指針的基本手法可分為揉、捫、切、捏、點5種。
1. 揉法:是用手指的尖端,輕按選下的穴位,作環形平揉的一種方法。揉動時手指的尖端不能離開所接觸的皮膚,手指連同皮膚及皮下組織,以穴位為中心,作一小圓形轉動,不要使手指與皮膚呈摩擦狀態。每次揉一小圓周為1次,每穴位一般以120~180次為標準,約2~3分鐘。次數的多少亦可視病情的輕重而定。常用拇拽和中指作揉法。本法在指針中套用較廣。施術時需要根據病人質強弱和病情輕重施以輕重不同的指力。本法可與捫法配合套用。
2. 捫法:是用手指捫按腧穴或身體一定部位的手法。將手指端深深按壓皮膚及皮下組織深部,同時根據病人體質強弱,施以輕重不同的指力,以感到酸麻脹痛為止。當指端按入時,應逐漸減輕指力,最後停止。每穴一般捫按3分鐘左右。捫法又分為單指法和雙指法2種。
(1)單指法:一般用拇指或中指指端按壓在穴位上。此法常用於胸腹部和四肢部的穴位,如氣海CV (RN)6 ,中脘CV (RN)12,曲池LI11 、足三里ST36等(見圖)
(2)雙指法:即兩手指同時捫按兩個穴位。此法常用於頭面、頸項、腹部、背腹部的穴位,如風池GB20 、陽白GB14 、天樞ST25 等穴位。(見圖)
3. 切法:用拇指指甲切按腧穴。操作時可用脫脂棉少許,復於指甲 ,防止切傷皮膚。指切時用力需要輕而緩慢,特別是壓痛處更應注意,儘量避免切處劇烈疼痛。本法多用於狹窄部位的穴位,如人中GV (DU)26 、迎香LI20 、少商LU11 等(見圖)。
4. 捏法:是用兩個手指對稱捏壓穴位的手法,可用拇、食二指,也可用拇、中二指或拇指與其它各指,在上下方或左右方對稱相向用力。可捏壓一個或兩個穴位上。如果捏壓一個穴位,拇指在這個穴位上,另一指或其它各指則在對稱地方,此法常用於四肢、肩頸部等部位的穴位,如合谷LI4 、曲池LI11 、足三里ST36 、三陰交SP6 等(見圖)。
5. 點法:是一指或二、三指點在痛點或穴位上,先輕後重。逐漸深透。本法常用於肩部、背部、臀部和大腿等部位的穴位。
適用範圍
由於指針療法不需要任何操作器械及穴位消毒,可以隨時隨地套用,因此可套用於多種急症的處理,如暈厥、劇烈疼痛等。又因指針療法具有疼痛小的特點,因此廣泛適用於年老體弱、兒童、懼怕針刺者及孕婦等。也可作為患者自我治療及預防疾病的一種方法。
注意事項
(1)施術者注意手的消毒,以免交叉感染;指甲宜常剪,以免切傷病人皮膚。
(2)指力的輕重以病人能耐受為宜。以免病人產生不適或暈針;對年老體弱者和兒童,施術時指力不可過重。
(3)指針的施術時間以1~3分鐘為標準,亦可根據病情增減。
(4)急性傳染病、皮膚病、腫瘤以及腹痛拒按的患者,不宜使用指針。
(5)小兒頭部的囟門區和孕婦的合谷LI4 、三陰交SP6 以及腹部穴位等,不宜用指針。
(6)過飢、過飽、酒醉、勞累過度時,不宜用指針。