天天看點

C語言速成手冊(四):指針、動态記憶體配置設定、标準輸入

指針的定義

    定義一個指針的方法如下:

類型名 *指針名;

    例如,下面的語句定義了一個指針:

int *pointer;

    這樣,pointer就是一個指針,它指向的是一個int類型的資料。

    一個指針可以是一個合法的記憶體位址,也可以為0(通常寫成NULL)。

    你可以用printf語句輸出一個指針,對應的辨別為"%p"。下面的代碼可以輸出上面定義的指針指向的位址。

printf("%p",p);

取位址與引用

    假如a是一個變量,p是一個指針,那麼&a傳回該變量的位址,*p傳回該指針所指的内容(稱做“引用”)。

    閱讀下面的代碼片段:

int *p; int a = 520; p = &a; printf( "%p -> %d/n", p, *p ); *p = 1314; printf( "%p -> %d/n", p, *p ); printf( "a = %d", a );

    程式輸出如下。當執行了p=&a後,存取*p就相當于是存取變量a了。

0022FF78 -> 520 0022FF78 -> 1314 a = 1314

動态記憶體配置設定

    首先介紹sizeof函數(準确地說是一個運算符),它的參數為一個變量名或類型名,傳回的是它所占記憶體空間的大小。下面的代碼輸出1 8 800 4 1 。

long long a; double b[100]; _Bool *c; printf( "%d " , sizeof(char) ); printf( "%d " , sizeof(a) ); printf( "%d " , sizeof(b) ); printf( "%d " , sizeof(c) ); printf( "%d " , sizeof(*c) );

    下面介紹四種動态記憶體配置設定函數,使用它們前需要在程式最前面包含頭檔案stdlib.h。四種函數的格式分别為:

void *malloc ( size ); void *calloc ( n, size ); void free ( pointer ); void *realloc( pointer, size );

    函數malloc将在記憶體裡尋找一個大小為size的連續空間,把配置設定到的記憶體位址作為一個指向void類型的指針(預設的無類型指針)傳回。如果空間配置設定失敗,函數傳回NULL。

    函數calloc将在記憶體裡尋找一個大小為n * size的連續空間,并且把這段記憶體的資料全部清0,傳回資料和malloc一樣。如果空間配置設定失敗,函數傳回NULL。

    函數free用于釋放記憶體空間,釋放後的空間被回收,可以用于以後的malloc或calloc操作。

    函數realloc在保證已有資料不變的情況下改變已有指針的空間大小,傳回重新分得的空間的記憶體位址(有可能和原來不同)。如果空間重新配置設定失敗,函數傳回NULL。

    Pascal中的new語句可以用前兩個函數代替,free語句則相當于Pascal中的dispose。

    注意,malloc和calloc函數所傳回的指針還沒确定類型,理論上需要用類型轉換。下面的程式合法地為p指針配置設定空間:

int *p; p = (int *) malloc( sizeof(int) ); *p = 520;

    事實上,由于指派時C語言自動轉換類型,是以那個類型轉換是沒有必要的(去掉(int *)沒有影響)。

指針與結構

    一個指針可以指向一個結構,一個結構也可以包含一個指針。結構裡包含一個指向結構的指針就構成了連結清單:

struct node{    int value;    struct node *next; }

    這樣,定義struct node *a,則(*a).next就是另一個指向node結構的指針。在C語言中,(*x).y的句型很常用,是以有一個專門的記号x->y來代替(*x).y這樣繁雜的寫法。

    你可以從下面的程式中看到連結清單的使用。

#include <stdio.h> #include <stdlib.h>      struct node {    int value;    struct node *next; }; int main() {     struct node *head = NULL;     int i;     for(i=1;i<=10;i=i+1)     {                 struct node *newNode;         newNode = malloc( sizeof(struct node) );         newNode->value = i;         newNode->next = head;         head = newNode;     }          struct node *p = head;     while (p)     {         printf( "%d/n", p->value );         p = p->next;     }     return 0; }

指針與函數

    前面說過,C語言中的函數參數和變量隻能夠供該函數使用。

    下面四個程式代碼的輸出分别是什麼?

    代碼一:

#include <stdio.h> void swap( int a, int b ) {    int c = a;    a = b;    b = c; } int main() {    int a = 520, b = 1314;    swap( a , b );    printf( "%d %d", a, b );    return 0; }

    代碼二:

#include <stdio.h> int a = 520, b = 1314; void swap( int a, int b ) {    int c = a;    a = b;    b = c; } int main() {    swap( a , b );    printf( "%d %d", a, b );    return 0; }

    代碼三:

#include <stdio.h> int a = 520, b = 1314; void swap() {    int c = a;    a = b;    b = c; } int main() {    swap();    printf( "%d %d", a, b );    return 0; }

    代碼四:

#include <stdio.h> void swap( int *a, int *b ) {    int c = *a;    *a = *b;    *b = c; } int main() {    int a = 520, b = 1314;    swap( &a, &b);    printf( "%d %d", a, b );    return 0; }

    答案:前兩個程式輸出520 1314,後兩個程式輸出1314 520。

    前兩個程式中,待交換的兩個數(即使是全局變量)作為參數傳給了swap函數,該函數裡的操作對函數外無影響。

    第三個程式中,swap函數對全局變量直接進行操作,其影響是全局的。

    最後一個程式巧妙地應用了指針來實作兩數交換。函數的參數是指針類型,這個函數不能改變指針本身,但可以改變指針所指的内容。這是寫此類函數通常所用的方法。

    為了強調函數void swap( int *a, int *b )中的指針本身不發生改變,很多地方喜歡寫成void swap(const int *a, const int *b ) 。

指針與數組

    數組由記憶體的連續空間構成,是以可以用指針來通路。事實上,數組名本身就是一個指針。觀察下列代碼:

int a[100]; printf("%p/n", &a[0]); printf("%p/n", a); printf("%d/n", a == &a[0] ); printf("%p/n", &a[1]); printf("%p/n", a+1);

    看看輸出結果(自己運作),你會發現,數組名其實就是一個指向數組起始位置的指針,其作用相當于&a[0]。而指針本身可以進行加減運算(表示記憶體位址的加減,具體加多少減多少取決于指針所指的類型),其本質是在數組中進行定位。是以,下面的兩個代碼是完全等價的:

int i, a[100]; for ( i=0; i<100; i=i+1 ) *(a+i)=i;

int i, a[100]; for ( i=0; i<100; i=i+1 ) a[i]=i;

    下面的程式輸出0 1 2 3 4 0。函數init不能改變變量的值,但由于數組的實質是指針,是以它可以改變數組所儲存的内容。

#include <stdio.h> void init( int a[5], int b ) {    int i;    for ( i=0; i<5; i=i+1 ) a[i]=i;    b = 5; } int main() {    int i, a[5], b=0;    init( a, b );    for ( i=0; i<5; i=i+1 ) printf("%d ",a[i]);    printf( "%d/n", b );    return 0; }

    你甚至可以用指針來建立數組。使用函數calloc可以友善地得到指定長度并已全部初始化為0的數組。再加上realloc函數後,你就可以實作真正意義上的動态數組(長度可變)。示例程式如下:

#include <stdio.h> #include <stdlib.h> int main() {    int i, *a;    a = calloc(sizeof(int),5);    for ( i=0; i<5; i=i+1 ) *(a+i)=i;    for ( i=0; i<5; i=i+1 ) printf("%d",a[i]);    a = realloc(a,sizeof(int)*10);    for ( i=5; i<10; i=i+1 ) a[i]=10-i;    for ( i=0; i<10; i=i+1 ) printf("%d",a[i]);    return 0; }

    上面的程式将輸出012340123454321。

标準輸入

    之是以現在才來說讀入操作,是因為讀入函數需要用到指針知識,否則解釋不清楚。

    輸入操作用scanf函數,和輸出一樣需要有stdio.h支援。

    和輸出操作一樣,scanf函數的第一個參數也是一個字元串,讀入同樣采用比對辨別符的方法進行。所不同的是,讀入函數的參數是需要随函數變化的,是以變量作為scanf的參數時需要加上取位址符号,作為指針代入函數才行。除了讀入%c類型之外,所有讀入操作均自動跳過空格。

    看下面四個例子:

代碼一:

int a, b; scanf( "%d%d" , &a , &b );

輸入:

8 2

則a為8,b為2。

===========性感的分割線===========

代碼二:

int a; char b; scanf( "%d%c" , &a , &b );

輸入:

8 2

則a為8,b為一個空格。

===========性感的分割線===========

代碼三:

int h, m, s; scanf( "%d:%d:%d" , &h , &m, &s );

輸入:

20:19:02

則h為20,m為19,s為2。

輸入:

20:19-02

則h為20,m為19,s沒有獲得新的值(比對失敗)。

===========性感的分割線===========

代碼四:

char st[80]; scanf( "My favourite website is%s" , st );

輸入:

My favourite website is matrix67.com, I love it!

則st為"matrix67.com," 。

    注意最後這個例子,st是數組,本質上是一個指針,是以不需要加&符号。

Matrix67原創

轉貼請注明出處

繼續閱讀