深入理解C指针 指针和多维数组的关系
1.数组名是什么
数组名是一个标识符,它标识出我们之前申请的一连串内存空间,而且这个空间内的元素类型是相同的——即数组名代表的是一个内存块及这个内存块中的元素类型 数组名的值是数组首元素的指针常量。
数组名不是指针,但大多数使用到数组名的地方,编译器都会把数组名隐式转换成一个指向数组首元素的指针来处理。只有两种情况下例外:
int a[ ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
1)第一种是对数组名使用sizeof
运算符:
sizeof(a)
这将会得到整个数组所占的内存大小,a是长度为10的int(4字节)数组,运算结果是40
2) 第二种是对数组名取地址: &a
运算结果是数组的地址。注意,数组的地址和数组首元素的地址是不同的概念,尽管二者的值是相同的。
2.下标引用
一维数组int arr[i] 可以表示为 *(arr + i) arr的值被转换成指针常量,指向第一个元素,向右移动i * sizeof(int)
个字节,然后解引用,便得到了第i个元素的内容。因为第一种写法会自动转换成第二种,这个过程需要一些开销,所以我们说第二种写法通常效率会高一些。
3.数组的类型
以一维数组 int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };为例:
数组a的类型是 int * a 是指向元素的指针。数组的类型取决于数组元素的类型:如果它们是int
类型,那么数组名的类型就是“指向int的常量指针”;如果它们是其他类型,那么数组名的类型就是“指向其他类型的常量指针”。(出自《C和指针》第141页)
&a 的类型是 int (*)[10] &a
是指向数组的指针。 取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针(出自《C和指针》第142页)。
int a[5] = { 1,2,3,4,5 }; printf("%p\n%p", a, &a); printf("\n%d %d %d", sizeof(a), sizeof(*a), sizeof(*&a)); //20 4 20 printf("\n%p\n%p", a, &a + 1); int *p = a; int(*pInt)[5] = &a; printf("\n%d,%d", sizeof(*p), sizeof(*pInt)); //4 20
二维数组
二维数组的类型同样取决于数组元素的类型,假设有二维数组int b[2][5]
因为C语言的多维数组实际上是一维数组,二维数组实际上只是一个一维数组,只不过里面每个元素又是一个一维数组而已。
所以b
的类型是int (*)[5]
,而&b
的类型是int (*)[2][5]
int a[2][5] = { 1,2,3,4,5,6,7,8,9,0 }; for (int i = 0; i < 2; i++) { for (int j = 0; j < 5; j++) { printf("%d,%p ", a[i][j], &a[i][j]); } putchar('\n'); } printf("\n%d %d %d %d\n", sizeof(a),sizeof(*a),sizeof(**a),sizeof(*&a)); printf("%p\n", a); printf("%p\n", a + 1); printf("%p\n", *a); printf("%p\n", *a + 1); printf("%p\n", *(a + 1)); printf("%p\n", &a); printf("%p\n", &a + 1);
sizeof(a)会得到整个数组所占内存大小。sizeof(*a)会得到一行数组所占内存大小。sizeof(**a)会得到一个元素所占内存大小。
*a是首元素的地址,*a + 1是指向下一个元素的地址,*(a + 1)指向下一行的地址。
a是行指针,指向一个共有5个元素的一维数组地址,a + 1地址增加20字节,下一行地址:%p 转换10进制加上 20字节,再转换16进制。
&a是一个指向二维数组指针,10个元素40字节,&a + 1地址增加40字节,相当于指向了下一个数组(实际上并不存在),或者说指向了数组a
最后一个元素的下一个元素,这在C++里称为尾后指针。
由于 a[i] = *(a + i) 那么一个二维数组int a[i][j]。
那么a[i]+j 表示i行j列元素的地址,所以 *(a[i] + j)表示a[i][j]的元素。
既然 *(*(a + i) + j)与* (a[i] + j)等价,表示a[i][j]的元素。
二级指针与指针数组的关系:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib.h> #include <ctype.h> void main() { char * cmd[] = { "notepad","calc","mspaint","write" }; //指针数组 数组的每一个元素都是一个指针 for (char **p = cmd; p < cmd + 4; p++)//指针循环方式循环指针数组 { printf("%p", p); printf(" %s", *p);//*p是一个指针变量,存储一个常量字符串的首地址 printf(" %c\n", **p); //**p等价于*str[0]等价于‘n’ } char *arry[] = { "Hello","world","Pumpkin","Comparative" }; //存储char *类型地址的数组 - 指针数组 printf("%s\n", arry[0]);//Hello arry[0]是一个地址 printf("%c\n", *(arry[0]));//H printf("%c\n", *(arry[2] + 4));//k system("pause"); }
指针做输入第一种模型:
#include<stdio.h> #include<string.h> #include<stdlib.h> void printMyArray(char *myArray[5], int num) { for (int i = 0; i < num; i++) printf("%s\n", myArray[i]);//*(myArray + i) } void sort(char ** myArray, int num) { char *temp = NULL; int i = 0; int j = 0; for (i = 0; i < num - 1; i++) for (j = i + 1; j < num; j++) { if (strcmp(myArray[i], myArray[j]) > 0)//if(myArray[i] > myArray[j]) 即是错误 { temp = myArray[i]; myArray[i] = myArray[j]; //交换指针值 改变指针指向 myArray[j] = temp; } } } int main(void) { //存储char *类型地址的数组 -指针数组 char *myArray[5] = { "Hello","world","Pumpkin","Comparative","Baby" }; int num = sizeof(myArray) / sizeof(myArray[0]); puts("--------排序之前--------:"); printMyArray(myArray, num); puts("--------排序之后--------:"); sort(myArray, num); printMyArray(myArray, num); system("pause"); }
指针做输入第二种模型:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib.h> //void printMyArray_error(char **myArray, int num) // 不能用这种 char ** 与char[5][30] 的间接级别不同 //指针布长不一样 指针所指数据内存空间的数据类型不一样 void printMyArray(char myArray[][30], int num) { for (int i = 0; i < num; i++) printf("%s\n", *(myArray + i)); } void sortMyArray2(char (*myArray)[30], int num) { char tempBuf[30]; for (int i = 0; i < num - 1; i++) for (int j = i; j < num; j++) if (strcmp(myArray[i], myArray[j]) > 0) { strcpy(tempBuf, myArray[i]);//交换内存块 strcpy(myArray[i], myArray[j]); strcpy(myArray[j], tempBuf); } } int main(void) { char myArray[5][30] = { "Hello","world","Pumpkin","Comparative","Baby" }; int num = sizeof(myArray) / sizeof(myArray[0]); puts("--------排序之前--------:"); printMyArray(myArray, num); sortMyArray2(myArray, num); puts("--------排序之后--------:"); printMyArray(myArray, num); system("pause"); }