Для итерации с помощью массива мы должны использовать size_t или ptrdiff_t?

Evan Carroll спросил: 13 июня 2018 в 08:01 в: c

В этой записи блога Андрея Карпова, озаглавленной size_t и ptrdiff_t" он показывает пример,

for (ptrdiff_t i = 0; i < n; i++)
  a[i] = 0;

Однако я не уверен, правильно ли это, кажется, что это должно быть

for (size_t i = 0; i < n; i++)
  a[i] = 0;

Правильно ли это?

Я знаю, что мы также должны использовать что-то вроде memset, но давайте избежим этого полностью. Я только спрашиваю о типе

3 ответа

Olaf Pascal Cuoq ответил: 14 июня 2018 в 09:49

В сообщении в блоге я утверждаю, что вы всегда должны воздерживаться от выделения блоков памяти больше, чем PTRDIFF_MAX (*), потому что это сделает компиляторы, такие как Clang и GCC, генерировать бессмысленный код, даже если вы это сделаете не вычитайте указатели на этот блок таким образом, чтобы результат переполнялся.

(*) Даже если malloc преуспевает, когда вы передаете ему значение больше, чем PTRDIFF_MAX. Суть проблемы в том, что GCC и Clang генерируют код, который ведет себя корректно, когда он связан с таким кодом malloc, но Glibc предоставляет функцию malloc, которая не реализует это ограничение.

Если вы следуете этому ограничению (я призываю вас: это сообщение сообщения в блоге), то оба типа одинаково правильны.

Это говорит о том, что только положительные смещения должны быть представленный size_t был бы естественным выбором в вашем примере.

Jonathan Leffler ответил: 13 июня 2018 в 09:01
@jwdonahue: Может быть, вас заинтересует Экстра, Экстра - Прочтите все об этом: почти все двоичные поиски и слияния не найдены - сообщение от Google о том, как массивы стали достаточно большими, чтобы арифметика индекса действительно переполнялась и ломалась.
jwdonahue ответил: 13 июня 2018 в 08:59
Это интересная морщина, о которой я блаженно не знал. Мне никогда не приходилось создавать приложение, которое требовало бы одного распределения, такого большого, как половина диапазона size_t. Я уверен, что они там, но я думаю, что они редки.
Olaf ответил: 13 июня 2018 в 09:58
PTRDIFF_MAX не является максимальным. разрешенный размер любого объекта, но макс. значение a ptrdiff_t. Столько, сколько SIZE_MAX - это просто макс. значение a size_t. Ничего не связано с макс. размер распределения. Это будет гарантировано реализацией для статических / atuomatic объектов и stdlib для динамически распределенных объектов (или любого другого механизма, предоставляемого средой, если таковая имеется). Тем не менее, size_t гарантированно удерживает макс. разрешенный индекс массива и размер самого большого возможного объекта в байтах. ptrdiff_t на самом деле нет (см. также 6.5.6p9).
Pascal Cuoq ответил: 13 июня 2018 в 10:55
@Olaf Подтверждение о том, что glibc позволяет выделять более 2GiB на 32-битных платформах, что противоречит предположениям компилятора, а также решение не менять: gcc.gnu.org/bugzilla/show_bug.cgi?id=67999#c8
Pascal Cuoq ответил: 14 июня 2018 в 09:32
@Olaf Вы читали сообщение в блоге или сообщение об ошибке? Они показывают, что GCC и Clang генерируют неправильный код для определенных программ. Отчет об ошибке содержит дискуссию с участием разработчиков GCC и поддерживающего libc, что должно быть исправлено между компиляторами и libc. Вы замечаете, что ничего не добавляет к обсуждению.
R Sahu ответил: 13 июня 2018 в 08:46

Использование ptrdiff_t в порядке, поскольку a[i] переводится как *(a + i).

Если вы берете два указателя, p1 и p2, рекомендуется использовать:

ptrdiff_t d = p2 - p1; // Assuming p2 - p1 is valid.

Учитывая, что p2 == p1 + d, то есть <ptr type> + ptrdiff_t является допустимым выражением. Является ли ptrdiff_t или size_t лучше, поскольку тип индекса - это вопрос мнения и / или стиль кодирования, используемый в команде.

Evan Carroll ответил: 14 июня 2018 в 07:06
Я не верю, что это доказательство звучит. И это путают представить типы и переменные вместе. Вопрос касается типов: конечно #=#-#, поэтому #+#=#. Но в нашем случае типы ограничены. Например, в приведенном выше примере вы начинаете с ptrdiff_t = uintptr_t - uintptr_t. Это not гарантированно работает, хотя и не на одном и том же объекте. ptrdiff_t + uintptr_t = uintptr_t также работает с меньшей вероятностью. Что касается ценностей, а не типов, вы правы, если какие-либо работы по строительству другие могут работать тоже (afaik), но в вопросе мы создаем аксессуар для памяти.
Evan Carroll ответил: 14 июня 2018 в 07:08
re: a[i] = 0;, и это мое утверждение о том, что поскольку i может быть двух типов (в вопросе size_t и ptrdiff_t и ptrdiff_t может быть больше, чем size_t, и отрицательный, что он менее безопасен для типов, выходящих за пределы size_t. Но это звучит не так, как стиль или мнение.
R Sahu ответил: 14 июня 2018 в 11:46
@EvanCarroll, если размер массива меньше PTRDIFF_MAX. вы можете использовать либо ptrdiff_t, либо size_t без каких-либо различий в настройке программы. Когда размер вашего массива превышает PTRDIFF_MAX, использование size_t - ваш единственный выбор. Похоже, это то, что вы пытаетесь сказать. В таких крайних случаях я вижу, что стиль кодировки или мнение не имеют значения.
Jonathan Leffler jwdonahue ответил: 13 июня 2018 в 12:54

Ответ на вопрос OP: yes, size_t наиболее подходит для кода примера, где значения указателя не вычитаются друг из друга, и нет проблем с совместимостью между компиляторами и библиотеками в malloc поведение. Независимо от разницы в менеджерах кучи, в C массив может быть длиной SIZE_MAX, а для представления его требуется size_t. Ничто в стандарте не требует, чтобы диспетчер кучи мог распределять все пространство памяти процесса в куче или выделять байты SIZE_MAX, но массив может быть SIZE_MAX в длину, поэтому size_t подходит.

Даже если n подписан, используя ptrdiff_t для i не поможет, так как исходный тест i < n будет сбой в любом случае, если n отрицательный, потому что i инициализируется до нуля. В i нет индекса, который не может получить индекс size_t. Единственное место, где требуется ptrdiff_t, это то, где вы вычитаете одно значение указателя из другого, и OP не спрашивает об этом.

Дополнительное видео по вопросу: Для итерации с помощью массива мы должны использовать size_t или ptrdiff_t?

pointers and arrays in c programming

What is the Difference Between a Pointer and a Reference C++

C POGRAMMING - POINTERS TO STRUCTURES