2016-03-06 6 views
0

В этом примере кода, может ли указатель действительно быть недействительным после цикла while, и следует ли это учитывать при написании кода? Или является стандартом C неправильным толкованием и/или дефектом?Является ли указатель в этом примере кода действительно недействительным?

#include <stdio.h> 

int main(int argc, char **argv) { 
    (void)argc; 
    (void)argv; 

    int *pointer; 
    int object[1]; 

    pointer = object; 
    printf("pointer -before: %p\n", (void*)pointer); 

    do { 
     int other_object[1]; 

     printf("a pointer \"just past\" other_object can look like: %p\n", (void*)(&other_object+1)); 
     printf("address of other_object: %p\n", (void*)&other_object); 
    } while (0); 
    puts("the lifetime of other_object has ended"); 
    printf("pointer -after: %p\n", (void*)pointer); 
} 

Возможный выход (бег на моей машине):

pointer -before: 0x7fff5f744ae4 
a pointer "just past" other_object can look like: 0x7fff5f744ae4 
address of other_object: 0x7fff5f744ae0 
the lifetime of other_object has ended 
pointer -after: 0x7fff5f744ae4 

Это, кажется, неопределенная указатель в соответствии с принятой SO ответ: Array resizing and realloc function

Этот вопрос также упоминается в следующей статье, с большим количеством примеров кода, создающих неожиданный выход из-за неопределенного поведения:

http://trust-in-soft.com/dangling-pointer-indeterminate/

Оба цитируют это предложение из ISO: «Значение указателя становится неопределенным, когда объект, на который он указывает (или только что прошел), достигает конца своего срока службы».


EDIT: Измененный исходный код немного основанный на комментарии о междунар * против пустоты *


EDIT: Измененный исходный код содержит массивы.

+0

Вы вызывали * неопределенное поведение * для передачи данных, имеющих неправильный тип, на 'printf()', так что это может быть. Поместите свой указатель на 'void *' перед тем, как передать его для печати через '% p'. – MikeCAT

+5

Код, который вы опубликовали, не изменяет (или фактически не использует) переменную 'pointer' в цикле - в отличие от связанной вами статьи. В вашем случае нет причин, по которым 'pointer' станет _indeterminate_. Может быть, вам следует объяснить, почему вы, возможно, начнете с этого. – dxiv

+0

«Почему вы думаете, что это может произойти» Я добавил образец вывода, поэтому его легче увидеть. «Указатель» может легко указывать только мимо other_object, который доходит до конца его жизни. –

ответ

2

Вы вызов два Неопределенных поведений по

  • передачи данных, имеющей неправильный типа для printf(): %p звонков для void*, но вы прошли int*.
  • Вычитание двух указателей, которые не указывают на элементы одного и того же объекта массива.

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

Если вы не ссылались неопределенное поведения, он не стал бы неопределенным, поскольку pointer не обновляется, так как адрес object, который все еще жив после do заявления, возложенные на него.

+0

Не отвечает на вопрос. В любом случае, как насчет кода с печатью и вычитания строк удалены? –

+0

Как и то, что написано в моем ответе: как указатель, так и указатель указателя жив. – MikeCAT

+0

Вопрос не в том, о чем он указывал. Речь идет о том, что оно (потенциально) указывает «только что прошло». –

6

Вы разборе это предложение неправильно:

Значение указателя становится неопределенным, когда объект он указывает (или просто мимо) достигает конца своей жизни

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

Это означает: если указатель указывает на объект, или, если он указывает на элемент за последним элементом массива, то она становится неопределенной , когда этот объект или что массив достигает конца своей жизни ,

Это не означает, что значение указателя становится неопределенным, когда объект, который только что происходит, приходит сразу после того, как объект, на который вы указываете, достигает конца его жизни, или если сам объект, на который указывает объект в памяти после другого объекта, который дошел до конца его жизни, что, как вы понимаете, понимает. В вашем примере тот факт, что other_object живет по адресу памяти непосредственно перед объектом, на который вы указываете, а затем достигает конца его жизни, совершенно не имеет значения, потому что объект, на который вы на самом деле указываете, а именно, object - все еще жив, и ни в коем случае ваш указатель никогда не указывает на что-либо еще, кроме этого все еще живого объекта.

Короткий ответ: вы читаете вещи в слова «только что прошедшие», которых нет. Это имеет смысл только в отношении указателя на объект массива, который увеличивается до элемента, который один за другим. Он не имеет ничего общего с любыми другими совершенно несвязанными объектами, которые могут произойти сразу же после заостренного объекта в памяти.

+0

«живет по адресу памяти сразу после того, как объект, на который вы указываете« часть, ошибается, это «непосредственно перед». Собственно прямо перед этим. Таким образом, указатель указывает сразу после 'other_object'. Кроме того, откуда вы, парни, получаете этот массив? ISO упоминает объект, где я смотрел. Не только специальные массивы (btw 'other_object' может быть заменен также массивом –

+1

@ BuellaGábor: в любом случае ваш указатель указывает на' object', поэтому время жизни 'other_object' не имеет ничего общего. вещь »объяснялась в комментариях - нет смысла говорить о« одном конце »для чего-либо другого, кроме массива. –

+2

@ BuellaGábor' это «непосредственно перед». Фактически непосредственно перед этим. «Нет. Это компилятор и архитектура В стандарте ничего не говорится о том, как адреса разных объектов (которые не являются элементами одного и того же массива) могут или не могут быть связаны друг с другом. Фактически, как я уже сказал в другом комментарии, стандарт заставляет UB даже попробовать для сравнения адресов несвязанных объектов. – dxiv

2

Вы спрашиваете,

В этом примере кода, может указатель действительно недействительным после того, как время цикла, и должен один действительно принять это во внимание при написании кода? Или же стандарт С неверно истолкован и/или неисправен?

, по-видимому, на основе этого текста взятого (немного из контекста) от стандарта:

«Значение указателя становится неопределенным, когда объект он указывает (или просто мимо) достигает конец его жизни ».

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

Так нет, в вашем коде, значение pointer не становится неопределенным, когда other_object достигает конца своей жизни, несмотря на его значение по отношению к указателю на other_object в течение всего срока службы.

+0

где это только единицы, упомянутые в стандарте? –

+0

", эта ситуация возникает, когда соответствующий указатель вызывается арифметикой указателя из указателя на (или в) объект." Где это упоминается в стандарте, касающемся уже упомянутого предложения? –

+0

@ BuellaGábor: C11 6.5.6.9: «Когда два указателя вычитаются, оба должны указывать на элементы одного и того же объекта массива или один за последним элементом объекта массива». –