關鍵字: c語言,資料類型,32位,64位
1、概述
C語言有一些非常基本的資料類型,正是這些基本類型讓我們可以延伸了無限的使用者自訂類型,本文主要介紹了 int, size_t, time_t, long, long long int 等基礎資料型別 (Elementary Data Type)在Linux32 及 Linux64 的使用方式。表面看上去,這些類型確實太基礎太簡單,似乎沒啥可講的,實事似乎也是如此,用過C的對這些都已經非常熟悉了,這還用講?在PC 64位機器 出來之前,我們確實不用太關注這些,因為在32位機上編程,似乎很少出現過什麼問題,但64位機出來了,象Linux 也支援64位機器,問題就來了,為什嗎?因為它們的長度發生了變化,而我們的程式也就有可能需要改變一下。
2、舉例
先舉個例子,如下:
C代碼
- #include <stdio.h>
- #include <stdlib.h>
-
- staticvoid get_length(size_t *size)
- {
- if (size)
- *size = 100;
- }
-
- staticvoid test(void)
- {
- char *buf = strdup("hello world");
- int n;
-
- printf("buf: %s\n", buf);
- get_length((size_t*) &n);
-
- printf("buf: %s, n: %d\n", buf, n);
- free(buf);
- }
-
- int main(int argc, char *argv[])
- {
- test();
- return (0);
- }
#include <stdio.h> #include <stdlib.h> static void get_length(size_t *size) { if (size) *size = 100; } static void test(void) { char *buf = strdup("hello world"); int n; printf("buf: %s\n", buf); get_length((size_t*) &n); printf("buf: %s, n: %d\n", buf, n); free(buf); } int main(int argc, char *argv[]) { test(); return (0); }
首先將此程式在32位機的 Linux 上運行一下,如下:
buf: hello world
buf: hello world, n: 100
OK,如我們所料,一切正常。
然後再將些程式在64位機的 Linux 上運行一下,如下:
buf: hello world
buf: (null), n: 100
奇怪的現象出來了,怎麼printf出的結果為空白呢?暈菜,為啥經過 get_length()/1 後世界改變了,buf 的內容沒有了,被指向一個null 指標,而 buf 明明是還沒有被釋放呀。趕快用 valgrind 檢查一下,
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./a.out
“2 bytes in 1 blocks are definitely lost in loss record 1 of 1”,說有塊記憶體未被釋放,而在 test() 後面確實釋放過 buf 呀,誰偷偷地給釋放了而沒有告訴俺?更暈菜,難道是 libc 的問題?再用 valgrind 在32位機檢查一下,一切OK,沒有出現64位機上的錯誤提示,說明記憶體確實由 test() 中的 free(buf) 釋放了。
正當對此問題百思不解時,忽然想到一個問題 int * 至 size_t* 類型轉換會不會有問題?因為 size_t 在32位機上是4位元組,而在64位機上是8位元組,int在32位及64位機上都是4位元組,嗯,問題就在於此,再回頭仔細看看上述代碼,在 test() 中將 &n 由 int * 強制轉換成 size_t *, 這樣可以避免編譯警告,但在 get_length()/1 中呢?它是不會知道 size_t *size 中 size 所指空間是4位元組的,而依然當8位元組對待,這樣在對 *size = 100 進行賦值時就發生了改變,size 所指的8位元組空間發生改變,而實際應該只改變4位元組才是,這便是問題的關鍵所在,所以在遇到此類問題時,一定得要注意基本類型在不同機器上的空間大小了。
3、小結
以上的例子只是一個簡單的例子,也許還容易看得出來,當我們的項目比較大時,這種錯誤可能會偶爾發生一下,那可能就是致命的了,因為有時它並不 會導致程式 異常退出產生core檔案,但卻會改變我們的運行結果,本人就因此問題調試了兩天多的時間才找到原因,另外,即使因此問題產生了 core 檔案,你會發現用 gdb 調試該 core 時根本找不到原因所在。
下面列出一些基本類型在32位及64位機上的大小差異
| |
int |
long |
size_t |
time_t |
long long int |
| 32位機器 |
4位元組 |
4位元組 |
4位元組 |
4位元組 |
8位元組 |
| 64位機器 |
4位元組 |
8位元組 |
8位元組 |
8位元組 |
8位元組 |
在寫跨平台的程式時,一定要注意這些基本類型的長度大小。
---------------------------------
一、程式運行平台
不同的平台上對不同資料類型分配的位元組數是不同的。
個人對平台的理解是CPU+OS+Compiler,是因為:
1、64位機器也可以裝32位系統(x64裝XP);
2、32位機器上可以有16/32位的編譯器(XP上有tc是16位的,其他常見的是32位的);
3、即使是32位的編譯器也可以弄出64位的integer來(int64)。
以上這些是基於常見的wintel平台,加上我們可能很少機會接觸的其它平台(其它的CPU和OS),所以個人認為所謂平台的概念是三者的組合。
雖然三者的長度可以不一樣,但顯然相互配合(即長度相等,32位的CPU+32位的OS+32位的Compiler)發揮的能量最大。
理論上來講 我覺得資料類型的位元組數應該是由CPU決定的,但是實際上主要由編譯器決定(佔多少位由編譯器在編譯期間說了算)。
二、常用資料類型對應位元組數
可用如sizeof(char),sizeof(char*)等得出
32位編譯器:
char :1個位元組
char*(即指標變數): 4個位元組(32位的定址空間是2^32, 即32個bit,也就是4個位元組。同理64位編譯器)
short int : 2個位元組
int: 4個位元組
unsigned int : 4個位元組
float: 4個位元組
double: 8個位元組
long: 4個位元組
long long: 8個位元組
unsigned long: 4個位元組
64位編譯器:
char :1個位元組
char*(即指標變數): 8個位元組
short int : 2個位元組
int: 4個位元組
unsigned int : 4個位元組
float: 4個位元組
double: 8個位元組
long: 8個位元組
long long: 8個位元組
unsigned long: 8個位元組