练习8:大小和数组

    在上一个练习中你做了一些算术运算,并且使用了(空)字符。这对于其它语言来说非常奇怪,因为它们把“字符串”和“字节数组”看做不同的东西。但是C中的字符串就是字节数组,并且只有不同的打印函数才知道它们的不同。

    在我真正解释其重要性之前,我先要介绍一些概念:sizeof和数组。下面是我们将要讨论的一段代码:

    这段代码中我们创建了一些不同数据类型的数组。由于数组是C语言工作机制的核心,有大量的方法可以用来创建数组。我们暂且使用type name[] = {initializer};语法,之后我们会深入研究。这个语法的意思是,“我想要那个类型的数组并且初始化为{..}”。C语言看到它时,会做这些事情:

    • 查看它的类型,以第一个数组为例,它是int
    • 查看[],看到了没有提供长度。
    • 查看初始化表达式{10, 12, 13, 14, 20},并且了解你想在数组中存放这5个整数。
    • 在电脑中开辟出一块空间,可以依次存放这5个整数。
    • 将数组命名为areas,也就是你想要的名字,并且在当前位置给元素赋值。

    areas的例子中,我们创建了一个含有5个整数的数组来存放那些数字。当它看到char name[] = "Zed";时,它会执行相同的步骤。我们先假设它创建了一个含有3个字符的数组,并且把字符赋值给name。我们创建的最后一个数组是full_name,但是我们用了一个比较麻烦的语法,每次用一个字符将其拼写出来。对C来说,namefull_name的方法都可以创建字符数组。

    在文件的剩余部分,我们使用了sizeof关键字来问C语言这些东西占多少个字节。C语言无非是内存块的大小和地址以及在上面执行的操作。它向你提供了sizeof便于你理解它们,所以你在使用一个东西之前可以先询问它占多少空间。

    这是比较麻烦的地方,所以我们先运行它,之后再解释。

    1. $ make ex8
    2. $ ./ex8
    3. The size of an int: 4
    4. The number of ints in areas: 5
    5. The first area is 10, the 2nd 12.
    6. The size of a char: 1
    7. The size of name (char[]): 4
    8. The number of chars: 4
    9. The size of full_name (char[]): 12
    10. name="Zed" and full_name="Zed A. Shaw"
    11. $

    译者注:16位机器上的int是16位的,不过现在16位机很少见了吧。

      5

      我的电脑认为int的大小是4个字节。你的电脑上根据位数不同可能会使用不同的大小。

      6

      areas中含有5个整数,所以我的电脑自然就需要20个字节来储存它。

      7

      如果我们把areas的大小与int的大小相除,我们就会得到元素数量为5。这也符合我们在初始化语句中所写的东西。

      接着我们访问了数组,读出areas[0]areas[1],这也意味着C语言的数组下标是0开头的,像Python和Ruby一样。

      9~11

      我们对name数组执行同样的操作,但是注意到数组的大小有些奇怪,它占4个字节,但是我们用了三个字符来打出”Zed”。那么第四个字符是哪儿来的呢?

      12~13

      我们对数组执行了相同的操作,但它是正常的。

      13

      最后我们打印出namefull_name,根据printf证明它们实际上就是“字符串”。

    使这个程序崩溃非常容易,只需要尝试下面这些事情:

    • full_name最后的'\0'去掉,并重新运行它,在valgrind下再运行一遍。现在将full_name的定义从main函数中移到它的上面,尝试在Valgrind下运行它来看看是否能得到一些新的错误。有些情况下,你会足够幸运,不会得到任何错误。
    • areas[0]改为areas[10]并打印,来看看Valgrind会输出什么。
    • 尝试上述操作的不同变式,也对namefull_name执行一遍。
    • 尝试使用areas[0] = 100;以及相似的操作对areas的元素赋值。
    • 尝试对namefull_name的元素赋值。
    • 尝试将的一个元素赋值为name中的字符。
    • 上网搜索在不同的CPU上整数所占的不同大小。