Подтвердить что ты не робот

Оператор 'sizeof' с условным (тройным) выражением

Мне трудно понять поведение sizeof при задании тройного выражения.

#define STRING "a string"

int main(int argc, char** argv)
{
  int a = sizeof(argc > 1 ? STRING : "");

  int b = sizeof(STRING);
  int c = sizeof("");

  printf("%d\n" "%d\n" "%d\n", a, b, c);

  return 0;
}

В этом примере (проверенном с gcc 4.4.3 и 4.7.2, скомпилированным с -std=c99), b равно 9 (8 символов + неявный '\0'), c - 1 (неявный '\0'). a по какой-то причине 4.

Я ожидал бы, что a будет либо 9, либо 1, в зависимости от того, является ли argc больше 1. Я думал, что, возможно, строковые литералы преобразуются в указатели перед передачей на sizeof, вызывая sizeof(char*) равным 4.

Я попытался заменить STRING и "" на char массивы...

char x[] = "";
char y[] = "a string";
int a = sizeof(argc > 1 ? x : y);

... но я получил те же результаты (a = 4, b = 9, c = 1).

Затем я попытался погрузиться в спецификацию C99, но я не нашел в ней каких-либо очевидных объяснений. Из любопытства я также попытался изменить изменения x и y на другие типы:

  • char и long long int: a становится 8
  • оба short или оба char: a становится 4

Итак, определенно происходит какое-то преобразование, но я изо всех сил пытаюсь найти какое-либо официальное объяснение. Я могу себе представить, что это произойдет с арифметическими типами (я смутно знаю, что много продвижений происходит, когда они задействованы), но я не понимаю, почему строковый литерал, возвращаемый тернарным выражением, будет преобразуется в размер 4.

NB: на этом аппарате sizeof(int) == sizeof(foo*) == 4.

Последующий

Спасибо за ребята. Понимание того, как работают sizeof и ?:, побудило меня попробовать еще несколько типов mashup и посмотреть, как реагирует компилятор. Я редактирую их для полноты:

foo* x = NULL; /* or foo x[] = {} */
int  y = 0;    /* or any integer type */

int a = sizeof(argc > 1 ? x : y);

Допустимые значения warning: pointer/integer type mismatch in conditional expression [enabled by default] и a == sizeof(foo*).

С foo x[], bar y[], foo* x, bar* y или foo* x, bar y[] предупреждение становится pointer type mismatch. Предупреждение при использовании void*.

float x = 0; /* or any floating-point type */
int   y = 0; /* or any integer type */

int a = sizeof(argc > 1 ? x : y);

Не дает никаких предупреждений и a == sizeof(x) (т.е. тип с плавающей запятой).

float x = 0;    /* or any floating-point type */
foo*  y = NULL; /* or foo y[] = {} */

int a = sizeof(argc > 1 ? x : y);

Допустимые значения error: type mismatch in conditional expression.

Если я когда-либо прочитал спецификацию, я обязательно отредактирую этот вопрос, чтобы указать на соответствующие части.

4b9b3361

Ответ 1

Вам нужно понять выражения, которые являются основным компонентом языка.

Каждое выражение имеет тип. Для выражения e, sizeof e - размер типа значения выражения e.

Выражение a ? b : c имеет тип. Тип - это общий тип двух выражений операнда b и c.

В вашем примере общий тип char[9] и char[1] равен char * (оба выражения с матрицей распадаются на указатель на первый элемент). (В С++ правила для строковых литералов различны и везде const.)

Ответ 2

Вам нужно понять, что sizeof является полностью компилятором. С VLA он может возвращать переменное выражение, в противном случае это константа времени компиляции.

Важным является тип его аргумента.

Итак, в sizeof(argc > 1 ? STRING : "") условие не оценивается. Тип аргумента разлагается до const char*. И на вашей машине это 4.

Вместо этого вы должны указать (argc > 1)?sizeof(STRING):1

Так как STRING является макрорасширенным до литерала "a string", sizeof(STRING) равно 9, как если бы вы объявили

const char STRING[] = {'a',' ','s','t','r','i','n','g','\0'};

Ответ 3

Оба STRING и "" являются объектами массива типов char[9] и char[1] соответственно. В языке C, когда объекты массива используются в выражениях, они получают неявное преобразование (распад) в типы указателей почти во всех контекстах с несколькими известными исключениями.

Одним из таких исключений является оператор sizeof. Когда вы используете объект массива как непосредственный операнд sizeof, этот объект массива не распадается на тип указателя, и вы получите размер всего массива в байтах в качестве результата. Вот почему sizeof(STRING) эквивалентно sizeof(char[9]) и оценивается как 9. И sizeof("") эквивалентен sizeof(char[1]) и оценивается как 1.

Но когда вы используете объекты массива в качестве операндов оператора ?:, контекст больше не является исключительным. В контексте ?: массивы операторов сразу же распадаются на указатели. Это означает, что ваш sizeof(argc > 1 ? STRING : "") эквивалентен sizeof(argc > 1 ? (char *) STRING : (char *) "") и, в свою очередь, эквивалентен sizeof(char *). Это оценивает размер указателя на вашей платформе, который как раз бывает 4.