可以说,C语言是世界上最成功的编程语言。当然,它的成功 无休止地诱惑人们改进它。因此,C很可能是 最长的语言列表。其中值得注意的是C++,D编程语言, 最近,去吧。关于如何修复C语言的讨论帖子层出不穷, 回到80年代。
所以这是一片平坦的土地。这汤里还能加点什么呢?我假设 大多数这样的讨论都是围绕细节展开的。更有趣的是 最大的根本性错误。我们应该考虑到时代的脉络。 这就产生了C语言,以及它试图解决的问题和环境 它原本打算在其中使用的地方。请记住,它是为16架飞机开发的 比特机,可用资源极其有限。 我想把它并不是这样的事情放在一边 执行垃圾收集、函数式编程、动态类型或OOP。这些不是问题C 试图解决,所以缺少它们并不是错误。
什么错误导致了更多的悲伤,更多的错误,更多的变通方法,更多无穷无尽的时间 消耗,等等,比其他任何东西都要多吗?很多人会说 空指针。 我不这样认为。
我的意思不是使用相同的语法,也不是隐式转换 指向指针的数组。我的意思是无法将数组传递给函数 作为数组,即使声明为数组也是如此。C将静默转换为 将数组转换为指针,并将重写函数声明 因此,它在语义上是一个指针:
这种看似无伤大雅的便利功能,其实是无穷无尽之恶的根源。 这意味着一旦数组离开定义它们的作用域, 它们变成指针,并丢失给出范围的信息 数组的-数组维度。输掉比赛的后果是什么? 这个信息?
必须使用替代方案。对于字符串,这是0终止符的全部原因。 为 其他数组,则从上下文以编程方式推断。当然, 每种情况都是不同的,因此无穷无尽的阵列(!)。大量的虫子接踵而至。
C字符串函数的星系,从不安全的strcpy()到sprintf(), 是一个直接的结果。有各种尝试来修复此问题,例如 安全C库。 然后就是所有的缓冲区溢出,因为函数 传递的指针没有 知道限制是什么,并且不可能进行数组边界检查。
这个问题被C++完全继承了下来,从而催生了10多年的时间 尝试创建可用的字符串类。即使最终的std::string结果也是如此 由于需要与以C0结尾的字符串兼容而受到损害。 C++通过发明std::Vector和 许多编程准则避免使用T[]样式的数组。但它留下的遗产 C数组在C++中继续使用 不安全的迭代器设计。
C99试图解决此问题,但它所犯的致命错误仍然存在 未将数组维度和数组指针组合为一种类型。
但并不是一切都失去了。C仍然可以修复。它所需要的只是一个小小的新语法:
这意味着数组作为所谓的“胖指针”传递,即由 指向数组开头的指针,以及数组维度的size_t。 当然,这不会修复任何现有代码,但可以编写新代码 正确而有力地。随着时间的推移,语法:
根据约定和编译器可以弃用。更好的是,过渡到 可以通过使声明与旧代码兼容二进制来实现新的方法:
#如果是NEWC 外部void foo(char a[..]); #Elif C99 外部void foo(size_t dim,char a[dim]); #其他 外部void foo(size_t dim,char*a); #endif。
这一改变不会把C语言转变成一种现代语言,因为它具有所有闪亮的 钟声和口哨声。无论是在精神上还是在实践中,它仍然是C级的。会的 只需将C程序员从处理一个特定的、有害的常量源中解脱出来 虫子。
K+R的The C Programming Language 5.3中的相关文本是 “将数组名称传递给函数时,传递的是 数组开头的位置。在被调用的函数内, 此参数是一个变量,就像任何其他变量一样,因此 数组名称参数实际上是一个指针,即包含 一个地址。“。
C99标准6.7.5.3.7的相关文本为 “将参数声明为‘类型数组’ 应调整为“指向类型的限定指针” “
摘自Stroupstrup的C++编程语言第一版7.1 像往常一样,数组名称将转换为指针。