《C编程.C以后》1.C的特殊性

C是一种高效,极简主义的语言,具有程序员必须注意的一些特性。为了解决这些问题,有时一个好的解决方案是将另一种语言与C相结合,以增加灵活性和功能,例如Emacs-LISP和用于Emacs的C的组合。有时,通过使用可保证功能和安全性的特殊结构,可以降低速度和增加复杂性来解决这些问题。然而,大多数情况下,通过实践,C程序员可以毫不费力地使用这里提到的东西,并且更喜欢使用一种紧密模拟通用Von Neumann硬件架构的语言。

以下是ANSI C的这些特性中的几个(有时也是它的优点),一些是次要的和一些主要的:数组和指针之间缺乏区别 第一个C(大约1973年)根本没有阵列; 现代实现是使用指针算法访问的内存中的连续区域(注意:声明的数组不能像指针那样分配),这避免了声明具有固定大小的数组的需要。但是,这种能力可能会导致缓冲区溢出错误。数组不存储它们的长度 上述特征的结果。这意味着程序可能需要在访问数组之前显式执行边界检查。除非函数传递给固定大小的数组,否则它无法发现给定数组的长度:因此函数必须给出长度,可能作为单独的变量传递给函数或者结构体。因此,大多数实现不提供自动数组边界检查,并且手动边界检查容易出错。如果C(或C ++)程序试图访问实际分配的内存之外的数组元素,则会发生缓冲区溢出,通常会导致程序崩溃。缓冲区溢出错误也是一个常见的安全漏洞。许多其他计算机语言提供自动边界检查,因此它们几乎不受这些错误的影响。可变长度数组 VLA – 可变长度数组 – 只能用于函数参数和自动变量。不能在堆[分类需要 ]上分配或在结构内部使用VLA (除了作为结构中的最后一项)。无法定义与标准Forth字典定义(具有2个可变长度部分)相对应的结构,除非作为未分化的数组char。没有广泛支持任意大小的内置2D或3D阵列 从可变长度数组的C99规范开始添加了此功能,尽管许多C编译器仍然不支持它。如果没有VLA,函数就无法接受任意大小的2D或3D数组。特别是,不可能定义一个接受 int a[5][4][3]; 一个调用的函数,然后 int b[10][10][10]; 在稍后的调用中接受。C程序员不使用内置的2D或3D数组数据类型,而是使用其他一些数据类型来保存(数学)任意大小的2D或3D数组(多维数组) – 参见C编程/常规实践#Dynamic multidimensional数组用于细节。没有正式的String数据类型 字符串是字符数组(缺少任何抽象)并继承所有约束(结构可以在一定程度上提供抽象)。弱式安全 C不是非常类型安全的。内存管理功能对非类型化指针进行操作,没有内置的运行时类型强制执行,类型系统可以通过指针和强制转换来规避。此外,typedef不会创建新类型,只会创建别名,因此它仅用于代码清晰度。但是,可以使用单个成员结构来强制类型安全。没有垃圾收集 作为一种设计用于最小开销的低级语言,C仅具有手动内存管理功能,可以允许简单的内存泄漏继续进行。声明时,局部变量未初始化 必须手动初始化本地(但不是全局)变量; 在此之前,它们包含当时记忆中的内容。这并不罕见,但C标准并不禁止访问未初始化的变量(即)。笨拙的函数指针语法 函数指针采用的形式 [return type] [name]([arg1 type])([arg2 type]),使它们有点难以使用。Typedef可以减轻这种繁琐的语法。例如,typedef int fn(int i);。有关详细信息,请参阅C编程/指针和数组#指针到函数。没有反思 C程序(在运行时)不可能将字符串计算为源C语句。嵌套函数不是标准函数但是,许多C编译器都支持嵌套函数,包括GNU C. 没有正式的异常处理 某些标准函数返回必须手动处理的特殊值。例如,malloc()失败时返回null。例如,为了可靠地检测文件结尾,必须将返回值存储getchar()int(不是,如人们所预料的那样char)中 – 请参阅EOF陷阱。不包含适当错误处理的程序在大多数情况下都可以正常工作,但在发生特殊情况时可能会崩溃或出现故障。POSIX系统通常用于signal()处理某些异常。(有关详细信息,请参阅{我在哪里可以阅读有关signal()的更多信息?)某些程序使用setjmp()longjmp()goto手工处理某些类型的异常。(参见C编程/控制#最后一件事:转到和C编程/协同程序的详细信息)。没有匿名函数定义

猜你想读:《C编程.C以后》2.C Trigraph

THE END
分享