指針的定義
定義一個指針的方法如下:
類型名 *指針名;
例如,下面的語句定義了一個指針:
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原創
轉貼請注明出處