wol*_*ats 6 c arrays pointers language-concepts
在关于C的介绍性书籍中,经常声称指针或多或少是数组.充其量只是一个巨大的简化吗?
存在是 C中的数组类型,它可以表现从指针完全不同的,例如:
#include <stdio.h>
int main(int argc, char *argv[]){
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *b = a;
printf("sizeof(a) = %lu\n", sizeof(a));
printf("sizeof(b) = %lu\n", sizeof(b));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
给出输出
sizeof(a) = 40
sizeof(b) = 8
Run Code Online (Sandbox Code Playgroud)
或者作为另一个例子a = b会产生编译错误(GCC:"赋值给带数组类型的表达式").
当然,指针和数组之间存在密切关系,在某种意义上,数组变量本身的内容是第一个数组元素的内存地址,例如int a[10] = {777, 1, 2, 3, 4, 5, 6, 7, 8, 9}; printf("a = %ul\n", a);打印包含777的地址.
现在,一方面,如果你在结构中"隐藏"数组,只需使用=运算符就可以轻松复制大量数据(数组,如果忽略包装结构)(这甚至也很快):
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARRAY_LENGTH 100000000
typedef struct {int arr[ARRAY_LENGTH];} struct_huge_array;
int main(int argc, char *argv[]){
struct_huge_array *a = malloc(sizeof(struct_huge_array));
struct_huge_array *b = malloc(sizeof(struct_huge_array));
int *x = malloc(sizeof(int)*ARRAY_LENGTH);
int *y = malloc(sizeof(int)*ARRAY_LENGTH);
struct timeval start, end, diff;
gettimeofday(&start, NULL);
*a = *b;
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
printf("Copying struct_huge_arrays took %d sec, %d µs\n", diff.tv_sec, diff.tv_usec);
gettimeofday(&start, NULL);
memcpy(x, y, ARRAY_LENGTH*sizeof(int));
gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
printf("memcpy took %d sec, %d µs\n", diff.tv_sec, diff.tv_usec);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
Copying struct_huge_arrays took 0 sec, 345581 µs
memcpy took 0 sec, 345912 µs
Run Code Online (Sandbox Code Playgroud)
但你不能用数组本身做到这一点.对于阵列x, y(相同大小和相同类型),表达式x = y是非法的.
然后,函数无法返回数组.或者如果数组被用作参数,C将它们折叠成指针 - 它不关心是否明确给出了大小,因此以下程序给出了输出sizeof(a) = 8:
#include <stdio.h>
void f(int p[10]){
printf("sizeof(a) = %d\n", sizeof(p));
}
int main(int argc, char *argv[]){
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
f(a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这种对数组的厌恶背后有什么逻辑吗?为什么C中没有真正强大的数组类型?如果有的话会发生什么不好?毕竟,如果一个数组隐藏在一个struct数组中,就像Go,Rust一样......,即数组是内存中的整个块,传递它将复制其内容,而不仅仅是第一个的内存地址元件.例如,像Go中的以下程序
package main
import "fmt"
func main() {
a := [2]int{-777, 777}
var b [2]int
b = a
b[0] = 666
fmt.Println(a)
fmt.Println(b)
}
Run Code Online (Sandbox Code Playgroud)
给出输出:
[-777 777]
[666 777]
Run Code Online (Sandbox Code Playgroud)
C语言最初是在20世纪70年代早期设计的一台PDP小型计算机上,尽管它具有巨大的24 kB内存,据说它只占据了半个房间.(那是kB,而不是MB或GB).
将编译器完全安装到该内存中是一项真正的挑战.因此,C语言旨在允许您编写紧凑的程序,并且添加了许多特殊的运算符(如+ =, - 和?:)以进行手动优化.
设计人员不会添加用于复制大型数组作为参数的功能.无论如何它都不会有用.
在C的前身B语言中,一个数组被表示为单独分配存储的指针(参见Lars回答中的链接).Ritchie想避免在C中使用这个额外的指针,因此在使用不期望数组的地方时,可以将数组名称转换为指针:
它消除了存储中指针的具体化,而是在表达式中提到数组名称时导致指针的创建.在今天的C中存活的规则是,数组类型的值在表达式中出现时转换为指向构成数组的第一个对象的指针.
尽管语言的语义存在潜在的变化,但本发明使大多数现有的B代码能够继续工作.
而struct小号也没有被添加到语言,直到后来.您可以将结构中的数组作为参数传递,然后提供另一个选项.
更改数组的语法已经太晚了.它会破坏太多的程序.已有100多名用户......
这部分问题...
这种对数组的厌恶背后有什么逻辑吗?为什么 C 中没有真正健壮的数组类型?如果有的话,会发生什么糟糕的事情呢?
...并不是一个真正的代码问题并且可以进行猜测,但我认为一个简短的答案可能是有益的:当创建 C 时,它的目标是 RAM 很少和 CPU 速度较慢的机器(以千字节和兆赫兹为单位测量,分别)。它旨在取代汇编语言作为系统编程语言,但不会引入其他现有高级语言所需的开销。出于同样的原因,C 仍然是微控制器的流行语言,因为它可以让您对生成的程序进行控制。
引入“健壮”的数组类型会给编译器和运行时带来潜在的性能和复杂性损失,而并非所有系统都无法承受。同时,C 为程序员提供了创建自己的“强大”数组类型的功能,并且仅在合理的情况下使用它们。
我发现这篇文章在这种情况下很有趣:Dennis Ritchie:Development of the C Language (1993)