Всегда ли 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.