Всегда ли sizeof (некоторый указатель) равен четырем?


227

Например: sizeof(char*)возвращает 4. Как же int*, long long*все , что я пробовал. Есть ли исключения из этого?


51
Зачем это отмечать? Хороший вопрос для любого новичка.
— Мартин Йорк,

2
Я подозреваю, что в этом скрывается еще один вопрос: "Что такое sizeof?" или может быть «Почему sizeof <любой указатель> == 4? Что такого особенного в 4?». Я прав?

2
Ну, это зависит от вашей платформы. Большинство реализаций имеют одинаковый размер для каждого вида указателя на конкретной платформе.
— Фоагон

Ответы:


194

Это гарантия, которую вы получаете sizeof(char) == 1. Других гарантий нет, в том числе и никаких sizeof(int *) == sizeof(double *).

На практике указатели будут иметь размер 2 в 16-битной системе (если вы можете его найти), 4 в 32-битной системе и 8 в 64-битной системе, но полагаться на конкретную информацию нечего размер.


96
И 3 байта в 24-битной системе. Да, я работал над одним. Добро пожаловать в мир встраиваемых устройств.
— dwj

30
Я также работал на 16-битных системах с 20-битными указателями. Я должен посмотреть, какой размер возвращается в этом случае ...
— Судья Мейгарден

5
@monjardin: IIRC, 8086 был таким. Был 16-битный адрес и 4-битный сегментный регистр. Я считаю, что обычный указатель «NEAR» был 16 бит, а указатель, объявленный как «FAR», был больше, вероятно, 24, хотя я не уверен.
— rmeador

18
другая гарантия заключается в том, что sizeof (char *) == sizeof (void *), поскольку они должны иметь одинаковое представление (объект [размер] и значение [набор битов, соответствующих их значению] представления)
— Йоханнес Шауб - litb

7
Поскольку вопрос требует исключений, следует отметить, что нестатические указатели на функции-члены часто имеют разный размер по сравнению с обычными указателями, а также различаются в зависимости от платформы, типа и т. Д. Кроме этого +1.
— John5342

36

Даже на простой 32-разрядной платформе x86 вы можете получить указатели разных размеров, попробуйте это на примере:

struct A {};

struct B : virtual public A {};

struct C {};

struct D : public A, public C {};

int main()
{
    cout << "A:" << sizeof(void (A::*)()) << endl;
    cout << "B:" << sizeof(void (B::*)()) << endl;
    cout << "D:" << sizeof(void (D::*)()) << endl;
}

В Visual C ++ 2008 я получаю 4, 12 и 8 для размеров функции указателя на член.

Раймонд Чен говорил об этом здесь .


4
Указатели на функции-члены являются настоящей болью. К сожалению, не все компиляторы делают это как компилятор Digital Mars C ++, который возвращает 4 во всех случаях.
— Dalle

gcc 4.72 напечатать все 8 ... Это не определено в стандарте c ++?
— Gob00st

2
@ Gob00st: определено только то, что char равен 1. Другие типы могут быть любого размера, подходящего для этого компилятора. Там нет требования для согласованности между этими типами указателей.
— Затмение

хорошо, спасибо. Тогда неудивительно, что у gcc & VC другая реализация.
— Gob00st

5
@ Затмение да, есть: char <= short <= int <= long <= long long
— Коул Джонсон

30

Еще одно исключение из уже опубликованного списка. На 32-битных платформах указатели могут занимать 6, а не 4 байта:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char far* ptr; // note that this is a far pointer
    printf( "%d\n", sizeof( ptr));
    return EXIT_SUCCESS;
}

Если вы скомпилируете эту программу с помощью Open Watcom и запустите ее, вы получите 6, потому что поддерживаемые ею дальние указатели состоят из 32-битных смещений и 16-битных значений сегментов.


5
Не сегмент, а скорее селектор - это не часть адреса памяти, а индексная запись в LDT или GDT и имеет некоторые флаги доступа
— Роуи Шенберг

1
Почему в x86 есть сегменты и смещения, а адресное пространство плоское?
— phuclv

@ LưuVĩnhPhúc Потому что это экономит место для очень распространенного случая близких указателей, которые могут быть закодированы короче.
— Кристофер Кройциг

1
@ChristopherCreutzig, что означает, что сегменты используются для расширения адресного пространства как PAE?
— phuclv

@ LưuVĩnhPhúc Давно я не собирал 32-битные сборки. Часть, которую я помню, это то, что вы можете сэкономить место для указателей, указывающих рядом с вашим кодом. Кроме того, не все 32-битные архитектуры - конечно, не все основанные на x86 - используют модель плоской памяти. См., Например, tenouk.com/Bufferoverflowc/Bufferoverflow1a.html для дальнейшего обсуждения этого вопроса, хотя, как я уже сказал, прошло некоторое время, и я не могу ручаться за что-либо.
— Кристофер Кройциг

24

если вы компилируете для 64-битной машины, то это может быть 8.


2
Хотя обычно это так, это не обязательно так. Например, если вы компилируете на 64-битном компьютере, где размер слова равен 64-битному, то sizeof (char *), вероятно, будет равен 1. Не говоря уже о более экзотических типах указателей даже на обычных машинах, таких как Eclipse и дмитюгов пиши.
— Каз Драгон

@KazDragon, sizeof(char*)==1? Ты уверен? Ты имеешь в виду size(char)==1?
— Аарон МакДейд

3
@AaronMcDaid Я действительно имел в виду sizeof (char *). sizeof (char) всегда равно 1. Но если ваше машинное слово 64-битное, а ваша среда разработки реализована таким образом, что CHAR_BITS = 64, то возможно, что указатель помещается в том же пространстве, что и char, и, следовательно, также быть 1.
— Kaz Dragon


1
@KazDragon Я создаю (очень медленно, когда не откладываю) машину с 16-битными словами и без байтовой адресации. Хотя он все равно не может запустить C
— user253751

17

Технически говоря, стандарт C только гарантирует, что sizeof (char) == 1, а остальное зависит от реализации. Но на современных архитектурах x86 (например, на чипах Intel / AMD) это вполне предсказуемо.

Вы, наверное, слышали, что процессоры описываются как 16-битные, 32-битные, 64-битные и т. Д. Обычно это означает, что процессор использует N-бит для целых чисел. Поскольку указатели хранят адреса памяти, а адреса памяти являются целыми числами, это фактически говорит вам, сколько битов будет использоваться для указателей. sizeof обычно измеряется в байтах, поэтому код, скомпилированный для 32-разрядных процессоров, сообщит, что размер указателей равен 4 (32 бита / 8 бит на байт), а код для 64-разрядных процессоров сообщит, что размер указателей равен 8 (64 бита / 8 бит на байт). Отсюда и ограничение в 4 ГБ ОЗУ для 32-разрядных процессоров - если каждый адрес памяти соответствует байту, для адресации большего объема памяти нужны целые числа больше 32-разрядных.


«Возможно, вы слышали, что процессоры описываются как 16-битные, 32-битные, 64-битные и т. Д. Обычно это означает, что процессор использует N-битные числа для целых чисел». -> У меня 64-битный компьютер, но размер (int) составляет 4 байта. Если ваше утверждение верно, как это возможно ?!
— Сангит Сараванарадж

6
@SangeethSaravanaraj: Для обратной совместимости с 32-битным кодом они решили, чтобы int продолжал быть 4 байтами и потребовал, чтобы вы включили использование 8-байтового типа, указав 'long'. long - это собственно размер слова на x86-64. Один из способов убедиться в этом заключается в том, что обычно компиляторы дополняют ваши структуры, чтобы выровнять их по словам (хотя могут быть архитектуры, в которых размер и выравнивание слова не связаны), поэтому, если вы создаете структуру с целым числом 32 (32), и вызовите для него sizeof (), если вы вернетесь к 8, вы знаете, что он дополняет их до 64-битного размера слова.
— Джозеф Гарвин

@SangeethSaravanaraj: Обратите внимание, что теоретически размер собственного слова процессора и то, что компилятор решает, что int может быть произвольно различным, просто было принято, что int является собственным размером слова до появления x86-64, где это долго, чтобы ослабить обратный компат.
— Джозеф Гарвин

Спасибо за объяснение! :)
— Сангит Сараванарадж

7

Размер указателя в основном зависит от архитектуры системы, в которой он реализован. Например, размер указателя в 32-разрядных системах составляет 4 байта (32-разрядных) и 8 байтов (64-разрядных) в 64-разрядных компьютерах. Типы битов в машине - это не что иное, как адрес памяти, который он может иметь. 32-битные машины могут иметь 2^32адресное пространство, а 64-битные машины могут иметь до 2^64адресного пространства. Таким образом, указатель (переменная, которая указывает на ячейку памяти) должен иметь возможность указывать на любой адрес памяти ( 2^32 for 32 bit and 2^64 for 64 bit), который хранится в машине.

По этой причине мы видим размер указателя равным 4 байта на 32-битной машине и 8 байтов на 64-битной машине.


6

В дополнение к 16/32/64-битным различиям могут происходить и более странные вещи.

Были машины, где sizeof (int *) будет одним значением, вероятно, 4, но где sizeof (char *) больше. Машины, которые естественным образом адресуют слова вместо байтов, должны «дополнять» символьные указатели, чтобы указать, какая часть слова вам действительно нужна, чтобы должным образом реализовать стандарт C / C ++.

Это теперь очень необычно, так как разработчики аппаратного обеспечения узнали значение адресации байтов.


4
Компилятор C для векторных машин Cray, таких как T90, делает нечто подобное. Аппаратные адреса составляют 8 байтов и указывают на 8-байтовые слова.void*и char*обрабатываются программно, и дополняются 3-битным смещением в слове - но поскольку на самом деле нет 64-битного адресного пространства, смещение сохраняется в старших 3-х битах 64-битного слово. Так char*и int*имеют одинаковый размер, но имеют разные внутренние представления - и код, который предполагает, что указатели «действительно», просто целые числа, могут сильно потерпеть неудачу.
— Кит Томпсон

5

8-битные и 16-битные указатели используются в большинстве низкопрофильных микроконтроллеров. Это означает, что каждая стиральная машина, микро, холодильник, старые телевизоры и даже автомобили.

Можно сказать, что это не имеет ничего общего с программированием в реальном мире. Но вот один пример из реальной жизни: Arduino с оперативной памятью 1-2-4 КБ (в зависимости от чипа) с 2-байтовыми указателями.

Это недавно, дешево, доступно для всех и заслуживает написания.


4

В дополнение к тому, что люди говорили о 64-битных (или любых других) системах, существуют и другие виды указателей, кроме указателя на объект.

Указатель на член может быть практически любого размера, в зависимости от того, как он реализован вашим компилятором: они не обязательно даже одного размера. Попробуйте указатель на член класса POD, а затем указатель на член, унаследованный от одного из базовых классов класса с несколькими основаниями. Как весело.


3

Насколько я помню, это основано на размере адреса памяти. Таким образом, в системе с 32-битной адресной схемой sizeof вернет 4, поскольку это 4 байта.


4
Там нет такого требования. Нет даже требования, что sizeof (unsigned int) == sizeof (sign int). Размер указателя на int всегда будет, по определению, sizeof (int *), на char sizeof (char *) и т. Д. Полагаться на любое другое предположение - плохая идея для переносимости.
— Михай Лимбанян

Ах, теперь я вижу. Спасибо за информацию.
— Уилл Мак

1
Может по-прежнему возвращать 2, если CHAR_BIT равен 16. sizeof () считает количество символов, а не октетов.
— MSalters

5
@Mihai: В C ++ sizeof (unsigned int) == sizeof (signed int)это требование встречается в 3.9.1 / 3. «Для каждого из стандарта , подписанного целым числом типов, существует соответствующее (но различный) стандартный беззнаковый целый типа: unsigned char, unsigned short int, unsigned int, unsigned long int, и unsigned long long int, каждый из которых занимает такое же количество хранения и имеет то же требование к выравниванию как соответствующие знаковому целому типа "
— Бен Фойгт

3

В общем случае sizeof (почти все) будет меняться при компиляции на разных платформах. На 32-битной платформе указатели всегда имеют одинаковый размер. На других платформах (64-битный является очевидным примером) это может измениться.


3

Нет, размер указателя может варьироваться в зависимости от архитектуры. Есть множество исключений.


3

Размер указателя и int составляет 2 байта в компиляторе Turbo C на Windows 32-битной машине.

Таким образом, размер указателя зависит от компилятора. Но, как правило, большинство компиляторов реализованы для поддержки 4-байтовой переменной указателя в 32-битной и 8-байтовой переменной указателя в 64-битной машине).

Таким образом, размер указателя не одинаков на всех машинах.


2

Причина, по которой размер вашего указателя составляет 4 байта, заключается в том, что вы компилируете для 32-битной архитектуры. Как отметил FryGuy, на 64-битной архитектуре вы увидите 8.


2

В Win64 (Cygwin GCC 5.4) , давайте посмотрим на приведенный ниже пример:

Сначала протестируйте следующую структуру:

struct list_node{
    int a;
    list_node* prev;
    list_node* next;
};

struct test_struc{
    char a, b;
};

Тестовый код ниже:

std::cout<<"sizeof(int):            "<<sizeof(int)<<std::endl;
std::cout<<"sizeof(int*):           "<<sizeof(int*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(double):         "<<sizeof(double)<<std::endl;
std::cout<<"sizeof(double*):        "<<sizeof(double*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(list_node):      "<<sizeof(list_node)<<std::endl;
std::cout<<"sizeof(list_node*):     "<<sizeof(list_node*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(test_struc):     "<<sizeof(test_struc)<<std::endl;
std::cout<<"sizeof(test_struc*):    "<<sizeof(test_struc*)<<std::endl;    

Выход ниже:

sizeof(int):            4
sizeof(int*):           8

sizeof(double):         8
sizeof(double*):        8

sizeof(list_node):      24
sizeof(list_node*):     8

sizeof(test_struc):     2
sizeof(test_struc*):    8

Вы можете видеть, что в 64-разрядной версии sizeof(pointer)есть 8.


1

Указатель - это просто контейнер для адреса. На 32-битной машине ваш диапазон адресов составляет 32 бита, поэтому указатель всегда будет 4 байта. На 64-битной машине, где у вас диапазон адресов 64 бита, указатель будет 8 байтов.


1
На 32-разрядной машине с 32-разрядными байтами размер sizeof (char *) может быть равен 1.
— Роберт Гэмбл,

"... с 32-битными байтами". Я не знал, что такие вещи существуют ... кажется, что.
— Эд С.

1
На 32-битной утке sizeof (char *) возвращает PI
— Adriano Varoli Piazza

0

Просто для полноты и исторического интереса, в 64-битном мире существовали различные соглашения о платформе для размеров длинных и длинных длинных типов, называемых LLP64 и LP64, в основном между системами Unix-типа и Windows. Старый стандарт с именем ILP64 также имеет ширину int = 64-bit.

Microsoft поддерживала LLP64, где longlong = 64 бит в ширину, но long остался на 32, для облегчения переноса.

Type           ILP64   LP64   LLP64
char              8      8       8
short            16     16      16
int              64     32      32
long             64     64      32
long long        64     64      64
pointer          64     64      64

Источник: https://stackoverflow.com/a/384672/48026

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.