C語言中數(shù)組和指針的一些理解

1.c語言中,有時需要使用只讀數(shù)組,也就是程序從數(shù)組中讀取數(shù)值,但是程序不向數(shù)組中寫數(shù)據(jù),在這種情況下聲明并初始化數(shù)組時,建議使用const。
2.變量和數(shù)組都是自動類型的,不同的存儲類有時具有不同的屬性,自動類型就是如果沒有進(jìn)行初始化,一些存儲類的變量和數(shù)組會把他們的存儲單元設(shè)置為0;

#include <stdio.h>
#define SIZE 4
int main(int argc, const char * argv[]) {
 int some_data[SIZE]={1492,1066};
    int i;
    printf("%s%s\n","i","some_data[i]");
    
    for (i=0; i<SIZE; i++) {
        printf("%d--%d\n",i,some_data[i]);
        
    }
return 0;
}

輸出結(jié)果

isome_data[i]
0--1492
1--1066
2--0
3--0

從上面的結(jié)果我們可以看出,編譯器做的很好,當(dāng)數(shù)值數(shù)目小于數(shù)組元素數(shù)目時,多余的數(shù)組元素被初始化為0。也就是說,如果不初始化數(shù)組,數(shù)組元素和末初始化的普通變量一樣,其中存儲的是無用的數(shù)值;但是如果部分初始化數(shù)組,未初始化的元素則被設(shè)置為0;
如果初始化列表中項目的個數(shù)大于數(shù)組大小,編譯器會毫不留情地認(rèn)為這是一個錯誤。一種做法就是你可以省略括號中的數(shù)字,從而讓編譯器自動匹配數(shù)組大小和初始化列表中的項目數(shù)目

3.指定初始化項目
傳統(tǒng)的我們一般會這樣初始化數(shù)組:

int arr[6]={0,0,0,0,0,211};

但我們也可以在初始化列表中使用帶有方括號的元素下標(biāo)可以指定某個特定的元素

int arr[6]={[5]=211};

在初始化一個或多個元素后,未經(jīng)初始化的元素都將被設(shè)置為0

4.指針和數(shù)組

    short dates[4];
    short*pti;
    short index;
    double bills[4];
    double*ptf;
    
    pti=dates;
    ptf=bills;
    
    for (index=0; index<4; index++) {
        printf("pointer +%d:%p %p\n",index,pti+index,ptf+index);
    }

輸出結(jié)果

pointer +0:0x7fff5fbff738 0x7fff5fbff710
pointer +1:0x7fff5fbff73a 0x7fff5fbff718
pointer +2:0x7fff5fbff73c 0x7fff5fbff720
pointer +3:0x7fff5fbff73e 0x7fff5fbff728

指針的數(shù)值就是它所指向的對象的地址,地址的內(nèi)部表示方式是由硬件來決定的
在指針錢運(yùn)用運(yùn)算符*就可以得到該指針?biāo)赶虻膶ο蟮臄?shù)值
對指針加1,等價于對指針的值加上它所指向的對象的字節(jié)大小

  dates+2==&dates[2];  //相同的地址
  *dates+2 == dates[2]; // 相同的值

這些關(guān)系總結(jié)了數(shù)組和指針間密切的關(guān)系,可以用指針標(biāo)示數(shù)組的每個元素,并得到每個元素的數(shù)值,
順便提一下,請注意區(qū)分(dates+2)和dates+2。間接運(yùn)算符的優(yōu)先級高于+,因此后者等價于:(dates)+2

*(dates+2 ) /* dates 的第3個元素的值*/
*dates+2 /*第一個元素的值和2相加*/

賦值:可以把一個地址賦給指針。通常使用數(shù)組名或地址運(yùn)算符&來進(jìn)行地址賦值。

 
    int urn[5]={100,200,300,400,500};
    int*ptr1,*ptr2,ptr3;
    
    ptr1=urn;
    ptr2=&urn[2];
    printf("ptr1=%p,*ptr1 =%d,&ptr1=%p\n",ptr1,*ptr1,&ptr1);
    
    //指針加法
    
    ptr3=*(ptr1+4);
    printf("ptr1+4=%p,*(ptr4+3)=%d\n",ptr1+4,*(ptr1+3));
    
    //遞增指針
    ptr1++;
    printf("ptr1=%p,*ptr1=%d,&ptr1=%p\n",ptr1,*ptr1,&ptr1);
    //遞減指針;
    ptr2--;
    printf("ptr2=%p,*ptr2=%d,&ptr2=%p\n",ptr2,*ptr2,&ptr2);

    --ptr1;//恢復(fù)為初始值
    ++ptr2;//恢復(fù)為初始值
    printf("ptr1=%p,ptr2=%p\n",ptr1,ptr2);
    
    //一個指針減去另一個指針
    printf("ptr2=%p,ptr1=%p,ptr2-ptr1=%ld\n",ptr2,ptr1,ptr2-ptr1);
    //一個指針減去一個整數(shù)
    printf("ptr3=%d,ptr3-2=%d\n",ptr3,ptr3-2);
   

輸入結(jié)果為:

*p1=100,*p2=100,*p3=300
*p1++=100,*++p2=200,(*p3)++=300
*p1=200,*p2=200,*p3=301
ptr1=0x7fff5fbff6e0,*ptr1 =100,&ptr1=0x7fff5fbff678
ptr1+4=0x7fff5fbff6f0,*(ptr4+3)=400
ptr1=0x7fff5fbff6e4,*ptr1=200,&ptr1=0x7fff5fbff678
ptr2=0x7fff5fbff6e4,*ptr2=200,&ptr2=0x7fff5fbff670
ptr1=0x7fff5fbff6e0,ptr2=0x7fff5fbff6e8
ptr2=0x7fff5fbff6e8,ptr1=0x7fff5fbff6e0,ptr2-ptr1=2
ptr3=500,ptr3-2=498

賦值:可以把一個地址賦給指針。通常使用數(shù)組名或地址運(yùn)算符&來進(jìn)行地址賦值。上面例子中,把數(shù)組urn的起始地址賦給ptr1。變量ptr2得到的是數(shù)組第三個也即最后一個元素的地址。注意:地址應(yīng)該和指針類型兼容,不能把一個double類型的地址賦給一個指向int的指針

求值或取值:運(yùn)算符可取出指針指向地址中存儲的數(shù)值。因此,ptr1開始為100,即存儲在地址0x7fff5fbff6e0中的值。
取指針地址:指針變量同其他變量一樣具有地址和數(shù)值,使用運(yùn)算符&可以得到存儲指針本身的地址。本例中,ptr1被存儲在內(nèi)存地址0x7fff5fbff678中,該內(nèi)存單元的內(nèi)容是0x7fff5fbff6e0,即urn的地址。
將一個整數(shù)加給指針:可以使用+運(yùn)算符來把一個整數(shù)加給一個指針,或者把一個指針加給一個正數(shù)。兩種情況下,這個正數(shù)都會和指針?biāo)割愋偷淖止?jié)數(shù)相乘,然后所得的結(jié)果會加到初始地址上。于是,ptr4的結(jié)果等同于&urn【4】。如果相加的結(jié)果超出了初始指針?biāo)赶虻臄?shù)組的范圍,那么這個結(jié)果是不確定的,除非超出數(shù)組最后一個元素的地址能夠確保是有效的。
增加指針的值:可以通過一般的加法或增量運(yùn)算符來增加一個指針的值。對指向某數(shù)組元素指針做增量運(yùn)算,可以讓指針指向該數(shù)組的下一個元素。因此,ptr++運(yùn)算把ptr1加上數(shù)值4,使ptr1指向urn[1]。現(xiàn)在ptr1的值是0x7fff5fbff6e4,*ptr的數(shù)值為200.請注意ptr1本身的地址仍然是0x7fff5fbff678。別忘了,變量不會因為它的值得變化而移動位置。
從指針中減去一個整數(shù):可以使用-運(yùn)算符來從一個指針中減去一個整數(shù)。指針必須是第一個操作數(shù),或者是一個指向整數(shù)的指針。這個正數(shù)都會和指針?biāo)割愋偷淖止?jié)數(shù)相乘,然后所得到的結(jié)果會從初始地址中國減掉。于是,ptr3-2的結(jié)果等同于&urn[2].因為ptr3是指向&urn[4]的。如果相減的結(jié)果超出了初始指針?biāo)赶虻臄?shù)組的范圍,那么這個結(jié)果是不確定的,除非超出數(shù)組最后一個元素的地址能夠確保是有效的。

減小指針的值:指針當(dāng)然也可以做減量運(yùn)算。ptr2自減1之后,它將不再指向第三個元素,而是指向第二個數(shù)組元素。請注意,你可以使用前綴和后綴形式的增量和減量運(yùn)算符。對指針ptr1和ptr2都指向同一個元素urn【1】,直到他們被重置。
5.關(guān)于const的內(nèi)容
兩個函數(shù)

void show_array(const double ar[],int n);
void mult_array(double ar[],int n,double mult);

double rates[5]={88.99,110.22,59.45,183.22,340.21};
const double locked[4]={0.08,0.075,0.0725,0.07};

像show_array這樣的函數(shù)可以接受普通數(shù)組和常量數(shù)組的名稱作為實際的參數(shù),因為這兩種參數(shù)都可以賦給指向常量的指針

show_arrary(rates,5);
show_arrary(locked,4);

但是,像mult_arrary()這樣的函數(shù)不能接受常量數(shù)組的名稱作為參數(shù)

mult_array(rates,5,1.2);
mult_array(locked,4,1.2);

函數(shù)參量定義中使用const,不僅可以保護(hù)數(shù)據(jù),而且使函數(shù)可以使用聲明為const的數(shù)組
關(guān)于const的用法還有很多,例如,你可以使用關(guān)鍵字const來聲明并初始化指針,以保證指針不會指向別處,關(guān)鍵在于const的位置

這樣的指針仍然 可以用于修改數(shù)據(jù),但它只能指向最初賦給它的地址,不能修改所指向的地址

double * const pc1 = rates; //pc1指向數(shù)組的開始處
*pc1 = 92.99;//允許
//pc1=&rates[2]; // 不允許

指向常量的指針不能用于修改數(shù)值,但是指向的地址是可以改變的

const double locked[4]={0.08,0.075,0.0725,0.07};
const double*pc=rates; //pc指向數(shù)組開始處
//*pc=320.0; //不允許
//pc[2]=222.22;  //不允許
pc=locked;//允許
pc=&rates[4];//允許
pc++;//讓pc指向rates[1]這是允許的
rates[0]=99.99; //允許 因為rates不是常量

也可以使用兩個const來創(chuàng)建指針,這個指針既不可以更改所指向的地址,也不可以修改所指向的數(shù)據(jù)

const double* const pc2 = rates;
//    pc2 = &rates[2];//不允許
//    *pc2 = 92.99 //不允許

6.指針和多維數(shù)組
函數(shù)是通過指針來處理多維數(shù)組的
來看一個例子:
假設(shè)有如下聲明:

int zippo[4][2]; 整數(shù)數(shù)組的數(shù)組

數(shù)組名zippo同時也是數(shù)組首元素的地址。在本例中,zippo的首元素本身又是包含兩個int的數(shù)組,因此zippo也是包含兩個int的數(shù)組的地址。下面從指針地址進(jìn)一步分析:
①因為zippo是數(shù)組首元素的地址,所以zippo的值和&zippo[0]相同。另一方面,zippo[0]本身是包含兩個整數(shù)的數(shù)組,因此zippo[0]的值同其首元素的地址&zippo[0][0]相同。簡單地說,zippo[0]是一個整數(shù)大小對象的地址,而zippo是兩個整數(shù)大小對象的地址。因為整數(shù)和兩個整數(shù)組成的數(shù)組開始于同一個地址,因此zippo和zippo[0]具有相同的數(shù)值。
②對一個指針加1,會對原來的數(shù)值加上一個對應(yīng)類型大小的值。在這方面,zippo和zippo[0]是不一樣的,zippo所指向?qū)ο蟮拇笮∈莾蓚€int,而zippo[0]所指向?qū)ο蟮拇笮∈且粋€int,因此zippo+1和zippo[0]+1的結(jié)果不同。
③對一個指針取值得到的是該指針?biāo)赶驅(qū)ο蟮臄?shù)值。因為zippo[0]是其首元素zippo[0][0]的地址,所以(zippo[0])代表存儲在zippo[0][0]中的數(shù)值,即一個int數(shù)值。同樣zippo代表其首元素zippo[0]的值,但是zippo[0]本事就是一個int數(shù)的地址,即&zippo[0][0],因此zippo是&zippo[0][0]。對這兩個表達(dá)式同時應(yīng)用取值元算符將得到zippo等價于&zippo[0][0],后者簡化后即為一個int數(shù)zippozippo[0][0]。簡言之,zippo是地址的地址,需要兩次取值才可以得到通常的數(shù)值。地址的地址或指針的指針是雙重間接的典型例子。

總結(jié)完的關(guān)系如下:

//    zippo[0] 表示首元素的地址
//    zippo[0][0] 表示元素的值
//    *zippo表示首元素的值
//    &zippo[0][0]= zippo[0]=*zippo=zippo

//    *(zippo[0])表示存儲在zippo[0][0]中的數(shù)值
//    *(zippo[0])=zippo[0][0] = **zippo = *&zippo[0][0]

下面我們看一下這個輸出結(jié)果

 printf("8-----*(*(zippo+2)+1)=%d\n",*(*(zippo+2)+1));

結(jié)果為:

8-----*(*(zippo+2)+1)=3

讓我們來分步來建立這個表達(dá)式

zippo  //第一個大小為2個int的元素地址
zippo+2 //第3個大小為2個int的元素地址
*(zippo+2)第3個元素,即包含2個int值得數(shù)組,因此也是其第一個元素(int值)的地址
*(zippo+2)+1包含2個int值的數(shù)值的第2個元素的地址
*(*(zippo+2)+1)數(shù)組第三行第2個int(zippo[2][1])的值

7.指向多維數(shù)組的指針
如何聲明一個指向二維數(shù)組的指針變量pz?例如,在編寫處理像zippo這樣的數(shù)組的函數(shù)時,就會用到這類指針。指向int的指針可以勝任嗎?不可以。這種指針只是和zippo[0]兼容,因為它們都指向一個單個int值。但是zippo是其首元素的地址,而該首元素又是包含兩個int值的數(shù)組。因此,pz必須指向一個包含兩個int值的數(shù)組,而不是指向一個單個int值。下面是正確的代碼:

int(*pz)[2];//pz指向一個包含2個int值的數(shù)組(使用圓括號的原因是是表達(dá)式中[]的優(yōu)先級高于*)
zippo[m][n]==*(*(zippo+m)+n)
pz[m][n]=*(*(pz+m)+n)

指針之間的賦值規(guī)則比數(shù)組類型的賦值更嚴(yán)格。例如,你可以不需要進(jìn)行類型轉(zhuǎn)換就直接把一個int數(shù)值賦給一個double類型的變量。但對于指針來說,這樣的賦值是不允許的
??

    int *pt;
    int(*pa)[3];
    int ar1[2][3];
    int ar2[3][2];
    int **p2;
    
    pt = &ar1[0][0];
    pt = ar1[0];
    pt = ar1;//非法
    pa = ar1;
    pa = ar2;//非法
    p2 = &pt;
    *p2 = ar2[0];
    p2 = ar2;//非法

8.函數(shù)和多維數(shù)組

void sum_rows(int ar[][COLS],int rows)

請注意下面的聲明是不正確的

int sum2(int ar[][],int rows);//錯誤的聲明

編譯器會把數(shù)組符號轉(zhuǎn)換成指針符號。這就意味著,ar[1]會被轉(zhuǎn)換成ar+1。編譯器這樣轉(zhuǎn)換的時候需要知道ar所指向?qū)ο蟮臄?shù)據(jù)大小。

int sum2(int ar[][4],int rows);//合法聲明

就表示ar指向由4個int值構(gòu)成的數(shù)組,也就是16個字節(jié)長的對象,所以ar+1表示”在這個地址上加16個字節(jié)大小“。
也可以如下這樣在另一對方括號中填寫大小,但編譯器將忽略之:

int sum2(int ar[3][4],int rows)//合法聲明,但3將被忽略

一般地,聲明N維數(shù)組的指針時,出了最左邊的方括號可以留空之外,其他都需要填寫數(shù)值

int sum4(int ar[][12][20][30],int rows);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運(yùn)用指針編程是C語言最主要的風(fēng)格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,473評論 3 44
  • 前言 最近真的是忙的不可開交,公司一直給安排任務(wù),連學(xué)習(xí)和寫筆記的時間都沒有了,落下好幾次課的筆記都沒有寫,所以我...
    Xiho丶閱讀 1,544評論 1 12
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,537評論 1 51
  • 每個函數(shù)都是Function類型的實例,而且都與其他引用類型一樣具有屬性和方法。函數(shù)是對象,因此函數(shù)名實際上也是一...
    royluck閱讀 266評論 0 0
  • 上些天回老家,剛進(jìn)門就看了新養(yǎng)的兔子,跟沒見過幾次面的一歲多的狗打了招呼,卻始終沒見到甚至已經(jīng)忘了跟了我家好幾年的...
    砍柴挑水先生閱讀 174評論 1 1