先看個簡單的:char *p,這定義了一個指針,指針指向的資料類型是字元型,char *(p)定義了一個指針p;
char *p[4], 為指針數組,由于[]的優先級高于*,是以p先和[]結合,p[]是一個數組,暫時把p[]看成是q,也就是char *(q),定義了一個指針q,隻不過q是一個數組罷了,故定義了一個數組,數組裡面的資料是char *的,是以數組裡面的資料為指針類型。是以char *p[4]是四個指針,這四個指針組成了一個數組,稱為指針數組,既有多個指針組成的數組。
char(*p)[4],為數組指針,強制改變優先級,*先與p結合,使p成為一個指針,這個指針指向了一個具有4個char型資料的數組。故p中存放了這個char型數組的首位址,可用數組指針動态記憶體申請:
char (*p)[10];
p=(char*)malloc(sizeof(char[x])*n);
char *f(char,char),為指針函數,()的優先級高于*,故f先與()結合,成為函數f(),函數的傳回值是char *類型的,故傳回值是一個指針。
char (*f)(char,char),為函數指針,*與f結合成為一個指針,這個指針指向函數的入口位址。函數名就是函數的首位址。函數指針是指向函數的指針變量。 因而“函數指針”本身首先應是指針變量,隻不過該指針變量指向函數。這正如用指針變量可指向整型變量、字元型、數組一樣,這裡是指向函數。c在編譯時,每一個函數都有一個入口位址,該入口位址就是函數指針所指向的位址。有了指向函數的指針變量後,可用該指針變量調用函數,就如同用指針變量可引用其他類型變量一樣,在這些概念上是一緻的。函數指針有兩個用途:調用函數和做函數的參數。
int func(int x); /* 聲明一個函數 */
int (*f) (int x); /* 聲明一個函數指針 */
f=func; /* 将func函數的首位址賦給指針f */
以後如果要調用函數func(),也就可以這樣調用:(*f)();
/****************************************************二級指針**************************************************/
二級指針簡單來說就是指向指針的指針。
char a=200;
char *p;
char **q;//q是一個二級指針
p=&a;
q=&p; //q指向指針p
假設變量a在記憶體中的位址為2000h,則它們的關系就如下面的示意圖:
指針p指向a,p的值是2000h,*p就是取位址2000h中的值即a為200,而p本身的位址是4000h,q指向指針p,*q就是取位址4000h中的值即p的值為2000h,而**q就是取位址2000h中的值即200。
是以:
*p==200;
*q=2000h;
**q=200;
以上的q是一個指針指針的二級指針,然而還有指向數組的二級指針。
當一個指針變量指向另一個指針變量時,則形成二級指針。使用二級指針可以在建立複雜的資料結構時提供較大的靈活性,能夠實作其他語言所難以實作的一些功能。定義二級指針的形式是:
類型辨別符**二級指針變量名
定義指針的同時可以對其指派,然後就可以使用了。
如果定義一個指針數組,則指針數組名就是一個二級指針。用指針數組元素值指向長度同的字元串,操作時可以節省記憶體空間,而對位址進行操作,提高了運作效率。
char s[3][5]={ "abc ", "uio ", "qwe "};
可以看成是三個指向字元串的一級指針(s[0],s[1],s[2]),由s[3]得。
而s[3]本身又是一個一維數組存儲s[0],s[1],s[2]三個一級指針,則s就可以看作是一個二級指針,即指向指針的指針。
這時定義一個二級指針char**p;就能通過p通路二維數組了。
也可以這樣char *p[] = {“ab“, “cd“, “ef“};定義了一個指針數組.
char **sp = p;
就可以使用sp[i]來通路字元串了。
大家都知道,要想在函數中改變形參的值,形參用指針傳遞就行了。
比如:
void f(char *p1,char *p2)
{
*p1=10;
*p2=20;
}
void main()
char a,b;
char *p,*q;
p=&a;
q=&b;
f(p,q);
執行後此時a=10,b=20;
原理如下:
當調用函數f後,p1指向a,p2指向b;
接着*p1=10; *p2=20;使p1指向的位址空間的值賦為10,p2指向的位址空間的值賦為20;
然後函數調用結束,這時a=10,b=20;
如果要在函數中改變指針的值,比如改變p,q的值就需要用到二級指針。
void getmemory(char **p, int num)
*p = (char *)malloc(sizeof(char) * num);
以上函數,就實作了在函數中改變指針的值,使指針指向新申請的空間。
附:/*************************以下内容轉載自http://msfengyun.spaces.live.com/blog/cns!7e7030e1847fd490!188.trak***////////////
指針是c語言的一大特色,它就像一把雙刃劍:使用得當能夠給程式帶來極大的靈活性和高效性;反之,程式就會變得難以調試,漏洞百出!
衆所周知,指針實質就是位址!一個變量的位址即稱為此變量的“指針”。如果有這樣一種變量:它的存儲單元裡存放的是其它變量的位址!我們就稱之為“指針變量”。(請注意兩者之間的差別:兩個完全不同的概念!)
我們都知道,數組名和函數名就是它們的入口位址。同理,一個變量名其實也是此變量的所在位址!c語言中有一種運算符為“&”:取址運算符。因為數組名與函數名本身代表的就是位址,通常不會對并且也不能對它們進行取址操作或其它運算操作(其實對于函數名的直接引用與對它取址是等價的)。這也是它們被稱為“常量”的原因!但對于一個變量來講,情況就不一樣了。要想獲得它的位址,就必須進行“&”運算,盡管它本身表示的也是位址值!而對變量直接進行引用得到卻是它所在的記憶體單元的資料内容!“指針變量”作為一種變量當然也不能例外!隻不過它與其它普通變量的差别是,它的内容是其它變量(包括“指針變量”)的位址,在win32上,它的大小恒為32位,4byte。而普通變量則不會有大小上的限制!對指針變量所指向的位址的資料内容的擷取則是通過操作符“*”。在了解上我們将“提領操作符*”視為類型的一部分,并且這種資料類型是一種變量位址類型(均對每一個“*”而言)!
隻要明白了以上常識,“指針”将不會再是程式設計中的“攔路虎”!
從記憶體的存儲映象的角度來講,c的規則數組(不包括通過資料結構設計的多元數組)不存在多元,也就是說所有的數組本質上都是一維的,而一級指針就等價于一維數組!關鍵的不同在于多元數組與一維數組語義上的差别!而我們了解多元數組通常将之形象地描述成“矩陣”形式。更為精确的了解是多元數組的每個元素就是一個數組,如此遞歸下去直至最後每個元素是一個簡單的變量類型,最終得到的就是一個特殊的一維數組!
看如下一個例程:
#include<stdio.h>
#include<stdlib.h>
int a[][3]={{250,250,250},{250,250,250}};
int* p=(int*)a;-------------------@
注:語句@在.c檔案中可以寫成int* p=a;但會給出警告;若是在.cpp檔案中寫成int* p=a;是通不過編譯的!是以規範起見,最好在任何時候都要進行強制類型轉換!
通過進行調試,在wathch視窗中檢視變量的記憶體位址情況如下:
從圖上可以看出二維數組在記憶體單元中是線性增長的。倘若此時有一個二級指針int** q=null;如何通過q來操作二維數組a[2][3]呢?
通過q=a;如何呢?在.c檔案中可以編譯通過,但會給出警告。若是在.cpp檔案中則不會編譯通過!我相信很多人的第一反應是加上強制類型轉換:q=(int**)a;如此以來,程式編譯、連結暢通無阻,連警告也沒有!但一運作就會出問題:這是當然的!下面進行詳細分析。。。。。。
根據我上面講述的:q可視為int**類型,且是int*變量的位址類型變量!對q (指針變量)的引用,得到是的其(即q)記憶體單元的資料,即int*變量的位址,*q則是擷取q所指向的int*變量類型位址的内容,相當于int* q變量q的直接引用,得到是int類型變量的位址。q所占的記憶體為4byte,*q所占的記憶體也為4byte。一切都清楚了。
現在來分析二維數組a的資料類型。我們知道指針與數組的聯系的常見具體應用有兩種:一種是“數組指針”:形如(*ptr)[];另外一種是“指針數組”:形如*ptr[]。兩者之間的差別想必大家都清楚。如果我定義一個:“數組指針”并初始化:int (*pp)[3]=a;那麼通過pp完全可以操作a[2][3]。來分析一下“數組指針”(*ptr)[size],ptr所指的對象是有size個某種資料類型值的數組。而ptr本身又是一級指針,一級指針又等價于一維數組。a[2][3]的低維是一個次元為3的一維數組。高維是一個次元為2的一維數組,不難了解,正如前面所述:二維數組的每個元素是一個一維數組,相當于一維數組的兩次嵌套。比如變量a[0]是一個次元為3的一維數組,a[1]亦是一樣。這樣一來,高維的那一部分可視為一個指針!一個膽大的設想出來了:二維數組本質上就等同于“數組指針”!這種想法雖然無懈可擊,但想歸想,事實是怎樣的還得驗證。現借用c++的類型識别,得出兩者的資料類型:(以下語句需用頭檔案<typeinfo>)
cout<<typeid(a).name()<<endl;
cout<<typeid(pp).name()<<endl;
輸出結果為:int (*)[3](換行) int (*)[3]
兩者完全相同,與設想一緻!
現在回到問題上來,q=(int**)a;強制轉換成功,但卻不可能正确運作!原因已浮出水面:q這個位址單元存放的是int*類型的“指針變量”的位址,而二維數組a骨子裡卻是一個“數組指針”。兩者完全是“八竿子打不着”!想一想它們的記憶體分布情況,前者(位址)所指向的記憶體大小恒為4byte,後者(位址)所指向的記憶體大小是随着你定義的數組維數而不斷變化的!即使通過強制類型轉換成功,q的記憶體值就是a所代表的位址,但這個位址僅僅是一個位址,而q的記憶體值不僅要求是一個位址,而且還必須是一個“指針變量”的位址!隻有這樣通過*q(前面說過:*q則是擷取q所指向的int*變量類型位址的内容,即一個int變量的位址)才能操作一個普通變量的位址,否則就是用“*”來操作普通變量,想一下int x=250;*x表示的是什麼呢?
或許有人會問:你不是說二維數組實質上就是一維數組嗎,怎麼二維數組實質上又是“數組指針”?這裡有必要強調一下:我是從它們的存儲映象上來講的,但編譯器的語義實作上兩者是絕不能劃等号的!你能夠将一個二維數組指派給一個一維數組嗎?顯然是不行的!是以我們這樣想:語句q=(int**)a;是将一個一維數組(等價于一級指針)賦給一個二級指針(要通過“&”賦一級指針的位址才行),地球人都知道這是行不通的!雖然乍聽起來還蠻合理的,其實此般了解無異于穿鑿附會。剛才解釋過,兩者的語義迥異!不過,這樣了解似乎更能深刻且友善地知道那樣做錯在哪裡了,呵呵。。。。。。
另:
/*******************************以下内容轉載自http://zhidao.baidu.com/question/126882280*************///////////////////////////
#include <iostream.h>
int a[2][3];
int**p=a;
請問為什麼是錯誤的??(請不要說數組名是一個指針這個我知道,我想知道為什麼不能用二級指針指向二維數組)