char * a, * b; какой тип (б-а) и как мне его распечатать? - PullRequest
5 голосов
/ 29 октября 2009
{
  char *a, *b;

  printf("%lx\n",(b-a));
}

Обычно работает, на самом деле, я не могу себе представить, что он выдает предупреждение или дает сбой на 32-битной или 64-битной машине. Но правильно ли это делать для ANSI C и определения размера? Мне бы хотелось, чтобы этот код работал на любой возможной платформе, включая не-Unix и встроенные системы.

Ответы [ 5 ]

22 голосов
/ 29 октября 2009

b - a - это ptrdiff_t, который можно распечатать с помощью %td в формате printf. Из раздела спецификации 6.5.6 Аддитивные операторы :

Когда вычитаются два указателя, оба должны указывать на элементы одного и того же объекта массива или один за последним элементом последнего объекта массива; Результатом является разница индексов двух элементов массива. Размер результата определяется реализацией, а его тип (целочисленный тип со знаком) равен ptrdiff_t, определенному в заголовке <stddef.h>.

Для printf и связанных функций, раздел 7.19.6 Форматированные функции ввода / вывода :

t Указывает, что следующие d, i, o, u, x или X спецификатор преобразования применяется к ptrdiff_t или соответствующему аргументу целочисленного типа без знака; или что следующий указатель преобразования n применяется к указателю на аргумент ptrdiff_t.

Я еще немного покопался в спецификации, и, похоже, это указывает на то, что разница в два указателя может даже не вписаться в ptrdiff_t, и в этом случае поведение не определено:

J.2 Неопределенное поведение
- Результат вычитания двух указателей не представлен в объекте типа ptrdiff_t (6.5.6).

Хотя я не могу представить себе какую-либо реализацию, где это может произойти. Я думаю, вы могли бы проверить PTRDIFF_MIN и PTRDIFF_MAX в <stdint.h>, чтобы быть уверенным.

12 голосов
/ 29 октября 2009

Результат b - a определяется только тогда, когда и a, и b указывают на элементы одного и того же массива символов. Это требование также можно интерпретировать как a и b, указывающие на байты, принадлежащие одному и тому же объекту, поскольку каждый объект может быть повторно интерпретирован как массив символов.

В противном случае результат не определен. То есть попытка вычесть такие указатели приводит к неопределенному поведению.

Когда результат определен, он имеет тип ptrdiff_t. ptrdiff_t - это имя определения типа, и тип, который скрывается за этим именем определения типа, определяется реализацией. Известно, что тип подписан.

Также обратите внимание, что язык C не гарантирует, что ptrdiff_t будет достаточно большим, чтобы содержать результат любого вычитания, даже если указатели указывают на элементы одного и того же массива. Если указатели слишком далеко друг от друга для типа ptrdiff_t, чтобы вместить результат, поведение не определено.

Нет конкретного спецификатора формата printf для ptrdiff_t даже в C99, поэтому вам, вероятно, будет лучше преобразовать его в достаточно большой целочисленный тип со знаком и использовать для этого типа спецификатор формата

printf("%ld\n", (long) (b - a));

Исправление: C99 имеет модификатор длины для ptrdiff_t. Правильный способ напечатать результат в C99 будет

printf("%td\n", b - a);

Обратите внимание, что t является модификатором длины. Его можно комбинировать со спецификаторами преобразования d, o, u, x или X, в зависимости от того, какой выходной формат вы хотите получить. В C89 / 90 вам все равно придется придерживаться достаточно большого типа со знаком.

P.S. Вы сказали, что не можете себе представить, что он выйдет из строя на 32-битной или 64-битной машине. На самом деле, очень легко представить (или сделать это) неудачу. Вы видите ptrdiff_t на 32-битном компьютере, как правило, 32-битного типа. Так как это тип со знаком, у него есть только 31 бит для представления величины значения. Если вы возьмете два указателя, которые находятся дальше друг от друга (то есть для представления «расстояния» требуется 32 бита), результат b - a будет переполнен и будет бессмысленным. Чтобы предотвратить эту ошибку, вам понадобится как минимум 33-битная подпись ptrdiff_t на 32-битной машине и как минимум 65-битная подпись ptrdiff_t на 64-битной машине. Реализации, как правило, этого не делают, они просто используют «разрешение» из стандарта для создания неопределенного поведения при переполнении.

5 голосов
/ 29 октября 2009

Это ptrdiff_t. От man stddef.h:

ptrdiff_t
              Signed integer type of the result of subtracting two pointers.

Напечатайте это с %td.

2 голосов
/ 29 октября 2009

Тип b-a ptrdiff_t

2 голосов
/ 29 октября 2009

Поскольку вы не инициализировали переменные a и b , код дает неопределенное поведение. Но, кроме этого, тип b-a равен ptrdiff_t , который достаточно большой, чтобы содержать результат. Если у вас достаточно современный C, вы можете напечатать его с помощью % tx .

Если вы не хотите использовать % tx , вы должны преобразовать свой результат так, чтобы он фактически соответствовал (и не просто случайно) вашему спецификатору формата:

printf("%lx", (unsigned long)(a-b));

Не исключено, что система может иметь, например, 32-разрядное адресное пространство и 32-разрядное ptrdiff_t, но 64-разрядное, и тогда ваш printf завершится ошибкой.

...