Что не так с последующим кодом C?

c arrays c-preprocessor

419 просмотра

3 ответа

4296 Репутация автора

Возможный Дубликат:
Запутанный о расширении макроса C и целочисленной арифметике
Загадка A (в C)

Ожидаемый результат следующей C- программы - распечатать элементы в массиве. Но когда на самом деле работает, это не так.

#include<stdio.h>

  #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
  int array[] = {23,34,12,17,204,99,16};
  int main() 
  {
      int d;
      for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
          printf("%d\n",array[d+1]);
      return 0;
  }
Автор: Parag Источник Размещён: 28.03.2012 06:21

Ответы (3)


6 плюса

675998 Репутация автора

Решение

Потому что sizeofдает вам беззнаковое значение, которое вы , вероятно , заметили бы , если бы вы оказались уровень предупреждения, например, используя -Wall -Wextraс gcc (а) :

xyzzy.c: In function 'main':
xyzzy.c:8: warning: comparison between signed and unsigned

Если вы заставите его подписать, он работает нормально:

#define TOTAL_ELEMENTS (int)((sizeof(array) / sizeof(array[0])))

То, что происходит в деталях, можно почерпнуть из стандарта ISO. В сравнении между различными типами, продвижения сделаны, чтобы сделать типы совместимыми. Выбранный совместимый тип зависит от нескольких факторов, таких как совместимость знаков, точность и ранг, но в этом случае считалось, что неподписанный тип size_tбыл совместимым типом, поэтому он dбыл обновлен до этого типа.

К сожалению, приведение -1к типу без знака (по крайней мере, для дополнения до двух, которое почти наверняка используется вами) приводит к довольно большому положительному числу.

Тот, который, конечно, больше, чем 5вы получаете (TOTAL_ELEMENTS-2). Другими словами, ваше заявление фактически становится:

for (d = some big honking number way greater than five;
     d <= 5;
     d++
) {
    // fat chance of getting in here !!
}

(а) Это требование использования extraостается предметом спора между gccразработчиками и мной. Очевидно, они используют какое-то новое определение слова «все», о котором я раньше не знал (с извинениями перед Дугласом Адамсом).

Автор: paxdiablo Размещён: 28.03.2012 06:26

0 плюса

4263 Репутация автора

TOTAL_ELEMENTSимеет тип size_t, вычитание 2 выполняется во время компиляции, и так оно и есть 5UL(акцент на суффикс без знака). Сравнение с подписанным целым числом dтогда всегда ложно. Пытаться

for(d=-1;d <= (ssize_t)(TOTAL_ELEMENTS-2);d++)

FTW, компилятор Intel предупреждает об этом точно, когда вы пытаетесь скомпилировать код.

Автор: hroptatyr Размещён: 28.03.2012 06:29

0 плюса

122886 Репутация автора

Чтобы выяснить, что пошло не так: sizeof () переводит в тип результата size_t, который представляет собой целое число без знака, больше или равно unsigned int.

Таким образом, результатом (sizeof(array) / sizeof(array[0]))является результат в двух операндах типа size_t. Разделение производится на этих операндов size_t / size_t. Оба операнда имеют одинаковый тип, поэтому он работает нормально. Результат деления имеет тип size_t, то есть тип, к которому приводит TOTAL_ELEMENTS.

Следовательно, выражение (TOTAL_ELEMENTS-2)имеет типы size_t - int, поскольку целочисленный литерал 2имеет тип int.

Здесь у нас есть два разных типа. Затем происходит то, что называется балансировкой (формально «обычные арифметические преобразования»), что происходит, когда компилятор обнаруживает два разных типа. Правила балансировки гласят, что если один операнд подписан, а другой - без знака, то подписанный молча, неявно преобразуется в тип без знака.

Это то, что происходит в этом коде. size_t - intпреобразуется в size_t - size_t, затем выполняется вычитание, результат size_t. Затем int <= size_tпреобразуется в size_t <= size_t. Переменная dстановится без знака, и если она имеет отрицательное значение, код становится бесполезным.

Автор: Lundin Размещён: 28.03.2012 06:54
Вопросы из категории :
32x32