2010-07-23 5 views
6

У меня есть функция, которая переадресует указатель, указанный в качестве аргумента для нового размера. Теперь проблема в том, что - согласно странице man - realloc нужен указатель, который был возвращен malloc или calloc.Как я могу убедиться, что вызывающий абонент передает указатель malloc'd?

Как я могу убедиться, что вызывающий абонент передает указатель, соответствующий этим требованиям? Кажется, что нет встроенных C-механиков (таких как классификаторы типов или что-то еще).

Теперь, прежде чем я реструктурирую свой API (поскольку я считаю, что функция, поскольку теперь она не достаточно надежна), можете ли вы подтвердить, что я ничего не пропустил?

Заранее спасибо.

Edit: Одно решение, очевидно, будет в таНос в функция. Проблема заключается в том, что вызывающий объект не «видит» распределение. Таким образом, мне нужно будет явно сказать в документах, что он должен освободить указатель. Это даже хуже, чем ожидать, что они предоставят указатель malloc'd (что будет означать, что вызывающий абонент должен его освободить).

Что я действительно хочу, это то, что блокирует злоупотребления во время компиляции. Это и пони. ;-)

+0

вам нужно сделать это на самом деле, что дурака делать? – Nyan

+0

, по крайней мере, я бы хотел, да – RWS

+0

Поскольку вы не можете, вы, вероятно, должны перестать беспокоиться и сделать что-то более интересное. Люди могут подорвать ** любые ** и ** все ** проверки или профилактики, которые вы хотите попробовать. Не тратьте время на предотвращение. Инвестируйте время в грохот и ясно, чтобы они могли прочитать сообщение об ошибке и осознать свою ошибку. –

ответ

10

Как я могу убедиться, что вызывающий абонент передает указатель, соответствующий этим требованиям?

Документация.

Определить API.

Если человек, напиравший звонящего, отказывается следовать API, все крушение. Они отказались играть по вашим правилам, и все разбилось. Чего они ожидали?

2

Вы не можете определить, была ли указана точка, на которую указывает указатель, [m|c]alloc(); 'd. Вы можете только определить свой API и надеяться, что ваши пользователи API последуют за ним.

Распределяет память (запрет) запретительно? Вы все равно можете сделать начальное распределение с realloc(NULL, size);, если вы связаны этим.

Дополнительная информация о вашей проблеме будет приятной.

0

Вы можете поддерживать статический указатель внутри своей функции и самостоятельно вызвать realloc. Верните статический указатель. Таким образом, у пользователя нет проблем с распределением или освобождением. Переменная будет автоматически высвобождаться, когда процесс умирает.

Помните, что если realloc не работает, он возвращает нулевой указатель, но исходная память malloc'ed остается нетронутой, это общий источник утечек памяти.

+0

Это не отвечает на вопрос. –

+2

Нет, но это важное примечание, которое, вероятно, применимо к любому ответу, поэтому я его поддержал, хотя я бы предпочел сказать «if (tmp) a = tmp; else {отметить сбой, но оставить as-is}» , Если realloc выходит из строя, никто не должен пытаться использовать «расширенную» область выделенного пространства, но тот, кому раньше принадлежал указатель, должен был удалить его. – supercat

4

В конце дня указатели на C - это не что иное, как машинное слово, которое может или не указывать на управляемые данные внутри памяти процесса, которые могут или не могут быть назначены malloc. Нет такой информации, связанной с любым указателем.

Просто добавьте большое, толстое предупреждение API. Если в документации четко сказано: «никогда не пропускайте этот указатель функции, который не был malloc'd», и кто-то делает это, тем не менее, и получает ошибки/сбои, это не ваша ошибка - здесь вы не можете запрограммировать оборону здесь.

0

Измените свою реализацию, чтобы не использовать realloc. Если пройденный буфер слишком мал, просто malloc новый более крупный и memcpy содержимое переданного в него буфера. Это улучшит удобство использования вашего API за счет предельной потери производительности.

+1

Это еще хуже. Теперь вам нужно полагаться на вызывающего, чтобы освободить() новый буфер. Если они этого не сделают, это не грохнет; это просто утечка памяти. Если вы хотите защищать пользователей, которые не читают документы API, не делайте этого так, чтобы это не помогло. – nmichaels

+0

@Nathon: Если вы переадресуете указатель на вызов API, который потенциально намного хуже, чем mallocing новой памяти. Исходный указатель теперь может быть недействительным, но если программист не прочитал документацию по API, он, возможно, не знает этого и решил записать на него больше данных. Это может легко привести к ошибкам с искажением данных, если, например, старый указатель теперь частично проходит через новый буфер. – JeremyP

2

Один возможный способ. Аннотация его.

foo.c 
#include "foo.h" 
struct foo{ 
    ... 
} ; 

foo *newFoo(...)   
void resize(foo *f)  

foo.h 

struct foo; 
typedef struct foo; 

caller.c 

foo *myfoo; 
myfoo = newFoo(..) 
resize(myfoo); 
+0

+1 Я думаю, что я просто ответил так же, как вы уже сделали – asr

0

Если все у вас есть значение указателя, нет (стандартный) способ, чтобы определить, что значение указателя был возвращен malloc(). Если у вас есть глубокое понимание того, как динамическое распределение памяти работает на вашей платформе, вы можете сделать образованное предположение, основанное на значении указателя, но даже это не гарантия того, что вы смотрите на то, что на самом деле является возвращаемым значением от malloc() as напротив значения, смещенного от этого (например, *ptr = malloc(10); foo(&ptr[5]);).

Одна из стратегий, которые могут или не могут быть стоит усилий (IME, это не так), чтобы скрыть malloc(), realloc() и free() за какой-то менеджер памяти, который отслеживает выделенных указателей и размеров. Вместо вызова realloc() ваш код вызовет функцию изменения размера в диспетчере памяти, которая вернет код ошибки, если указатель, переданный ему, не входит в список управляемых указателей.

2

Как пользователь должен получить этот указатель malloc'ed на первом месте? Разве не возможно, что он получен другой функцией в вашем API? В противном случае это звучит как шаблонный код для меня, нужно выделить кусок памяти с помощью malloc перед вызовом вашей функции.

В вашей ситуации я бы попробовал это. Когда вызывающий абонент получает действительный указатель через функцию, я предоставляю и освобождаю ее через другую функцию. Еще лучше, оберните все вместе в непрозрачной структуре.

my_param_type my_param = init_param(....); 
... 
my_function_that_wants_malloc(.....,my_param); 
... 
free_param(my_param); 

Имеет ли это значение в вашем API?

Имея свой собственный тип, будет понятно даже самому хромому пользователю, что он должен получить его через ожидаемую функцию init_param().

0

Создайте пользовательский тип данных, который является переименованным указателем void.

typedef void* myPointerType; 

написать выделить функцию (которая может быть простой оберткой malloc), что пользователь должен использовать для выделения памяти.

myPointerType myAllocateFunction (size_t size) { 
    return (myPointerType)(malloc(size)); 
} 

Вы также хотели бы создать соответствующую «свободную» функцию.

Теперь, ваша функция (тот, который выполняет realloc), принимает объект myPointerType как параметр вместо общего указателя. Вы можете отбросить его до void* для использования с realloc. Чтобы обойти это, пользователю пришлось бы вручную нарисовать указатель не malloc ed на myPointerType, но вы можете указать в своей документации, что листинг в и от myPointerType не разрешен (так что если они это сделают и их сбои приложений, это потому что они неправильно использовали API).

Есть еще более сильные способы, которыми вы можете обеспечить это, но я не уверен, что это будет стоить проблемы. Вы не можете полностью обмануть API (вы были бы удивлены тому, насколько способны дураки в эти дни).Независимо от того, что вы в конечном итоге реализуете, лучший способ убедиться, что ваш API используется правильно, - предоставить четкую и полную документацию.

Вы по-своему для пони, извините.

+0

Делает ли это typedef в файле * .h при создании как static/shared lib? Извините, новый вопрос. – 2012-04-11 23:11:24

+0

@ TristonJ.Taylor- Если это часть открытого интерфейса к вашей общей библиотеке, то да, он должен попасть в файл заголовка. В противном случае у вас будут функции, которые не заданы типами ссылок, и ваши пользователи получат ошибки во время компиляции. – bta

0

Если вы ищете проверку времени компиляции, единственное, что вы действительно можете сделать, это объявить новый тип, который возвращается «одобренными» вызовами распределения. Я хотел бы предложить что-то вроде:

 
typedef struct MEM_INFO {void *ptr; int allocsize; struct *privateMemInfo priv;} ALLOCMEM[0]; 

privateMemInfo будет определен в файле .c, а не заголовок, таким образом, несколько защиты полей в нем от озорства. Обратите внимание, что поскольку ALLOCMEM будет типом массива, ptr и allocsize всегда будут доступны с помощью оператора стрелки, а не точки. Подпрограмма, которая принимает ALLOCMEM в качестве параметра, естественно, получит указатель на ALLOCMEM, позволяя ему выполнять перемещение «красиво». Так, например, можно было бы использовать:

 
    ALLOCMEM foo = {0}; 
    if (reallocate(foo,12345) >= 0) /* It worked */ 
+0

Отличная идея здесь: typedef char * strptr; побеждает вас по-королевски! strptr может быть передан char *, и CPP поймает ошибку во время компиляции/проектирования.В случае, если кто-то пытается подорвать это, о да, realloc WILL шумно проваливается сам по себе. Отличная идея. Благодарю. Я думаю, что это то, что я и оп действительно ищут. – 2012-04-11 00:15:28

+0

Мое объявление ошибочно, поскольку оно должно быть ALLOCMEM [1], но основная идея заключается в том, что для всех подпрограмм распределения требуется ссылка на «struct MEM_INFO». Хотя ничто не помешало бы коду испортить содержимое «struct MEM_INFO», проверка времени компиляции обеспечит, чтобы ни одна ссылка, кроме ссылки на такую ​​структуру, не могла быть передана в процедуры распределения/освобождения. – supercat

0

Таким образом, я должен был бы сказать в Явно Документах, что он должен освободить указатель. Это даже хуже, чем до , ожидая, что они предоставят указатель malloc'd (что означает, что вызывающий абонент должен его освободить).

В библиотеках C это обычная практика:

Смежные вопросы