zoukankan      html  css  js  c++  java
  • c语言指针难点

    先来一个例子

    例:

    #include "stdio.h"
    
    int main()
    {
      int a[5] = {1,2,3,4,5};
      printf("a是一个地址%d
    ",a);
      printf("a[0]是一个数%d
    ",a[0]);
      printf("&a[0]是地址%d
    ",&a[0]);
       printf("&a[1]是地址%d
    ",&a[1]);
       printf("上面等价于a+1,地址%d
    ",a+1);
      printf("&a应该地址的地址,非法,但竟然和a一样,地址%d
    ",&a);
      printf("为什么加了20呢,跨越了整个数组,把数组改成6确实如此%d
    ",&a+1);
    
      int *p = (int *)(&a+1);
      printf("%d,%d", *(a+1), *(p-1));
    }
    /* 
    http://segmentfault.com/q/1010000000303353
    
    假设有以下数组
    
    int a[5] = {1,2,3,4,5};
    int *x = &a+1;
    虽然上面的程序可以编译通过,但是,编译器会出警告,因为x和a的类型不匹配,要想把警告去掉,有两种方法
    
    1、通过强制类型转换
    
    int *x = (int *)(&a+1); 
    print("%d
    ",*(x-1));
    2、为它寻找合适的类型 &a的类型是int (*)[5]
    
    int (*x)[5] = &a+1;
    printf("%d
    ",**(x-1));
    这里就牵涉到了如何写出适当的数据类型,这在赋值和参数传递中很重要!
    
    所以,首先我得总结一下a,&a和&a[0]这三个数据的数据类型
    
    1.a是数组名,是指向数组第一个元素的指针,毫无疑问,在这里,数组第一个元素的数据类型是int所以a的数据类型是就是int*。
    
    2.&a是对一个一维数组取地址,得出来的是指向数组的指针(在这里是pointer to array of int), 也就是int(*)[5]。
    
    3.&a[0]就很简单,首先a[0]得到的是一个整形数int,然后对它取地址,所以它的数据类型就是int*。
    
    知道了数据类型,那么对指针运算看起来就清晰多了!
    
    
    */

    a得出来的是指向数组的指针,所以&a+1其实是以数组的长度为单位来移动的。如果你只是想要得到数组的第二个元素的话,那么就用*(&a[0]+1),因为&a[0]的数据类型是int*。画个图先: 请输入图片描述

    假设有以下数组

    int a[5] = {1,2,3,4,5};
    int *x = &a+1;
    

    虽然上面的程序可以编译通过,但是,编译器会出警告,因为x和a的类型不匹配,要想把警告去掉,有两种方法

    1、通过强制类型转换

    int *x = (int *)(&a+1); 
    print("%d
    ",*(x-1));
    

    2、为它寻找合适的类型 &a的类型是int (*)[5]

    int (*x)[5] = &a+1;
    printf("%d
    ",**(x-1));
    

    这里就牵涉到了如何写出适当的数据类型,这在赋值和参数传递中很重要!

    所以,首先我得总结一下a,&a和&a[0]这三个数据的数据类型

    1. a是数组名,是指向数组第一个元素的指针,毫无疑问,在这里,数组第一个元素的数据类型是int所以a的数据类型是就是int*

    2. &a是对一个一维数组取地址,得出来的是指向数组的指针(在这里是pointer to array of int), 也就是int(*)[5]

    3. &a[0]就很简单,首先a[0]得到的是一个整形数int,然后对它取地址,所以它的数据类型就是int*

    知道了数据类型,那么对指针运算看起来就清晰多了!

    先看看通过强制类型转换的那部分代码,它会输出什么数字呢?

    答案是5!通过刚才对数据类型的总结可以知道,&a的数据类型是int (*)[5],所以&a+1其实是已经移动了5*sizeof(int)个字节了现在指针是指到了数组最后一个元素的后一个元素(图1),也就是说,已经越界了!但是因为x的数据类型其实是int * 所以对于x-1,其实是向左移动了1*sizeof(int)个字节,也就是指向了最后一个元素,所以*(x-1)得出来的值就是数组的最后一个元素:5

    好了,现在再看第二部分,它又会输出什么数字呢?先不说答案,刚才也说了,在这里 x的数据类型是int (*)[5],是一个指向含有5个int元素的一维数组的指针,对它进行加减运算的话就会以 sizeof(int)*5个字节为单位进行移动,所以x-1其实是向左移动了sizeof(int)*5个字节,在我的机器上是移动了 20个字节,也就是回到了数组的第一个元素,所以得出来的答案就是:1 请输入图片描述

    以上是一维数组的,下面我想说说二维数组的情况,有以下一段代码:

    int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
    /*
    ?? = a; // int (*b)[3] = a;
    ?? = a[0]; // int *c = a[0];
    ?? = a[0][0] // int d = a[0][0];
    ?? = &a; // int (*e)[3][3] = &a;
    ?? &a[0]; // int (*f)[3] = &a[0]
    ?? &a[0][0]; // int *g = &a[0][0]
    */
    

    还是先看看a,a[0],&a,&a[0],&a[0][0]这几者的数据类型:

    注意上面加粗了的文字,数组名是指向数组第一个元素的指针!

    1. a的数据类型是??int *?不是,这句加粗了的文字的核心就是第一个元素这五个字, a的第一个元素不就是a[0][0]吗?严格来说,不是,a的第一个元素其实是a[0],那么a[0]的数据类型是什么呢?a[0] 是一个包含三个int元素的数组,所以a[0]的类型就和int t[3]中t的类型一样,是int*,既然第一个元素 的数据类型是 int*,那把这个二维数组看成一维数组的话,实际上它就是一个含有三个int*元素的数组,也就是指针数组,所以a的数据类型就是int (*)[3]

    2. 再来看看&a,在一维数组的时候说了,对一个数组名取地址得出来的是指向数组的指针,所以&a的数据类型 就是int (*)[3][3]

    3. &a[0]这个看上去有点蛋疼,但是在上上段文字中也说了,a[0]是一个包含三个int元素的数组,和int t[3] 中的t的数据类型一样,是int*,自然,&a[0]的数据类型就和&t的数据类型一样,也就是int (*)[3]

    到这里,二维数组中关于数据类型就写得差不多了,既然知道了数据类型,那么运算起来就可以准确知道指针会移动到哪里!

    看看下面这段代码:

    #include "stdio.h"
    
    int main()
    {
    int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
    int (*x)[3] = a;
    //x的数据类型是int (*)[3],所以,x+1实际上是移动了3*sizeof(int)个字节 
    int *k = a[0];
    int (*y)[3][3] = &a;
    int (*q)[3] = &a[0];
    int *z = &a[0][0];
    
    printf("%d
    ",(*(x+1))[0]);
    printf("%d
    ",*(k+1));
    printf("%d
    ",(*(*(y+1)))[0]);
    printf("%d
    ",(*(q+1))[0]);
    printf("%d
    ",*(z+1));
    
    int *p = (int *)(y+1);
    printf("%d
    ",*(p-1));
    
    
    }
    • x的数据类型是int (*)[3],所以,x+1实际上是移动了3*sizeof(int)个字节,如图所示: 请输入图片描述

    • k的数据类型是int *,所以,k+1实际上是移动了sizeof(int)个字节。

    • y的数据类型是int (*)[3][3],所以y+1实际上是移动了3*3*sizeof(int)个字节,也就是到了数组最后一个元素的后面的一个元素。如图所示: 请输入图片描述

    • q的数据类型是int (*)[3],所以,q+1实际上也是移动了3*sizeof(int)个字节。

    • z的数据类型是int *,所以z+1实际上是移动了sizeof(int)个字节。

    所以对数组指针进行加减运算,最重要的是知道它的步长,而步长又是由数据类型决定的!

    参考文件汇总:

    1、http://www.cnblogs.com/bluewelkin/p/4059840.html

    2、http://www.cnblogs.com/bluewelkin/p/3738383.html

    3、http://www.cnblogs.com/bluewelkin/p/3565011.html

    4、http://www.cnblogs.com/bluewelkin/p/4056847.html

  • 相关阅读:
    【stanford】梯度、梯度下降,随机梯度下降
    [philosophy]空间
    【crawler】heritrix 3 使用
    【database】database domain knowledge
    【java】Java异常处理总结
    【computer theory】一、集合、关系和语言
    【java】ubuntu部署web项目war包到tomcat上
    【MachineLeaning】stanford lesson one
    【数据立方】由表和电子数据表到数据立方体,cuboid方体
    PHP变参函数的实现
  • 原文地址:https://www.cnblogs.com/bluewelkin/p/4059940.html
Copyright © 2011-2022 走看看