Почему адрес данных char не отображается? - PullRequest
48 голосов
/ 01 февраля 2011
class Address {
      int i ;
      char b;
      string c;
      public:
           void showMap ( void ) ;
};

void Address :: showMap ( void ) {
            cout << "address of int    :" << &i << endl ;
            cout << "address of char   :" << &b << endl ;
            cout << "address of string :" << &c << endl ;
}

Вывод:

         address of int    :  something
         address of char   :     // nothing, blank area, that is nothing displayed
         address of string :  something 

Почему?

Еще одна интересная вещь: если int, char, string общедоступны, то вывод

  ... int    :  something 
  ... char   :   
  ... string :  something_2

something_2 - something всегда равно 8. Почему?(не 9)

Ответы [ 8 ]

78 голосов
/ 01 февраля 2011

Когда вы берете адрес b, вы получаете char *. operator<< интерпретирует это как строку C и пытается вывести последовательность символов вместо ее адреса.

вместо cout << "address of char :" << (void *) &b << endl.

[РЕДАКТИРОВАТЬ] Как прокомментировал Томек, в данном случае более правильным приведением является static_cast, что является более безопасной альтернативой. Вот версия, которая использует его вместо приведения в стиле C:

cout << "address of char   :" << static_cast<void *>(&b) << endl;
23 голосов
/ 07 февраля 2011

Есть 2 вопроса:

  • Почему не печатается адрес для символа:

Печать указателей будет печатать адрес дляint* и string*, но не будут печатать содержимое для char*, поскольку в operator<< имеется специальная перегрузка.Если вам нужен адрес, введите: static_cast<const void *>(&c);

  • Почему разница адресов между int и string составляет 8

На вашей платформе sizeof(int) - это 4, а sizeof(char) - это 1, поэтому вы действительно должны спросить, почему 8 не 5.Причина в том, что строка выровнена по 4-байтовой границе.Машины работают со словами, а не с байтами, и работают быстрее, если слова не «разбиваются» на несколько байтов здесь и несколько байтов там.Это называется alignment

Ваша система, вероятно, выравнивается по 4-байтовым границам.Если бы у вас была 64-битная система с 64-битными целыми числами, разница была бы 16.

(Примечание: 64-битная система обычно ссылается на размер указателя, а не на int. Так что 64-битнаясистема с 4-байтовым int все равно будет иметь разность 8, так как 4 + 1 = 5, но округляется до 8. Если sizeof (int) равен 8, то 8 + 1 = 9, но округляется до 16)

13 голосов
/ 01 февраля 2011

Когда вы передаете адрес символа в ostream, он интерпретирует его как адрес первого символа строки "C-стиля" ASCIIZ и пытается напечатать предполагаемую строку.У вас нет NUL-терминатора, поэтому выходные данные будут пытаться читать из памяти, пока не произойдет его поиск, или ОС не отключит его для попытки чтения с недопустимого адреса.Весь мусор, который он просматривает, будет отправлен на ваш выход.

Вы, вероятно, можете заставить его отображать нужный вам адрес, приведя его, как в (void*)&b.

Re смещения вструктура: вы заметили, что строка помещена со смещением 8. Вероятно, это связано с тем, что у вас есть 32-разрядные числа, затем 8-разрядный символ, а затем компилятор решает вставить еще 3 8-разрядных символа, чтобы строковый объект былвыровнен по границе 32-битного слова.Многим ЦП / архитектурам памяти нужны указатели, целые числа и т. Д., Чтобы они находились на границах размера слова, чтобы выполнять над ними эффективные операции, и в противном случае им пришлось бы выполнять гораздо больше операций для чтения и объединения нескольких значений из памяти, прежде чем они смогут использовать значенияв операции.В зависимости от вашей системы может потребоваться, чтобы каждый объект класса начинался с границы слова, или std::string, в частности, начинается с size_t, указателя или другого типа, который требует такого выравнивания.

10 голосов
/ 01 февраля 2011

Потому что, когда вы передаете char* в std::ostream, он напечатает строку в стиле C (то есть: массив символов, char*), на которую он указывает.

Помните, что "hello" являетсяchar*.

4 голосов
/ 01 февраля 2011

Адрес char обрабатывается как строка с нулевым символом в конце и отображает содержимое этого адреса, которое, вероятно, не определено, но в данном случае это пустая строка.Если вы приведете указатели к void *, вы получите желаемые результаты.

Разница между чем-то2 и чем-то, будучи 8, обусловлена ​​выравниванием и способностью компилятора самостоятельно решать, где в стекепеременные объявлены.

2 голосов
/ 01 февраля 2011

Ваш синтаксис должен быть

cout << (void*) &b
2 голосов
/ 01 февраля 2011

Для второго вопроса - компилятор по умолчанию будет дополнять элементы структуры.Пэдом по умолчанию является sizeof(int), 4 байта (на большинстве архитектур).Вот почему int, за которым следует char, займет 8 байтов в структуре, поэтому элемент string имеет смещение 8.

Чтобы отключить заполнение, используйте #pragma pack(x), где x - эторазмер пэда в байтах.

0 голосов
/ 13 февраля 2011

hrnt прав насчет причины пробела: &b имеет тип char* и поэтому печатается в виде строки до первого нулевого байта. Предположительно b равно 0. Если вы установите b, скажем, 'A', то вы должны ожидать, что распечатка будет строкой, начинающейся с 'A' и продолжающейся с мусором до следующего нулевого байта. Используйте static_cast<void*>(&b), чтобы распечатать его как адрес.

Для вашего второго вопроса, &c - &i - это 8, потому что размер целого числа равен 4, символ - 1, и строка начинается на следующей 8-байтовой границе (вы, вероятно, находитесь в 64-битной системе) , Каждый тип имеет определенное выравнивание, и C ++ выравнивает поля в структуре в соответствии с ним, соответственно добавляя отступы. (Практическое правило заключается в том, что примитивное поле размера N выравнивается по кратному N.) В частности, вы можете добавить еще 3 char поля после b, не затрагивая адрес &c.

...