首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言資料編碼全攻略:型態大小、端序、對齊與位元操作實戰

     編輯:關於C語言

前言:資料在機器眼中長什麼樣?

C 語言讓程式設計師直面硬體資料模型。理解型態大小、位元組序、記憶體對齊、位元級編碼,是寫出正確、高效、可移植系統代碼的基石。

一、資料型態大小:可移植性的第一道坎

標準保證與實際情況

型態標準最小寬度32-bit Linux (ILP32)64-bit Linux (LP64)64-bit Windows (LLP64)
char8 bits111
short16 bits222
int16 bits444
long32 bits484
long long64 bits888
float32 bits444
double64 bits888
指標—488

編譯期檢測腳本

printf("char[%zu] short[%zu] int[%zu] long[%zu] ll[%zu] float[%zu] double[%zu] ptr[%zu]\n",
       sizeof(char), sizeof(short), sizeof(int), sizeof(long),
       sizeof(long long), sizeof(float), sizeof(double), sizeof(void*));

鐵律:跨平台代碼永不假設具體大小,改用 <stdint.h> 固定寬度型態 (int32_t, uint64_t 等)。

二、端序:跨平台資料交換的隱形殺手

大端 vs 小端

uint32_t val = 0x12345678;
/* 記憶體位址遞增方向 → */
/* Little-Endian (x86, ARM little-endian) : 78 56 34 12 */
/* Big-Endian   (MIPS, PowerPC, Network)  : 12 34 56 78 */

執行期檢測

bool is_little_endian(void) {
    uint32_t x = 0x12345678;
    return *(uint8_t*)&x == 0x78;
}

網路傳輸必轉換

#include <arpa/inet.h>   // htonl, htons, ntohl, ntohs
uint32_t net_val = htonl(host_val);   // Host TO Network Long
uint16_t net_port = htons(host_port);

檔案格式 (PNG, BMP, ELF) 亦多指定端序,讀寫時務必轉換。

三、記憶體對齊:效能與正確性的雙重考量

為什麼要對齊?

  1. 硬體限制:MIPS、SPARC 等架構要求 4/8-byte 對齊,否則觸發 SIGBUS (Bus Error)。
  2. 效能優化:x86 雖支援非對齊存取,但跨快取行 (64 bytes) 會額外耗費週期;對齊保證單次匯流排傳輸完成。

對齊規則 (System V AMD64 ABI)

  • 基本型態:對齊至 sizeof(type) (最大 8/16 bytes)
  • 結構體:成員依宣告順序排列,各自對齊;整體大小為最大成員對齊值的倍數。

實例:結構體填充

struct A { char c; int i; short s; };  // 1 + 3(pad) + 4 + 2 + 2(pad) = 12
struct B { int i; char c; short s; };  // 4 + 1 + 1(pad) + 2 + 2(pad) = 12
struct C { char c; char d; int i; };   // 1 + 1 + 2(pad) + 4 = 8

控制對齊

編譯器語法範例
GCC/Clang__attribute__((aligned(n)))struct __attribute__((aligned(1))) Packed { ... };
MSVC#pragma pack(n)#pragma pack(1) struct Packed { ... }; #pragma pack()

警告:壓縮結構體省記憶體但導致非對齊存取,嵌入式系統務必確認 CPU 支援。

四、資料表示策略:從位元操作到高階抽象

策略 1:位元級編碼 (極致壓縮)

撲克牌 52 張 + 大小王 = 54 種,6 bits 即足夠 (2⁶=64):

/* 7-4 bits: 點數 0-12 (A-K), 3-2 bits: 花色 0-3, 1-0 bits: 保留 */
uint8_t encode_card(uint8_t rank, uint8_t suit) {
    return (rank << 2) | (suit & 0x3);
}

策略 2:標準型態 + 結構體/陣列/指標

typedef struct {
    uint8_t rank;   // 0-12
    uint8_t suit;   // 0-3
} Card;

Card deck[52];          // 線性陣列
Card *hand = malloc(5*sizeof(Card)); // 動態手牌

使用基本型態前必問三問題

  1. 表示範圍夠嗎?(<limits.h>, <float.h>)
  2. 二進制佈局是否符合預期?(端序、填充、補數)
  3. 會溢出/精度遺失?如何偵測?(有號溢出 UB、浮點 FP_ILOGB0 等)

五、整數範圍速查表 (32-bit 系統)

/* 編譯期常數,零開銷 */
#include <limits.h>
#include <stdint.h>

printf("int8_t:   [%4d, %4d]\n", INT8_MIN, INT8_MAX);     // -128, 127
printf("uint8_t:  [   0, %4d]\n", UINT8_MAX);             // 255
printf("int16_t:  [%6d, %6d]\n", INT16_MIN, INT16_MAX);   // -32768, 32767
printf("int32_t:  [%11d, %11d]\n", INT32_MIN, INT32_MAX); // -2.147B, 2.147B
printf("int64_t:  [%20lld, %20lld]\n", INT64_MIN, INT64_MAX);

六、浮點數關鍵常數 (<float.h>)

常數意義float 典型值double 典型值
FLT_DIG / DBL_DIG十進位有效位數615
FLT_EPSILON / DBL_EPSILON1.0 + eps != 1.0 的最小值1.19e-72.22e-16
FLT_MAX / DBL_MAX最大有限值3.4e381.8e308
FLT_MIN / DBL_MIN最小正規化正數1.18e-382.23e-308

比較浮點數:fabs(a-b) <= EPSILON * fmax(fabs(a), fabs(b)),勿用 ==。

七、完整驗證程式

#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <float.h>

int main(void) {
    /* 型態大小 */
    printf("=== 型態大小 ===\n");
    printf("char=%zu short=%zu int=%zu long=%zu ll=%zu float=%zu double=%zu ptr=%zu\n",
           sizeof(char), sizeof(short), sizeof(int), sizeof(long),
           sizeof(long long), sizeof(float), sizeof(double), sizeof(void*));

    /* 端序 */
    printf("\n=== 端序 ===\n");
    uint32_t test = 0x12345678;
    printf("Little-Endian: %s\n", *(uint8_t*)&test == 0x78 ? "是" : "否");

    /* 對齊 */
    printf("\n=== 結構體對齊 ===\n");
    struct { char c; int i; short s; } s1;
    struct { int i; char c; short s; } s2;
    printf("char,int,short: %zu bytes\n", sizeof(s1));
    printf("int,char,short: %zu bytes\n", sizeof(s2));

    /* 整數範圍 */
    printf("\n=== 整數範圍 ===\n");
    printf("int32_t: [%d, %d]\n", INT32_MIN, INT32_MAX);
    printf("uint32_t: [0, %u]\n", UINT32_MAX);

    /* 浮點常數 */
    printf("\n=== 浮點常數 ===\n");
    printf("float:  eps=%e max=%e min=%e dig=%d\n",
           FLT_EPSILON, FLT_MAX, FLT_MIN, FLT_DIG);
    printf("double: eps=%e max=%e min=%e dig=%d\n",
           DBL_EPSILON, DBL_MAX, DBL_MIN, DBL_DIG);
    return 0;
}

結語

資料編碼是軟硬體交界面。掌握型態大小、端序、對齊、位元操作、溢出/精度邊界,才能在嵌入式、網路、高效能運算、逆向工程等領域遊刃有餘。下一步建議:閱讀 CSAPP (Computer Systems: A Programmer's Perspective) 第 2 章,並動手實作自訂序列化協定。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved