2013-07-29 2 views
10

Я читал новый (май 2013) O'Reilly книгу «Понимание и использование C указателей» Ричард Риз, и у меня есть вопрос о какой-то код в нем, на странице 87.Использование исходного указателя после realloc?

if (++length > maximumLength) { 
    char *newBuffer = realloc (buffer, maximumLength += sizeIncrement); 

    if (newBuffer == NULL) { 
     free (buffer); 
     return NULL; 
    } 

    currentPosition = newBuffer + (currentPosition - buffer); 
    buffer = newBuffer; 
} 

Я надеюсь, имена переменные не требуют пояснений; если контекст необходим, я отредактирую, чтобы предоставить весь фрагмент кода, а не только эту выдержку.

Мой вопрос о линии currentPosition = newBuffer + (currentPosition - buffer);. Мое понимание realloc() заключается в том, что когда новое распределение успешно завершается, изначально выделенная память освобождается. Если это правильно, тогда строка, о которой идет речь, использует оборванные указатели, не так ли? Оба buffer и currentPosition на RHS этого выражения являются указателями на память, которые были освобождены.

Моим инстинктом было бы переписать это, чтобы избежать использования оборванных указателей, используя length, который ведь уже существует. Я хочу, чтобы заменить эти последние две строки с:

buffer = newBuffer; 
currentPosition = buffer + length; 

Однако, предположительно кода в письменных работах, так как два указателя все еще держат адреса (хотя и мусора), а смещение между этими двумя адресами еще может быть рассчитана как способ переназначения currentPosition. Так что я просто пронзительный, чувствуя себя неловко об этом?

Чтобы обобщить вопрос: как только указатель свисает, безопасно ли использовать адрес, содержащийся в указателе, для любых целей, например вычисления смещений? Благодарю.

+0

В момент перераспределения длина 'length 'больше размера буфера (' maximumLength' до корректировки). Вы должны использовать 'currentPosition = buffer + length - 1', если я правильно интерпретирую значения. – Casey

+0

Я проверил это, прежде чем публиковать вопрос. Код книги инициализирует «length» и «currentPosition» равным нулю. 'length' увеличивается в первом условном выражении, поэтому он всегда один за индексом последнего добавленного элемента. 'currentPosition' - это то, где новый элемент должен быть добавлен, и после добавления добавляется. Это не то, как я бы написал код для начала, но принимая код как заданный, 'buffer + length' верен. – verbose

+0

Итак, 'currentPosition' является предварительно скомпонованным' buffer + length'? Я стою исправлен (и немного недоумеваю из-за избыточности). – Casey

ответ

9

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

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

+0

Источник? Я бы этого не ожидал. –

+2

@CoryNelson C11, 6.5.6p8 указатель арифметики. Если результат не находится в объекте массива или один за последним элементом => неопределенное поведение. – ouah

+1

@Ouah, я думаю, это относится к тому, когда вы пытаетесь разыменовать результат. – ROTOGG

0

Безопасно использовать свисающий указатель (например, для «арифметики указателя»), если вы не пытаетесь разыменовать указатель (т. Е. Применить оператор *).

+3

Это неправильно, потому что стандарт C не гарантирует, что арифметика на прежних указателях на объекты, которые больше не существуют, работает. Вы можете предположить, что указатель реализован как адреса памяти, а арифметика по адресам явно работает, но указатели не обязательно реализуются таким образом. Кроме того, оптимизатору разрешено делать вычеты на основе правил C, что может привести к неожиданному поведению. –

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