Представьте, что у вас есть эта функция:
void foo(long l) { /* do something with l */}
Теперь вы называете это так на сайте вызова:
foo(65); // here 65 is of type int
Почему (технически), когда вы указываете в объявлении своей функции, что вы ожидаете long
и вы передаете только номер без L
суффикс, это рассматривается как int
?
Теперь я знаю, что это так, потому что в стандарте C ++ так говорится, что является технической причиной 65
не просто повышен до типа long
и так спаси нас от глупой ошибки забвения L
суффикс, чтобы сделать его длинным явно?
Я нашел это в стандарте C ++:
4.7 Интегральные преобразования [conv.integral]
5 Преобразования, разрешенные как интегральные продвижения, исключаются из набора интегральных преобразований.
Я могу подумать, что сужающее преобразование не выполняется неявно, но здесь тип назначения явно шире, чем тип источника.
РЕДАКТИРОВАТЬ
Этот вопрос основан на вопрос Я видел ранее, который имел забавное поведение, когда вы не указали L
суффикс. пример, но, возможно, это вещь C, больше, чем C ++? !!
В C ++ объекты и значения имеют тип, который не зависит от того, как вы их используете. Затем, когда вы используете их, если вам нужен другой тип, он будет преобразован соответствующим образом.
Проблема в связанном вопросе заключается в том, что varargs не является типобезопасным. Предполагается, что вы передаете правильные типы и декодируете их такими, какие они есть. При обработке вызывающей стороны компилятор не знает, как вызываемый будет декодировать каждый из аргументов, поэтому он не может преобразовать их для вас. По сути, varargs столь же безопасен, как и преобразование в void*
и преобразование обратно в другой тип, если вы понимаете это правильно, вы получаете то, что вы нажали, если вы ошибаетесь, вы получаете мусор.
Также обратите внимание, что в данном конкретном случае с включением компилятора имеет достаточно информации, но это всего лишь маленький случай из общей семьи, если ошибки. Рассмотрим printf
Семейство функций, в зависимости от содержимого первого аргумента, каждый из аргументов обрабатывается как отдельный тип. Попытка исправить этот случай на уровне языка может привести к несоответствиям, когда в некоторых случаях компилятор делает правильные или неправильные действия, и пользователю будет неясно, когда ожидать чего, включая тот факт, что он может это сделать. правильно сегодня и неправильно завтра, если во время рефакторинга определение функции перемещено и недоступно для вставки, или если логика функции изменяется и аргумент обрабатывается как тот или иной тип на основе некоторого предыдущего параметра.
Функция в этом случае получает long
не int
, Компилятор автоматически преобразует любой аргумент в требуемый тип параметра, если это возможно, без потери какой-либо информации (как здесь). Это одна из основных причин, по которым прототипы функций важны.
По сути, это то же самое, что и выражение (1L + 1)
— потому что целое число 1
не правильный тип, он неявно преобразуется в long
выполнить расчет, и в результате long
,
Если вы пройдете 65L
в этом вызове функции преобразование типов не требуется, но практической разницы нет — 65L
используется в любом случае.
Хотя это и не C ++, это важная часть стандарта C99, которая также объясняет примечание к var args:
Если выражение, которое обозначает вызываемую функцию, имеет тип, который
действительно включает в себя прототип, аргументы неявно преобразуются, как
если по назначению, к типам соответствующих параметров, принимая
тип каждого параметра, который будет безусловной версией его
объявленный тип. Нотация многоточия в прототипе функции
объявитель останавливает преобразование типа аргумента после последнего
объявленный параметр. Продвижение аргументов по умолчанию выполняется на
последние аргументы.
Почему, (технически), когда вы указываете в объявлении своей функции, что вы ожидаете длинное, и вы передаете только число без
L
суффикс, это рассматривается какint
?
Поскольку тип литерала определяется только формой литерала, а не контекстом, в котором он используется. Для целого числа это int
если значение не слишком велико для этого типа или суффикс не используется для указания другого типа.
Теперь, я знаю, это потому, что в стандарте C ++ так говорится, однако, что является технической причиной, по которой эти 65 не просто повышены до типа
long
и так спаси нас от глупой ошибки забвенияL
суффикс, чтобы сделать егоlong
явно?
Значение должно быть повышено до long
независимо от того, указали ли вы этот тип явно, поскольку объявлена функция, принимающая аргумент типа long
, Если этого не происходит, возможно, вы могли бы привести пример кода, который дает сбой, и описать, как он дает сбой?
ОБНОВЛЕНИЕ: пример, который вы приводите, передает литерал функции, принимающей нетипизированный многоточие (...
) аргументы, а не типизированные long
аргумент. В этом случае вызывающая функция понятия не имеет, какой тип ожидается, и только продвижение аргумента по умолчанию применяются. В частности, значение типа int
остается int
когда прошло через аргументы многоточия.
Стандарт C гласит:
«Тип целочисленной константы является первым из соответствующего списка, в котором ее значение может быть представлено».
В C89 этот список:
int, long int, unsigned long int
C99 расширяет этот список, чтобы включить:
long long int, unsigned long long int
Таким образом, когда вы компилируете код, литерал 65 вписывается в тип int, поэтому его тип соответственно int. Затем int повышается до long при вызове функции.
Если, например, sizeof (int) == 2 и ваш литерал имеет значение, близкое к 64000, тип значения будет длинным (при условии, что sizeof (long)> sizeof (int)).
Суффиксы используются, чтобы перезаписать поведение по умолчанию и заставить указанное литеральное значение иметь определенный тип. Это может быть особенно полезно, когда целочисленное продвижение будет дорого (например, как часть уравнения в замкнутом цикле).
У нас должно быть стандартное значение для типов, потому что для приложений более низкого уровня тип ДЕЙСТВИТЕЛЬНО имеет значение, особенно для целочисленных типов. Операторы низкого уровня (такие как bithift, add, ect) полагаются на тип входа для определения местоположения переполнения. ((65 << 2) с целыми числами — 260 (0x104), но с одним символом — 4! (0x004)). Иногда вам нужно такое поведение, иногда нет. Как программист, вы просто должны всегда иметь возможность знать, что собирается делать компилятор. Таким образом, проектное решение было принято, чтобы человек явно объявил целочисленные типы своих констант, с «недекорированным» как наиболее часто используемым типом, целым числом.
Компилятор автоматически «приводит» ваши константные выражения во время компиляции, так что эффективное значение, передаваемое функции, является длинным, но по этой причине вплоть до приведения оно считается int.