C語言簡析指針用途

在C語言中,任何一個變量,都有兩層含義:

(1) 代表該變量的存儲單元的地址;變量的地址 左值 lvalue

(2) 代表該變量的值;右值 rvalue

對於一個變量的訪問,隻有兩種情況:

一是把一個值寫到變量的地址中去 (lvalue)

二是從變量的地址中取變量的值 (rvalue)

對象的訪問方式

直接訪問 : 通過對象名去訪問

如:

			int a;
			a = 1024;
			b = a; 

註:直接訪問 受到作用域的限制

間接訪問 :

通過對象的地址去訪問,指針訪問。

隻要你知道瞭對象的地址,就可以在任何地方去訪問它。

不受作用域的限制。

什麼是指針

存儲單元(如: memory 內存)的地址:

分配給每個對象的內存單元都有一個編號,這個編號就是我們所說

存儲單元的地址。並且存儲單元按照字節來編號。

在C語言中,指針的概念與地址差不多的,你可以直接認為指針就是一個地址。

一個變量的地址,我們也稱為變量的“指針”

& 取地執符

單目運算符, “取xxx對象的地址”

通過一個對象的指針去訪問它,首先要解決對象的指針(地址) 的保存問題。

需要定義另外的一個變量去保存它的地址,這種變量我們稱為指針變量。

指針變量

指針變量也是一個變量,也是用來保存數據,隻不過指針變量保存的數據是其他對象的地址。

指針變量的定義:

指向的對象的類型 * 指針變量名;

“指向的對象的類型” :

指針變量指向的對象的類型,而不是指針的類型。

如:

int *p;
int a=10;
p=&a;//p保存瞭對象a的地址 
	//  p 指向 a 

與指針相關的運算符

& : 取地址符

int a;

&a : 表示取對象a的地址

對象a的地址,其實就是對象a的指針。

* : 指向運算符  ,單目運算符 

*地址(*指針): 地址對應的那個變量(對象)。

int a=10;
int *p=&a;
int b=*&a;//b=10,int b=a

所以*(&a) <=> a

*& 可以直接約掉 (編譯原理狀態機的詞法分析)

指針變量作為函數參數

傳遞的還是值!! 隻不過這個“實參的值”可能是某個對象的地址!

C語言中,函數參數的傳遞隻能是"值傳遞"

形參 = 實參的值

野指針:

void XXX2(int * m, int * n) 
		{
			int * t;//定義瞭一個指針變量t, 但是t沒有被賦值
					//t沒有賦值,但是不代表它沒有值,相反它會有一個相對應的
					//不知道這個值是多少 undefine 
			*t = *m; //操作野指針,可能導致 段錯誤(對一個未知的空間進行寫操作)
			*m = *n; 
			*n = *t; //操作野指針,可能導致 段錯誤(對一個未知的空間進行讀操作)		

*t 放置到 ‘=’ 符號的左邊,代表t指向對象的左值

對t的指向的對象進行寫的操作

*t 放置到 ‘=’ 符號的右邊,代表t指向對象的右值

對t的指向的對象進行read的操作

所以t是一個“野指針”,我們也不知道 t指向的內存空間是否可讀可寫,

如果不可讀或者不可寫,那麼後續對*t操作,就會導致內存的非法訪問 => 段錯誤!!

空指針:

NULL 0

在計算機中地址為0的存儲空間是不存在的

如果一個指針的值,指向空(NULL)的指針,稱之為空指針。

int *p=NULL;
*p=1024;//操作空指針
int b=*p;//操作空指針

我們去使用空指針,一定會 造成內存的非法訪問 => 段錯誤

數組與指針

數組元素與普通變量是一樣的,數組元素也有自己的地址。

數組元素也有左值和右值,並且數組元素間的地址是相鄰的。

數組名可以代表首元素的地址。

a => &a[0], 數組名a當做指針來看

int a[]={1,2,3};
int *p=a;//a是數組名當指針來看,==&a[0]

指針作加減的問題:

能不能通過指針p去訪問a[1]?

p=p+1;//&a[0]+1==&a[1]

p + i (p是一個指針,i是一個整數值)

註:不是簡單地加減數值,而是加減i個指針指向單元的長度。

p + 1 => 往後面挪瞭一個int單元的長度 ,&a[1]

			int a[10]; 
			int * p = a; // p = &a[0]
			a[1] <=> *&a[1] <=> *(&a[0] + 1) <=> *(a + 1) <=> a[1] 
			a[1] <=> *&a[1] <=> *(&a[0] + 1) <=> *(p + 1) <=> p[1]

數組名a,在代碼中有兩層含義:

int a[10];

(1) 數組名代表整個數組

&a : 取整個數組a的地址。

&a + 1 : 往後面挪瞭1個(int[10])單元長度。

(2) 在合適情況下面,數組名可以當作指針來看

a <=> &a[0]

a + 1 : 當作指針來看

=> &a[0] + 1

=> &a[1]

多維數組與指針

在C語言中,所有的數組其實都是一維數組!

int a[3][4]; //int[4] a[3]; 

        表達式         表達式的含義                              表達式的值 
          a             數組名: 
                          (1) 當作指針 &a[0]                     取整個一維數組a[0]的地址 
                          (2) 當作整個數組來看       
          a[0]             數組名: 
                           (1) 當作指針 &a[0][0]                  取數組元素a[0][0]的地址 
                           (2) 當作整個一維數組a[0]來看 
          a[0][0]       數組元素a[0][0]                           a[0][0] (左值/右值)

 a + 1          a是數組名,在此處當作指針 ==&a[0]+1==&a[1]
 a[0] + 1       a[0] 是數組名,在此處當作指針 ==&a[0][0]+1==&a[0][1]
a[0][0] + 1       a[0][0]是二維數組a中的一個int類型的元素 ==a[0][0]+1
a[1] + 2         a[1]是數組名,此處當作指針 ==&a[1][0]+2==&a[1][2]
*(a + 1) + 2    a是數組名,在此處當作指針 ==*(&a[0]+1)+2==*(&a[1])+2==a[1]+2==&a[1][2]

指針常量 和 常量指針

他們都是指針,隻不過他們之間的屬性有一點區別。

指針常量:

指針本身不能改變(指針的指向不能變),但是

它所指向的內存空間裡面的內容是可以改變的。

最具有代表性例子,數組名!

定義方式:

指向對象的類型 * const 指針變量名;

				int a , b;
				int * const p = &a; 
				p = &b ; //error 

常量指針:

是一個指向常量的地址。指針指向的對象是常量,

那麼指針本身的指向是可以改變的,但是這個指針指向

內存空間中的內容是不能夠改變。

最具有代表性例子,字符串

char * p = "abcde"; //"abcde" <=> &'a' 

定義方式:

const 類型 * 變量名;

or

類型 const * 變量名;

指針數組 與 數組指針

(1) 指針數組

指針數組是一個數組,隻不過它裡面的每一個元素都是指針罷瞭!

定義數組:

數組元素類型 數組名[元素個數];

“數組元素類型” : C語言中任意合法的類型都可以。

int * p[4]; //定義瞭一個指針數組,數組名為p,裡面含有4個int*類型的指針。

(2) 數組指針

數組指針是一個指針,隻不過這個指針是用來指向一個數組罷瞭!!

int (*p)[4] ; //定義瞭一個數組指針,指針變量名為p,用來指向一個含有4個int類型元素的數組的。 

int a[3][4];
int (*p)[4];
p = a; //&a[0]
*(*p + 1) // *(a[0]+1)==*(&a[0][1])==a[0][1]
*(*(p + 2)) // *(*(&a[2]))==*(&a[2][0])==a[2][0]
*(*(p+3) + 3)// *(*(&a[0]+3)+3)==*(*(&a[3])+3)==*(&a[3][0]+3)==a[3][3]

字符串與指針

字符串就是一串字符。在C語言中,沒有字符串這個類型。

C語言字符串是通過 char *(字符指針)來實現。

C語言中的字符串,是用""引起來的一串字符來表示,並且字符串後面默認會加一個\0,\0(ASCII為0的那個字符)字符串結束的標志。

            "abc"  //字符串 
            "\n"   //字符串 
            ""     //字符串 => 空串 
            '\123' //字符
            '\n'   //字符 

註:

我們隻需要保存字符串的首字符地址就可以瞭,從首字符地址開始找到第一個\0,前面的這些字符就是字符串裡面的字符。

在C語言中的字符串(如: “ssssssabcd”)是保存在一個叫做 .rodata(隻讀數據)的內存區域,字符串代表是這個內存空間的首地址。

"ssssssabcd" => 表達式的值  => &'s'
			char *p="12345" ==&'1'
				int *p=&'1';	
				p+1=&'2';

字符數組:

			char s[5] = {'a', 'b', 'c', 'd', 'e'}; 
			//字符數組,與普通數組是一樣的,保存在一個.data/棧空間 
			//數組區域是可讀可寫的 
			sizeof(s) == 5 
			strlen(s) >= 5 //沒有'\0' 長度不確定
			char s[] = {"abcde"}; //自動生成'\0'
			<=> char s[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 
			//s字符數組,與普通數組是一樣的,保存在一個.data/棧空間 
			//數組區域是可讀可寫的
			sizeof(s) == 6	 
			strlen(s) == 5
			char s[] = {"abcde"};
			s[1] = 'B'; //OK 
			char ch = s[1]; //OK 
			*(s + 1) = 'B'; //OK 
			ch = *(s + 1); //OK 
			s = s + 1; //ERROR 
						//s是數組名,數組名當作指針,指針常量 
						//s的指向 是不能夠改變的,但是s指向的內存空間
						//裡面的內容是可變的
			printf("%s\n", s); //abcde   %s : char* 地址
			//把後面的那個地址當作是一個字符串的首地址,一個一個字符進行輸出,直到遇到\0才會結束!			
					

函數指針

函數也有地址 ,那麼咱們可以定義一個指針,去存放一個函數的地址,像這樣的指針,稱為函數指針。

函數指針如何定義

int *  abc(int a, float b)
			{
			}

描述函數abc的類型: int (int , float ) : 是一個返回值為int類型,帶有一個Int類型和float類型的參數的函數類型

指向函數的返回值的類型 (*指針變量名) (指向函數的形參類型列表);

int (* p)(int, int) ; 

定義瞭一個指針變量p,p是一個函數指針,是用來指向一個返回值為int類型,並且帶有兩個int類型參數的函數的。

函數指針如何賦值

&函數名

or

函數名 : 在C語言中,函數名本身就可以代表函數的地址

通過函數指針去調用指向的函數,有如下兩種方案:

p為函數的指針。

(1) (*p)(實參列表)

(2) p(實參列表)

int *p=abc//函數名
p(a,b);//調用函數

二級指針 與 多級指針

int a,b;

可以定義一個指針變量p1,來保存變量a的地址:

int *p1=&a;
b=*p1=*&a=a;//通過指針p1訪問a變量

可以定義一個指針變量p2,來保存變量p1的地址:

int *p2=&p1
int b=**p2=*p1=a//通過指針p2訪問a變量

p2保存的是一個一級指針p1的地址,所以說,

p2指向一個一級指針,p2就是一個二級指針

可以定義一個指針變量p3,來保存變量p2的地址:

int *p3=&p2;
int b=***p3=**p2=*p1=a;

p3保存的是一個二級指針p2的地址,所以說,

p3指向一個二級指針,p3就是一個三級指針

到此這篇關於C語言簡析指針用途的文章就介紹到這瞭,更多相關C語言指針內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: