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

У меня есть небольшой фрагмент кода о операторе 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) дает вывод четырех байтов?

Ответ 1

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

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

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

Ответ 2

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

Хорошо, еще к этому!

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

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


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

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

true

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

false

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

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

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

и

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

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

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

Ответ 3

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

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

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

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

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

В C вообще нет rvalues ​​типа bool. Каждое такое rvalue немедленно продвигается до int, даже если используется в качестве аргумента для sizeof. Изменить: этот абзац неверен, аргументы sizeof не получают повышение до int. Однако это не влияет на какие-либо выводы.

Ответ 4

Что касается булевого типа в C

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

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

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

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

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

Относительно условного оператора ?:

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

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

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

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

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

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

Ответ 5

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

  • 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.

  • также отметим, что операторы сравнения вычисляют значения boolean 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: С++ имеет другую семантику относительно типа bool, логических значений true и false, операторов сравнения и тернарного оператора: все эти выражения sizeof() оценивают на 1 в С++.

Ответ 6

Вот фрагмент, из которого что-то включено в исходный

#ifndef __cplusplus

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

#else /* __cplusplus */

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

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

а sizeof(int) в вашем случае - 4.

Ответ 7

В 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); }