Чи завжди розмірofof (деякий вказівник) дорівнює чотирма?


227

Наприклад: sizeof(char*)повертає 4. Як же int*, long long*все , що я пробував. Чи є з цього винятки?


51
Навіщо це позначати? Гарне запитання для будь-якого новачка.
Мартін Йорк

2
Я підозрюю, що в цьому ховається ще одне питання: "Що таке розмір?" або може бути "Чому розмір <будь-який вказівник> == 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 біт. Я вважаю, що звичайний покажчик "БЛИЗЬКИЙ" був 16 біт, а вказівник, оголошений як "ЗПД", було більше, напевно, 24, хоча я не впевнений.
rmeador

18
Інша гарантія полягає в тому, що sizeof (char *) == sizeof (void *), оскільки вони повинні мати однакові представлення (object [size] та value [набір бітів, що відповідають їх значенню] представлення)
Johannes Schaub - 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. Інші типи можуть бути будь-якого розміру, відповідного цьому компілятору. Немає вимог щодо узгодженості між цими типами вказівників.
Eclipse

добре, дякую. Тоді недарма gcc & VC мають різну реалізацію.
Gob00st

5
@Eclipse так: є: char <= короткий <= int <= довгий <= довгий довгий
Коул Джонсон

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 і має деякі прапори доступу
Roee Shenberg,

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 біт, то розмір (char *), мабуть, буде 1. Не кажучи вже про більш екзотичні типи покажчиків навіть у звичайних машинах, як Eclipse та dmityugov написати.
Kaz Dragon

@KazDragon sizeof(char*)==1,? Ти впевнений? Ви не маєте на увазі size(char)==1?
Аарон Макдейд

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

це неправда в x32-abi sites.google.com/site/x32abi
phuclv

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

17

Технічно кажучи, стандарт C гарантує лише, що розмір (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 байти. Якщо ваше твердження вірно, як це могло бути можливим ?!
Sangeeth Saravanaraj

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

@SangeethSaravanaraj: Зауважте, що теоретично розмір основного слова CPU і те, що компілятор вирішує "int", можуть бути довільно різними, це просто умовою для "int" бути нативним розміром слова до появи x86-64, де довго полегшити зворотний співвідбір.
Джозеф Гарвін

Дякую за пояснення! :)
Sangeeth Saravanaraj

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-4k таран (залежно від мікросхеми) з 2-х байт-покажчиками.

Це недавнє, дешеве, доступне для всіх і вартує кодування.


4

На додаток до того, що люди говорили про 64-бітні (або будь-які інші) системи, існують і інші види покажчика, ніж вказівник на об’єкт.

Вказівник на члена може бути майже будь-якого розміру, залежно від того, як їх реалізує ваш компілятор: вони не обов'язково навіть усі однакового розміру. Спробуйте вказівник на члена класу POD, а потім вказівник на члена, успадкований від одного з базових класів класу з декількома базами. Що весело.


3

Як я пам’ятаю, це ґрунтується на розмірі адреси пам’яті. Отже, у системі з 32-бітовою схемою адреси sizeof поверне 4, оскільки це 4 байти.


4
Такої вимоги немає. Не існує навіть вимоги, що sizeof (непідписаний int) == sizeof (підписаний 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

Загалом, розмір (майже все) зміниться при компіляції на різних платформах. На 32-бітній платформі вказівники завжди однакового розміру. На інших платформах (очевидний приклад - 64 біт) це може змінитися.



3

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

Отже, розмір вказівника залежить від компілятора. Але зазвичай більшість компіляторів реалізовані для підтримки 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-бітовими байтами розмір (char *) міг бути 1.
Роберт Гембл

"... з 32-бітовими байтами". Я не знав, що існують такі речі ... уявляю, що.
Ред С.

1
На 32-бітній качці sizeof (char *) повертає PI
Adriano Varoli Piazza

0

Тільки для повноти та історичного інтересу у 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.