WedX - журнал о программировании и компьютерных науках

Sizeof с разными спецификаторами

Я хочу знать, почему sizeof не работает с различными типами спецификаторов формата.

Я знаю, что sizeof обычно используется со спецификатором формата %zu, но я хочу знать для себя, что происходит позади и почему он печатает nan, когда я использую его с %f, или длинное число, когда я использую его с %lf

int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%d\n", sizeof(a + d));  // prints normal size of expression
printf("%lf\n", sizeof(s));     // prints big number
printf("%f", sizeof(d));        // prints nan
23.10.2019

  • Попробуйте номер 5 вместо sizeof. Вы получите аналогичный, но упрощенный вопрос. 23.10.2019
  • Тип возврата sizeof всегда один и тот же, он не меняется 23.10.2019
  • См. stackoverflow.com/questions/27296011/ 23.10.2019
  • Я позволю кому-то другому выбрать подходящего обманщика.... 23.10.2019
  • sizeof is usually used with the %zu - нет, sizeof следует использовать только с %zu. Это не обычно, это может быть только. 23.10.2019
  • @KamilCuk За исключением обычного случая M$, которые не поддерживают %zu.... 23.10.2019
  • @KamilCuk хорошо, но что происходит, когда я использую% f? Почему он печатает как нан? 23.10.2019
  • Потому что это неопределенное поведение. Вы говорите printf, что даете ему float, а на самом деле даете ему size_t. Эти два по-разному представлены в памяти. 23.10.2019
  • @ЕвгенийШ. что такое M$, сэр? 23.10.2019
  • Это не имеет ничего общего с оператором sizeof и имеет отношение ко всему, связанному с природой printf() varargs. 23.10.2019
  • @snr Ну, городской жаргон для MS AKA Microsoft :) 23.10.2019
  • @ЕвгенийШ. вы несете свое нагруженное мнение, которое неверно. MSVC поддерживает "%zu". 23.10.2019
  • @WeatherVane mingw gcc с библиотекой времени выполнения Microsoft C не поддерживает. Это не голословное мнение, а личный опыт. 23.10.2019
  • @ЕвгенийШ. "%zu" в MSVC задокументировано на эта страница и подтверждено в вопросе, на который вы ссылаетесь, - с 2013 года. Возможно, MS не поддержала его, как это сделали другие. Сказать ОП, что M$ не поддерживает% zu, вводит в заблуждение, и ваш предыдущий комментарий показался еще одним утомительным поп-музыкой в ​​MS. 23.10.2019
  • @ЕвгенийШ. спасибо, сэр (: 24.10.2019

Ответы:


1

sizeof оценивается как значение типа size_t. Правильным спецификатором для size_t в C99 является %zu. Вы можете использовать %u в системах, где size_t и unsigned int относятся к одному типу или, по крайней мере, имеют одинаковый размер и представление. В 64-битных системах значения size_t имеют 64 бита и, следовательно, больше, чем 32-битные целые числа. В 64-разрядных версиях Linux и OS/X этот тип определяется как unsigned long, а в 64-разрядных версиях Windows — как unsigned long long, поэтому использование %lu или %llu в этих системах также допустимо.

Передача size_t для несовместимой спецификации преобразования имеет неопределенное поведение:

  • программа может рухнуть (и, вероятно, так и будет, если вы используете %s)
  • программа может отображать ожидаемое значение (как это может быть для %d)
  • программа может выдавать странный вывод, например, nan вместо %f или что-то еще...

Причина этого в том, что целые числа и значения с плавающей запятой по-разному передаются в printf, и они имеют другое представление. Передача целого числа, где printf ожидает double, позволит printf получить значение с плавающей запятой из регистров или областей памяти, которые имеют случайное содержимое. В вашем случае регистр с плавающей запятой просто содержит значение nan, но он может содержать другое значение в другом месте программы или позже, ничего нельзя ожидать, поведение не определено.

Некоторые устаревшие системы не поддерживают %zu, особенно среды выполнения C от Microsoft. В этих системах вы можете использовать %u или %lu и использовать приведение для преобразования size_t в unsigned или unsigned long:

int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%u\n", (unsigned)sizeof(a + d));       // should print 8
printf("%lu\n", (unsigned long)sizeof(s));     // should print 4
printf("%llu\n", (unsigned long long)sizeof(d)); // prints 4 or 8 depending on the system
23.10.2019

2

Я хочу знать для себя, что происходит позади и почему он печатает nan, когда я использую его с %f, или длинное число, когда я использую его с %lf.

Некоторые причины.

Во-первых, printf не знает типы дополнительных аргументов, которые вы ему фактически передаете. Он полагается на строку формата, чтобы сообщить ему количество и типы дополнительных аргументов, которые нужно ожидать. Если вы передадите size_t в качестве дополнительного аргумента, но скажете printf ожидать float, то printf будет интерпретировать битовую комбинацию дополнительного аргумента как float, а не size_t. Целочисленные типы и типы с плавающей запятой имеют совершенно разные представления, поэтому вы получите значения, которых не ожидаете (включая NaN).

Во-вторых, разные типы имеют разные размеры. Если вы передадите 16-битное short в качестве аргумента, но скажете printf ожидать 64-битного double с %f, то printf будет искать дополнительные байты сразу после этого аргумента. Не гарантируется, что size_t и double имеют одинаковые размеры, поэтому printf может либо игнорировать часть фактического значения, либо использовать байты из памяти, которые не являются частью значения.

Наконец, это зависит от того, как передаются аргументы. В некоторых архитектурах для передачи аргументов (по крайней мере, для первых нескольких аргументов) используются регистры, а не стек, и разные регистры используются для чисел с плавающей запятой и целых чисел, поэтому, если вы передаете целое число и сообщаете ему ожидать двойное значение с %f, printf может посмотрите не в том месте и напечатайте что-то совершенно случайное.

printf не умный. Он зависит от того, используете ли вы правильный спецификатор преобразования для типа аргумента, который вы хотите передать.

23.10.2019
Новые материалы

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

Учебные заметки: создание моего первого пакета Node.js
Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


Для любых предложений по сайту: [email protected]