萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 作業系統 >  >> Linux 教學

Linux 32 位與 64 位數據類型長度差異詳解:int/long/指針/off_t/size_t 完全對照表

C/C++ 跨平台開發中,32 位(ILP32)與 64 位(LP64)數據模型差異是移植 Bug 的高發區。本文系統對比基本類型、標準庫類型、POSIX 類型在兩種模型下的長度差異,並提供 stdint.h 固定寬度類型、_FILE_OFFSET_BITS 大文件支援等移植最佳實踐。

一、基本數據類型長度對照表

類型C 標準最小長度32 位 (ILP32)64 位 (LP64)備註
char8 bit8 bit (1B)8 bit (1B)有符號性實現定義
short16 bit16 bit (2B)16 bit (2B)
int16 bit32 bit (4B)32 bit (4B)兩者相同
long32 bit32 bit (4B)64 bit (8B)核心差異點
long long64 bit64 bit (8B)64 bit (8B)C99 標準化
pointer—32 bit (4B)64 bit (8B)地址總線寬度
float—32 bit (4B)32 bit (4B)IEEE 754 single
double—64 bit (8B)64 bit (8B)IEEE 754 double
long double—80/96/128 bit80/128/160 bit實現相關,x86 常 80bit

基本數據類型長度對照表

二、固定寬度類型 <stdint.h> (C99) — 移植首選

類型長度用途
int8_t / uint8_t8 bit精確位寬需求
int16_t / uint16_t16 bit
int32_t / uint32_t32 bit網絡協議、文件格式
int64_t / uint64_t64 bit大數據、時間戳
intptr_t / uintptr_t指針寬度指針與整數轉換
intmax_t / uintmax_t最大寬度通用整數運算

固定寬度類型示意

鐵律: 除非與 ABI 交互(如系統調用、網絡協議),業務代碼一律使用 int32_t/int64_t 等固定寬度類型,禁用 long、long long 作為存儲類型。

三、關鍵 POSIX/系統類型差異

類型32 位64 位說明
size_t32 bit64 bitsizeof 結果類型,跟隨指針寬度
ssize_t32 bit64 bit帶符號 size_t,read/write 返回值
off_t32 bit (默認)64 bit文件偏移量,32 位需 _FILE_OFFSET_BITS=64
ino_t32 bit64 bitinode 編號,跟隨指針寬度
time_t32 bit (Y2038 問題)64 bit現代系統均遷移至 64 bit
pid_t32 bit32 bit進程 ID,通常 32 bit 足夠
uid_t/gid_t32 bit32 bit用戶/組 ID

四、大文件支援(LFS):32 位程序訪問 >4GB 文件

編譯時開啟(推薦)

# 編譯旗標
CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE

# 或在代碼最前面(包含頭文件前)
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

效果

  • off_t 變為 64 位(即 off64_t)
  • struct stat 變為 struct stat64
  • lseek/ftruncate/mmap 等自動映射到 64 位版本
  • fopen 模式字串可用 "rb" 而非 "r"

運行時檢測

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("sizeof(off_t) = %zu\n", sizeof(off_t));
    printf("_FILE_OFFSET_BITS = %d\n", _FILE_OFFSET_BITS);
    return 0;
}

五、指針與整數轉換陷阱

// ❌ 錯誤:64 位下截斷指針高 32 位
long ptr_val = (long)ptr;
void *ptr2 = (void*)ptr_val;

// ✅ 正確:使用 intptr_t/uintptr_t
uintptr_t ptr_val = (uintptr_t)ptr;
void *ptr2 = (void*)ptr_val;

// 打印指針
printf("ptr = %p\n", (void*)ptr);           // 標準方式
printf("ptr = 0x%lx\n", (unsigned long)ptr); // 兼容方式(需匹配長度)

六、結構體對齊與大小差異

struct Example {
    char c;      // 1B
    // 3B/7B padding
    int i;       // 4B
    // 0B/4B padding
    long l;      // 4B/8B
    // 0B/4B padding
    void *p;     // 4B/8B
};

// 32 位:1+3+4+0+4+0+4 = 16B (或 20B 視對齊)
// 64 位:1+7+4+4+8+0+8 = 32B

建議:

  • 按類型大小降序排列成員(大→小)減少填充
  • 使用 #pragma pack 或 __attribute__((packed)) 僅在網絡/存儲協議必要時
  • 序列化協議(Protobuf/FlatBuffers/Cap'n Proto)替代原始結構體傳輸

七、避坑清單

  1. printf("%d", long_var) 64 位下未定義行為 → 用 %ld 或 PRId64
  2. malloc(n * sizeof(int)) 整數溢出 → 用 calloc 或檢查 n < SIZE_MAX/sizeof(int)
  3. 網絡字節序轉換:htonl/ntohl 僅處理 32 位,64 位用 htonll/ntohll (非標準) 或手動移位
  4. 共享庫 ABI 兼容:導出接口使用固定寬度類型,避免 long/size_t

總結: 32/64 位核心差異在 long、pointer、size_t、off_t。現代開發默認 64 位,32 位僅作兼容。代碼中全面採用 stdint.h 固定寬度類型,編譯開啟 -D_FILE_OFFSET_BITS=64,即可消除 90% 移植問題。

copyright © 萬盛學電腦網 all rights reserved