指针和数组分析
文章目錄
- 1 數組的訪問方式
- 1 數組的訪問方式
- 2 指針和數組名的不同
- 2.1 指針和數組名的不同
- 3 數組作為函數參數
- 3.1 數組參數退化的意義
- 3.2 一維數組作為函數參數
- 3.3 二維數組作為函數參數
- 4 數組指針和指針數組
- 4.1 數組指針
- 4.2 指針數組
- 5 指針和數組的糾纏
1 數組的訪問方式
1 數組的訪問方式
我們首先來看一個問題,數組名可以當作常量指針使用,那么指針是否也可以當作數組名來使用呢?
數組可以以下標的形式和以指針的形式兩種方式進行訪問:
下標形式VS指針形式:
- 指針以固定增量在數組中移動時,效率高于下標形式。
- 指針增量為1且硬件具有硬件增量模型時,效率更高。
- 下標形式與指針形式的轉換如下:
注意: 現代編譯器的生成代碼優化率已經大大提高,在固定增量時,下標形式的效率已經和指針形式相當;但從可毒性和代碼維護的角度來看,下標形式更優。
示例代碼:
#include <stdio.h>int main() {int a[5] = {0};int* p = a;int i = 0;for(i=0; i<5; i++){p[i] = i + 1;}for(i=0; i<5; i++){printf("a[%d] = %d\n", i, *(a + i));}printf("\n");for(i=0; i<5; i++){i[a] = i + 10;}for(i=0; i<5; i++){printf("p[%d] = %d\n", i, p[i]);}return 0; }2 指針和數組名的不同
2.1 指針和數組名的不同
雖然我們可以通過指針訪問數組,但是我們必須知道數組名和指針僅使用方式相同:
- 數組名的本質不是指針。
- 指針的本質不是數組。
看如下代碼:
// main.c #include <stdio.h>int main() {extern int* a;printf("&a = %p\n", &a);printf("a = %p\n", a);printf("*a = %d\n", *a);return 0; }// extern.c a[] = {1, 2, 3, 4, 5};/* 分析:當編譯器編譯到extern int* a;這句話話的時候就會把a這個標識符當作一個指針看待。指針a的地址為就 是數組a標識符所在的地址,&a就是a所在的地址值,a就是里面保存的值(0x1),*a就是保存的地址值所對應的內存中 的值,因此會產生段錯誤。*/3 數組作為函數參數
3.1 數組參數退化的意義
首先我們要知道C語言中只會以值拷貝的方式進行參數傳遞。
當我們向函數傳遞數組時,其實有如下兩種實現方案:
C語言選擇的是第二種方式,是因為C語言以高效作為最初設計目標:
- 參數傳遞的時候如果拷貝整個數組執行效率將大大下降。
- 參數位于棧上,太大的數組拷貝將導致棧溢出。
3.2 一維數組作為函數參數
當一位數組作為函數參數時,編譯器將其編譯成對應的指針。
結論: 一般情況下,當定義的函數中有數組參數時,需要定義另一個參數來標示數組的大小。
3.3 二維數組作為函數參數
二維數組同樣存在退化的問題:
- 二維數組可以看作是一維數組。
- 二維數組中的每個元素是一維數組。
二維數組參數中第一維的參數可以省略:
- void f(int a[5]) <--> void f(int a[]) <--> void f(int* a)
- void f(int a[3][3]) <--> void f(int a[][3]) <--> void f(int(*a)[3])
二維數組并不是退化為二級指針,而是退化為數組指針!指針數組才是退化為二級指針!
記住如下表格!!!
關于數組作為參數,還需要注意以下幾點:
- C語言無法向一個函數傳遞任意的多維數組,對于多維數組的函數參數只有第一維是可變的。
- 必須提供除第一維之外的所有維長度
- 第一維之外的維度信息用于完成指針運算。
- N維數組的本質是一維數組,元素是N-1維的數組。
- 對于多維數組的函數參數只有第一維是可變的。
傳遞與訪問二維數組:
#include <stdio.h>void access(int a[][3], int row) {int col = sizeof(*a) / sizeof(int);int i = 0;int j = 0;printf("sizeof(a) = %d\n", sizeof(a)); // 4printf("sizeof(*a) = %d\n", sizeof(*a)); // 12for(i=0; i<row; i++){for(j=0; j<col; j++){printf("%d\n", a[i][j]);}}printf("\n"); }void access_ex(int b[][2][3], int n) {int i = 0;int j = 0;int k = 0;printf("sizeof(b) = %d\n", sizeof(b)); // 4printf("sizeof(*b) = %d\n", sizeof(*b)); // 24for(i=0; i<n; i++){for(j=0; j<2; j++){for(k=0; k<3; k++){printf("%d\n", b[i][j][k]);}}}printf("\n"); }int main() {int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};int aa[2][2] = {0};int b[1][2][3] = {0};access(a, 3);access(aa, 2); // compile warningaccess_ex(b, 1);access_ex(aa, 2); // compile warningreturn 0; }4 數組指針和指針數組
4.1 數組指針
先來看下數組類型:
- C語言中的數組有自己特定的類型。
- 數組的類型由元素類型和數組大小共同決定。比如,int array[5]的類型為int[5]。
定義數組類型:
- C語言中通過typedef為數組類型重命名,typedef type(name)[size]。
- 數組類型:
- typedef int(AINT5)[5];
- typedef float(AFLOAT10)[10];
- 數組定義:
- AINT5 iArray;
- AFLOAT10 fArray;
數組指針:
- 數組指針用于指向一個數組。
- 數組名是數組首元素的起始地址,但并不是數組的起始地址。
- 通過將取地址符&作用于數組名可以得到數組的起始地址。
- 可通過數組類型定義數組指針:ArrayType* pointer;。
- 也可以直接定義:type(*pointer)[n];
- pointer為數組指針變量名。
- type為指向的數組的元素類型。
- n為指向的數組的大小。
4.2 指針數組
指針數組:
- 指針數組是一個普通的數組。
- 指針數組中每個元素為一個指針。
- 指針數組的定義:type* pArray[n];
指針數組的應用:
5 指針和數組的糾纏
有時候還是會分不清指針和數組名之間的關系,主要是二維數組,一維數組倒是沒什么問題。懶得寫太多,就直接拿代碼測試下,如下:
// CodeTest.cpp : 定義控制臺應用程序的入口點。 //#include "stdafx.h" #include <iostream> #include <windows.h>using namespace std;int main() {/****************************一維數組************************************/int array1[5] = { 1, 2, 3, 4, 5 };// 當數組名放在sizeof中代表的是整個數組cout << sizeof(array1) << endl; //20// 當數組名直接做右值時,其代表數組空間中第一個元素的地址int* p1 = array1;//int* p11 = &array1; //注意:&array1的類型是int (*)[5],數組指針cout << p1 << endl; //0x003DF998cout << &array1 << endl; //0x003DF998cout << p1[1] << endl; //其本質也是簡單的指針運算 + 解引用cout << array1[1] << endl; //其本質也是簡單的指針運算 + 解引用cout << *(p1 + 1) << endl; //簡單的指針運算cout << "------------------" << endl;/****************************二維數組************************************/int array2[3][3] = {{ 1, 2, 3 },{ 4, 5, 6 },{ 7, 8, 9 }};// 當數組名放在sizeof中代表的是整個數組cout << sizeof(array2) << endl; //36// 當數組名直接做右值時,其代表數組空間中第一個元素的地址int (*p2)[3] = array2; //二維數組也可以看成是一維的,不過每一維都是一個一維數組//所以其數組空間的第一個元素的地址就是指向一維數組的//int (*p22)[3] = &array2; //&array2的類型是int (*)[3][3]cout << (*p2)[0] << endl; //*p2得到其所指向的一維數組的第一個元素的指針cout << *(*p2 + 0) << endl;cout << (*array2)[0] << endl; //[]有兩個作用:1.指針運算 2.解引用//*array2 得到二維數組中第一個元素(也就是一個一維數組)中//的第一個int類型元素的地址,然后通過[]運算符進行地址偏移//和解引用然后得到第一個int類型元素的值cout << *array2[0] << endl; //array2[0]得到第一個元素(也就是一個一維數組)中第一個int類型//的地址值,然后再通過*拿到其中存儲的值//其實array2、&array2、*array2的地址值都是一樣的,只是其類型不同而已,需要注意。system("pause");return 0; }參考資料:
總結
- 上一篇: Linux中目录结构
- 下一篇: 鼠标不可移动怎么办 鼠标无法移动怎么处理