zoukankan      html  css  js  c++  java
  • C Program进阶-数组

    (一)数组的内存布局

    对于语句int a[5]; 我们明白这里定义了一个数组,数组里有5个元素,每一个元素都是int类型,我们可以用a[0],a[1]等访问数组里的元素,但是这些元素的名字就是a[0],a[1]吗?

    请看下面的内存布局图:

    如上图所示,当我们定义一个数组int a[5],编译器根据指定的数组元素以及数组元素类型分配内存,分配内存的大小是:元素类型大小*元素个数,并且把这个空间命名为a,a[0],a[1]是数组a的元素,但不是数组元素的名字,数组的每一个元素是没有名字的

    我们再来看看sizeof关键字的几个问题:

    sizeof(a)的值是sizeof(int)*5,32位系统下是20;

    sizeof(a[0])的值是sizeof(int),32位系统下是4;

    sizeof(&a[0])的值在32位系统下为4,这很好理解,取首元素a[0]的首地址;

    sizeof(&a)的值也是4,取数组a的首地址。

    程序:

     1 #include <stdio.h>
     2 
     3 void main()
     4 {
     5     int a[5] = {1, 2, 3, 4, 5};
     6     printf("sizeof(a):%d
    ", sizeof(a));
     7     printf("sizeof(a[0]):%d
    ", sizeof(a[0]));
     8     printf("sizeof(&a[0]):%d
    ", sizeof(&a[0]));
     9     printf("sizeof(&a):%d
    ", sizeof(&a));
    10 }

    程序输出:

    1 sizeof(a):20
    2 sizeof(a[0]):4
    3 sizeof(&a[0]):4
    4 sizeof(&a):4

    (二)省政府和市政府的区别-&a和&a[0]

    我们首先在程序中把&a和&a[0]的值打印出来:

    1 #include <stdio.h>
    2 
    3 void main()
    4 {
    5     int a[5] = {1, 2, 3, 4, 5};
    6     printf("&a:%d, &a[0]:%d
    ", &a, &a[0]);
    7 }

    其结果是: &a:3012628, &a[0]:3012628 

    我们发现这两者的值是一样的,那么这两者有什么区别呢?a是整个数组,而a[0]是数组首元素,因此&a是整个数组首地址,&a[0]是数组首元素地址举个例子更好理解:湖南的省政府在长沙,而长沙的市政府也在长沙,二者地址一样,但代表的意义却完全不同。

    (三)数组名a作为左值和右值的区别

    先来理解下左值和右值:

    简而言之,出现在赋值符号“=”左边的就是左值,而右边则是右值,

    比如x=y;

    左值:在这个上下文环境中,编译器认为x的含义是x所代表的地址,这个地址只有编译器知道,在编译的时候确定,编译器在一个特定的区域保存这个地址,我们完全不必考虑这个地址保存在哪里;

    右值:在这个上下文环境中,编译器认为y的韩式是y所代表的地址里面的内容。这个内容是什么,只有到运行时才知道。

    既然明白了左值和右值的区别,下面就讨论下数组作为左值和右值的区别:

    当a 作为右值的时候代表的是什么意思呢?很多书认为是数组的首地址,其实这是非常错误的。a 作为右值时其意义与&a[0]是一样,代表的是数组首元素的首地址,而不是数组的首地址。这是两码事。但是注意,这仅仅是代表,并没有一个地方(这只是简单的这么认为,其具体实现细节不作过多讨论)来存储这个地址,也就是说编译器并没有为数组a分配一块内存来存其地址,这一点就与指针有很大的差别

    a 作为右值,我们清楚了其含义,那作为左值呢?
    a 不能作为左值!这个错误几乎每一个学生都犯过。编译器会认为数组名作为左值代表的意思是a 的首元素的首地址,但是这个地址开始的一块内存是一个总体,我们只能访问数组的某个元素而无法把数组当一个总体进行访问。所以我们可以把a[i]当左值,而无法把a当左值。其实我们完全可以把a 当一个普通的变量来看,只不过这个变量内部分为很多小块,我们只能通过分别访问这些小块来达到访问整个变量a 的目的。

    (四)a和&a的区别

    对于数组int a[5];若我们要定义指针变量接受a,我们可以这样做 int *pa = a; 但是我们若要定义指针接受&a,使用int *pa = &a却不行,编译器报错a value of type "int(*)[5]" cannot be assigned to an entity of type "int *",从这个报错我们知道&a的类型是int(*)[5],这是一个指向数组指针,数组里有5个元素。

    为了看到a,&a的区别,

    我们将a和&a的值打印出来: printf("a:%d, &a:%d ", a, &a); ,并将a+1和&a+1打印: printf("a+1:%d, &a+1:%d ", a+1, &a+1); 

    其结果是:

    1 a:2948708, &a:2948708
    2 a+1:2948712, &a+1:2948728

    从上面两行的打印我们可以看出,数组名a里存放的是数组首元素的地址,而&a是整个数组的地址,这两者在值上是一样的,但是a和&a 做+1操作时,a+1增加的是一个元素的大小4,而&a+1增加的是整个数组的大小20。

  • 相关阅读:
    如何使用Arrays工具类操作数组
    Java 内存模型详解
    HashSet源码分析:JDK源码系列
    在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory
    在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求
    在ASP.NET Core中用HttpClient(四)——提高性能和优化内存
    ASP.NET Core与Redis搭建一个简易分布式缓存
    在ASP.NET Core中用HttpClient(三)——发送HTTP PATCH请求
    在ASP.NET Core中用HttpClient(二)——发送POST, PUT和DELETE请求
    在ASP.NET Core中用HttpClient(一)——获取数据和内容
  • 原文地址:https://www.cnblogs.com/z-joshua/p/6892998.html
Copyright © 2011-2022 走看看