1. 程式人生 > >深入理解C語言-----各資料型別大小

深入理解C語言-----各資料型別大小

首先看下C標準中“未明確定義”的三種類型Implementation-defined、Unspecified和Undefined。

Implementation-defined 的情況,是C 標準沒有明確規定,但是要求編譯器必須對此做出明確規定,並寫在編譯器的文件中。

Unspecified的情況,往往有幾種可選的處理方式,C 標準沒有明確規定按哪種方式處理,編譯器可以自己決定,並且也不必寫在編譯器的文件中,這樣即使用同一個編譯器的不同版本來編譯也可能得到不同的結果,因為編譯器沒有在文件中明確寫它會怎麼處理,那麼不同版本的編譯器就可以選擇不同的處理方式,比如一個函式呼叫的各個實參表示式按什麼順序求值是Unspecified的。

Undefined的情況則是完全不確定的,C 標準沒規定怎麼處理,編譯器很可能也沒規定,甚至也沒做出錯處理,有很多Undefined的情況是編譯器是檢查不出來的,最終會導致執行時錯誤,比如陣列訪問越界就是Undefined的。

除了char型在C 標準中明確規定佔一個位元組之外,其它整數型別佔幾個位元組都是Implementation Defined。通常的編譯器實現遵守ILP32 或LP64規範。

LP32 這個縮寫的意思是int (I )、long(L )和指標(P )型別都佔32位,通常32位計算機的C 編譯器採用這種規範,x86 平臺的gcc 也是如此。LP64是指long(L )和指標佔64位,通常64位計算機的C 編譯器採用這種規範。指標型別的長度總是和計算機的位數一致。


(PS下,long long 型別對應的佔位符和平臺和編譯器有關,linux中gcc很統一用%lld,在windows中,MinGW的gcc和VC6都需要用到%I64d,但VS2008卻是%lld)

在limit.h檔案中有比較詳細的定義

/* Copyright (C) 1991, 1992, 1996, 1997, 1998, 1999, 2000, 2005
   Free Software Foundation, Inc.
   This file is part of the GNU C Library.


   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.


   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.


   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */


/*
 *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_ */

 /* Get the compiler's limits.h, which defines almost all the ISO constants.

    We put this #include_next outside the double inclusion check because
    it should be possible to include this file more than once and still get
    the definitions from gcc's header.  */
#if defined __GNUC__ && !defined _GCC_LIMITS_H_
/* `_GCC_LIMITS_H_' is what GCC's file defines.  */
# include_next <limits.h>
#endif

/* 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

#ifdef	__USE_POSIX
/* POSIX adds things to <limits.h>.  */
# include <bits/posix1_lim.h>
#endif

#ifdef	__USE_POSIX2
# include <bits/posix2_lim.h>
#endif

#ifdef	__USE_XOPEN
# include <bits/xopen_lim.h>
#endif


注意一點:對於不帶signed 或unsigned 關鍵字的char型,C 標準規定這是Implementation Defined ,編譯器可以定義char型是無符號的,也可以定義char型是有符號的,在該編譯器所對應的體系結構上哪種實現效率高就可以採用哪種實現,x86 平臺的gcc 定義char是有符號的。這也是C 標準的Rationale之一:優先考慮效率,而可移植性尚在其次。另外,除了char型以外的整數型別(short,int,long,long long)如果不明確寫signed 或unsigned 關鍵字都表示有符號數,這一點是C 標準明確規定的。

有符號數在計算機中的表示形式是Sign and Magnitude 、1's Complement還是2's Complement?C 標準也沒有明確規定,也是Implementation Defined 。大多數體系結構都採用2's Complement表示形式和加減運算規則,x86 平臺也是如此。

注意,ASCII碼的取值範圍是0~127 ,所以不管char型是有符號的還是無符號的,存一個ASCII碼都沒有問題,一般來說,如果用char型存ASCII碼字元,就不必明確寫signed 還是unsigned ,如果把char型當作8 位的整數來用,為了可移植性就必須寫明是signed 還是unsigned 。

如果不是為了效率,一般來說就沒有理由故意寫不可移植的程式碼。比如Linux核心程式碼使用了很多gcc 特性以得到最佳的執行效率,在寫的時候就沒打算用別的編譯器編譯,也就沒考慮可移植性的問題。可見C 語言與平臺和編譯器是密不可分的,離開了具體的平臺和編譯器討論C 語言必然難以深入

C 標準規定的浮點型有float、double 、long double ,和整數型別一樣,既沒有規定每種型別佔多少位元組,也沒有規定採用哪種表示形式。浮點數的實現在各種平臺上差異很大,x86 處理器通常是有浮點運算單元的,遵循IEEE 754,float型通常是32位,double 型通常是64位。要詳細瞭解浮點數的IEEE表示可以檢視CSAPP的第二章2.4.2 .具體的定義可以檢視float.h

(文中部分內容摘自宋勁彬老師的文章)