C陷阱和缺陷--第七章 “可移植性缺陷”

2024-02-26 13:36

本文主要是介绍C陷阱和缺陷--第七章 “可移植性缺陷”,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C语言在许多不同的系统平台上都有实现;因此我们应该能够预料到,机器不同则其上的C语言实现也有细微差别;

7.1 应对C语言标准变更

作者写这本书的时候,还是1988年;对于2024年的我们,应该不需要考虑C语言标准变更的问题;对于标准版本变更而言,我们必须在当前需要产生的工作量和未来的收益之间做出选择;

7.2 标识符名称的限制

为了保证程序的可移植性,谨慎的选择外部标识符的名称是重要的;

// 错误示例,使用 含义相同的变量名称
int print_fields;
int print_float;	
// 错误示例,变量名称仅仅知识大小写存在区别
char state;
char STATE;

定义的函数名称和库函数,仅仅只是大小写不一致;
ps: 但是现在的编译器都已经区分大小写了,可能作者那个时候还不支持;

// 错误示例, 如果编译器不区分大小写,则变为递归调用了
char *Malloc(unsigned n)
{char *p,*malloc(unsigned);p = malloc(n);if (p == NULL)panic("out of memory);return p;
}

7.3 整数的大小

C语言为编程者提供了 3种不同长度的整数:short, int, long 型;但是在不同的硬件设备上(8位,13位,32位,64位),同一个类型对应的数据长度,可能是不一样的;
C语言的定义中,对各种不同类型整数的相对长度作了一些规定
1: 3种类型的整数其长度是非递减的;也就是说,short 型整数容纳的值肯定能够被 int型整数容纳,int型整数容纳的值也肯定能够被 long型整数容纳;
2:一个普通int型整数足够大,可以容纳任何数组的下标;
3:字符长度由硬件特性决定;
ANSI标准要求long型整数的长度至少应该是32位,而short和int型整数的长度至少应该是16位;
为了适配不同设备上的数据长度,我们最好使用的使用重新定义一个参数类型

// 当我们在不同的设备上的时候,只需要修改 Int16 的定义就行
#define short Int16
#define unsigned short UInt16// 改动上面的#define 之后,这里的长度就会自动适配了
Int16 a;
UInt16 b;

7.4 字符是有符号整数还是无符号整数

有符号类型的最高bit位为1,当我们把 一个字符值转换为较大的整数时,可能会发现数据的符号位发生非预期的变化;

void func5()
{char a=10;char b=20;char c = 10*20;						// 溢出了printf("c= %d \n",c );      // c= -56 
}

当数据类型发生变化时候,也应该注意符号位是否发生变化;

void func6()
{char a=-10;int  b=0;memcpy(&b,&a,1);								 // b的符号位没有发生变化printf("a=%d b=%d \n",a, b );      // a=-10 b=246
}

7.5 移位运算符

1:向右移位时,空出的位是由 0 填充,还是由符号位的副本填充呢?
如果操作对象是无符号数,空出的位被0填充;如果操作对象是有符号数,那么不同的编译器可能填充的结果是不一致的;尽量不要对有符号数,进行移位运算;
2:移位计数(即移位操作的位数)允许的取值范围是什么;
根据不对称边界计算,

int a=9;				// 认为 int 型长度是32位
if ((n>=0) && (n<31))a = a>>n;

以除法运算来代替移位运算,将可能导致程序运行速度大大减慢;

unsigned int low;
unsigned int high;
// something
mid = (low+high) >> 1;		//执行速度更快 
// 等价于
mid = (low+high) / 2;

7.6 内存位置0

null指针并不指向任何对象。因此,除非是用于赋值或比较运算,出于其它任何目的使用null指针都是非法的;
在所有的C程序中,误用 null 指针的效果都是未定义的,可能会造成程序崩溃问题;

#include <stdio.h>
main()
{char *p;p = NULL;// 在禁止读取内存地址0的机器上,这个程序将会执行失败。在可以读取的机器上,会打印内存位置0中存储的字符内容printf("location 0 contains %d \n", *p);
}

7.7 除法运算时发生的截断

// TODO 没看明白这里想说的重点是什么

7.8 随机数的大小

在不同的系统上,rand函数中随机数的取值范围是不一致的;
在16位系统上,rand 函数将返回一个介于 0 ~ ( 2 15 2^{15} 215-1) 之间的整数
在32位系统上,rand 函数将返回一个介于 0 ~ ( 2 31 2^{31} 231-1) 之间的整数
为了处理这个问题,ANSI C标准中定义了一个常数 RAND_MAX, 它的值等于岁数的最大取值;

7.9 大小写转换

库函数 toupper和tolower 也有与随机数类似的历史;
不同的库函数,可能具体的实现方式不一致;使用者需要特别注意

// 函数会判断传进来的参数是否是有效的
int toupper(int c)
{if (c >= 'a' && c <= 'z')return c+'A'-'a';return c;
}
// 宏定义需要用户自己判断参数,不然可能出错
#define _toupper(c) ((c)+'A'-'a')

7.10 首先释放,然后重新分配

大多数C语言实现都为使用者提供了三个内存分配函数: malloc, realloc, free。
不同版本的C语言,可能会realloc的使用要求不一致;有的需要先释放,在realloc,使用时需要特别注意;

// 申请一块n大小的内存空间
int *p = malloc(n);	
// 重新申请一块 m 大小的内存空间,可能涉及到内存的拷贝; min(n,m)部分存储的内容将保持不变
int *rea_p = realloc(p,m);					
if (p != NULL)free(p);						// 将申请的内容空间释放掉
if (rea_p  != NULL)free(rea_p );

在某些版本的代码中,下面的代码是合法的

free(p);
p = realloc(p, new_size);

那么在如上系统中,我们可以使用下面的方法来释放 一个链表中的所有元素; 同时不必担心调用free之后,会是 p->next 变的无效;不过这种技巧不值得推荐,因为并非所有的C实现在某块内存被释放后还能较长时间的保留之;

for (p = head; p != NULL; p = p->next)free(p);

这篇关于C陷阱和缺陷--第七章 “可移植性缺陷”的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/749046

相关文章

C++学习笔记----6、内存管理(四)---- 通常的内存陷阱(2)

3、Windows环境下使用Visual C++发现并修复内存渗露         内存渗露很难跟踪是因为你无法很容易地看着内存并且看到什么对象处于使用中,一开始在哪儿分配的内存。然而,是有程序可以为你做到这一点的。内存渗露检测工具有昂贵的专业软件包,也有免费下载的工具。如果你是在Microsoft Visual C++环境下工作,它的排错工具库有内建的对于内存渗露检测的支持。该内存检测默认没有

Java基础入门 【第七章 抽象、接口、内部类、枚举】(二)

匿名内部类书写格式: 父类或接口类型变量名=new父类或接口(构造方法实参列表){ //重写所有的抽象方法 @Override public返回值类型method1(形参列表){ 方法体实现 } @Override public返回值类型method2(形参列表){ 方法体实现 } //省略... }; //匿名内部类对象调用方法 变量名.重写方法(实参列表); 匿名

用异或交换两个整数的陷阱

前面我们谈到了,可用通过异或运算交换两个数,而不需要任何的中间变量。 如下面: void exchange(int &a, int &b) {     a ^= b;     b ^= a;     a ^= b; } 然而,这里面却存在着一个非常隐蔽的陷阱。 通常我们在对数组进行操作的时候,会交换数组中的两个元素,如exchang(&a[i], &b[j]),

C++学习笔记----6、内存管理(四)---- 通常的内存陷阱(1)

使用new/delete/new[]/delete[]处理动态内存以及底层内存操作是非常容易出错的。对于引起内存有关的问题还特别难以定位。每一个内存泄露与错误指针都有其细微差别。没有能够解决内存问题的银弹。我们就来谈一谈一些通常问题以及能够检测和解决的一些工具。 1、少分配了数据空间与越界内存访问         对于C风格的字符串来讲少分配了数据空间是一个常见的问题,当程序员

OpenCV结构分析与形状描述符(9)检测轮廓相对于其凸包的凹陷缺陷函数convexityDefects()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 查找一个轮廓的凸性缺陷。 下图显示了一个手部轮廓的凸性缺陷: convexityDefects 是 OpenCV 库中的一个函数,用于检测轮廓相对于其凸包的凹陷缺陷。这个函数可以帮助识别轮廓中的凹进去的部分,通常被用来分析手部或其他物体的形状

YOLO缺陷检测学习笔记(3)

文章目录 PCA主成分分析PCA的核心思想PCA的步骤PCA降维后的数据表达示例数据Step 1:标准化数据 Step 2:计算协方差矩阵Step 3:计算协方差矩阵的特征值和特征向量Step 4:选择主成分Step 5:将数据映射到主成分总结PCA的应用PCA的局限性 AUC和ROC1. ROC 曲线2. AUC 随机森林1. **随机森林的基本概念**2. **随机森林的优点**3. *

基于yolov8的包装盒纸板破损缺陷测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的包装盒纸板破损缺陷检测系统是一种高效、智能的解决方案,旨在提高生产线上包装盒纸板的质量检测效率与准确性。该系统利用YOLOv8这一前沿的深度学习模型,通过其强大的目标检测能力,能够实时识别并标记出包装盒纸板上的各种破损缺陷,如划痕、撕裂、孔洞等。 在系统中,首先需对包含破损缺陷的包装盒纸板图像进行数据采集和标注,形成训练数据集。随后,利用这些数据进行模型训练,使

基于yolov8的NEU-DET钢材缺陷检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的NEU-DET钢材缺陷检测系统是一种创新的解决方案,旨在通过深度学习技术实现对钢材表面缺陷的自动检测和识别。该系统利用YOLOv8算法,该算法以其高效、准确和实时检测的特点著称。 NEU-DET数据集为该系统提供了丰富的训练资源,涵盖了热轧带钢的六种典型表面缺陷,包括轧制氧化皮、斑块、开裂、点蚀表面、内含物和划痕,每种缺陷均有大量样本,确保了模型的全面性和准确性

C++字符串操作中的陷阱

休对故人思故国,且将新火试新茶。诗酒趁年华。                                                                             ——《望江南·超然台作》【宋】苏轼 目录 正文: 首先我们要明白出现问题的原因: 1. 缓冲区溢出 2. 错误的字符串声明方式 3. 缺乏对NULL指针的检查  解决方案: 下期预

第七章 软件编码

第七章  软件编码 编码阶段的任务:将详细设计的阶段的过程描述转换成用程序设计语言来实现的源程序。 程序语言的特性: 1.心理特性 1)二义性 2)简洁性 3)局部性和顺序性 4)传统性 2.工程特性 1)源代码的可移植性 2)配套的开发工具 3)可维护性 4)可重用性 将设计变换为源程序的便利程度以及编译器的有效性