前言?
hello,我又來了,今天有我繼續帶領大家深入的學習指針,通過上次的學習,我們已經了解到了指針的基本概念,指針如何使用,指針使用的益處,以及一些相關的概念,那今天我們就繼續深入的學習,加深對指針的理解,還沒有看上期的uu,記得補功課喲 ,鏈接在這里了http://t.csdnimg.cn/VBh89,
那廢話就不多說,開始我們今天的正題,如果覺得不錯的話,就不要吝嗇手中的三連哦,萬分感謝!!
1. 數組名的理解??
在上一次我們學習的時候我們有遇到過這樣的代碼。
int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];
這里使用&arr[0],就相當于拿到了首元素的地址,?但其實數組名本來就是地址,而且是首元素的地址。
我們來做個測試。
#include <stdio.h>int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n",arr); return 0;}
?測試結果:
我們發現數組名和數組?元素的地址打印出的結果?模?樣,數組名就是數組?元素(第?個元素)的地址。
這時候有同學會有疑問?數組名如果是數組?元素的地址,那下?的代碼怎么理解呢??
#include <stdio.h>int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0;}
?結果輸出的是40,如果arr是數組首元素的地址,那因該輸出 4/8才對呀?
其實關于數組名的使用,有兩個例外:
? sizeof(數組名),sizeof中單獨放數組名,這?的數組名表?整個數組,計算的是整個數組的??, 單位是字節
? &數組名,這?的數組名表?整個數組,取出的是整個數組的地址(整個數組的地址和數組?元素 的地址是有區別的)
除此之外,任何地?使?數組名,數組名都表??元素的地址。?
好奇的uu可以來試試這個代碼呀:
#include <stdio.h>int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", &arr); printf("&arr= %p\n", arr); return 0;}
三個打印的結果一摸一樣,這時候又納悶了,那arr和&arr的區別是什么呢?
#include <stdio.h>int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0]+1);printf("arr = %p\n", arr); printf("arr+1 = %p\n", arr+1); printf("&arr = %p\n", &arr); printf("&arr+1 = %p\n", &arr+1) return 0;}
?
這?我們發現&arr[0]和&arr[0]+1相差4個字節,arr和arr+1相差4個字節,是因為&arr[0]和arr都是 ?元素的地址,+1就是跳過?個元素。 但是&arr和&arr+1相差40個字節,這就是因為&arr是數組的地址,+1操作是跳過整個數組的。 到這??家應該搞清楚數組名的意義了吧。 數組名是數組?元素的地址,但是有2個例外。?
2. 使?指針訪問數組
有了前面的只是支持,再結合數組的特點,我們就可以很方便的使用指針訪問數組。
#include <stdio.h>int main(){int arr[10] = {0};//輸?int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);//輸?int* p = arr;for(i=0; i<sz; i++){scanf("%d", p+i);//scanf("%d", arr+i);//也可以這樣寫}//輸出for(i=0; i<sz; i++){printf("%d ", *(p+i));}return 0;
}
這個代碼搞明?后,我們再試?下,如果我們再分析?下,數組名arr是數組?元素的地址,可以賦值給p,其實數組名arr和p在這?是等價的。那我們可以使?arr[i]可以訪問數組的元素,那p[i]是否也可以訪問數組呢??
#include <stdio.h>int main(){int arr[10] = {0};//輸?int i = 0;int sz = sizeof(arr)/sizeof(arr[0]);//輸?int* p = arr;for(i=0; i<sz; i++){scanf("%d", p+i);//scanf("%d", arr+i);//也可以這樣寫}//輸出for(i=0; i<sz; i++){printf("%d ", p[i]);}return 0;}
將*(p+i)換成p[i]也是能夠正常打印的,所以本質上p[i]是等價于*(p+i)。?
同理arr[i] 應該等價于*(arr+i),數組元素的訪問在編譯器處理的時候,也是轉換成?元素的地址+偏移量求出元素的地址,然后解引?來訪問的。?
3. ?維數組傳參的本質
數組我們學過了,之前也講了,數組是可以傳遞給函數的,這個?節我們討論?下數組傳參的本質。 ?先從?個問題開始,我們之前都是在函數外部計算數組的元素個數,
那我們可以把函數傳給?個函數后,函數內部求數組的元素個數嗎?
#include <stdio.h>void test(int arr[]){int sz2 = sizeof(arr)/sizeof(arr[0]);printf("sz2 = %d\n", sz2);}int main(){int arr[10] = {1,2,3,4,5,6,7,8,9,10};int sz1 = sizeof(arr)/sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}
輸出的結果:
?我們發現在函數內部是沒有正確獲得數組的元素個數。
這就要學習數組傳參的本質了,上個?節我們學習了:數組名是數組?元素的地址;那么在數組傳參的時候,傳遞的是數組名,也就是說本質上數組傳參本質上傳遞的是數組?元素的地址。
所以函數形參的部分理論上應該使?指針變量來接收?元素的地址。那么在函數內部我們寫 sizeof(arr) 計算的是?個地址的??(單位字節)?不是數組的??(單位字節)。正是因為函數的參數部分是本質是指針,所以在函數內部是沒辦法求的數組元素個數的。
void test(int arr[])//參數寫成數組形式,本質上還是指針{printf("%d\n", sizeof(arr));}void test(int* arr)//參數寫成指針形式{printf("%d\n", sizeof(arr));//計算?個指針變量的??
}int main(){int arr[10] = {1,2,3,4,5,6,7,8,9,10};test(arr);return 0;}
?總結:?維數組傳參,形參的部分可以寫成數組的形式,也可以寫成指針的形式。
4. 冒泡排序
冒泡排序的核心思想:兩類相鄰元素比較。
//?法1
void bubble_sort(int arr[], int sz)//參數接收數組元素個數{int i = 0;for(i=0; i<sz-1; i++){int j = 0;for(j=0; j<sz-i-1; j++){if(arr[j] > arr[j+1]){int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}int main(){int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);for(i=0; i<sz; i++){printf("%d ", arr[i]);}return 0;}
//?法2 - 優化void bubble_sort(int arr[], int sz)//參數接收數組元素個數{int i = 0;for(i=0; i<sz-1; i++){int flag = 1;//假設這?趟已經有序了int j = 0;for(j=0; j<sz-i-1; j++){if(arr[j] > arr[j+1]){flag = 0;//發?交換就說明,?序int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}if(flag == 1)//這?趟沒交換就說明已經有序,后續?序排序了break;}}int main(){int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);for(i=0; i<sz; i++){printf("%d ", arr[i]);}return 0;}
好,今天的指針學習就可以先告一段落了,感興趣的uu們一定不要吝嗇手中三連啊!!
咱們下期再見,拜拜!!