当前位置:  开发笔记 > 编程语言 > 正文

为什么C会有"伪阵列"?

如何解决《为什么C会有"伪阵列"?》经验,为你挑选了5个好方法。

我正在阅读Unix仇恨手册,在第9章中有一些我不太懂的东西:

C也没有真正的数组.它有一些看起来像数组但实际上是指向内存位置的指针.

除了使用指针来索引内存位置之外,我无法想象将数组存储在内存中的任何方法.无论如何C如何实现"假"数组?这种说法是否有任何真实性?



1> zoul..:

我认为作者的观点是C数组实际上只是指针算术的薄单板.是下标运算简单地定义为a[b] == *(a + b),所以你可以很容易地说5[a],而不是a[5]做其他可怕的事情,如访问数组过去的最后一个索引.

与此相比,"真正的数组"将是一个知道它自己的大小,不允许你做指针算术,没有错误地访问最后一个索引,或使用不同的项类型访问其内容的数组.换句话说,"真正的数组"是一种严格的抽象,它不会将您绑定到单个表示 - 例如,它可以是链接列表.

PS.为了给自己省点麻烦:我对此没有任何意见,我只是在解释书中的引用.


嗯,所以允许数组有O(n)项访问权限,仍然被称为数组?这有点令人沮丧.
@ApproachingDarknessFish你从哪里得到"O(n)"?
@ApproachingDarknessFish无论如何,当我看到"链表"时,我读到了"缓存未命中和性能恐怖"并且运行得很远
这个答案在任何级别上都是错误的.C***中的数组确实知道它自己的大小.为什么会限制你做指针算术?如果你愿意,C不会限制你对任何指针进行指针运算; 该语言的设计理念是,您知道自己在做什么,应该被允许这样做.如已经指出的那样,声明数组可以作为链表实现是很多废话.如果有一种语言可以做到这一点,那肯定不会是具有表现意识的C语言!
关于你唯一能做到的就是第一句话.是的,作者可能试图说C数组只是指针算术上的薄单板.但是C中有很多东西,所以需要更多的解释.这并不意味着数组不是语言中真正的一流实体 - 它们是.初学者经常混淆数组和指针,在许多情况下,语言似乎将它们模糊在一起,但它们是不同的.您对下标运算符的说法与数组没有任何关系; 它只是[]运算符的形式语法.
究竟!这就是为什么我理解是否有人不认为数组是C中真正的一等公民类型.

2> user268396..:

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); 
}

sample2sample4特别可能是为什么人们往往混为一谈与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 */
}


@Olaf我知道但是在*Unix-haters'手册*的时代与一些(奇怪的)流行的C编译器(`msvc`)缺乏对现代C的支持之间我觉得最好不要居住这个问题很多......

3> AnT..:

您引用的陈述事实上是不正确的.C中的数组不是指针.

在B和BCPL语言(C的祖先)中使用了将数组作为指针实现的想法,但它没有幸免于过渡到C.在C的早期阶段,与B和BCPL的"向后兼容性"被认为是有些重要的,这就是为什么C数组紧密模拟 B和BCPL数​​组的行为(即C数组很容易"衰减"到指针).然而,C数组不是"指向内存位置的指针".

这本书的报价完全是虚假的.这种误解在C新手中相当普遍.但它如何设法进入一本书是超出我的.


你错过了这本书的背景.这是一个史诗般的拖钓行为,而不是一本旨在教授关于C和Unix事实上正确的事情的严肃书.

4> hyde..:

作者可能意味着,数组受到限制,从程序员的角度来看,它们让他们感觉像二等公民.例如,两个函数,一个是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数组.


没有语法从函数返回数组.无法分配数组.对我来说听起来不是一流的.

5> R....:

就像整本书一样,这是一个拖钓的例子,特别是涉及陈述几乎是真实的但却错误的拖钓类型,以征求关于为何错误的愤怒回应.C肯定有实际的数组/数组类型,正如指针到数组类型(和多维数组)的工作方式所证明的那样.

推荐阅读
TXCWB_523
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有