Например:
sizeof(char*) возвращает 4. Как и int*, long long*, все, что я пробовал. Есть ли исключения из этого?
Подозреваю, что в этом кроется еще один вопрос: "Что такое sizeof?" или может быть «Почему sizeof <any pointer> == 4? Что такого особенного в 4?». Я прав?
Ну, это зависит от вашей платформы. Большинство реализаций имеют одинаковый размер для всех типов указателей на определенной платформе.
Пожалуйста, спрашивайте C или C++, а не оба в одном и том же вопросе. Это 2 разных языка. Включайте и то и другое только тогда, когда вы спрашиваете о различиях или сходстве между ними.





если вы компилируете для 64-битной машины, то это может быть 8.
Хотя это обычно так, это не всегда так. Например, если вы компилируете на 64-битной машине, где размер слова составляет 64 бита, то sizeof (char *), вероятно, будет 1. Не говоря уже о более экзотических типах указателей даже на обычных машинах, таких как Eclipse и дмитюгов пишите.
@KazDragon, sizeof(char*)==1? Уверены ли вы? Вы не имеете в виду size(char)==1?
@AaronMcDaid Я действительно имел в виду sizeof (char *). sizeof (char) всегда равен 1. Но если ваше машинное слово 64-битное, а ваша среда разработки реализована таким образом, что CHAR_BITS = 64, то возможно, что указатель уместится в том же пространстве, что и char, и, следовательно, будет также быть 1.
это не правда в x32-abisites.google.com/site/x32abi
Просто любопытно, @KazDragon вы можете привести пример машины, на которой CHAR_BITS = 64?
@rsaxvc Нет, но я мог бы построить такой, если бы захотел.
@KazDragon Я создаю (очень медленно, если не откладывать на потом) машину с 16-битными словами и без байтовой адресации. Хотя он все равно не может запустить C.
@ user253751: Конечно, есть ряд готовых микросхем DSP, подобных этому.
Насколько я помню, он основан на размере адреса памяти. Таким образом, в системе с 32-битной схемой адреса sizeof вернет 4, поскольку это 4 байта.
Такого требования нет. Нет даже требования, чтобы sizeof (unsigned int) == sizeof (signed int). Размер указателя на int всегда по определению будет sizeof (int *), на char sizeof (char *) и т. д. Полагаться на любые другие предположения - плохая идея для переносимости.
Ах, теперь я понял. Спасибо за информацию.
По-прежнему может возвращать 2, если CHAR_BIT равен 16. sizeof () подсчитывает количество символов, а не октеты.
@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, каждый из которых занимает такой же объем памяти и имеет те же требования к выравниванию, что и соответствующий целочисленный тип со знаком»
Причина, по которой размер вашего указателя составляет 4 байта, заключается в том, что вы компилируете для 32-битной архитектуры. Как указал FryGuy, на 64-битной архитектуре вы увидите 8.
В общем, sizeof (почти все) будет меняться при компиляции на разных платформах. На 32-битной платформе указатели всегда имеют одинаковый размер. На других платформах (очевидным примером является 64-битная версия) это может измениться.
Указатель - это просто контейнер для адреса. На 32-битной машине ваш диапазон адресов составляет 32 бита, поэтому указатель всегда будет 4 байта. На 64-битной машине с диапазоном адресов 64 бита указатель будет иметь размер 8 байт.
На 32-битной машине с 32-битными байтами sizeof (char *) может быть 1.
«... с 32-битными байтами». Я не знал, что такие вещи существуют ... представьте себе.
На 32-битной утке sizeof (char *) возвращает PI
Вы получаете гарантию, что sizeof(char) == 1. Нет никаких других гарантий, включая отсутствие гарантии, что sizeof(int *) == sizeof(double *).
На практике указатели будут иметь размер 2 в 16-битной системе (если вы можете его найти), 4 в 32-битной системе и 8 в 64-битной системе, но полагаться на данную размер.
И 3 байта в 24-битной системе. Да, я работал над одним. Добро пожаловать в мир встраиваемых устройств.
Я также работал над 16-битными системами с 20-битными указателями. Я должен пойти посмотреть, какой размер возвращается в таком случае ...
@monjardin: IIRC, 8086 был таким. Был 16-битный адрес и 4-битный сегментный регистр. Я считаю, что нормальный указатель «NEAR» был 16 бит, а указатель, объявленный как «FAR», был больше, вероятно, 24, хотя я не уверен.
Я работал над (32-битной) системой, в которой «char *» для чего-то имело другое битовое представление, чем «anthing_else *» в том же месте памяти. Вы научились гарантировать, что был объявлен malloc () (потому что в те давние времена malloc () возвращал char *, а не void *). Остерегаться!
(продолжение): размер указателей был одинаковым во всех случаях; отличался просто битовый узор. Я не встречал систем, в которых указатели на разные типы данных имели разные размеры. Я видел системы, в которых указатели кода (функции) отличаются по размеру от указателей данных.
В некоторых моделях памяти указатель на 8086 был 32-битным. 16-битный сегмент и 16-битное смещение. Процессор объединит их, сдвинув сегмент на 4 бита влево и добавив смещение, в результате чего получится 20-битный адрес. Два разных указателя могут указывать на один и тот же физический адрес! веселье!
16-битные системы легко найти. Любой код, который работает в режиме THUMB на микросхеме ARM.
Извините, @ApplePieIsGood: код ARM THUMB по-прежнему 32-битный. Это просто метод сжатия размера инструкции с 32 до 16 бит, но адресное пространство остается прежним.
другой гарантией является то, что sizeof (char *) == sizeof (void *), потому что они должны иметь одинаковое представление (объект [размер] и значение [набор бит, соответствующих их значению] представление)
Ферруччо: Он не старый. Мой процессор Core 2 Quad также поддерживает реальный режим ;-P
Конечно, если у вас есть указатель на функцию-член класса, который использует множественное или виртуальное наследование, вы, скорее всего, получите совершенно другие размеры указателя.
itanium иногда использует 2 слова (2 указателя) для представления 1 указателя на функцию.
Есть и другие гарантии, например sizeof(int) <= sizeof(long). Они могут быть такими же, или long может быть значительно больше, чем int, но меньше никогда не будет.
@JudgeMaygarden звучит как 16-битный реальный мир
Поскольку вопрос требует исключений, следует отметить, что указатели нестатических функций-членов часто отличаются по размеру от обычных указателей, а также зависят от платформы, типа и т. д. Кроме этого +1.
8 и 16 бит везде. Стиральные машины, холодильники, микроконтроллеры, дистанционные контроллеры, ключи от машины.
Также существует гарантия, что все указатели на структуры имеют одинаковый размер и макет, а все указатели на объединения имеют одинаковый размер и представление. И указатели на подписанные / беззнаковые количества. И указатели на простые / константные / летучие объекты.
@ gnasher729: Я знаю, что есть гарантии относительно указателей Кастинг, но где гарантия относительно размера и представления?
@ JohannesSchaub-litb: где в стандарте указано, что sizeof(char*) == sizeof(void*)?
Означает ли это, что, независимо от типа данных, адрес есть адрес, и это то, что хранит указатель, по этой причине он должен быть одинакового размера?
@JudgeMaygarden Так что там написано?
@ chux-ReinstateMonica: N1570 6.2.5 параграф 28 определяет, что в C, «указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа». Я думаю, это будет означать, что они одного размера.
@supercat Согласен. За последние 3 года я узнал об этой спецификации. Я подозреваю, что допустимые вариации размера указателя на объект среди int, FP, Struct, char - это особенность прошлого - сейчас я не знаю никаких вариаций. OTOH, указатели на функции и указатели на объекты различаются.
Нет, размер указателя может варьироваться в зависимости от архитектуры. Есть множество исключений.
В дополнение к различиям в 16/32/64 бит могут происходить даже более странные вещи.
Были машины, на которых sizeof (int *) будет иметь одно значение, вероятно, 4, но где sizeof (char *) больше. Машины, которые естественным образом обращаются к словам, а не к байтам, должны «увеличивать» указатели на символы, чтобы указать, какую часть слова вы действительно хотите, чтобы правильно реализовать стандарт C / C++.
Сейчас это очень необычно, поскольку разработчики оборудования узнали значение байтовой адресации.
Компилятор C для векторных машин Cray, таких как T90, делает нечто подобное. Аппаратные адреса составляют 8 байтов и указывают на 8-байтовые слова. void* и char* обрабатываются программно и дополняются 3-битным смещением внутри слова, но поскольку на самом деле нет 64-битного адресного пространства, смещение сохраняется в 3-х старших битах 64 -битное слово. Таким образом, char* и int* имеют одинаковый размер, но имеют разные внутренние представления - и код, который предполагает, что указатели «на самом деле» просто целые числа, может сильно потерпеть неудачу.
Еще одно исключение из уже опубликованного списка. На 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-битных значений сегмента.
Не сегмент, а скорее селектор - это не часть адреса памяти, а запись индекса в LDT или GDT и имеет некоторые флаги доступа.
Почему в x86 есть сегменты и смещения, а адресное пространство плоское?
@ LưuVĩnhPhúc Потому что он экономит место для очень распространенного случая ближних указателей, которые можно закодировать короче.
@ChristopherCreutzig, что означает, что сегменты используются для расширения адресного пространства, например, PAE?
@ LưuVĩnhPhúc Я уже давно занимаюсь сборкой чего-либо 32-битного. Я, кажется, помню, что вы можете сэкономить место для указателей, указывающих рядом с вашим кодом. Кроме того, не все 32-битные архитектуры - конечно, не все основанные на x86 - используют плоскую модель памяти. См., Например, tenouk.com/Bufferoverflowc/Bufferoverflow1a.html для более подробного обсуждения этого вопроса, хотя, как я уже сказал, прошло много времени, и я не могу ни за что поручиться.
@ Lưu Vĩnh Phúc Сегментированная архитектура, представленная в 8088 и 8086, была основана на предположении, что цены на память были высокими, поэтому расходы можно было сэкономить, сохранив все в сегментах по 64 КБ. Это было большой головной болью для тех из нас, кто программировал низкоуровневый код в этой среде. Сейчас он редко используется, но все еще присутствует в процессорах Intel для мазохистов среди нас.
В дополнение к тому, что люди говорят о 64-битных (или любых других) системах, существуют другие виды указателей, кроме указателей на объект.
Указатель на член может иметь почти любой размер, в зависимости от того, как они реализованы вашим компилятором: они даже не обязательно имеют одинаковый размер. Попробуйте использовать указатель на член класса POD, а затем указатель на член, унаследованный от одного из базовых классов класса с несколькими базами. Как весело.
Технически говоря, стандарт 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-битная машина, но sizeof (int) составляет 4 байта. Если ваше утверждение верно, как это могло быть возможно ?!
@SangeethSaravanaraj: для обратной совместимости с 32-битным кодом они решили, что int по-прежнему будет 4 байта, и требуют, чтобы вы согласились на использование 8-байтового типа, указав 'long'. long - собственно размер слова на x86-64. Один из способов убедиться в этом заключается в том, что обычно компиляторы дополняют ваши структуры, чтобы выровнять их по словам (хотя могут быть архитектуры, в которых размер слова и выравнивание не связаны), поэтому, если вы создаете структуру с int (32-битным) в ней, и вызовите для него sizeof (), если вы вернетесь 8, вы знаете, что он дополняет их до 64-битного размера слова.
@SangeethSaravanaraj: обратите внимание, что теоретически собственный размер слова ЦП и то, что компилятор решает как int, может быть произвольно различным, просто до появления x86-64 было условием, что int - это собственный размер слова, где это долго, чтобы облегчить обратную совместимость.
Спасибо за объяснение! :)
Даже на простой 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 для размеров указателей на функцию-член.
Раймонд Чен говорил об этом здесь.
Указатели на функции-члены - настоящая боль. К сожалению, не все компиляторы делают это так, как компилятор Digital Mars C++, который во всех случаях возвращает 4.
gcc 4.72 распечатать все 8 ... Это не определено в стандарте C++?
@ Gob00st: Единственное, что определено, это то, что char равно 1. Другие типы могут иметь любой размер, соответствующий этому компилятору. Нет требований к согласованности между этими типами указателей.
хорошо спасибо. Тогда неудивительно, что у gcc и VC разные реализации.
@Eclipse да, есть: char <= short <= int <= long <= long long
@ColeJohnson, откуда эта связь? Должен ли первый <= быть <, а именно char <short?
@ user3207158 Это отношение взято из стандарта. Он ничего не говорит об их битовой ширине, но гарантирует, что все размеры больше или равны предыдущим. char всегда меньше или равен short, ..., int всегда меньше или равен long и т. д. У меня нет стандарта под рукой, поэтому я не могу назвать эталонный банкомат.
8-битные и 16-битные указатели используются в большинстве низкопрофильных микроконтроллеров. Это означает каждую стиральную машину, микро, холодильник, старые телевизоры и даже автомобили.
Можно сказать, что это не имеет ничего общего с программированием в реальном мире. Но вот один пример из реального мира: Arduino с оперативной памятью 1-2-4k (в зависимости от чипа) с 2-х байтовыми указателями.
Он недавний, дешевый, доступный для всех и заслуживающий программирования.
Размер указателя и int составляет 2 байта в компиляторе Turbo C на 32-битной машине Windows.
Таким образом, размер указателя зависит от компилятора. Но обычно большинство компиляторов реализовано для поддержки 4-байтовой переменной-указателя в 32-битной и 8-байтовой переменной-указателя на 64-битной машине).
Таким образом, размер указателя не одинаков на всех машинах.
Размер указателя в основном зависит от архитектуры системы, в которой он реализован. Например, размер указателя в 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-битной машине.
Просто для полноты и исторического интереса в мире 64-битных платформ существовали различные соглашения о размерах длинных и длинных длинных типов, называемых LLP64 и LP64, в основном между системами типа Unix и Windows. Старый стандарт с именем ILP64 также сделал int = 64-битной шириной.
Microsoft сохранила LLP64, где longlong = 64-битная ширина, но долгое время оставалась равной 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
В 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.
Зачем отмечать это? Хороший вопрос для любого новичка.