為何不精通C? 04 辨析數組與指針


前言

    學習C語言,指針絕對是一道大坎,很多人談指針色變,使用起來小心翼翼的。“一切指針都是紙老虎” ,同時,對我們得“在戰略上藐視指針,戰術上重視指針”。

本文先剖析一維數組和指針,多維的情況后序博客繼續更新。

文章流程:

1、辨析指針和數組的不同

2、辨析它們相同的時刻

3、總結


指針和數組為什么這么糾纏不清

    首先說一點,指針的使用等同於數組的情況遠遠多於他們不同的情況,因此,在初學時,為了容易理解,很多人都說 “指針=數組”。 但是,這卻是錯誤的!

來個例子吧:

// file1.c 定義一個數組array
int array[100];
以上是在文件1中的定義

// file2.c 聲明file1.c中的array
extern int* array
以上是在文件2中的聲明

上面的例子對不對呢? 大家可以自己測試下,會很神奇的發現: 咔,怎么編譯出啦! 為什么呢?

繼續深入聲明和定義

    在辨析數組和指針前,首先要說下聲明和定義的區別,之前博文“為何不精通C? 03 深入剖析聲明” 中的末尾已經提過這一點了。這里重新說下:

C語言中,對象有且僅有一個定義,而聲明卻可以有多個extern 聲明

定義:只能出現在一個地方,確定同時分配內存,它是特殊的聲明

聲明:只是描述其他地方創建對象的屬性。有extern前綴,作用於變量

對於聲明,就像斷言一樣,說是什么就是什么,不容許有妥協的余地,因為在聲明的同時,也規定了指針/數組的移動方式及長度。

So, 對於上面的例子,我們可以知道,file2.c中,array被聲明成一個int指針。但是,file1.c 中的定義卻是數組。 他們不兼容! 為什么不兼容呢?

通過指針和數組訪問數據的方式不同

    這里先補充一個知識點,sizeof(x) 這個運算符,它返回的是x在內存中的字節數。

  1 #include "stdio.h"
  0 main ()
  1 {
  2   int array[20];
  3   printf("類型\t字節數\t指針字節數\n");
  4   printf("%s\t%d\t%d\n","char",sizeof(char), sizeof(char*));
  5   printf("%s\t%d\t%d\n","short",sizeof(short), sizeof(short*));
  6   printf("%s\t%d\t%d\n","int",sizeof(int), sizeof(int*));
  7   printf("%s\t%d\t%d\n","long",sizeof(long), sizeof(long*));
  8   printf("%s\t%d\t%d\n","float",sizeof(float), sizeof(float*));
  9   printf("%s\t%d\t%d\n","double",sizeof(double), sizeof(double*));
 10   printf("%s\t%d\t%d\n","array",sizeof(array), sizeof(&array[0]));
 11   return 0;
 12 }

這里,我們可以歸納出,所有的指針長度都是固定的, 都是4字節(因為我的電腦是32位=4*8bit。同理,在64位電腦這里顯示8)。
我們認真觀察下第10行,sizeof(array), sizeof(&array[0]),(因為我們都說數組名就是首地址指針,所以我這么表示。)

可以看出,sizeof(array)打印出了80, 即20*4,20個int的字節長度。但是sizeof(&array[0]),還是4,指針的固定長度。喏,一大區別就在這!

好,通過sizeof, 能夠很好認識到原來數組和指針還真存在着不同點。現在繼續說下通過指針和數組對數組元素訪問的不同策略

由sizeof可以知道,在定義數組時,等同於在內存中連續分配了N長度的空間,數組的首地址(假設是5000)代表這段空間的起始點。比如通過array[i]訪問時,步驟如下:

  • 移動到 5000+i*4 的地址
  • 取存在這里的值

而通過指針呢? 我們都只是指針中保存的是地址值, 如 int*p = array; 這里,假設p的地址為6000, 它存的值為array的首地址5000, 通過 p[i]訪問是,步驟如下:

  • 取指針p中的值5000,移動到該地址
  • 移動到5000+i*4的地址
  • 取存在這里的值

可以看出,多了一個步驟。這個多出來的步驟,其實大家都知道,這也說明了指針的靈活性。

關於字符串數組、指針的初始化

關於字符數組,我們可以這么來初始化:

char *p  = "helloworld";
char a[] = "helloworld";

我們來辨析下內部隱含的區別吧:

對於指針p, 系統為我們分配了匿名一個字符串常量,這個常量是只讀的,把它的地址給了p, 因此不能通過p[i]修改字符串常量的值。

對於數組a, 系統定義了一個連續的內存塊來分配字符串,它的首地址是a, 我們可以通過 a[i]操作來修改某個字符的值。

關於字符串數組,指針的左值性

在C語言中,數組的首地址被定義為“不可修改的左值”, 即類似  int * const a 這樣的聲明, 我們可以通過它修改它指向的內容,卻不能把它賦個新值。即:允許a[i]=yyy,而對 a=xxx 卻是提示非法的!

指針呢,就看他怎么聲明咯, 一般來說,就是 int* p, 我們可以 p=a, p=xxx, p[i]=yyy ……,都是允許的。

好啦,大概的區別點就是上述表示的了,那我們繼續探討下他們什么時候相同。


指針等同於數組的時候

我們知道,指針充滿了靈活性,而數組卻是比較死板的東西,這么說來,就必須先理解好數組,才能更好的了解指針,我們先來說下數組。對於C語言來說,任何事物都必須聲明,再使用,我們也按這個順序,先說下聲明:

數組的聲明

  • 1、外部數組的聲明
  • 2、數組的定義
  • 3、函數參數的聲明, 這時候,隨便你寫指針形式還是數組形式,他們都是等同的。

數組的使用

     任何時候,都可以變成指針表達式

 

 

 

 

 

 

 

 

 

 

我們歸納下:

1、 “表達式中的數組名” 就是指針, 及你可以使用a[i]; *(a+i)p=a, p+i,*p;等形式表達同樣的意思。

2、C語言將數組下標作為指針的偏移量

3、作為函數形參的數組名,等同於指針,即:

my_func(int* p);
my_func(int p[]);
my_func(int p[200]); 

都是一個意思,編譯器都把它翻譯成指針。


總結

采用《C專家編程》中的總結,我適當修改合並了一些。作者建議我們在自己真正理解數組、指針后,要先自我總結下,再看他的總結。

1、用 a[i]形式的表達式,編譯器都解釋成*(a+i);

2、指針永遠是指針,不可改寫成數組。你可以通過下標形式訪問指針,且必須是你知道指針指向了是一個數組。 畢竟,編譯器並不知道 int*指向了什么,一個int數組還是int變量地址。

3、作為函數參數時,數組都被改寫成指針的形式來表達,即類似於 p=&a[0];因此,對於編譯器而言,參數表中沒有數組,只有指針。

4、聲明和定義必須匹配!若是定義了數組,在其他文件聲明時也必須是數組,指針亦然。


免責聲明!

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



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