Разница в целочисленном размере для 64-битной системы (путайте с моей старой 32-битной системой ПК) - PullRequest
1 голос
/ 18 сентября 2011

Несколько месяцев назад я приобрел себе ноутбук с процессором Intel i7-2630qm с 64-битными окнами.Практикуя свои навыки программирования в этой системе, я столкнулся с некоторой разницей с точки зрения целочисленного размера, которая заставляет меня думать, что это, вероятно, связано с моей новой 64-битной системой.

Давайте посмотрим на код.

Код C:

#include <stdio.h>

int main(void)
{
    int num = 20;

    printf("%d %lld\n" , num , num);

    return 0;
}

Вопрос:

1.) Я помню, как получил этот новый ноутбук, которыйозначает, что в то время, когда я все еще использую мою старую 32-битную систему, когда я запускаю этот код, программа выведет целое число 20, а рядом с ним будет какое-то случайное число из-за спецификатора %lld.

2.) Но этого явления больше не происходит, когда я использую свой новый ноутбук, вместо этого он будет правильно печатать оба целых числа, даже если я изменю переменную num на тип short.

3.) Является ли это в 64-битной системе, есть новое целочисленное продвижение, которое будет продвигать int на длинные, если оно используется в качестве аргумента? Или это short целое число может быть преобразовано в long long, что является 64-битнымтоже когда передать в качестве аргумента ??

4.) Кроме тЕсли я в какой-то степени путаюсь, то в 16-битной системе int будет 16-битной и 32-битной в 32-битной системе. Но почему она не станет 64-битной?когда он на 64-битном ??

======================================================================================= Дополнение:

1.) Я выбираю "консольную программу (64-разрядную)" в качестве своего проекта в IDE при использовании моего нового ноутбука, но "консольную программу" на моей 32-разрядной старой системе ПК.

2.) Я проверил размер int в проекте "консольная программа (64-разрядная версия)", используя оператор sizeof, и он возвращает 32-разрядную версию, тогда как short все еще остается 16-разрядной.. Единственное изменение - long тип, оно 64-битное, а long long все еще остается обычным 64-битным размером.

Ответы [ 4 ]

3 голосов
/ 19 сентября 2011

Вы видите этот побочный эффект, потому что соглашение о вызовах отличается для кода x64.Аргументы функции в 32-битном коде x86 передаются в стек.Функция printf () будет читать слово из стека, которое не является частью фрейма активации.Вероятность того, что он содержит значение 0, чрезвычайно мала.

В коде x64 первые 4 аргумента для функции передаются через регистры процессора, а не через стек.Шансы на то, что старшее слово 64-битного регистра случайно равно нулю, весьма хороши.Оставленная там предыдущая 64-битная операция, которая работала с небольшими числами.Но, конечно, это не гарантировано.

Попытка объяснить поведение неопределенного поведения в противном случае бесполезна.Кроме попыток угадать, как реализован язык для ядра, которое находится на вашем компьютере.Есть лучшие ресурсы для этого.Изучение машинного кода, применимого к вашему компилятору, является отличным способом.Вместе с приличным отладчиком, который показывает вам, как ваш C-код был переведен в машинный код.Машинный код не имеет неопределенного поведения.

1 голос
/ 18 сентября 2011

У меня сейчас нет доступа к 64-битному компилятору Windows, но я думаю следующее.

Ваш вопрос не о целочисленном продвижении, а о том, как параметры передаются от вызывающей функции вызываемой функции. Это выходит за рамки спецификации C, но интересно знать.

В 32-битном режиме все параметры делятся на 32-битные блоки, так как все регистры могут содержать 32 бита. Так что в этом случае у нас есть следующий макет стека:

[ 32-bit format string pointer ][ num as 32-bit ][ num as 32-bit ] junk...

В 64-битном режиме все параметры разделены на 64-битные блоки, так как все регистры могут содержать 64 бита. Таким образом, стек будет содержать следующее:

[ 64-bit format string pointer ][ num as 64-bit ][ num as 64-bit ] junk...

Старшие 32 бита 64-битных регистров, содержащих 32-битные значения, обычно устанавливаются в ноль.

Таким образом, когда printf читает 64-разрядное число, оно загружает эквивалент двух 32-разрядных регистров на 32-разрядной платформе, но только один 64-разрядный регистр с очищенными старшими битами на 64-разрядной платформе. битовая платформа.

1 голос
/ 18 сентября 2011

(1 и 2) Как уже говорилось, поведение в этой ситуации не определено, поэтому компилятору разрешается вести себя по-разному по любой причине или вообще без причины.

(3) Компиляторразрешено определять int как 64-битный, и в этом случае продвижение не потребуется, поскольку все рассматриваемые переменные будут иметь одинаковый размер.Но это почти наверняка не так.

(4) В большинстве или во всех 64-битных компиляторах int является 32-битным.Это потому, что int был 32-битным так долго, что программисты ожидали его, и изменение его нарушило бы существующий код.Насколько я знаю, это официально не является частью стандарта, но это один из тех стандартов де-факто, которые еще сложнее изменить.: -)

1 голос
/ 18 сентября 2011

Все, что вы описываете, зависит от спецификаций вашего компилятора и платформы, на которой вы работаете (за исключением того, что long гарантированно будет иметь такой же размер, как int):

Записи в Википедии:

длинный длинный

INT

Стандарт c99 стремится покончить с этой двусмысленностью, добавив определенные типы; int32_t, uint64_t и т. Д. Есть также спецификация POSIX, которая определяет u_int32_t и т. Д.

Редактировать: Я пропустил вопрос о printf(), извините. Как указывает @nos в комментариях к вашему вопросу, передача чего-то кроме long long в %lld приводит к неопределенному поведению . Это означает, что нет никакого рифмы или причины относительно того, что он будет делать; спонтанно появляющиеся единороги не могли быть исключены.

Да, и на всех известных мне компиляторах и ОС int 32-битная. Изменение, которое может сломать вещи, которые зависят от того, что оно 32-битное.

...