C语言指针系列三——指针运算本质

C语言指针系列三——指针运算本质

对于如下的声明:

int arr[]={1,3,5,7,8};
int *p;
p=arr;


尽管最后一个语句看起来是将一个指针赋值为数组名,但C语言中并不存在指向数组的指针,日常说某某个指针指向了数组只不过是一种习惯,并不是说真的有一个数组型的指针指向了数组。事实上,如上面的程序段,因为arr是一个int类型的数组,arr里面的元素全都是int型,p=arr实际上是将整型指针p指向了数组的首元素1(是一个整型),更具体的是p指向了数组首元素的首字节——p存储的是数组首元素的首字节的地址。

因为p是一个整型指针变量(假设int占4个字节),它指向了一个int。所以对p进行自加的结果是p原来的值加上4(4刚好是一个int占的字节数)。有了这个铺垫,也就有了下面的巧合:

#include<stdio.h>
int main()
{
   int arr[] = { 1,3,5,8,9 };
   int* p;
   for (p=arr; p < arr + 5; p++)
   {
       printf_s("%d----%p\n",*p,p);
   }
   printf_s("%p--%p", arr,arr+5);
   return 0;
}

在for循环里,因为p指向了一个整型变量,对p进行自加是在原来的值上加上4,因为数组在内存中是连续存储的,所以对p进行自加的结果恰好是下一个元素的首地址。此时输出*p就是数组的下一个元素。arr+5就是在arr本身的值的基础上加上5个int所占的字节,也就是20。输出结果如下:


内存地址是用16进制表示的,可以看到,每一次自加p,p的值就相差4。在最后的输出中,arr与arr+5相减的结果是00000014,它等当10进制数20(5个int占的字节数)。

再看下面的代码:

#include<stdio.h>
int main()
{
      int arr[] = { 1,3,5,8,9 };
      char arr2[] = {'a','b','c'};
      double arr3[] = {10,12,5,8};
      int* p=arr;
      char *p2=arr2;
      double *p3=arr3;
      printf_s("int占%d字节数,char占%d字节数,double占%d字节数\n",sizeof(int),sizeof(char),sizeof(double));
      printf_s("%p----%p\n",p,p+1);
      printf_s("%p----%p\n", p2,p2+1);
      printf_s("%p----%p\n", p3,p3+1);
      return 0;
}

输出为:

试着将对应的16进制数相减,然后再转换为10进制,看看有什么发现…

原来,将一个指针变量加1(或自加)就是在指针变量原始的值的基础上加上指针变量所指向的类型在内存所占的字节数。如指向int型的指针加1将会加上4,指向char型的指针加1将会加上1,指向double型的指针加1将会加上8

让我们再来看一下数组,arr[1]是选取数组arr的第1位元素(从第0开始),事实上,数组名再加一对中括号选取数组元素是一个语法糖,编译器并不是按照这种方式来处理数组的。对编译器说arr[1]相当于*(arr+1),这才是编译器处理数组的方式。语句*(arr+1)首先对括号里的arr+1进行运算,它将得到数组元素第1个元素(从第0开始)的地址,然后利用*号取得这个位置的值,它等价于arr[1]。由于编译器是按照*(arr+1)的方式来处理数组的。因此下面的这两种方式可能非常常见:

#include<stdio.h>
int main()
{
      int arr[] = { 1,3,5,8,9 };
      int* p=arr;
      for (int i = 0; i < 5; i++)
      {
             printf_s("%d,", *(p +i));
      }
      printf_s("\n");
      //or
      for (; p < arr + 5;)
      {
             printf_s("%d,", *p++);
      }     
      return 0;
}


输出为:

通过指针来访问数组被认为是高效的,毕竟这是编译器处理数组的方式。但不得不说,这是一种古老的说法(以前的确是更加高效),现代的编译器都是经过高度优化的,使用*(arr+1)来访问数组并不比使用arr[1]访问数组带来实质的效率提升,但它的坏处非常明显——它更难以阅读,到底使用何种方式来使用数组,这里不给出建议。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言中的指针是一种特殊的变量类型,用于存储内存地址。指针变量存储的是某个对象的内存地址,而不是对象本身的值。 指针运算是对指针进行操作的过程。C语言中提供了一些基本的指针运算符,包括: 1. 取址运算符(&):用于获取变量的地址。例如,`int a = 10; int *p = &a;`中的`&a`表示变量`a`的地址。 2. 解引用运算符(*):用于获取指针变量所指向的对象的值。例如,`int a = 10; int *p = &a; printf("%d", *p);`中的`*p`表示指针变量`p`所指向的对象的值,即输出为`10`。 3. 指针加法运算:可以对指针进行加法运算。例如,`int a[5] = {1, 2, 3, 4, 5}; int *p = a; p = p + 2;`中的`p + 2`表示将指针变量`p`向后移动两个元素,即指向数组`a`的第个元素。 4. 指针减法运算:可以对指针进行减法运算。例如,`int a[5] = {1, 2, 3, 4, 5}; int *p = &a[4]; p = p - 2;`中的`p - 2`表示将指针变量`p`向前移动两个元素,即指向数组`a`的倒数第个元素。 5. 指针与整数的加法和减法运算:可以将整数与指针进行加法或减法运算。例如,`int a[5] = {1, 2, 3, 4, 5}; int *p = a; p = p + 2; p = p - 1;`中的`p + 2`表示将指针变量`p`向后移动两个元素,然后再减去一个元素,即指向数组`a`的第二个元素。 需要注意的是,指针运算要遵循一些规则和限制,以防止出现不可预料的错误。例如,对空指针进行解引用或越界访问数组等操作都是不安全的。在使用指针运算时,应该确保指针指向有效的内存地址,并且不越界访问数组。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一尺丈量

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值