C 語言讓程式設計師直面硬體資料模型。理解型態大小、位元組序、記憶體對齊、位元級編碼,是寫出正確、高效、可移植系統代碼的基石。
| 型態 | 標準最小寬度 | 32-bit Linux (ILP32) | 64-bit Linux (LP64) | 64-bit Windows (LLP64) |
|---|---|---|---|---|
| char | 8 bits | 1 | 1 | 1 |
| short | 16 bits | 2 | 2 | 2 |
| int | 16 bits | 4 | 4 | 4 |
| long | 32 bits | 4 | 8 | 4 |
| long long | 64 bits | 8 | 8 | 8 |
| float | 32 bits | 4 | 4 | 4 |
| double | 64 bits | 8 | 8 | 8 |
| 指標 | — | 4 | 8 | 8 |
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 等)。
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) 亦多指定端序,讀寫時務必轉換。
SIGBUS (Bus Error)。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 支援。
撲克牌 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);
}
typedef struct {
uint8_t rank; // 0-12
uint8_t suit; // 0-3
} Card;
Card deck[52]; // 線性陣列
Card *hand = malloc(5*sizeof(Card)); // 動態手牌
<limits.h>, <float.h>)FP_ILOGB0 等)/* 編譯期常數,零開銷 */
#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 | 十進位有效位數 | 6 | 15 |
| FLT_EPSILON / DBL_EPSILON | 1.0 + eps != 1.0 的最小值 | 1.19e-7 | 2.22e-16 |
| FLT_MAX / DBL_MAX | 最大有限值 | 3.4e38 | 1.8e308 |
| FLT_MIN / DBL_MIN | 最小正規化正數 | 1.18e-38 | 2.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 章,並動手實作自訂序列化協定。