在C語言程式設計中,指標的長度與類型是跨平台移植與系統級開發的核心議題。本文深入探討指標長度的平台相關性、函式指標與資料指標的差異,並詳細解析四個關鍵預定義類型:size_t、ptrdiff_t、intptr_t、uintptr_t 的定義、用途與最佳實踐。
C標準並未強制規定所有類型指標長度相同,但在現代主流平台(x86, x64, ARM)上,資料指標長度通常一致:
關鍵差異:函式指標長度可能與資料指標不同(如某些嵌入式架構採用 Harvard 架構,程式碼與資料地址空間分離)。
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stddef.h>
struct Point { int n; float f; };
int main() {
struct Point *sptr;
printf("sizeof(char*) = %zu\n", sizeof(char*));
printf("sizeof(int*) = %zu\n", sizeof(int*));
printf("sizeof(float*) = %zu\n", sizeof(float*));
printf("sizeof(double*) = %zu\n", sizeof(double*));
printf("sizeof(struct*) = %zu\n", sizeof(sptr));
return 0;
}
典型64位Linux輸出:全部為 8 字節。
定義位置:<stddef.h>(C++ 為 <cstddef>)
本質:無符號整數類型,大小足以容納記憶體中任一物件的位元組數。
unsigned int (4字節)unsigned long (8字節)主要用途:
sizeof 運算子回傳類型malloc(size_t)strlen 回傳 size_t格式化輸出:優先使用 %zu (C99),舊編譯器可用 %u (32位) 或 %lu (64位)。
定義位置:<stddef.h>
本質:有符號整數,用於儲存同一陣列中兩個指標相減的結果。
long intsize_t 的關鍵差異:ptrdiff_t 可為負值,表示指標相對位置經典應用:
#include <stdio.h>
#include <stddef.h>
#include <string.h>
int main() {
char str[] = "Hello world!";
char *pstart = str;
char *pend = str + strlen(str);
ptrdiff_t diff = pend - pstart; // 12
printf("字串長度: %td\n", diff);
return 0;
}
定義位置:<stdint.h> (C99)
核心價值:保證能完整容納指標值的整數類型,解決「指標轉整數」的移植性問題。
intptr_t:有符號版本uintptr_t:無符號版本(內核開發常用)平台相關定義範例:
#if __WORDSIZE == 64 typedef long int intptr_t; typedef unsigned long int uintptr_t; #else typedef int intptr_t; typedef unsigned int uintptr_t; #endif
將結構體指標轉為 intptr_t 傳遞給上層介面,實現資訊隱藏:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#define ID_STR_LEN 12
#define NAME_STR_LEN 10
typedef struct student {
char id[ID_STR_LEN];
char name[NAME_STR_LEN];
uint8_t age;
} Student;
Student* create_student() {
Student *stu = malloc(sizeof(Student));
if (!stu) return NULL;
memset(stu, 0, sizeof(Student));
return stu;
}
void free_student(Student *stu) {
if (stu) free(stu);
}
static void init_student(Student *stu) {
assert(stu);
memcpy(stu->id, "2013112210", 10);
memcpy(stu->name, "Anker", 5);
stu->age = 21;
}
// 介面層只見 intptr_t,不知內部結構
static int handle_student(intptr_t handle) {
if (!handle) return -1;
Student *stu = (Student*)handle;
printf("ID: %s\nName: %s\nAge: %u\n", stu->id, stu->name, stu->age);
return 0;
}
int main() {
Student *stu = create_student();
init_student(stu);
intptr_t handle = (intptr_t)stu; // 指標轉整數
handle_student(handle);
free_student(stu);
return 0;
}
「概念上,雖然位址是指標,記憶體管理常使用無符號整數更佳。內核將物理記憶體視為巨大陣列,位址只是索引。指標易被解引用,直接處理記憶體存取時幾乎不想這樣做。使用整數類型避免了解引用,從而避免 bug。因此內核常用
unsigned long,利用指標與長整型同尺寸的事實。C99 標準化了intptr_t和uintptr_t。」
| 類型 | 符號性 | 主要用途 | 格式化輸出 |
|---|---|---|---|
| size_t | 無符號 | 物件大小、陣列索引、迴圈計數 | %zu |
| ptrdiff_t | 有符號 | 指標差值、陣列元素距離 | %td |
| intptr_t | 有符號 | 指標轉整數、跨介面傳遞控制代碼 | PRIdPTR (inttypes.h) |
| uintptr_t | 無符號 | 位址運算、硬體暫存器映射、內核開發 | PRIuPTR (inttypes.h) |
掌握這四類型,能寫出更安全、可移植、符合現代C標準的系統級程式碼。