C 对数组的厌恶[关闭]

2023-11-23

在 C 的入门书籍中,经常声称指针或多或少are数组。这充其量不是一个巨大的简化吗?

There isC 中的数组类型,它的行为与指针完全不同,例如:

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

给出输出

sizeof(a) = 40 
sizeof(b) = 8 

或者作为另一个例子a = b会给出编译错误(GCC:“分配给具有数组类型的表达式”)。

当然有一个亲密的关系介于指针和数组之间,从某种意义上说,是的,数组变量的内容itself是第一个数组元素的内存地址,例如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;
}

Output:

Copying struct_huge_arrays took 0 sec, 345581 µs
memcpy took 0 sec, 345912 µs

但你不能用数组本身来做到这一点。对于数组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;
}

这种对数组的厌恶背后有什么逻辑吗?为什么没有一个trueC 中的健壮数组类型?如果有的话,会发生什么糟糕的事情呢?毕竟,如果一个数组隐藏在struct数组does行为与 Go、Rust 等中一样,即数组is the whole内存中的块并将其传递将复制其内容,而不仅仅是第一个元素的内存地址。例如,在 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)
}

给出输出:

[-777 777]
[666 777]

C 语言最初是在 20 世纪 70 年代初设计的等离子微型计算机据报道,尽管它有 24 kB 的巨大内存,但它只占了半个房间。 (这是 kB,不是 MB 或 GB)。

将编译器安装到内存中才是真正的挑战。因此,C 语言被设计为允许您编写紧凑的程序,并且添加了相当多的特殊运算符(如 +=、-- 和 ?:)以进行手动优化。

设计者没有想到添加将大型数组复制为参数的功能。反正也没啥用。

在 C 的前身 B 语言中,数组被表示为指向单独分配的存储的指针(请参阅the link在拉斯的回答中)。 Ritchie 希望在 C 语言中避免这个额外的指针,因此想到在不需要数组的地方使用时可以将数组名称转换为指针:

它消除了存储中指针的具体化,而是在表达式中提到数组名称时导致了指针的创建。在今天的 C 中仍然存在的规则是,当数组类型的值出现在表达式中时,它们会被转换为指向构成数组的第一个对象的指针。

尽管语言语义发生了潜在的变化,但这项发明使大多数现有的 B 代码能够继续工作。

And structs 直到后来才被添加到该语言中。你可以通过结构体中的数组作为参数当时的一项功能提供了另一种选择。

改变数组的语法已经太晚了。它会破坏太多程序。已经有100多个用户了...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 对数组的厌恶[关闭] 的相关文章

随机推荐

  • 当应用程序在后台运行时 Android 传感器正在监听

    即使应用程序不在前台 是否可以连续跟踪 Android 上的运动传感器事件 如果是的话 电池的消耗是什么 一位客户询问是否可以编写一个应用程序 在人 跌倒 时启动一个动作 这基本上意味着不断监听运动传感器的快速移动 首先 您绝对可以在后台监
  • 无法在清单资源中找到该报告

    我将所有水晶报表保存在一个文件夹中 VOUCHERS gt SALE gt BILLFORMATS 例如 第一个报告的位置是 VOUCHERS gt SALE gt BILLFORMATS gt BillFormat1 rpt Vegi M
  • 如何绘制按日期聚合的 pandas 数据框

    我有这个数据框 df pd DataFrame 2017 01 14 1 2017 01 14 30 2017 01 16 216 2017 02 17 23 2017 02 17 2 2017 03 19 745 2017 03 19 3
  • 下面的掩码输入问题如何解决?

    我有一个脚本来屏蔽文本框 在这里它 我还有一个脚本可以在单击按钮时动态添加文本框
  • 将国家/地区名称转换为国家/地区代码缩写 php [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 有没有办法将国家名称转换为国家代码缩写 或者一些php函数 这样当 澳大利亚 时
  • 当我尝试在驱动器中搜索时,程序抛出 NullPointerException?

    我编写了一个程序来查找文件或目录 当我尝试使用 in 搜索文件时 它工作正常目录 example java FileSearch abc txt f xyz但是当我尝试从中搜索文件时本地驱动器比程序抛出异常 java FileSearch
  • C# 迭代枚举? (索引 System.Array)

    我有以下代码 Obtain the string names of all the elements within myEnum String names Enum GetNames typeof myEnum Obtain the val
  • Swift - 是什么决定了字典集合的顺序?

    当我说顺序时 我的意思是编译器选择显示结果的顺序 我知道字典没有像数组那样的索引 我有以下字典 let groups Dictionary
  • IDisposable GC.SuppressFinalize(this) 位置

    我的代码使用默认的 IDisposable 实现模板 模式 snippet public void Dispose Dispose true GC SuppressFinalize this protected virtual void D
  • 在 OS X 上安装 GCC 4.7.1

    我正在尝试安装GCC4 7 1 在我的 Mac 上 因为我想更新LLVM GCC4 2 XCode 中给出 我已经下载了GCC4 7 1 我已经放置了gcc 4 7 1文件夹中 Downloads 然后我按照此处给出的说明进行操作 http
  • 如何尽快实现strlen

    假设您使用的是 x86 32 位系统 您的任务是尽快实现 strlen 有两个问题你需要注意 1 地址对齐 2 以机器字长 4 个字节 读取内存 找到给定字符串中的第一个对齐地址并不难 然后我们可以用这4个字节读取一次内存 并计算总长度 但
  • 这是什么? (函数(){})()[重复]

    这个问题在这里已经有答案了 可能的重复 这个 JavaScript 片段是什么意思 自动执行匿名 JavaScript 函数的括号位置 function something here lt This part right here 这到底是
  • 为什么使用静态块而不是直接初始化实例变量?

    为什么我要使用静态块 static B 10 over Integer B 10 一种相对于另一种的优点 缺点是什么 The static块允许您为属性编写更复杂的初始化逻辑 而单行初始化将您限制为单个表达式 请注意 实例属性和静态属性都存
  • 中断线程自身

    我不明白为什么线程不抛出InterruptedException当自己被打断时 我正在尝试使用以下代码片段 公共类中断测试 public static void main String args MyThread t new MyThrea
  • 如何在 django 视图中使用 python 多处理模块

    我有一个简单的函数来遍历 URL 列表 使用GET检索一些信息并更新数据库 PostgresSQL 因此 该功能运行完美 然而 一次一个地浏览每个 URL 会花费太多时间 使用 python 我可以执行以下操作来并行这些任务 from mu
  • 使用Django Rest框架序列化自定义数据类型并返回响应

    大多数关于 Django Rest Framework 的教程都解释了如何使用 Django 模型并执行 CRUD 操作 这是一个GET如果我使用 JSON 序列化程序 对用户模型的请求将以 JSON 格式返回用户对象的属性 我正在设计 D
  • 使用 HTML 属性作为 CSS 属性值 [重复]

    这个问题在这里已经有答案了 规范说 attr 函数返回元素上的属性值 用作属性中的值 如果在伪元素上使用 它将返回伪元素的原始元素上的属性值 http www w3 org TR css3 values attr 然而 这似乎不起作用 当我
  • jQuery - 表单不会使用 jQuery 提交

    进一步解决我的问题 jQuery 提交验证 最后有模式对话框 看起来表单根本不会使用 jQuery 提交 这是我的非常简化的代码
  • EFCore 对于简单的 LEFT OUTER 连接返回太多列

    我目前正在将 EFCore 1 1 预览版 与 SQL Server 结合使用 我正在做我认为是一个简单的 OUTER JOIN 之间的事情Order and OrderItem table var orders from order in
  • C 对数组的厌恶[关闭]

    Closed 这个问题需要细节或清晰度 目前不接受答案 在 C 的入门书籍中 经常声称指针或多或少are数组 这充其量不是一个巨大的简化吗 There isC 中的数组类型 它的行为与指针完全不同 例如 include