指向数组的指针
刚刚我们验证了数组的地址就是数组第一个元素的地址。那么指向数组的指针自然也就有两种定义的方法:
...
char* p;
//方法1
p = a;
//方法2
p = &a[0];
指针的运算
当指针指向数组元素的时候,可以对指针变量进行「加减」运算,+n表示指向p指针所指向的元素的「下n个元素」,-n表示指向p指针所指向的元素的「上n个元素」。并不是将地址加1。
如:
//Example 04
#include
int main(void)
{
int a[] = { 1,2,3,4,5 };
int* p = a;
printf("*p = %d, *(p+1) = %d, *(p+2) = %d\n", *p, *(p + 1), *(p + 2));
printf("*p -> %p, *(p+1) -> %p, *(p+2) -> %p\n", p, p + 1, p + 2);
return 0;
}
执行结果如下:
//Consequence 04
*p = 1, *(p+1) = 2, *(p+2) = 3
*p -> 00AFF838, *(p+1) -> 00AFF83C, *(p+2) -> 00AFF840
有的小伙伴可能会想,编译器是怎么知道访问下一个元素而不是地址直接加1呢?
其实就在我们定义指针变量的时候,就已经告诉编译器了。如果我们定义的是整型数组的指针,那么指针加1,实际上就是加上一个sizeof(int)的距离。相对于标准的下标访问,使用指针来间接访问数组元素的方法叫做指针法。
其实使用指针法来访问数组的元素,不一定需要定义一个指向数组的单独的指针变量,因为数组名自身就是指向数组「第一个元素」的指针,因此指针法可以直接作用于数组名:
...
printf("p -> %p, p+1 -> %p, p+2 -> %p\n", a, a+1, a+2);
printf("a = %d, a+1 = %d, a+2 = %d", *a, *(a+1), *(a+2));
...
执行结果如下:
p -> 00AFF838, p+1 -> 00AFF83C, p+2 -> 00AFF840
b = 1, b+1 = 2, b+2 = 3
现在你是不是感觉,数组和指针有点像了呢?不过笔者先提醒,数组和指针虽然非常像,但是绝对「不是」一种东西。
甚至你还可以直接用指针来定义字符串,然后用下标法来读取每一个元素:
//Example 05
//代码来源于网络
#include
#include
int main(void)
{
char* str = "I love TechZone!";
int i, length;
length = strlen(str);
for (i = 0; i < length, i++)
{
printf("%c", str[i]);
}
printf("\n");
return 0;
}
程序运行如下:
//Consequence 05
I love TechZone!
在刚刚的代码里面,我们定义了一个「字符指针」变量,并且初始化成指向一个字符串。后来的操作,不仅在它身上可以使用「字符串处理函数」,还可以用「下标法」访问字符串中的每一个字符。
当然,循环部分这样写也是没毛病的:
...
for (i = 0, i < length, i++)
{
printf("%c", *(str + i));
}
这就相当于利用了指针法来读取。
指针和数组的区别
刚刚说了许多指针和数组相互替换的例子,可能有的小伙伴又开始说:“这俩货不就是一个东西吗?”
随着你对指针和数组越来越了解,你会发现,C语言的创始人不会这么无聊去创建两种一样的东西,还叫上不同的名字。指针和数组终究是「不一样」的。
比如笔者之前看过的一个例子:
//Example 06
//代码来源于网络
#include
int main(void)
{
char str[] = "I love TechZone!";
int count = 0;
while (*str++ != '\0')
{
count++;
}
printf("总共有%d个字符。\n", count);
return 0;
}
当编译器报错的时候,你可能会开始怀疑你学了假的C语言语法:
//Error in Example 06
错误(活动) E0137 表达式必须是可修改的左值
错误 C2105 “++”需要左值
我们知道,*str++ != ‘\0’是一个复合表达式,那么就要遵循「运算符优先级」来看。具体可以回顾《C语言运算符优先级及ASCII对照表》。
str++比*str的优先级「更高」,但是自增运算符要在「下一条语句」的时候才能生效。所以这个语句的理解就是,先取出str所指向的值,判断是否为\0,若是,则跳出循环,然后str指向下一个字符的位置。
看上去貌似没啥毛病,但是,看看编译器告诉我们的东西:表达式必须是可修改的左值
++的操作对象是str,那么str到底是不是「左值」呢?
如果是左值的话,那么就必须满足左值的条件。
❝
拥有用于识别和定位一个存储位置的标识符
存储值可修改
❞
第一点,数组名str是可以满足的,因为数组名实际上就是定位数组第一个元素的位置。但是第二点就不满足了,数组名实际上是一个地址,地址是「不可以」修改的,它是一个常量。如果非要利用上面的思路来实现的话,可以将代码改成这样:
//Example 06 V2
//代码来源于网络
#include
int main(void)
{
char str[] = "I love TechZone!";
char* target = str;
int count = 0;
while (*target++ != '\0')
{
count++;
}
printf("总共有%d个字符。\n", count);
return 0;
}
这样就可以正常执行了:
//Consequence 06 V2
总共有16个字符。
这样我们就可以得出:数组名只是一个「地址」,而指针是一个「左值」。 |