Почему «sizeof (a? True: false)» выдает четыре байта?

133

У меня есть небольшой фрагмент кода об sizeofоператоре с троичным оператором:

#include <stdio.h>
#include <stdbool.h>

int main()
{
    bool a = true;
    printf("%zu\n", sizeof(bool));  // Ok
    printf("%zu\n", sizeof(a));     // Ok
    printf("%zu\n", sizeof(a ? true : false)); // Why 4?
    return 0;
}

Выход ( GCC ):

1
1
4 // Why 4?

Но здесь,

printf("%zu\n", sizeof(a ? true : false)); // Why 4?

троичный оператор возвращает booleanтип, а sizeof boolявляется 1байтом в C.

Тогда почему sizeof(a ? true : false)выдается четыре байта?

ЦКМ
источник
39
sizeof(true)а sizeof(false)также 4: ide.geeksforgeeks.org/O5jvuN
tkausl
7
Более интересный вопрос здесь заключается в том, почему эта реализация «противоречива» в том смысле, что она явно определяет _Boolразмер 1, но не « trueи» false. Но стандарту нечего сказать об этом, насколько я могу судить.
12
@FelixPalmen та же причина, почему дано char a; sizeof(a) == 1и sizeof('a') == sizeof(int)(на С). Дело не в реализации, а в языке.
нет. местоимения м.
10
Вы пытались напечатать sizeof(true)? возможно, это прояснит ситуацию (в частности, станет очевидным, что троичный оператор - красная сельдь).
нет. местоимения м.
4
@FelixPalmen trueесть #defineд , чтобы быть 1 с помощью stdbool.hда так, что это определение буквальным.
нет. местоимения м.

Ответы:

223

Это потому что у тебя есть #include <stdbool.h>. Этот заголовок определяет макросы true и falseбыть 1и 0, поэтому ваше утверждение выглядит следующим образом:

printf("%zu\n", sizeof(a ? 1 : 0)); // Why 4?

sizeof(int) 4 на вашей платформе.

Джастин
источник
21
"Это потому что у вас есть #include <stdbool.h>" Нет, это не так. sizeof(a ? (uint8_t)1 : (uint8_t)0);также даст результат 4. ?:Важной частью здесь является целочисленное продвижение операндов, а не размер trueи false.
Лундин
9
@Lundin: оба важны. Как написано, тип уже intбез продвижения. Причина, по которой вы не можете «исправить», - это продвижение по умолчанию.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
5
@PeterSchneider Это не C ++. Это C. В C ++ trueи неfalse являются макросами; они являются ключевыми словами. Они не определены как и , но как истинные и ложные значения типа. 10bool
Джастин
5
@PeterSchneider Нет, вы узнали кое-что о C сегодня. Не путайте два языка. В C ++ sizeof(true)есть 1. Демо .
Rakete1111
1
Правда, перепутал. Не читал внимательно и был заправлен cppreference-link. Моя вина, спасибо. Но в любом случае у меня такое чувство к с ++.
Питер Шнайдер
66

Здесь тернарный оператор возвращает booleanтип,

ОК, это еще не все!

В С результат этой троичной операции имеет тип int. [примечания ниже (1,2)]

Следовательно, результат совпадает с выражением sizeof(int)на вашей платформе.


Примечание 1: Цитирование C11, глава §7.18,Boolean type and values <stdbool.h>

[....] Оставшиеся три макроса подходят для использования в #ifдирективах предварительной обработки. Они есть

true

который расширяется до целочисленной константы 1,

false

который расширяется до целочисленной константы 0, [....]

Примечание 2: Для условного оператора, глава §6.5.15, ( выделено мной )

Первый операнд оценивается; между оценкой и оценкой второго или третьего операнда существует точка последовательности (в зависимости от того, что оценивается). Второй операнд оценивается, только если первый сравнивается с неравным 0; третий операнд оценивается, только если первый сравнивается равным 0; результат - значение второго или третьего операнда (какой бы ни был оценен), [...]

и

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

следовательно, результат будет целочисленным, а из-за диапазона значений константы имеют тип int.

Тем не менее, общий совет int main()должен быть лучше int main (void)соответствовать стандартам.

Сурав Гош
источник
@ user694733 ммм .. почему бы и нет? <stdbool.h>определяет MACROS для типа int.. это что-то не так?
Сурав Гош
@BasileStarynkevitch Хорошо, я вижу, что сейчас, это кажется действительно неправильным, обновлено сейчас.
Сурав Гош
58

Тройной оператор - красная сельдь.

    printf("%zu\n", sizeof(true));

печатает 4 (или что-нибудь sizeof(int)на вашей платформе).

Далее предполагается, что boolэто синоним charили аналогичный тип размера 1 и intбольше, чем char.

Причина , почему sizeof(true) != sizeof(bool)и sizeof(true) == sizeof(int)просто потому , что trueэто не выражение типа bool. Это выражение типа int. Это #defineкак 1в stdbool.h.

Нет никаких значений типа boolв C вообще. Каждому такому значению сразу присваивается значение int, даже если оно используется в качестве аргумента sizeof. Изменить: этот абзац не соответствует действительности, аргументы, которые sizeofне будут выдвинуты int. Это не влияет ни на один из выводов, хотя.

п. местоимения м.
источник
Хороший ответ. После того, как я прочитал наиболее одобренный в настоящее время ответ, я подумал, что все утверждения должны быть оценены до 4. Это прояснило ситуацию. +1
Педро А
5
Разве это не (bool)1тип значения bool?
Бен Фойгт
printf("%u\n", sizeof((char) 1));печатает 1на моей платформе, тогда как printf("%u\n", sizeof(1));печатает 4. Не означает ли это, что ваше утверждение «Каждое такое значение сразу переводится в int, даже если оно используется в качестве аргумента для sizeof», является ложным?
Джонатан
Это на самом деле не отвечает на вопрос. Размер и тип и trueт. Д. На самом деле не имеет значения в случае, ?:так как в intлюбом случае он получает целое число . То есть ответ должен касаться, почему ?: это красная сельдь.
Лундин
6
Я думаю, что ответ решает проблему наилучшим образом. Вы можете понизить или улучшить его.
нет. местоимения м.
31

Относительно логического типа в C

Булевый тип был введен довольно поздно в языке C, в 1999 году. До этого C не имел булева типа, а вместо этого использовался intдля всех булевых выражений. Поэтому все логические операторы, такие как > == !etc, возвращают intзначение 1или 0.

В приложениях было принято использовать домашние типы, такие как typedef enum { FALSE, TRUE } BOOL;, который также сводится к intтипам.

C ++ имел гораздо лучший и явный логический тип bool, который был не больше 1 байта. В то время как в худшем случае логические типы или выражения в C будут иметь размер 4 байта. Некоторый способ совместимости с C ++ был введен в C со стандартом C99. Затем C получил логический тип, _Boolа также заголовок stdbool.h.

stdbool.hобеспечивает некоторую совместимость с C ++. Этот заголовок определяет макрос bool(то же самое, что и ключевое слово C ++), который расширяется до _Boolтипа, представляющего собой небольшой целочисленный тип, вероятно, размером 1 байт. Точно так же заголовок обеспечивает два макроса trueи то falseже самое написание, что и ключевые слова C ++, но с обратной совместимостью со старыми программами на Си . Поэтому trueи falseрасширяются до 1и 0в С и их тип есть int. Эти макросы на самом деле не имеют логического типа, как соответствующие ключевые слова C ++.

Точно так же в целях обратной совместимости логические операторы в C все еще возвращают значение intпо сей день, хотя C в настоящее время получил логический тип. В то время как в C ++ логические операторы возвращают bool. Таким образом, выражение, такое как sizeof(a == b)даст размер intв C, но размер boolв C ++.

По поводу условного оператора ?:

Условный оператор ?:- это странный оператор с несколькими причудами. Это распространенная ошибка, полагать, что это на 100% эквивалентно if() { } else {}. Не совсем.

Между оценкой 1-го и 2-го или 3-го операнда существует точка последовательности. ?:Оператор гарантированно только оценить либо 2 - го или 3 - го операнда, поэтому он не может выполнить какие - либо побочные эффекты операнда, не оценивается. Код вроде true? func1() : func2()не будет выполняться func2(). Все идет нормально.

Однако существует специальное правило, согласно которому 2-й и 3-й операнд должны быть неявно продвинуты по типу и сбалансированы друг с другом с помощью обычных арифметических преобразований . ( Неявные правила продвижения типов в C объяснены здесь ). Это означает, что 2-й или 3-й операнд всегда будет по крайней мере таким же большим, как и int.

Таким образом, это не имеет значения, trueи false, как оказалось, имеет тип intв C, потому что выражение всегда будет давать по крайней мере размер, intневажно.

Даже если вы перепишите выражение, оно все равно вернет размер !sizeof(a ? (bool)true : (bool)false) int

Это из-за неявного продвижения типов посредством обычных арифметических преобразований.

Лундин
источник
1
C ++ на самом деле не гарантирует sizeof(bool)==1.
aschepler
1
@aschepler Нет, но реальный мир вне стандарта C ++, тем не менее, гарантирует это. Назовите один компилятор, где его нет 1.
Лундин
Здравствуй. Я думаю, что этот ответ был бы лучше без его первой части. Вторая часть отвечает на вопрос. Остальное, хотя и интересно, просто шум.
МСК
@YSC Первоначально они были помечены как C, так и C ++, поэтому необходимо было сравнить их различные типы bool и историю их создания. Я сомневаюсь, что написал бы первую часть, если бы не тег C ++. Однако нужно понять, почему sizeof (bool) равен 1, а sizeof (false) равен 4 в C.
Лундин
21

Быстрый ответ:

  • sizeof(a ? true : false)оценивается как 4потому что trueи falseопределяется <stdbool.h>как 1и 0соответственно, поэтому выражение расширяется до sizeof(a ? 1 : 0)целочисленного выражения с типом int, которое занимает 4 байта на вашей платформе. По той же причине, sizeof(true)также будет оцениваться в 4вашей системе.

Обратите внимание, что:

  • sizeof(a ? a : a)также оценивается, 4поскольку троичный оператор выполняет целочисленные преобразования для своего второго и третьего операндов, если они являются целочисленными выражениями. То же самое, конечно , происходит из- за sizeof(a ? true : false)и sizeof(a ? (bool)true : (bool)false), но литье целое выражение , как boolведет себя , как и ожидалось: sizeof((bool)(a ? true : false)) -> 1.

  • Также обратите внимание , что операторы сравнения оценки для логических значений 1или 0, но имеют intтипа: sizeof(a == a) -> 4.

Единственные операторы, которые сохраняют логическую природу a, будут:

  • оператор запятой: оба sizeof(a, a)и sizeof(true, a)оценивать 1во время компиляции.

  • операторы присваивания: оба sizeof(a = a)и sizeof(a = true)имеют значение 1.

  • операторы приращения: sizeof(a++) -> 1

Наконец, все вышеперечисленное относится только к C: C ++ имеет различную семантику, касающуюся boolтипа, логических значений trueи falseоператоров сравнения и троичного оператора: все эти sizeof()выражения оцениваются 1в C ++.

chqrlie
источник
2
Хороший ответ, который на самом деле позволяет указать, что на самом деле не имеет значения, какой тип trueи falseесть, потому что ?:операнды в intлюбом случае получат целое число . Таким образом sizeof(a ? (uint8_t)true : (uint8_t)false), также даст 4 как результат.
Лундин
Этот ответ покрывает главный важный момент, повышение стоимостиint
Чинни
1

Вот фрагмент из того, что входит в источник

#ifndef __cplusplus

#define bool    _Bool
#define true    1
#define false   0

#else /* __cplusplus */

Там макросы trueи falseобъявляются как 1 и 0 соответственно.

однако в этом случае тип является типом литеральных констант. И 0, и 1 являются целочисленными константами, которые помещаются в int, поэтому их тип - int.

и sizeof(int)в вашем случае это 4.

u__
источник
-3

В C нет логического типа данных, вместо этого логические выражения оцениваются как целочисленные значения, 1если в противном случае они имеют значение true 0.

Условные выражения , как if, for, whileили c ? a : bожидать целое число, если число не равно нулю это считается trueза исключением некоторых особых случаев, вот рекурсивная функция суммы , в которой тройной-оператор будет оценивать trueдо nдосягаемости 0.

int sum (int n) { return n ? n+sum(n-1) : n ;

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

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Khaled.K
источник