(以下为本人的一些学习心得,有错误希望大家能指出来)
C语言指针详解
二.指针进阶
首先我们介绍一下几种其他类型的指针用法:
我们都知道怎么在C语言里声明一个常量,就是用关键字const来指定就好,这样这个常量的值就不能被语句改动了,一旦被改动,那就会报错。
1. 指向常量的指针
long number = 9999L;
const long *pnumber = &number;
那如果我们也希望有一个指针pnumber能访问number,但是不希望它能改number的值,也就是说number的值可以自己变,pnumber的值也可以自己变,但是不能通过pnumber修改number的值。我们可以采用指向常量的指针,就是让这个指针以为自己指向的是一个常量,这样可以实现(有没有很熟悉这个模式?你可以读我但是你没资格改我)。
2. 常量指针
long number = 9999L;
long* const pnumber = &number;
如果我们希望这个指针只能指向number,不能再改了,可以使用常量指针,即只要交换const和long * 的位置。当然了,这样就可以通过pnumber修改number的值了,即这里的const指定的是指针的值无法修改。
我们可以结合上面两个,拥有一个无法改变自己指向也无法修改自己指向的值的指针,那就是:
long number = 9999L;
const long* const pnumber = &number;
当然,如果希望更进一层即number的值也无法改动只要把number用const声明就好了。
3. 指针和数组的关系
指针涉及到内存的存储,而众所周知数组本质上是一段连续存储的内存。所以我们很自然的想到这两个肯定有关系。上一节我们说到对指针的值+1就是加一个指针类型的字节,所以对于数组这种连续内存,我们会想到用指针来访问数组的元素。
1)一维数组
如果以数组名称的形式使用而不带索引值,那就是引用数组第一个元素的地址:
char array[10];
scanf("%s", array);
之前我们说过对于函数scanf(),有两个参数,第二个参数就是要在内存中存放的地址,那么对于字符串的输入,我们只需要写上要存放的数组的名字,那么就代表该数组的首地址而无需加上取址运算符&。而当访问某个元素时,array和array + i都代表一个地址,类似于一个指针,那么* array就是数组的第一个元素的值,* (array + i)表示第i个元素的值。
但是需要注意,数组和指针是有区别的,数组名称是一个固定的地址,而不是一个指针,可以在表达式右边使用数组名及其使用的地址,但是不能修改它。
2)多维数组
前面讨论的是一维数组和指针的关系,二维或多维数组在某种程度上是相同的。然而,指针和数组名称之间的差异变得更为明显。对于二维数组来说,我们知道在内存中依旧是按照一维数组的形式即连续内存存放的,而不是按照我们直觉想的那样的矩形。只不过这个一维数组的每一个元素都是一个数组。
对于二维数组来说,如果我们要使用指针引用第一个元素,需要** array,或者* array[0],又或者直接根据下标array[0][0]。也就是说使用array,就是在使用char** 类型的地址值,取消对array的引用,会得到相同的地址值,但其类型是char*,给它加 i 会得到一个char* 的地址,而这个地址是内存中第 i 个元素,即array[0]数组中第 i 个元素。取消对它的引用,会得到该地址中存储的内容。
总结一下就是我们要理解
**array + i //数组的元素值 + i
*(*array + i) //数组第一维第i个元素的值
**(array + i) //数组第i维第一个元素的值
*(*(array + i) + j) //数组第i维第j个元素的值
以上几种取消引用board方式的意义
Hint:int board[5][5];
* (* board + 1)和* ((*board) + 1)有区别吗?
答:无区别,*的优先级比+高。
以上为用数组名称引用二维数组,我们也可以声明一个二维数组的指针来引用二维数组中的每一个元素。
char array[10][10];
char *pArray = *array;
for(int i = 0; i < 10 * 10; i ++ )
printf("%c ", *(pArray + i));
显然,在上面这个例子中我们就是把二维数组当成一维数组在处理,直接在指针上处理可以只用一个一层循环来遍历整个数组。
这里需要注意的是给指针初始化时我们取消了对array的引用(* array),因为array属于char **类型的指针,而我们声明的是char *类型的指针,所以类型不同需要对其进行一次取消引用,也可以使用:char *pArray = &array[0][0]; 效果相同,但是如果使用char *pArray = array就会导致报错或警告,因为它们有着不同的间接级别:pArray指针引用的地址包含一个char类型的值,而array引用一个地址,那个地址引用另一个含有char类型值的地址(array[0])。
所以现在大家应该懂了int *和int **的区别了吧