#c #pointers #memory-address #void-pointers
#c #указатели #память-адрес #void-указатели
Вопрос:
Я должен получить адрес обратно из функции. Я использую 32-битную систему и буду, этот код не обязательно должен быть переносимым, однако я бы счел это преимуществом.
Из всех вариантов, я думаю, эти три имеют наибольший смысл для читателя.
- Передавая его как собственный size_t, как uint32. Здесь мало места для ошибок. Итак, я ДУМАЮ, что это безопасно, верно? Вам нужно четко указать его повторное приведение, когда вы хотите его использовать.
uint32_t address = 0;
example_structure_t *str;
find_address(amp;address);
str = (example_structure_t*) address;
- Похоже, для этого и нужны указатели void. Но количество раз, когда меня били по голове «пустые указатели плохие», не равно нулю. Это небезопасно в любом случае? Возвращаясь таким образом, я теряю способность возвращать ошибки, отличные от успеха / неудачи, подразумевая, что АДРЕС равен нулю или нет.
void *address;
example_structure_t *str;
address = find_address();
str = address;
- Кажется, хорошее место для двойного указателя. У меня есть парень, который работает под моим началом и только изучает C, поэтому я пока не пытался его запутать.
example_structure_t **str;
find_address(str);
Я знаю, что существует множество мнений о том, как это можно сделать. Но есть ли что-нибудь фактическое с точки зрения безопасности или наилучшей практики?
Комментарии:
1. Почему не просто
example_structure_t *str = find_address();
?2. Если указатель указывает на один тип, просто используйте правильный тип указателя. Если он может указывать на любой из нескольких разных типов данных, тогда используйте
void *
. Если это может быть либо указатель на данные, либо указатель на функцию, то, строго говоря,void *
не гарантируется работа (указатели на функции не обязательно должны быть представлены какvoid *
).3. «пустые указатели плохие», потому что они являются нетипизированными адресами. Если вам нужно, чтобы ваша программа возвращала нетипизированный адрес (возможно, плохая идея, но вы, кажется, знаете, что делаете), тогда вам следует использовать указатель void.
4. Скажите мне, какой тип
malloc
возвращался?5. Строго говоря, в варианте 2 вы не теряете опцию для значений ошибок. Ядро Linux делает это несколько раз. Вы можете получить указатель, который на самом деле содержит
-ENOENT
или похож. Но после многодневных поисков, пока не было найдено несколько пропущенных проверок,IS_ERR
чтобы избежать доступа к такому недопустимому адресу и условию гонки, которое создало эти недопустимые указатели, я твердо верю, что для людей, использующих этот механизм, должен быть специальный ад. (Это напоминает мне, что мне все еще нужно разобраться в привычках LKML и предоставить исправление ошибки)
Ответ №1:
Передавая его как собственный size_t, как uint32. Здесь мало места для ошибок. Итак, я ДУМАЮ, что это безопасно, верно? Вам нужно четко указать его повторное приведение, когда вы хотите его использовать.
Нет, size_t
ни uint32_t
один из них не подходит для этой цели, даже в 32-разрядных системах. Размер адреса не обязательно такой же или меньше размера машинного слова — если это действительно то, что a uint32_t
представляет в данной реализации — и нет никакой гарантии, что он поместится в a size_t
либо. Если вы хотите передавать адреса в целочисленной форме, вам следует использовать intptr_t
or uintptr_t
(from stdint.h
) . Если ваша реализация не определяет эти необязательные типы, то может не быть никакого целочисленного типа, подходящего для этой цели.
С учетом сказанного, однако, если вы пытаетесь передать указатель, то странно предлагать передавать его не как указатель.
Похоже, для этого и нужны указатели void. Но количество раз, когда меня били по голове «пустые указатели плохие», не равно нулю. Это небезопасно в любом случае? Возвращаясь таким образом, я теряю способность возвращать ошибки, отличные от успеха / неудачи, подразумевая, что АДРЕС равен нулю или нет.
Это то, для чего void
нужны указатели, если нет более подходящего типа указателя. С другой стороны, если вы всегда будете возвращать указатель на определенный тип данных, то тип указателя должен отражать это. В возврате указателя нет ничего изначально небезопасного, несмотря на целевой тип.
Если вам нужно предоставить более подробную информацию о состоянии, чем успех / неудача, тогда вам следует использовать параметр out в дополнение к возвращаемому значению функции. Тогда вам решать, передается ли указатель или код состояния через этот параметр.
Я понятия не имею, какая ошибочная философия программирования или соглашение о кодировании могут научить тому, что использование типа void *
в C безусловно плохо. Определенно есть ситуации, в которых это правильно, и они не так уж редки.
Кажется, хорошее место для двойного указателя. У меня есть парень, который работает под моим началом и только изучает C, поэтому я пока не пытался его запутать.
Это будет параметр out, который уже обсуждался. В этом нет ничего особенно плохого, но если вы можете обойтись без параметра out, используя возвращаемое значение, то я бы сам пошел по этому пути.
Я знаю, что существует множество мнений о том, как это можно сделать. Но есть ли что-нибудь фактическое с точки зрения безопасности или наилучшей практики?
Я не думаю, что существует большое разнообразие мнений о том, как это можно сделать. Я не склонен полагать, что многие люди не согласятся с тем, что если вы специально хотите передать указатель, то вам лучше всего сделать это как указатель, или что вам следует предпочесть более конкретный тип указателя void *
, чем тот, который служит этой цели. Помимо этого, я бы сказал, что детали зависят от соглашения о проекте, требований, специфичных для конкретной ситуации, и личных предпочтений.
Комментарии:
1. Все это имеет смысл, и я проголосую за это как за ответ. Я забыл об uintptr_t. И да, идея заключается в том, чтобы работать с целочисленным значением указателя. Часть проблемы в том, что я действительно хотел сохранить ошибку в качестве возврата. Итак … err = findAddress(someAddressArg); Без использования двойного указателя я не уверен, что есть действительно чистый способ сделать это без создания локального uintptr и передачи адреса в качестве самого указателя. Если я чего-то не упускаю? Верно? В какой-то момент он должен находиться внутри локальной памяти у вызывающей функции. Я думаю, что даже один void * здесь не помогает.
2. @mintbranchconditioner, если вы хотите вернуть указатель через параметр out, то тип этого параметра должен быть типом указателя, ориентированным на тип (указатель) передаваемого значения. По крайней мере, это обязательно двойной указатель. Если вы хотите, чтобы ваша функция имела возвращаемое значение, которое передает информацию об ошибке в целочисленном кодировании, тогда это нормально, но затем вы должны использовать параметр out для передачи указателя, как уже обсуждалось. Я не слежу за остальной частью вашего мыслительного процесса здесь, но я уверяю вас, что то, что я описываю, возможно и довольно естественно.
3. Да, это в основном так. Я не был уверен, что вы могли бы сделать это каким-либо другим способом, кроме двойного указателя — без создания целого числа какого-либо адреса. В этом случае, поскольку я предоставляю код для кого-то неопытного, я буду использовать uintptr_t и возвращать значение адреса.
4. Конечно, ты будешь делать то, что считаешь лучшим, @mintbranchconditioner, но если бы это был я, я бы попытался смоделировать и научить хорошему стилю кодирования моего младшего, помимо большинства других соображений. Конечно, я не хочу, чтобы они отучились позже.