終於到了精髓的地方了,這確實有點懵,總感覺這太麻煩了,而且寫著也不爽,還是懷念py或者java,但也沒辦法,還是要繼續學下去。 一、運算符& 1. scanf("%d" , &i); 里的& 2. 獲取變數的地址,它的操作數必須是變數 3. 地址的大小是否與int相同取決於編譯器 &不能取的地址 & ...
終於到了精髓的地方了,這確實有點懵,總感覺這太麻煩了,而且寫著也不爽,還是懷念py或者java,但也沒辦法,還是要繼續學下去。
一、運算符&
- scanf("%d" , &i); 里的&
- 獲取變數的地址,它的操作數必須是變數
- 地址的大小是否與int相同取決於編譯器
#include <stdio.h>
int main(void)
{
int i = 0;
printf("0x%x\n", &i);
// 0x62fe4c
return 0;
}
&不能取的地址
&不能對沒有地址的東西取地址
- &(a+b) ?
- &(a++) ?
二、指針
- 如果能夠將獲得變數的地址傳遞個一個函數,能否通過這個地址在那個函數內訪問這個變數?
- scanf("%d" , &i);
- scanf()的原型應該是怎樣的?我們需要一個參數保存別的變數的地址,如何表達能夠保存地址的變數
指針
就是保存地址的變數 , *p
// p是一個指針,現在把i的地址交給了p
int i;
int* p = &i;
// 下麵兩種形式一樣,p是一個指針,而q是一個普通的int變數
int* p , q;
int *p , q;
指針變數
- 變數的值是記憶體的地址
- 普通變數的值是實際的值
- 指針變數的值是具有實際值的變數的地址
作為參數的指針
- void f(int *p);
- 在被調用的時候得到了某個變數的地址
- int i = 0;f(&i);
- 在函數裡面可以通過這個指針訪問到外面的的這個i
#include <stdio.h>
void f(int *p);
int main(void)
{
int i = 6;
printf("&i=%p\n", &i);
f(&i);
return 0;
}
void f(int *p)
{
printf(" p=%p\n", p);
}
// 可以看到這裡獲取的地址是相同的
// &i=000000000062FE4C
// p=000000000062FE4C
訪問那個地址上的變數*
- *是一個單目運算符,用來訪問指針的值所表示的地址上的變數
- 可以是右值也可以是左值
- int k = *p;
- *p = k + 1;
*左值之所以叫左值
- 是因為出現在賦值號左邊的不是變數,而是值,是表達式計算的結果
- a[0] = 2;
- *p = 3;
- 是特殊的值,所以叫左值
#include <stdio.h>
// 聲明兩個函數
void f(int *p);
void g(int k);
int main(void)
{
int i = 6;
printf("&i=%p\n", &i);
f(&i);
// 此時i的值已經發生了變化
g(i);
return 0;
}
// 傳入的是地址
void f(int *p)
{
printf(" p=%p\n", p);
printf("*p=%d\n", *p);
*p = 66;
}
// 傳入的普通int
void g(int k){
printf("k=%d\n", k);
}
// &i=000000000062FE4C
// p=000000000062FE4C
// *p=6
// k=66
三、指針的使用
指針應用場景一
交換兩個變數的值
#include <stdio.h>
void swap(int *pa , int *pb);
int main()
{
int a = 5;
int b = 10;
swap(&a , &b);
printf("a=%d , b=%d\n", a , b);
return 0;
}
void swap(int *pa , int *pb){
int t = *pa;
*pa = *pb;
*pb = t;
}
指針應用場景二
- 函數返回多個值,某些值就只能通過指針返回
- 傳入的參數實際上是需要保存帶回的結果的變數
#include <stdio.h>
void minmax(int a[] , int len , int *min , int *max);
int main(void)
{
int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,15,34,35,66,};
int min , max;
minmax(a , sizeof(a)/sizeof(a[0]) , &min , &max);
printf("min = %d , max = %d \n", min , max);
return 0;
}
void minmax(int a[] , int len , int *min , int *max)
{
int i;
*min = *max = a[0];
for (i = 0; i < len; i++)
{
if (a[i] > *max)
{
*max = a[i];
}
if (a[i] < *min)
{
*min = a[i];
}
}
}
指針應用場景二b
- 函數返回運算的狀態,結果通過指針返回
- 常用的套路是讓函數返回特殊的不屬於有效範圍內的值表示出錯
- -1 或 0
- 但是當任何數值都是有效的可能結果時,就得分開返回了
#include <stdio.h>
// 如果成功就返回1,否則就是0
int divide(int a , int b , int *result);
int main(void)
{
int a = 5;
int b = 2;
int c;
if (divide(a,b,&c))
{
printf("%d/%d = %d\n", a, b, c);
}
return 0;
}
int divide(int a , int b , int *result)
{
int ret = 1;
if ( b== 0)
{
ret = 0;
}else{
*result = a/b;
}
return ret;
}
指針常見的錯誤
定義了指針變數,還沒有指向任何變數,就開始使用了
四、指針與數組
傳入函數的數組成什麼了?
- 函數參數表中的數組實際上是指針
- sizeof(a) == sizeof(int*)
- 但是可以用數組的運算符[]進行運算
下麵的四種函數原型是等價的
int sum(int *ar , int n);
int sum(int* , int);
int sum(int ar[] , int n);
int sum(int[] , int);
數組變數是特殊的指針
數組變數本身表達地址,所以
- int a[10]; int*p = a; // 無需用&取地址
- 但是數組的單元表達的是變數,需要用&取地址
- a == &a[0]
[]運算符可以對數組做,也可以對指針做
- p[0] <===> a[0]
*運算符可以對指針做,也可以對數組做
- *a = 25
數組變數是const的指針,所以不能被賦值
五、指針與const
- 表示一旦得到了某個變數的地址,不能再指向其他變數
// q內寫的地址不能被改變
int *const q = &i;
*q = 26; // ok
q++; // error
- 表示不能通過這個指針去修改那個變數(並不能使得那個變數成為const)
const int *p = &i;
*p = 26; // error (*p是不能變的)
i = 26; // ok
p = &j; // ok
- 看懂下麵的意思?
const int * p1 = &i;
int const * p2 = &i;
int *const p3 = &i;
判斷那個被const的標誌是const在的前面還是後面
前面兩個p不能被修改,就像第二種情況
const數組
const int a[] = {1,2,3,4,5,6,};
- 數組變數已經是const的指針了,這裡的const表明數組的每個單元都是const int
- 所以必須通過初始化進行賦值
保護數組值
- 因為把數組傳入函數時傳遞的是地址,所以那個函數內部可以修改數組的值
- 為了保護數組不被函數破壞,可以設置參數為const
int sum(const int a[] , int length);
六、指針運算
1+1 = 2 ?那麼指針加1等於什麼呢
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%p\n", p);
printf("p + 1 =%p\n\n", p+1);
// p =000000000062FE30
// p + 1 =000000000062FE31
// 相差了1
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
printf("q =%p\n", q);
printf("q + 1 =%p\n\n", q+1);
//q =000000000062FE00
//q + 1 =000000000062FE04
// 相差了4
return 0;
}
我們不難發現,在char中,相差了1,在int中相差了4,這是怎麼回事?
sizeof(char) = 1 , sizeof(int) = 4
對指針做一個加1的動作,意味著要把它移動到下一個單元去
int a[10];
int *p = a;
*(p+1) --> a[1]
如果指針不是指向一片連續分配的空間,如數組,那麼這運算沒有意義
可以對指針的算術運算
- 給指針加、減、一個整數(+、+=、-、-=)
- 遞增遞減(++/--)
- 兩個指針相減
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,20,6,7,8,9,};
char *p1 = &ac[5];
char *p2 = &ac[4];
printf("p1 - p2 = %d\n" , p1 - p2);
printf("*p1 - *p2 = %d\n" , *p1 - *p2);
// p1 - p2 = 1 也就是 5 -4
// *p1 - *p2 = 16 也就是 20 - 4
int ai[] = {0,1,2,3,4,5,6,7,8,18,};
int *q1 = &ai[9];
int *q2 = &ai[2];
printf("q2 - q1 = %d\n", q2 - q1);
printf("*q2 - *q1 = %d\n", *q2 - *q1);
// q2 - q1 = -7 也就是 2 - 9
// *q2 - *q1 = -16 也就是 2 - 18
return 0;
}
*p++
- 取出p所指的那個數據來,完事之後順便把p移動到下一個位置
- *的優先順序雖然高,但沒有++高
- 常用於數組類的連續空間操作
- 在某些cpu上,這可以直接被翻譯成一條彙編指令
char ac[] = {0,1,2,3,4,20,6,7,8,9,-1};
char *p = &ac[0];
// 普通方法遍曆數組
// int i ;
// for (i = 0; i < sizeof(ac)/sizeof(ac[0]); i++)
// {
// printf("%d\n", ac[i]);
// }
// 普通指針遍曆數組
// for (p = ac; *p != -1; p++)
// {
// printf("%d\n", *p);
// }
// printf("---------------\n");
// 使用*p++,快
while(*p != -1){
printf("%d\n", *p++);
}
0地址
- 當然你的記憶體中有0地址,但是0地址通常是個不能隨便碰的地址
- 所以你的指針不應該具有0值
- 因此可以用0地址來表示特殊的事情
- 返回的指針是無效的
- 指針沒有被真正的初始化(先初始化為0)
- NULL是一個預定定義的符號,表示0地址
指針的類型
- 無論指向什麼類型,所有的指針的大小都是一樣的,因為都是地址
- 但是指向不同類型的指針是不能直接互相賦值的
- 只是為了避免用錯指針
用指針來做什麼
- 需要傳入較大的數據時用作參數
- 傳入數組後對數組做操作
- 函數返回不止一個結果
- 需要用函數修改不止一個變數
- 動態申請記憶體時
七、動態記憶體分配
輸入數據
- 如果輸入數據時,先告訴你個數,然後再輸入,要記錄每個數據
- C99可以用變數做數組定義的大小,C99之前呢?
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int number;
int *a;
int i;
printf("請輸入數量:\n");
scanf("%d" , &number);
// 動態記憶體分配用malloc,此時a就相當於數組
a = (int*)malloc(number*sizeof(int));
for (i = 0; i < number; i++)
{
scanf("%d" , &a[i]);
}
for (i = number - 1; i >=0; i--)
{
printf("%d\n", a[i]);
}
// 最後釋放空間
free(a);
return 0;
}
malloc函數
#include <stdlib.h>
void* malloc(size_t size);
- 向malloc申請的空間的大小是以位元組為單位的,需要什麼類型就在sizeof裡面寫什麼類型
- 返回的結果是void* , 需要類型轉換為自己需要的類型
- 最後釋放空間
(int*)malloc(n*sizeof(int));
沒空間了?
- 如果申請失敗則返回0 , 或者叫做NULL
- 你的系統可以給你多大的空間?
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *p;
int cnt = 0;
while((p = malloc(100 * 1024 *1024))){
cnt ++ ;
}
printf("分配了%d00MB的空間\n", cnt);
return 0;
}
// 分配了27200MB的空間
free()
- 把申請得來的空間還給系統
- 只能還申請的空間的首地址
- 一個malloc對應一個free