设为首页 加入收藏

TOP

C语言进阶指南(1)丨整型溢出和类型提升、内存申请和管理(一)
2019-04-04 16:08:37 】 浏览:337
Tags:语言 进阶 指南 溢出 类型 提升 内存 申请 管理

C语言可用于系统编程、嵌入式系统中,同时也是其他应用程序可能的实现工具之一。 当你对计算机编程怀有强烈兴趣的时候,却对C语言不感冒,这种可能性不大。想全方位地理解C语言是一件极具挑战性的事。

一、整型溢出和类型提升

多数C程序员以为,整型间的基本操作都是安全的。事实上,整型间基本操作也容易出现问题,例如下面的代码:

int main(int argc, char** argv) {

    long i = -1;

 

    if (i < sizeof(i)) {

         printf("OK\n");

    }

    else {

         printf("error\n");

    }

 

    return 0;

}

上述代码中,变量i 被转换为无符号整型。这样一来,它的值不再是-1,而是size_t 的最大值。变量i的类型之所以被转换,是因为sizeof 操作符的返回类型是无符号的。具体参见C99/C11标准之常用算术转换一章:

“If the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.”

若无符号整型类型的操作数的转换优先级不低于另一操作数,则有符号数转为无符号数的类型。

C标准中,size_t 被定义为不低于16位的无符号整型。通常size_t 完全对应于long。这样一来,int 和size_t 的大小至少相等,可基于上述准则,强转为无符号整型。

(译者注:本人印象深刻的相关问题是“if(-1U > 0L)”在32、64位机器上的判断结果分别是什么,为什么;除long long外,long 类型在涉及兼容性的产品代码中应被禁用)

这个故事给了我们一个关于整型大小可移植性的观念。C标准并未定义short、int、long、long long 的确切大小及其无符号形式。标准仅限定了它们的最小长度。以x86_64架构为例,long 在Linux环境中是64比特,但在64位Windows系统中是32比特。为了使代码更具移植性,常见的方法是使用C99的stdint.h 文件中定义的、指定长度的特殊类型,包括uint16_t、int32_t 等。此文件定义了三种整型类型:

有确切长度的:uint8_t uint16_t,int32_t等

有长度最小值的最短类型:uint_least8_t,uint_least16_t,int_least32_t等

执行效率最高的有长度最小值的类型:uint_fast8_t,uint_fast16_t,int_fast32_t等

但不幸的是,仅依靠stdint.h 并不能根除类型转换的困扰。C标准中“整型提升规则”中写道:

若int的表达范围足以覆盖所有的基础类型,此值将被转换为int;否则将转为unsigned int。这就叫做整型提升。整型提升过程中,所有其他的类型保持不变。

下述代码在32位平台中将返回65536,在16位平台上返回0:

uint32_t sum()

{

    uint16_t a = 65535;

    uint16_t b = 1;

    return a+b;

}

无论C语言实现中,是否把未修饰的char看做有符号的,整型提升都连同符号一起把值保留下来。

如何实现char类型通常取决于硬件体系或操作系统,常由其平台的ABI(应用程序二进制接口)指定。如果你愿意自己尝试的话,char会被转为signed char,下述代码将打印出-128和-127,而不是128和129。x86架构中可用GCC的-funsigned-char参数切换到强制无符号提升。

char c = 128;

char d = 129;

printf(&quot;%d,%d\n&quot;,c,d);

二、内存申请和管理

malloc, calloc, realloc, free

使用malloc分配指定字节大小的、未初始化的内存对象。若入参值为0,其行为取决于操作系统实现,或者说,这是C和POSIX标准均未定义的行为。

若请求的空间大小为0,则结果视具体实现而定:返回值可以是空指针或特殊指针。

malloc(0) 通常返回有效的特殊指针。或者返回的值可成为free 函数的参数,且函数不会错误退出。例如free 函数对NULL指针不做任何操作。

因此,若空间大小参数是某个表达式的结果的话,要确保测试过整型溢出的情况。

size_t computed_size;

if (elem_size &amp;&amp; num &gt; SIZE_MAX / elem_size) {

    errno = ENOMEM;

    err(1, &quot;overflow&quot;);

}

computed_size = elem_size*num;

一般说来,要分配一个元素大小相同的序列,可考虑使用calloc 而非用表达式计算大小。同时calloc 将把分配的内存初始化为0。像往常一样使用free 释放分配的内存。

realloc 将改变已分配内存对象的大小。此函数返回一个指针,指针可能指向新的内存起始位置,内存大小取决于入参中请求的空间大小,内容不变。若新的空间更大,额外的空间未被初始化。若realloc 入参中,指向旧对象的指针为NULL,并且大小非0,此行为等价于malloc。若新的大小为0,且提供的指针非空,此时realloc 的行为依赖于操作系统。

多数实现将尝试释放对象内存,返回NULL或与malloc(0)相同的返回值。例如在Windows中,此操作会释放内存并返回NULL。OpenBSD也会释放内存,但返回的指针指向的空间大小为0。

realloc 失败时会返回NULL,也因此断开与旧的内存对象的关联。所以不但要检查空间大小参数是否存在整型溢出,还要正确处理realloc 失败时的对象大小。

#include <stdio.h>

#include <stdint.h>

#include <malloc.h>

#include <errno.h>

 

#define VECTOR_OK            0

#define VECTOR_NULL_ERROR    1

#define VECTOR_SIZE_ERROR    2

#define VECTOR_ALLOC_ERROR   3

 

struct vector {

    int *data;

    size_t size;
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇数据结构——线性表的顺序存储结构 下一篇C语言编程笔记丨位反转的最佳算法

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目