Предупреждение о конверсии с шагом [дубликат]

Короче говоря, объекты Java обладают некоторыми очень своеобразными свойствами.

В общем, Java имеет примитивные типы (int, bool, char, double и т. д.), которые передаются непосредственно по значению. Тогда у Java есть объекты (все, что происходит от java.lang.Object). Объекты на самом деле всегда обрабатываются посредством ссылки (ссылка является указателем, который вы не можете коснуться). Это означает, что по сути объекты передаются по ссылке, так как ссылки обычно не интересны. Тем не менее, это означает, что вы не можете изменить, на какой объект указывается, поскольку сама ссылка передается по значению.

Звучит ли это странно и запутанно? Рассмотрим, как C реализует передачу по ссылке и передает значение. В C по умолчанию принято передать значение. void foo(int x) передает значение int по значению. void foo(int *x) - это функция, которая не хочет int a, а указатель на int: foo(&a). Можно было бы использовать это с оператором & для передачи адреса переменной.

Возьмите это на C ++, и у нас есть ссылки. Ссылки в основном (в этом контексте) синтаксического сахара, которые скрывают указательную часть уравнения: void foo(int &x) вызывается foo(a), где сам компилятор знает, что это ссылка и адрес без ссылки a должен быть принят. В Java все переменные, относящиеся к объектам, фактически относятся к ссылочному типу, фактически вызывая вызов по ссылке для большинства целей и целей без мелкозернистого управления (и сложности), предоставляемого, например, C ++.

63
задан Community 23 May 2017 в 12:09
поделиться

4 ответа

Если мы посмотрим на Обоснование для языков международного стандартного программирования-C в разделе 6.3.1.8 Обычные арифметические преобразования , в нем говорится ( акцент мой в будущее ):

Правила в Стандарте для этих преобразований являются небольшими изменениями в K & amp; R: модификации соответствуют добавленным типам и правилам сохранения значения. Явная лицензия была добавлена ​​для выполнения вычислений в «более широком» типе, чем это абсолютно необходимо, поскольку иногда это может приводить к меньшему и более быстрому коду, не говоря уже о правильном ответе более часто. Вычисления также могут выполняться в «более узком» типе с помощью правила «как если бы», пока тот же конечный результат получен. Явное литье всегда можно использовать для получения значения в желаемом типе

Раздел 6.3.1.8 из черновика стандарта C99 охватывает Обычные арифметические преобразования , который применяется к операндам арифметических выражений, например, в разделе 6.5.6 Аддитивные операторы говорят:

Если оба операнда имеют арифметический тип, обычные арифметические преобразования выполненных над ними.

Мы находим аналогичный текст в разделе 6.5.5 Мультипликативные операторы . В случае операнда short сначала целочисленные рекламные акции применяются из раздела 6.3.1.1 Boolean, characters и integers , который гласит:

Если int может представлять все значения исходного типа, значение преобразуется в int; в противном случае он преобразуется в unsigned int. Они называются целыми рекламными акциями.48) Все остальные типы не изменяются целыми рекламными акциями.

Обсуждение из раздела 6.3.1.1 Обоснование или Международные языки стандартного программирования - C в целых рекламных акциях на самом деле интереснее, я собираюсь выборочно указывать b / c, что слишком долго, чтобы полностью указать:

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

[...]

Подход без знака предполагает использование двух меньших неподписанных типов для unsigned int. Это простое правило и дает тип, который не зависит от среды исполнения.

Подход сохранения значения требует продвижения этих типов к подписанному int, если этот тип может правильно представлять все значения исходного типа, и в противном случае для продвижения этих типов к unsigned int. Таким образом, если среда выполнения представляет короткий, чем нечто меньшее, чем int, unsigned short становится int; в противном случае он становится беззнаковым int.

Это может иметь некоторые довольно неожиданные результаты в некоторых случаях как . Непоследовательное поведение неявного преобразования между неподписанными и более крупными подписанными типами демонстрирует, есть таких примеров больше. Хотя в большинстве случаев это приводит к работам, которые ожидаются.

35
ответ дан Community 18 August 2018 в 08:43
поделиться
  • 1
    Да иногда это будет меньше и быстрее, потому что вам не нужны дополнительные инструкции для подписи / нулевого расширения, чтобы расширить значения до int или замаскировать высокие биты. В x86 вам не нужны дополнительные префиксы команд для изменения размеров аргументов – phuclv 24 June 2014 в 03:46
  • 2
    Слишком плохо, логика не добавила вторичного правила, что если результат аддитивного, мультипликативного или побитового оператора принуждается к неподписанному типу, меньшему, чем int, выражение будет вести себя так, как если бы его операнды были также принудительно и операция выполненных на меньшем типе. Нет определенных случаев, которые противоречат такому правилу, но некоторые компиляторы могут использовать продвижение в качестве оправдания, чтобы сделать вывод, что выражение типа x*=y; (с обеими переменными unsigned short) обещает, что x не может превышать 2147483648 / y. – supercat 12 September 2015 в 18:11
  • 3
    если у меня есть что-то вроде этого int x = 1234 и char *y = &x. Двоичное представление 1234 - 00000000 00000000 00000100 11010010. Моя машина немного ориентирована, поэтому она меняет ее и сохраняет в памяти 11010010 00000100 00000000 00000000 LSB приходит первым. Теперь основная часть. если я использую printf("%d" , *p). printf будет читать первый байт 11010010, только выход -46, но 11010010 - 210, поэтому зачем он печатает -46. Я действительно смущен, я думаю, что какой-то символ для целостного продвижения делает что-то, но я не знаю. – Suraj Jain 17 August 2016 в 10:24
  • 4
    Вы цитируете стандарт C99, но такое поведение не старше этого? Мне нужно ложиться спать, o / w Я бы увидел, могу ли я найти что-то в K & amp; R. – PJTraill 10 March 2017 в 00:39
  • 5
    @PJTraill хорошо wikipedia указывает на версию c89 , хотя вы не можете получить официальный черновик. В этой версии в Обычные арифметические преобразования он описывает очень похожую процедуру. Поэтому я бы сказал «да». Обратите внимание, что приведенная выше цитата говорит о небольших модификациях тех, что указаны в K & amp; R , поэтому K & amp; R должен быть другим. – Shafik Yaghmour 10 March 2017 в 05:46
Типы

float, short и char рассматриваются стандартным типом «типов хранения», то есть субдиапазонами, которые вы можете использовать для сохранения некоторого пространства, но которые не собираются покупать вам какую-либо скорость, потому что их размер «неестественен» для CPU.

На некоторых процессорах это неверно, но хорошие компиляторы достаточно умны, чтобы заметить, что если вы, например, добавьте константу в unsigned char и сохраните результат обратно в unsigned char, тогда нет необходимости проходить через преобразование unsigned char -> int. Например, с g ++ код, сгенерированный для внутреннего цикла

void incbuf(unsigned char *buf, int size) {
    for (int i=0; i<size; i++) {
        buf[i] = buf[i] + 1;
    }
}

, является просто

.L3:
    addb    $1, (%rdi,%rax)
    addq    $1, %rax
    cmpl    %eax, %esi
    jg  .L3
.L1:

, где вы можете видеть, что команда беззнакового добавления сложения (addb) Используется.

То же самое происходит, если вы выполняете вычисления между короткими int и сохраняете результат в коротких int.

15
ответ дан 6502 18 August 2018 в 08:43
поделиться

Связанный вопрос, похоже, очень хорошо его охватывает: CPU просто нет. 32-разрядный процессор имеет свои собственные арифметические операции, настроенные для 32-разрядных регистров. Процессор предпочитает работать в своем любимом размере, и для таких операций копирование небольшого значения в регистр собственного размера дешево. (Для архитектуры x86 32-разрядные регистры названы так, как если бы они были расширенными версиями 16-разрядных регистров (eax - ax, ebx - bx и т. Д.), См. x86 целочисленные инструкции ).

Для некоторых чрезвычайно распространенных операций, в частности векторной / float-арифметики, могут быть специализированные инструкции, которые работают с другим типом или размером регистра. Для чего-то вроде короткого, заполнение (до) 16 бит нулей имеет очень небольшую стоимость исполнения и добавление специализированных инструкций, вероятно, не стоит времени или места на уме (если вы хотите получить действительно физическое о том, почему, я не уверен, что они будут занимать реальное пространство, но он становится более сложным).

7
ответ дан Keith Thompson 18 August 2018 в 08:43
поделиться
  • 1
    Это не является чисто аппаратной проблемой, был сделан сознательный выбор, сделанный при разработке стандарта C99, чтобы сделать целостные рекламные акции определенным образом. – Shafik Yaghmour 24 June 2014 в 03:24
  • 2
    «Обратите внимание, что 32-разрядные регистры также называются так, как если бы они были расширенными версиями 16-разрядных регистров (от eax до ax, ebx-bx и т. д.)» это верно для x86, но неверно для большинства других архитектур. Регистры MIPS имеют одно и то же имя независимо от 32 или 64-битного режима, и они всегда работают в собственном размере, поэтому вы не можете делать арифметику в 8 или 16 бит в любом случае – phuclv 24 June 2014 в 03:41

Это не особенность языка, так как это ограничение физических процессорных архитектур, на которых выполняется код. Экран int в C обычно является размером вашего стандартного регистра CPU. Больше кремния занимает больше места и больше мощности, поэтому во многих случаях арифметику можно выполнять только по типам «натурального размера». Это не универсально, но большинство архитектур все еще имеют это ограничение. Другими словами, при добавлении двух 8-битных чисел то, что на самом деле происходит в процессоре, это тип 32-разрядной арифметики, за которой следует либо простая битовая маска, либо другое подходящее преобразование типа.

20
ответ дан Phonon 18 August 2018 в 08:43
поделиться
  • 1
    Я не уверен, что обязательно будет небольшая маска. Процессор выполняет арифметику в своем собственном размере слова, а затем сохраняет только младшие бит обратно в память. (Кроме того, хотя вы правы, что большинство архитектур делают только арифметику слов, одно заметное исключение - Intel довольно распространено.) – James Kanze 23 June 2014 в 20:00
  • 2
    @JamesKanze Ты прав. Я отредактировал ответ. И да, Intel - это путь, когда дело доходит до оптимизированной арифметики, особенно с их IPP-библиотеками. – Phonon 23 June 2014 в 20:04
  • 3
    Я не согласен с «это не особенность языка»; it является признаком языка. Он определяется так, потому что ... но он определяется языком, а не процессором. – Jonathan Leffler 24 June 2014 в 00:55
  • 4
    @JonathanLeffler Это, безусловно, особенность языка. По-моему, большинство языков. Но ответ Phonon объясняет, что почему языки имеют эту функцию. (Вероятно, стоит отметить, что в прошлом машины имели только слова, а не байты, пол-слова и т. Д. И когда была введена байтовая адресация, это затрагивало доступ к памяти, а не регистры и операции. Так что, хотя PDP-11 как байтовые, так и текстовые инструкции, когда целевой адрес байтовой команды был регистром, байт был знаком расширен до слова.) – James Kanze 24 June 2014 в 08:58
  • 5
    Как процессор выполняет команды, полностью скрывается от кода пользователя. Вы вообще не ответили на вопрос. – Sophit 24 June 2014 в 21:37
Другие вопросы по тегам:

Похожие вопросы: