Вопрос по pointer-arithmetic, c, gcc, void-pointers, visual-studio – Арифметика указателя, когда пустота имеет неизвестный размер [закрыто]

2

В Visual Studio C ++ версии 9 (и, возможно, в других версиях) следующий код:

int a = sizeof(void);
void const *b = static_cast("hello world");
b += 6;

Формируетэти ошибки:

error C2070: 'void': illegal sizeof operand
error C2036: 'const void *' : unknown size

Этот код работает под GCC, который лечитsizeof(void) как .1

Есть ли способ обойти это ограничение, так как приведение явно кchar * в целях указателя арифметика добавляет путаницы (void * хорошо распознается и используется как указатель на типизированную память).

Update0Пожалуйста, обратите внимание, яЯ хорошо осведомлен о существовании стандарта.хочу сделать арифметику необработанных указателей.я беруsizeof(void) чтобы показать, что яЯ хорошо осознаю тот факт, что это не так1 является причиной проблемы.Пример кода просто демонстрирует, что требуется для генерации ошибок.Я знаю это нет а "нормальный" способ использованияvoid, но это С, и такие вещи случаются.Да, люди должны делать это на низком уровне. Я'Я не после того, почему, ям после как. Если вы хотите знать почему, взгляните на какой-нибудь исходный код ядра или ваш дружественный glibc.Update1

Кажется, этот вопрос породил много путаницы. Вопрос не оЗачем имеющийsizeof(void) == 1 не является стандартным, но что делать, когда это не такт.

В случае, когда должна выполняться арифметика однобайтового указателя, оказывается, что приведение кchar * правильный ответ, не потому что*(void *) не имеет размера, но потому что стандарт фактически гарантирует, что*(char *) всегда1, Поэтому использованиеchar * всегда правильно и согласуется сvoid * с расширением GCC для арифметики необработанных указателей.

Чтобы еще больше подчеркнутьvoid * является правильным выбором для указателей на типизированную память, иchar * правильный тип для приведениядля арифметики необработанных указателей.

@ Крис Лутц: "Исходник ядра / glibc практически написан на ассемблере, Нет, исходный код ядра - почти что C.Как ядра могут переноситься между архитектурами. Голый минимум написан на ассемблере. DrPizza
Насколько я'я указал в моем ответе ниже, соответствующий ответ теперь у меня есть больше информации, чтоchar * является правильный тип указателя, чтобы использовать для этого. Не только потому, что мы все хотим посмеяться о том, что пустота бессмысленна бла-бла. Matt Joiner
Да, люди должны делать это на низком уровне ", Нетнеобходимость сделать это. GCC предоставляет это для удобства программиста, что позволяет программисту набирать текст. Вы могли бы#define advance(ARG1, ARG2) ((ARG1) = (void*)((ARG2) + (char*)(ARG1))), затемadvance(b,6);, Вы'вероятно, я хочуplus макрос, чтобы заменить.b + 6 Steve Jessop
Нет веской причины не использовать для этой цели указатель на (без знака) символ. Это'Например, что делают разработчики ядра NT (без глупых расширений gcc для ядра NT, в конце концов). Если ты так думаешьуродливо, создать макрос или что-то. DrPizza

Ваш Ответ

7   ответов
1

void * хорошо распознается и используется как указатель на типизированный объем памяти.

Правильный. Отсутствие типа означает отсутствие размера.

а как насчет реального вопроса? Matt Joiner
Что насчет этого? Вы, очевидно, в конце концов согласились с тем, что sizeof (void) - это нонсенс, потому что void не имеет размера, примерно в то же время, когда вы разместили вышеупомянутый комментарий. DevSolar
1

чтобы просто указывать на необработанные данные и увеличивать этот указатель на количество байтов, занимаемых порцией данных, я всегда используюchar *, Затем я изменяю указатель на соответствующий указатель на структуру данных, как только мне нужно обработать его как нечто конкретное. Увеличениеvoid * ISN»т портативный среди компиляторов.

Увеличениеvoid * это, конечно, ужасная идея ... но так поступает, как будто ваше решение лучше. Как и большинство другихreinterpret_castс, отливка изchar * на несовместимый тип указателя и разыменование результата isn 't определяет, нарушает правила псевдонимов и подрывает требования выравнивания платформы. Так что, если все ваши структуры данных не совместимы благодаря наличию только одногоchar член каждого, что вряд ли ... единственноепортативный» В этом совете важна его роль как чистого генератора UB. underscore_d
Я разместил это>6,5 лет назад и предполагал, что его можно будет использовать, если только они будут знать, что они делают для своей платформы. :) Сказав, что на платформах, которые часто требуют низкоуровневого программирования для достижения максимальной производительности (например, консольные игры, где я провел большую часть последних 20 лет), это все еще распространено, несмотря на то, что промежуточное ПО, такое как Unity, обеспечивает достойное проникновение. (И, для записи, этот метод часто используетсяименно так из-за необходимости иметь дело с требованиями по выравниванию платформы.) Но, не беспокойтесь, я получил только одно голосование за почти 7 лет, так что вы 'безопасно. ;) Jim Buck
-3

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

Короче говоря, нет никакого способа обойти ограничение,char * на самом деле это правильный способ сделать это.

Матиас Вандель имелправильный ответ но с другим обоснованием.

Я не меняю это. я знаю, что я имел в виду, и я знал ответ, когда увидел это. "есть ли способ обойти это ограничение? ":"нет, а здесьпочему, если кто-то сделает более точный ответ, яЯ буду рад измениться. Matthias' ответ вполне правильный, но не то, что я был после. Matt Joiner
Там нет ограничений. DevSolar
... и через неделютвой "принято" ответ оценен -2, в то время как ответ, на который вы претендуете, неверныйобоснование» имеет рейтинг 11 ... я DevSolar
Ваш вопрос был не о том, что будет правильным типом для арифметики указателей. Ваш вопрос был о том, почему MSVC выложил ошибку дляsizeof( void ) и если естьлюбой способ обойти это ограничение " (чего нет, как правильно указали Матиас и другие). Включая некоторые сильные слова в отношении людей, пытающихся быть полезными. Будь мужчиной и прими другого »ответ вместо того, чтобы жаловаться, как правильный ответ нене соответствует твоему разбитому вопросу ... DevSolar
18

что увеличение указателя увеличивает его на любой размер, на который он указывает. Пустота указывает ни на что, поэтому размер неизвестен.

Некоторым компиляторам было бы удобно увеличивать пустые указатели.

Кастинг раздражает, потому что вы должны либо привести его к другому указателю, а затем вернуться назад, или сделать что-то неловкое, например(*(char *)&voidptr) += 6

Лично яя бы просто объявил этоchar * в целях арифметики

Манагу прав. Тем не менее, это будет акт или сырьепереосмысление изvoid * указатель какchar * указатель. Переосмысление всегда плохая идея. Лучше использоватьпреобразование как в .I 'voidptr = (char *) voidptr + 6 AnT
Может быть, вы хотели(*(char **)&voidptr) += 6? Я неНе верьте, что фрагмент кода в настоящее время делает то, что вы подразумеваете. Managu
Мы внедрили связанный список, используя указатели void. Когда список создан, ему передаетсяsize_t data_size так что он может выполнять операции копирования и т. д. Теперь яМы подошли к проблеме создания подпрограммы преобразования списка в массив. Интуитивно понятная (для меня) вещь должна была добавитьdata_size к пустому указателю, чтобы переместить его к следующему элементу в массиве ... Неважно. Я'Мы проголосовали за этот вопрос, потому что он оказался полезным. James Morris
1

в первой строке я вижу ошибку - у вас есть sizeof (void), и она должна быть sizeof (void *), нет?

sizeof void бессмысленно. Измени свой подход. GManNickG
Нет, я имею в виду пустоту. Matt Joiner
10

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

Указатель арифметикиvoid * указатели также не поддерживаются в C. Для использования с арифметикой указателя указатель должен указывать на тип объекта, которыйvoid не является.

GCC допускает и то и другое как странное расширение языка.

Как ни странно это не считается ошибкой впараноидальный режим если только-Werror или же-pedantic-errors указано. Я просто пришел к тому же выводу, когда взглянул на Стандарт. D.Shawley
6

void * подразумевается "указатель на неизвестное

Следовательно, добавление 6 к пустому указателю не определено, потому что вы 'просит компилятор продвинуть указательразмер 6 неизвестных предметов ".

Вы не должны делать это.

Похожие вопросы