我正在阅读Unix仇恨手册,在第9章中有一些我不太懂的东西:
C也没有真正的数组.它有一些看起来像数组但实际上是指向内存位置的指针.
除了使用指针来索引内存位置之外,我无法想象将数组存储在内存中的任何方法.无论如何C如何实现"假"数组?这种说法是否有任何真实性?
我认为作者的观点是C数组实际上只是指针算术的薄单板.是下标运算简单地定义为a[b] == *(a + b)
,所以你可以很容易地说5[a]
,而不是a[5]
做其他可怕的事情,如访问数组过去的最后一个索引.
与此相比,"真正的数组"将是一个知道它自己的大小,不允许你做指针算术,没有错误地访问最后一个索引,或使用不同的项类型访问其内容的数组.换句话说,"真正的数组"是一种严格的抽象,它不会将您绑定到单个表示 - 例如,它可以是链接列表.
PS.为了给自己省点麻烦:我对此没有任何意见,我只是在解释书中的引用.
C数组和指针之间存在差异,可以通过sizeof()
表达式的输出看出它.例如:
void sample1(const char * ptr) { /* s1 depends on pointer size of architecture */ size_t s1 = sizeof(ptr); } size_t sample2(const char arr[]) { /* s2 also depends on pointer size of architecture, because arr decays to pointer */ size_t s2 = sizeof(arr); return s2; } void sample3(void) { const char arr[3]; /* s3 = 3 * sizeof(char) = 3 */ size_t s2 = sizeof(arr); } void sample4(void) { const char arr[3]; /* s4 = output of sample2(arr) which... depends on pointer size of architecture, because arr decays to pointer */ size_t s4 = sample2(arr); }
在sample2
和sample4
特别可能是为什么人们往往混为一谈与C指针C数组,因为在其他语言中,你可以简单地将数组作为参数传递给函数,它的工作"一样的",因为它在调用函数一样.类似地,由于C的工作方式,你可以传递指针而不是数组,这是'有效',而在其他语言中,数组和指针之间的区别更明显,它不会.
您还可以查看sizeof()
输出作为C的值传递值语义的结果(因为C数组衰减为指针).
此外,一些编译器也支持这种C语法:
void foo(const char arr[static 2]) { /* arr must be **at least** 2 elements in size, cannot pass NULL */ }
您引用的陈述事实上是不正确的.C中的数组不是指针.
在B和BCPL语言(C的祖先)中使用了将数组作为指针实现的想法,但它没有幸免于过渡到C.在C的早期阶段,与B和BCPL的"向后兼容性"被认为是有些重要的,这就是为什么C数组紧密模拟 B和BCPL数组的行为(即C数组很容易"衰减"到指针).然而,C数组不是"指向内存位置的指针".
这本书的报价完全是虚假的.这种误解在C新手中相当普遍.但它如何设法进入一本书是超出我的.
作者可能意味着,数组受到限制,从程序员的角度来看,它们让他们感觉像二等公民.例如,两个函数,一个是ok,另一个不是:
int finefunction() { int ret = 5; return ret; } int[] wtffunction() { int ret[1] = { 5 }; return ret; }
你可以通过在结构中包装数组来解决这个问题,但它只是强调数组是不同的,它们不像其他类型.
struct int1 { int a[1]; } int[] finefunction2() { struct int1 ret = { { 5 } }; return ret; }
这样做的另一个影响是,您无法在运行时获取数组大小:
int my_sizeof(int a[]) { int size = sizeof(a); return size; } int main() { int arr[5]; // prints 20 4, not 20 20 as it would if arrays were 1st class things printf("%d %d\n", sizeof(arr), my_sizeof(arr)); }
说出作者所说的另一种方式是,在C(和C++)术语中,"数组"意味着除了大多数其他语言之外的其他东西.
那么,你的标题问题,"真正的数组"将如何存储在内存中.好吧,没有一种"真正的阵列".如果你想在C中使用真正的数组,你基本上有两个选择:
使用calloc分配缓冲区,并在此处存储指针和项目计数
struct intarrayref { size_t count; int *data; }
这个结构基本上是对数组的引用,你可以很好地将它传递给函数等.你会想要编写函数来对它进行操作,比如创建实际数据的副本.
使用灵活的数组成员,并使用单个calloc分配整个结构
struct intarrayobject { size_t count; int data[]; }
在这种情况下,您可以count
一次性分配元数据()和数组空间,但价格是,您不能再将此结构作为值传递,因为这会留下额外的数据.你必须将指向这个结构的指针传递给函数等.因此,人们会认为这是一个"真正的数组"还是只是略微增强的普通C数组.
就像整本书一样,这是一个拖钓的例子,特别是涉及陈述几乎是真实的但却错误的拖钓类型,以征求关于为何错误的愤怒回应.C肯定有实际的数组/数组类型,正如指针到数组类型(和多维数组)的工作方式所证明的那样.