# 指針
每一個變量都有一個內存位置,每一個內存位置都定義了可使用連字號(&)運算符訪問的地址,它表示了在內存中的一個地址。
## 基礎
### 什么是指針
指針是一個變量,其值為另一個變量的地址,即,內存位置的直接地址。
所有指針的值的實際數據類型,不管是整型、浮點型、字符型,還是其他的數據類型,都是一樣的,都是一個代表內存地址的長的 **十六進制數**。不同數據類型的指針之間唯一的不同是,指針所指向的變量或常量的數據類型不同。
### 如何使用
```
// 定義
int *p = NULL;
// 賦值(&取地址)
p = &var;
// 訪問指針內容
*p;
```
```
#include <stdio.h>
int main ()
{
int var = 20; /* 實際變量的聲明 */
int *ip; /* 指針變量的聲明 */
ip = &var; /* 在指針變量中存儲 var 的地址 */
printf("Address of var variable: %p\n", &var );
/* 在指針變量中存儲的地址 */
printf("Address stored in ip variable: %p\n", ip );
/* 使用指針訪問值 */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
```
打印結果:
```
Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20
```
### NULL指針(空指針)
在變量聲明的時候,如果沒有確切的地址可以賦值,為指針變量賦一個 NULL 值是一個良好的編程習慣。
NULL 指針是一個定義在標準庫中的值為零的常量。
在大多數的操作系統上,程序不允許訪問地址為 0 的內存,因為該內存是操作系統保留的。
然而,內存地址 0 有特別重要的意義,它表明該指針不指向一個可訪問的內存位置。但按照慣例,如果指針包含空值(零值),則假定它不指向任何東西。
```
if(ptr) /* 如果 p 非空,則完成 */
if(!ptr) /* 如果 p 為空,則完成 */
```
> YJ:野指針會造成崩潰。
## 指針的算術運算
```
++
--
+
-
```
### ?? 舉個栗子
條件:
1. 假設 ptr 是一個指向地址 1000 的整型指針,是一個 32 位的整數;
2. ptr++;
結果:
在執行完上述的運算之后,ptr 將指向位置 1004,因為 ptr 每增加一次,它都將指向下一個整數位置,即當前位置往后移 4 個字節。
這個運算會在不影響內存位置中實際值的情況下,移動指針到下一個內存位置。
如果 ptr 指向一個地址為 1000 的字符,上面的運算會導致指針指向位置 1001,因為下一個字符位置是在 1001。
### 遞增(++)
> 我們喜歡在程序中使用指針代替數組,因為變量指針可以遞增,而數組不能遞增,因為數組是一個常量指針。
```
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指針中的數組地址 */
ptr = var;
for ( i = 0; i < MAX; i++)
{
printf("存儲地址:var[%d] = %x\n", i, ptr );
printf("存儲值:var[%d] = %d\n", i, *ptr );
/* 移動到下一個位置 */
ptr++;
}
return 0;
}
```
打印結果:
```
存儲地址:var[0] = bf882b30
存儲值:var[0] = 10
存儲地址:var[1] = bf882b34
存儲值: var[1] = 100
存儲地址:var[2] = bf882b38
存儲值:var[2] = 200
```
### 遞減(--)
```
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指針中最后一個元素的地址 */
ptr = &var[MAX-1];
for ( i = MAX; i > 0; i--)
{
printf("存儲地址:var[%d] = %x\n", i-1, ptr );
printf("存儲值:var[%d] = %d\n", i-1, *ptr );
/* 移動到下一個位置 */
ptr--;
}
return 0;
}
```
### 指針的比較
指針可以用關系運算符進行比較,如 ==、< 和 >。如果 p1 和 p2 指向兩個相關的變量,比如同一個數組中的不同元素,則可對 p1 和 p2 進行大小比較。
```
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指針中第一個元素的地址 */
ptr = var;
i = 0;
while ( ptr <= &var[MAX - 1] )
{
printf("Address of var[%d] = %x\n", i, ptr );
printf("Value of var[%d] = %d\n", i, *ptr );
/* 指向上一個位置 */
ptr++;
i++;
}
return 0;
}
```
## 指針數組
```
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr[MAX];
for ( i = 0; i < MAX; i++)
{
ptr[i] = &var[i]; /* 賦值為整數的地址 */
}
for ( i = 0; i < MAX; i++)
{
printf("Value of var[%d] = %d\n", i, *ptr[i] );
}
return 0;
}
打印結果:
Value of var[0] = 10
Value of var[1] = 100
Value of var[2] = 200
```
> ?? 下面這個栗子,驗證一下!
```
#include <stdio.h>
const int MAX = 4;
int main ()
{
const char *names[] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};
int i = 0;
for ( i = 0; i < MAX; i++)
{
printf("Value of names[%d] = %s\n", i, names[i] );
}
return 0;
}
打印結果:
Value of names[0] = Zara Ali
Value of names[1] = Hina Ali
Value of names[2] = Nuha Ali
Value of names[3] = Sara Ali
```
## 指向指針的指針
指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鏈。
```
#include <stdio.h>
int main ()
{
int var;
int *ptr;
int **pptr;
var = 3000;
/* 獲取 var 的地址 */
ptr = &var;
/* 使用運算符 & 獲取 ptr 的地址 */
pptr = &ptr;
/* 使用 pptr 獲取值 */
printf("Value of var = %d\n", var );
printf("Value available at *ptr = %d\n", *ptr );
printf("Value available at **pptr = %d\n", **pptr);
return 0;
}
打印結果:
Value of var = 3000
Value available at *ptr = 3000
Value available at **pptr = 3000
```
## 傳遞指針給函數
```
#include <stdio.h>
#include <time.h>
void getSeconds(unsigned long *par); /*傳遞一個無符號的 long 型指針給函數*/
int main ()
{
unsigned long sec;
getSeconds( &sec );
/* 輸出實際值 */
printf("Number of seconds: %ld\n", sec );
return 0;
}
void getSeconds(unsigned long *par)
{
*par = time( NULL ); /* 獲取當前的秒數 */
return;
}
打印結果:
Number of seconds :1294450468
```
> ?? YJ:可以改變外部的指針內容。
## 函數返回指針
同 1.4,函數返回數組。