《编写高质量代码:改善c程序代码的125个建议》——建议2:防止整数类型产生回绕与溢出

简介:

本节书摘来自华章计算机《编写高质量代码:改善c程序代码的125个建议》一书中的第1章,建议2,作者:马 伟 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

建议2:防止整数类型产生回绕与溢出

到C99为止,C语言为我们提供了12个相关的数据类型关键字来表达各种数据类型。如表1-2所示,K&R C提供了7个,C89/C90新增了2个,C99新增了3个。


<a href=https://yqfile.alicdn.com/2c575593fb8fbf685cadc4bce887b4fa480c6a62.png" >

整型是C语言最基本的数据类型,它以二进制编码的方式进行存储,具体可以包括字符、短整型、整型和长整型等。例如,整数2的二进制表示为10,它在8位与32位的操作系统中存储方式如图1-3所示。


<a href=https://yqfile.alicdn.com/2fcca96a5d805b2b0d815980e34c637ab10ef7f2.png" >

虽然在计算机中整数是以二进制编码方式进行存储的,但为了便于表达,有时候又会用十六进制编码方式表示(例如,在32位操作系统下,整数2的十六进制编码方式为0x00000002),二进制和十六进制之间能够很方便地进行转换。
与此同时,整数类型又可分为有符号(signed)和无符号(unsigned)两种类型,limits.h文件定义了整型数据类型的表达值范围。在GCC 4.8.3中,limits.h文件定义如下:
/*
 *    ISO C99 Standard: 7.10/5.2.4.2.1 Sizes of integer types <limits.h>
 */

#ifndef _LIBC_LIMITS_H_
#define _LIBC_LIMITS_H_    1

#include <features.h>


/* Maximum length of any multibyte character in any locale.
   We define this value here since the gcc header does not define
   the correct value.  */
#define MB_LEN_MAX 16


/* If we are not using GNU CC we have to define all the symbols ourself.
   Otherwise use gcc's definitions (see below).  */
#if !defined __GNUC__ || __GNUC__ < 2

/* We only protect from multiple inclusion here, because all the other 
   #include's protect themselves, and in GCC 2 we may #include_next through 
   multiple copies of this file before we get to GCC's.  */
# ifndef _LIMITS_H
#  define _LIMITS_H 1

#include <bits/wordsize.h>

/* We don't have #include_next.
   Define ANSI <limits.h> for standard 32-bit words.  */

/* These assume 8-bit `char's, 16-bit `short int's,
   and 32-bit `int's and `long int's.  */

/* Number of bits in a `char'.    */
#  define CHAR_BIT 8

/* Minimum and maximum values a `signed char' can hold.  */
#  define SCHAR_MIN    (-128)
#  define SCHAR_MAX    127

/* Maximum value an `unsigned char' can hold.  (Minimum is 0.)  */
#  define UCHAR_MAX    255

/* Minimum and maximum values a `char' can hold.  */
#  ifdef __CHAR_UNSIGNED__
#   define CHAR_MIN 0
#   define CHAR_MAX    UCHAR_MAX
#  else
#   define CHAR_MIN    SCHAR_MIN
#   define CHAR_MAX    SCHAR_MAX
#  endif

/* Minimum and maximum values a `signed short int' can hold.  */
#  define SHRT_MIN    (-32768)
#  define SHRT_MAX    32767

/* Maximum value an `unsigned short int' can hold.  (Minimum is 0.)  */
#  define USHRT_MAX    65535

/* Minimum and maximum values a `signed int' can hold.  */
#  define INT_MIN    (-INT_MAX - 1)
#  define INT_MAX    2147483647

/* Maximum value an `unsigned int' can hold.  (Minimum is 0.)  */
#  define UINT_MAX    4294967295U

/* Minimum and maximum values a `signed long int' can hold.  */
#  if __WORDSIZE == 64
#   define LONG_MAX    9223372036854775807L
#  else
#   define LONG_MAX    2147483647L
#  endif
#  define LONG_MIN    (-LONG_MAX - 1L)

/* Maximum value an `unsigned long int' can hold.  (Minimum is 0.)  */
#  if __WORDSIZE == 64
#   define ULONG_MAX    18446744073709551615UL
#  else
#   define ULONG_MAX    4294967295UL
#  endif

#  ifdef __USE_ISOC99

/* Minimum and maximum values a `signed long long int' can hold.  */
#   define LLONG_MAX    9223372036854775807LL
#   define LLONG_MIN    (-LLONG_MAX - 1LL)

/* Maximum value an `unsigned long long int' can hold.  (Minimum is 0.)  */
#   define ULLONG_MAX    18446744073709551615ULL

#  endif /* ISO C99 */

# endif    /* limits.h  */
#endif    /* GCC 2.  */

#endif    /* !_LIBC_LIMITS_H_ */

/* The <limits.h> files in some gcc versions don't define LLONG_MIN,LLONG_MAX,
   and ULLONG_MAX.  Instead only the values gcc defined for ages are   available. */
#if defined __USE_ISOC99 && defined __GNUC__
# ifndef LLONG_MIN
#  define LLONG_MIN    (-LLONG_MAX-1)
# endif
# ifndef LLONG_MAX
#  define LLONG_MAX    __LONG_LONG_MAX__
# endif
# ifndef ULLONG_MAX
#  define ULLONG_MAX    (LLONG_MAX * 2ULL + 1)
# endif
#endif


<a href=https://yqfile.alicdn.com/42f82c32e528e68de88131263ce8751e6662f54a.png" >

简单地讲,有符号和无符号整数间的区别在于怎样解释整数的最高位。如果定义一个有符号整数,则C编译程序生成的代码认为该数最高位是符号标志:符号标志为0,则该数为正;符号标志为1,则该数为负。
负数采用2的补码的形式来表示,即对原码各位求反(符号位除外),再将求反的结果加1,最后将符号位设置为1。例如,在32位操作系统中,有符号整数-2的存储方法如下。
第一步:取绝对值2的二进制编码。
00000000 00000000 00000000 00000010

第二步:求反(符号位除外)。

01111111 11111111 11111111 11111101

第三步:将求反的结果加1。

01111111 11111111 11111111 11111110

第四步:将符号位设置为1。

11111111 11111111 11111111 11111110

因此,有符号整数-2的二进制编码为11111111 11111111 11111111 11111110,十六进制编码为0xFFFFFFFE。
最后还需要说明的是,当类型修饰符被自身使用时(即它不在基本类型之前时),假定其为int型。也就是说,表1-4的两种类型是等效的。


77a8c9ace3cd575b25fb13b395b8426d0e80b2af
相关文章