1. 指向指针的指针(多维指针)
(1)指针的本质是变量,会占用一定的内存空间
(2)可以定义指针的指针来保存指针变量的地址值
(3)指针是个变量,同样也存在传值调用与传址调用
【实例分析】重置动态空间的大小
1 #include <stdio.h> 2 3 #include <malloc.h> 4 5 6 7 int reset(char**p,int size,int new_size) 8 9 { 10 11 int ret = 1; 12 13 int i = 0; 14 15 int len = 0; 16 17 char* pt = NULL; 18 19 char* pp = *p; 20 21 22 23 if((p != NULL)&&(new_size > 0)) 24 25 { 26 27 pt = (char*)malloc(new_size); 28 29 30 31 len = (size < new_size)?size : new_size;//取较小者 32 33 34 35 //复制原内容 36 37 for(i=0; i<len;i++){ 38 39 *pt = *pp++; 40 41 } 42 43 44 45 free(*p); //释放原空间 46 47 *p = pt; //*p指针指向新空间 48 49 } 50 51 else 52 53 { 54 55 ret = 0; 56 57 } 58 59 60 61 return ret; 62 63 } 64 65 66 67 int main(int argc,char* argv[], char* env[]) 68 69 { 70 71 72 73 char*p =(char*)malloc(5); 74 75 76 77 printf("%p ", p);//重置前二维数组的内存地址 78 79 80 81 //重置大小后二维数组的内存地址 82 83 //因为重置后的内存地址可以是新的地址,所以这里 84 85 //需传入p指针的地址,以便在reset函数内部改变p,让 86 87 //p指向新的地址。 88 89 if (reset(&p, 5, 3)) 90 91 { 92 93 printf("%p ", p); 94 95 } 96 97 98 99 free(p); 100 101 return 0; 102 103 }
2. 二维数组与二级指针
(1)二维数组在内存中以一维的方式排布
(2)二维数据中的第一维是一维数组
(3)二维数组中的第二维才是具体的值
(4)二维数组的数组名可看做常量指针
【实例分析】遍历二维数组
1 #include <stdio.h> 2 3 4 5 //以一维数组的方式来遍历二维数组 6 7 void printfArray(int a[],int size) 8 9 { 10 11 int i = 0; 12 13 14 15 printf("printfArray:%d ",sizeof(a)); //退化为指针 16 17 18 19 for(i=0; i<size; i++) 20 21 { 22 23 printf("%d ",a[i]); 24 25 } 26 27 } 28 29 30 31 int main(int argc,char* argv[], char* env[]) 32 33 { 34 35 int a[3][3] = {{0, 1, 2},{3, 4, 5},{6, 7, 8}}; 36 37 int* p = &a[0][0];//指向a[0][0]元素 38 39 40 41 int i; 42 43 int j; 44 45 46 47 for(i = 0;i < 3; i++) 48 49 { 50 51 for(j = 0;j < 3;j++) 52 53 { 54 55 printf("%d, ",*(*(a + i) + j));//以指针方式访问元素 56 57 } 58 59 60 61 printf(" "); 62 63 } 64 65 66 67 printf(" "); 68 69 70 71 printfArray(p,9);//以一维数组的方式访问二维数组 72 73 74 75 return 0; 76 77 }
3. 数组名
(1)一维数组名代表数组首元素的地址:int a[5]; a的类型为int*
(2)二维数组名同样代表数组首元素的地址:如int a[3][5], a的类型为int(*)[5]。
二维数组 |
含义及类型 |
取地址符(&) |
sizeof |
数组名:a |
①二维数组名a指向数组首元素的地址,即第1行(a[0])的地址(注意,不是a[0][0]的地址)。因此,a被称为行指针,指向第1行元素(一维数组)的地址。这个元素的类型是int(*)[5](即一维数组)。所以a的类型为int(*)[5];a可以看作是行指针, ②a + 1表示第二行的指针,a + i表示第i + 1行的指针(也是int(*)[5]类型)…… |
①&a表示整个二维数组的地址,所以&a + 1指向这个二维数组最后一个元素的后面。
|
①sizeof(a)表示整个二维数组的大小。 ②sizeof(&a)为指针大小4字节。 ③sizeof(*&a)等于sizeof(a) |
a[i] |
①a[i]的类型:如果把二维数组看作是由三个元素(一维数组)组成的数组,那么这三个一维数组的数组名分别为a[0]、a[1]、a[2]。因此,a[0]可以看作是指向第1行(一维数组)首元素(a[0][0])的地址,a[i]是指向第i + 1行(一维数组)首元素的地址,所以a[i]为int*型。 ②a[i] + 1表示这行数组第2个元素的地址,即a[i] + 1是指向a[i][1]元素的地址,*(a[i] + 1)是a[i][1]元素的值。同理,a[i] + j是指向a[i][j]的地址,*(a[i] + j)是a[i][j]的值。 |
&a[i]表示第i + 1行这行整个一维数组的地址。因此,&a[i] + 1是指向这行数组的下一行的指针。 |
①sizeof(a[i]):a[i]是一个一维数组。sizeof(a[i])为这个数组的大小。 ②sizeof(&a[i])为指针大小4字节。 ③sizeof(*&a[i])等于sizeof(a[i]) |
a[i][j] |
与a、a[i]类型的含义不同,a[i][j]不再是一个指针,而是元素的类型,即int型。 |
&a[i][j]表示这个元素的地址,即int*型 |
①sizeof(a[i][j]):a[i][j]表示元素的类型。 ②sizeof(&a[i][j])为指针大小。 ③sizeof(*&a[i][j])等于sizeof(a[i][j]) |
备注 |
①通常情况下,数组名可看作是首元素的地址,而表格中所说的数组名a、a[i]的类型是指当他们代表各自数组的首元素时的类型。 ②但当对数组名取地址符(&)或sizeof时,它不能看作是首元素的地址,而代表的是整个数组。请注意表格中&和sizeof两列的分析。 |
【实例分析】数组的信息
#include <stdio.h> int main(int argc,char* argv[], char* env[]) { int a[3][5] = {0}; int c; printf("Information for array:a[3][5]: "); printf("a = 0x%08X, a + 1 = 0x%08X, sizeof(a) = %d ", a, a + 1, sizeof(a)); printf("&a = 0x%08X, &a + 1 = 0x%08X, sizeof(&a) = %d, sizeof(*&a) = %d ", &a, &a + 1, sizeof(&a),sizeof(*&a)); printf(" "); //a[i]指向一个一维数组的首元素,a[i]+1指向该行第2个元素。sizeof(a[i])时不能看成首元素,而是这行整个一维数组 for(c=0;c< 5;c++) { printf("a[%d] = 0x%08X, a[%d] + 1 = 0x%08X, sizeof(a[%d]) = %d, ", c, a[c], c, a[c] + 1,c, sizeof(a[c])); } printf(" "); //对a[i]进行&取地址符时,a[i]不能看作这一行的首元素,而是整个一维数组。即&a[i]表示第i+1的整个数组 //&a[i]+1表示下一行。 for(c=0;c< 5;c++) { printf("&a[%d] = 0x%08X, &a[%d] + 1 = 0x%08X, sizeof(&a[%d]) = %d, sizeof(*&a[%d]) = %d ", c, &a[c],c, &a[c] + 1,c, sizeof(&a[c]), c, sizeof(*&a[c])); } return 0; }
/*
输出结果:
Information for array:a[3][5]:
a = 0x0023FE80, a + 1 = 0x0023FE94, sizeof(a) = 60
&a = 0x0023FE80, &a + 1 = 0x0023FEBC, sizeof(&a) = 4, sizeof(*&a) = 60
a[0] = 0x0023FE80, a[0] + 1 = 0x0023FE84, sizeof(a[0]) = 20,
a[1] = 0x0023FE94, a[1] + 1 = 0x0023FE98, sizeof(a[1]) = 20,
a[2] = 0x0023FEA8, a[2] + 1 = 0x0023FEAC, sizeof(a[2]) = 20,
a[3] = 0x0023FEBC, a[3] + 1 = 0x0023FEC0, sizeof(a[3]) = 20,
a[4] = 0x0023FED0, a[4] + 1 = 0x0023FED4, sizeof(a[4]) = 20,
&a[0] = 0x0023FE80, &a[0] + 1 = 0x0023FE94, sizeof(&a[0]) = 4, sizeof(*&a[0]) = 20
&a[1] = 0x0023FE94, &a[1] + 1 = 0x0023FEA8, sizeof(&a[1]) = 4, sizeof(*&a[1]) = 20
&a[2] = 0x0023FEA8, &a[2] + 1 = 0x0023FEBC, sizeof(&a[2]) = 4, sizeof(*&a[2]) = 20
&a[3] = 0x0023FEBC, &a[3] + 1 = 0x0023FED0, sizeof(&a[3]) = 4, sizeof(*&a[3]) = 20
&a[4] = 0x0023FED0, &a[4] + 1 = 0x0023FEE4, sizeof(&a[4]) = 4, sizeof(*&a[4]) = 20
*/
(3)二维数元素的访问方式:int a[i][j];
①a[i][j]
②*(*(a + i) + j); //a + i是第i+1行首元素(一维数组)的地址,即保存一个一维数的地址,*(a + i)取出当中保存的一维数组地址。注意这也是一个地址,而*(a + i)+j表示在这个一维数组地址基础上加j的偏移处,然后*(*(a + I)+ j)取出该元素出来。
【实例分析】如何动态申请二维数组
#include <stdio.h> #include <malloc.h> int** malloc2d(int row,int col) { int** ret = NULL; if((row >0) && (col >0)) { int* p = NULL; //先申请第一维数组,用于存放行指针 ret = (int**)malloc(row * sizeof(int*)); //再申请整个数组存储空间的大小,用于存放所有元素 p = (int*)malloc(row * col * sizeof(int)); //将数组空间分为二维 if ((ret != NULL) && (p != NULL)) { int i=0; for(i=0; i<row; i++) { ret[i] = p + i*col; } } else { free(ret); free(p); ret = NULL; } } return ret; } void free2d(int** p) { if(*p != NULL) //p指向二维数组 { //*p指向p[0],而这里保存的即是 //第1个元素的地址,也是整个数 //组元素空间地址。 free(*p); //释放元素所占空间 } free(p);//释放行指针所占空间 } int main() { int** a = malloc2d(3, 3); int i = 0; int j = 0; for(i=0; i<3; i++) { for(j=0; j<3; j++) { printf("%d, ",a[i][j]); } printf(" "); } free2d(a); return 0; }
【实例分析】无法将指针变量本身传递给一个函数,须传递指针的地址
#include <stdio.h> #include <malloc.h> #include <string.h> void GetMemory(char* p,int num) { //由于p是函数参数,当函数返回时,会被释放 p = (char*)malloc(num*sizeof(char)); } void GetMemoryEx(char** p,int num) { //p指向一个指针,将该指针的值指向新的开辟的内存空间。 *p = (char*)malloc(num * sizeof(char)); } int main() { char* str = NULL; /* //错误的做法 GetMemory(str,10);//试图让str让指向新开辟的内存空间。因为须改变 //指针str的值,所以得传递指针的地址过去。否则 //在传参时,GetMemory只是复制str的值过去。即, //将NULL复制给参数。 strcpy(str,"hello"); free(str);//free并没有起作用,内存泄漏 */ //正确的做法 GetMemoryEx(&str,10);//将str指针的地址传递到函数里,函数内部就 //可以改变str指针的值,让其指向新的地址。 strcpy(str,"hello"); free(str);//free并没有起作用,内存泄漏 return 0; }
4. 小结
(1)C语言中只支持一维数组
(2)C语言中的数组大小必须在编译期就作为常数确定
(3)C语言中的数组元素可以是任何类型的数据
(4)C语言中的数组的元素可以是另一个数组