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

C語言指標長度與類型深度解析:size_t、ptrdiff_t、intptr_t 完全指南

     編輯:關於C語言

前言

在C語言程式設計中,指標的長度與類型是跨平台移植與系統級開發的核心議題。本文深入探討指標長度的平台相關性、函式指標與資料指標的差異,並詳細解析四個關鍵預定義類型:size_t、ptrdiff_t、intptr_t、uintptr_t 的定義、用途與最佳實踐。

指標長度的平台相關性

C標準並未強制規定所有類型指標長度相同,但在現代主流平台(x86, x64, ARM)上,資料指標長度通常一致:

  • 32位系統:4字節(32位)
  • 64位系統:8字節(64位)

關鍵差異:函式指標長度可能與資料指標不同(如某些嵌入式架構採用 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 字節。

核心預定義類型詳解

1. size_t —— 物件大小與陣列索引的標準類型

定義位置:<stddef.h>(C++ 為 <cstddef>)

本質:無符號整數類型,大小足以容納記憶體中任一物件的位元組數。

  • 32位系統:unsigned int (4字節)
  • 64位系統:unsigned long (8字節)

主要用途:

  • sizeof 運算子回傳類型
  • 記憶體分配函式參數:malloc(size_t)
  • 字串長度:strlen 回傳 size_t
  • 迴圈計數器、陣列索引、指標算術運算

格式化輸出:優先使用 %zu (C99),舊編譯器可用 %u (32位) 或 %lu (64位)。

2. ptrdiff_t —— 指標差值的有符號類型

定義位置:<stddef.h>

本質:有符號整數,用於儲存同一陣列中兩個指標相減的結果。

  • 通常定義為 long int
  • 與 size_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;
}

3. intptr_t / uintptr_t —— 指標與整數轉換的可移植類型

定義位置:<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 實現不透明控制代碼

將結構體指標轉為 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;
}

內核開發視角:為何偏好 uintptr_t

「概念上,雖然位址是指標,記憶體管理常使用無符號整數更佳。內核將物理記憶體視為巨大陣列,位址只是索引。指標易被解引用,直接處理記憶體存取時幾乎不想這樣做。使用整數類型避免了解引用,從而避免 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標準的系統級程式碼。

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