• 发一篇关于 数组名称 和 指针区别 的专题探讨


    char p_arr[] = "p_arr : hello world"; 

    声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值时一个常量,指向这段空间的起始位置。数组名是符号地址常量

    一个常量指针,这个值不能被改变,在编译时求值并存在编译器的符号表里面,其值就是个内存地址;所以可以认为程序没有给他分配空间,数组名只是代表了那个数组空间;  

    与指针不一样,指针指向一块空间,同时指针本身也存储在某个空间;可以认为数组名存在在符号表里,符号表是编译器用的,我们管不到;p_arr和&p_arr值是一样的,本来对常量取地址是非法的,但是标准组织没有定对数组名取地址是非法还是合法,所以因编译器而异.

    引用chinaunix上一个叫dump_crash会员的发言: 

    C专家编程里解释的很好:

    经典的错误:在一个文件定义了数组,却在另外一个文件声明其为指针。代码如下:
    /*File name: main.c*/
    extern int* array; /*声明指针*/
    int main(void)
    {
     array[0]=1; /*用指针访问数据*/
     return 0;
    }
     
    /*File name: def.c*/
    int array[5]={0}; /*定义的却是数组*/  

    1。编译器对数组名和指针变量的处理方式
    编译器在编译时会产生一个符号表,记录了符号名和它的地址。对于指针变量,这显然很好理解。而数组名就不那么明显了,它仅仅是一个符号而已,何来地址?编译器是这样处理的,它记录了array[0]的地址;这和我们通常的理解也是一样的。
    2。带下标形式的数组和指针寻址方式
    (1)数组情形
    char a[9]="abcdefgh";
    ...
    c=a[i];
    在编译期,会在符号表中创建这样一条记录:
    name:a address:9980
    要获取a[i]的值分两个步骤:
    step 1:取得i的值并和9980相加
    step 2:在内存地址(9980+i)处取其内容
     
    (2)指针情形
    char* p="abcdefgh";
    ...
    c=p[i];
    在编译期,会在符号表中创建这样一条记录:
    name:p address:4624
    要获取p[i]的值分三个步骤:
    step 1:在内存地址4624处取其内容,比如说“5081”
    step 2:取得i的值并和5081相加
    step 3:在内存地址(5081+i)取其内容
     
    现在有了这些知识,前面出现的错误就很好解决了。在def.c中定义array是一个int型数组,在符号表中就会有把array和array[0]的地址关联起来。而在main.c中却把把array看作是一个int型指针,于是乎按照上面介绍的指针寻址方式,把符号表中array的地址(即&array[0])中的内容当作指针来寻址,这显然是不对的。
     

    上面程序运行的后果是core dumped,但是这种情况和下面的还不一样: 

    #include <iostream>
    using namespace std;
    void print(int arr[]){
       cout
    <<arr[0]<<endl;
       printf(
    "0x%X\n",arr);
       printf(
    "ox%X\n",&arr);
    }

    main()
    {
        
    int a[4]={1,2,3,4};
        printf(
    "0x%X\n",a);
        print(a);

    }  

    输出结果
    0xD26D4BC0
    1
    0xD26D4BC0
    oxD26D4BA8

    在这个例子中,注意这里面虽然print的参数arr被声明为一个貌似指针一样的东西int arr[]

    但是这个东西其实和你在栈上声明的int arr[5]已经有了本质的区别,区别在哪呢?请看:

    1. 在栈上声明 int arr[] 是办不到的,因为编译器不能在编译的时候确定这个array的空间大小,也不能分配空间
    2. 在栈上声明 int arr[5]的时候,arr本身是不占用任何空间的,他的大小是数组元素的大小总和。这个符号本身并不占任何空间,这个是和指针的本质区别
    3. 在print函数参数中声明的int arr[]已经丧失了上面说它在的栈上本质特点,他在本质上已经变成了一个指针你可以用sizeof看到他在64位机器上占用的空间是8个字节。而且,这次它是占用空间的有地址的,这个可以从输出的结果上看出来。

    所以,当main中调用print(a)的时候,把a的值传给了arr,这个时候编译器使用指针的方式来寻址是没有问题的,因为arr其实就是一个指针,他已经不再是一个数组名称!

    以上讨论了,像int arr[]的这种声明

    • 不能在栈上出现
    • 在函数的参数列表里面声明的时候,作用等同于指针
    • 最后说一下这个东西在struct/class这样的东西里面声明

    比如有这样一个声明:

    struct test{
        int32_t len;
        
    int arr[];
    };

    先说结论:这种情况下,arr还是一个array,所以他不占用任何空间,他的存在就像是一个隐形的指针。因为他的存在不占用空间,然而你却可以像使用指针一样来使用他,你可以写obj.arr[0]=3(前提是你给obj分配了足够的空间),但是你却不能写obj.arr=&temp_integer,因为arr是一个arrar名称,是一个常量,不能被赋值。

    但是,这就有了一个很尴尬的局面:

    你似乎不能操作这个arr里面的内容,因为不论是在构造函数还是使用他的代码中,你都不能写arr=(int*)malloc(sizeof(int));

    这个时候别急,你可以给test的对象分配更大的空间,比如:

    test * obj = (test *) malloc(sizeof(test)+4)0;

    这里后面+40的空间就可以被arr来使用,想要对它操作的话既可以把的值传给一个真正的指针来操作,也可以直接使用arr。 

    你可能会说,这种属于奇巧淫技,没处用,可惜你错了,redis中就是这么用的,

    不信请看:http://redis.io/topics/internals-sds 


  • 相关阅读:
    防抖和节流
    关于keep-alive的学习
    elementUI上传图片前判断图片的尺寸大小
    vue 判断线上环境还是本地环境
    elementUi table表格的拖拽功能
    获取文件的md5值
    element ui上传腾讯云,更新视频时长
    js音视频文件的时长
    手写一个移动端带惯性的轮播图vue组件
    这个用来总结一些常用的工具函数
  • 原文地址:https://www.cnblogs.com/welkinwalker/p/2170165.html
Copyright © 2020-2023  润新知