← Назад к вопросам

Чем определяется размер указателя?

1.8 Middle🔥 241 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Размер указателя в C/C++

Это фундаментальный вопрос, который раскрывает понимание взаимодействия языка с архитектурой процессора. Правильный ответ зависит от нескольких факторов.

Главный определитель: архитектура процессора

Размер указателя = размер адресного пространства процессора

32-bit архитектура (x86, ARM32, MIPS32)

  • Размер указателя: 4 байта
  • Максимальный адрес: 2^32 = 4 GB

64-bit архитектура (x86-64, ARM64, RISC-V64)

  • Размер указателя: 8 байтов
  • Максимальный адрес: 2^64 = 16 EB (exabytes)
#include <iostream>
#include <cstdint>

int main() {
    std::cout << "Size of pointer: " << sizeof(void*) << " bytes\n";
    std::cout << "Size of int*: " << sizeof(int*) << " bytes\n";
    std::cout << "Size of char*: " << sizeof(char*) << " bytes\n";
    std::cout << "Size of function pointer: " << sizeof(void(*)(void)) << " bytes\n";
    return 0;
}

Вывод на x86-64:

Size of pointer: 8 bytes
Size of int*: 8 bytes
Size of char*: 8 bytes
Size of function pointer: 8 bytes

Почему все указатели одного размера?

Все указатели на одной архитектуре имеют ОДИНАКОВЫЙ размер, независимо от типа, на который они указывают.

int* ptr_to_int = nullptr;       // 8 bytes (на x64)
double* ptr_to_double = nullptr; // 8 bytes (на x64)
class MyClass* ptr_to_class;     // 8 bytes (на x64)

static_assert(sizeof(int*) == sizeof(double*));  // Всегда true
static_assert(sizeof(int*) == 8);                // true на 64-bit

Почему? Потому что указатель — это просто число, адрес в памяти. Какой тип данных находится по этому адресу — для CPU'а не важно. Размер определяется только тем, как CPU может адресовать память.

Стандарты и гарантии

В C++, std::ptrdiff_t и std::intptr_t — типы, которые гарантированно совпадают по размеру с указателем.

#include <cstdint>

// std::intptr_t — целое число, совпадающее по размеру с указателем
static_assert(sizeof(std::intptr_t) == sizeof(void*));

void* ptr = (void*)0x12345678;
std::intptr_t addr = reinterpret_cast<std::intptr_t>(ptr);
// Безопасно преобразовать указатель в число

Data Model'и (ABI)

Есть несколько стандартизированных моделей данных (Application Binary Interface):

LP32 (старый 16-bit)

  • int: 2 bytes
  • long: 2 bytes
  • pointer: 2 bytes

ILP32 (32-bit)

  • int: 4 bytes
  • long: 4 bytes
  • pointer: 4 bytes

LP64 (modern 64-bit на Unix/Linux)

  • int: 4 bytes
  • long: 8 bytes
  • pointer: 8 bytes

LLP64 (64-bit на Windows)

  • int: 4 bytes
  • long: 4 bytes (!) — отличается от LP64
  • pointer: 8 bytes
// На Linux/macOS (LP64):
static_assert(sizeof(long) == 8);  // true

// На Windows (LLP64):
static_assert(sizeof(long) == 4);  // true

// Везде:
static_assert(sizeof(void*) == 8);  // true на 64-bit

Это частая ошибка: думать, что long имеет размер указателя. На Windows это не так!

Практическое применение

Вычисления с адресами:

char buffer[1000];
char* ptr1 = buffer;
char* ptr2 = buffer + 100;  // + 100 байт (char = 1 byte)

std::cout << (ptr2 - ptr1);  // 100 (разница в bytes)

int arr[10];
int* p1 = arr;
int* p2 = arr + 5;  // + 5 * sizeof(int) = 20 bytes (на 32-bit) или всё равно + 20 bytes

std::cout << (p2 - p1);  // 5 (разница в elements, не в bytes!)

Выравнивание (alignment):

struct Data {
    char c;      // 1 byte
    // 7 bytes padding
    void* ptr;   // 8 bytes (на x64), выравнено на 8-byte boundary
    int x;       // 4 bytes
    // 4 bytes padding
};

static_assert(sizeof(Data) == 24);  // На x64
// Если бы указатель был 4 байта, padding было бы меньше

Как узнать размер указателя в runtime

#include <iostream>
#include <cstddef>
#include <climits>

int main() {
    std::cout << "sizeof(void*): " << sizeof(void*) << std::endl;
    std::cout << "POINTER_SIZE macro: " << POINTER_SIZE << std::endl;
    
    // Определить архитектуру:
    if (sizeof(void*) == 4) {
        std::cout << "32-bit system\n";
    } else if (sizeof(void*) == 8) {
        std::cout << "64-bit system\n";
    }
    
    return 0;
}

Интересные детали

Функциональные указатели

// На большинстве архитектур функциональный указатель = обычный указатель
static_assert(sizeof(void(*)()) == sizeof(void*));

// Исключение: некоторые архитектуры (ARM Thumb, MIPS16) используют
// младший бит для флага, но всё равно занимает sizeof(void*)

Member функции указатели — более сложный случай

class MyClass {};

using MemFuncPtr = void (MyClass::*)();

std::cout << sizeof(MemFuncPtr);  // Часто больше, чем sizeof(void*)
// Потому что нужно хранить объект (this) и адрес функции
// На некоторых архитектурах это 16 байт!

Практическая рекомендация

Никогда не предполагай размер указателя! Всегда используй:

#include <cstdint>

// Для хранения адреса
std::intptr_t address = reinterpret_cast<std::intptr_t>(ptr);

// Для расчётов
std::cout << sizeof(void*);  // Вместо hardcode'а 4 или 8

// Для переносимости
static_assert(sizeof(void*) >= 4);  // Минимальная гарантия

Вывод

Размер указателя определяется архитектурой CPU:

  • x86-64, ARM64, RISC-V64 → 8 bytes
  • x86, ARM32, MIPS32 → 4 bytes

Все указатели на одной платформе одного размера, независимо от типа. Это фундаментальное свойство, вытекающее из того, что указатель — это просто число, адрес в виртуальном адресном пространстве.

Понимание этого — критично для работы с памятью, структурами, сериализацией и переносимостью кода.

Чем определяется размер указателя? | PrepBro