2011-03-17 3 views
6

Я просматривал webpage, у которого было какое-то c FAQ, я нашел это заявление.Арифметика указателя в границах c и массива

Аналогично, если имеет 10 элементов и ф пункты к [3], вы не можете вычислить или доступа IP + 10 или IP - 5. (Существует один особый случай: вы может, в этом случая, вычислительный, но не доступ, указателя на несуществующий элемент сразу за конец массива, который в данном случае является & а [10].

Я был смущен по заявлению

вы не можете вычислить ф + 10

Я могу понять доступ к элементу вне границ не определен, но вычисления !!!.

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

#include <stdio.h>                                         

int main()                                           
{                                             
     int a[10], i;                                        
     int *p;                                          

     for (i = 0; i<10; i++)                                      
       a[i] = i;                                       

     p = &a[3];                                         

     printf("p = %p and p+10 = %p\n", p, p+10);                                 
     return 0;                                         
}      

$ ./a.out                                  
p = 0xbfa53bbc and p+10 = 0xbfa53be4  

Мы можем видеть, что p + 10 указывает на 10 элементов (40 байт) мимо p. Так что же означает утверждение, сделанное на веб-странице. Я что-то неправильно интерпретировал.

Даже в K & R (A.7.7) производится это утверждение:

Результат оператора + является сумма операндов. Указатель на объект в массиве и значение любого интегрального типа . ... Сумма является указателем того же типа, что и , и указывает на другой объект в том же массиве, соответственно смещенный от исходного объект. Таким образом, если P является указателем на объект в массиве, выражение P + 1 является указателем на следующий объект в массиве . Если указатель суммы указывает за пределы массива, , за исключением первого места за пределами верхнего предела, результат не определен.

Что значит быть «неопределенным». Означает ли это, что сумма будет неопределенной или это будет означать только тогда, когда мы разыграем ее, поведение не определено. Является ли операция неопределенной, даже если мы ее не разыскиваем и просто вычисляем указатель на элемент за пределами границ.

+0

Это означает, что контент в этом конкретном месте не определен (случайный). – bioffe

+2

undefined означает, что получение того, что вы ожидаете, так же плохо, как получение денег на вашем банковском счете, переведенном на мой банковский счет. Продолжайте кодирование неопределенных конструкций ... когда-нибудь произойдет неожиданное ... и я возьму отпуск своей мечты :) – pmg

ответ

9

Неопределенное поведение означает, что именно: абсолютно все может случиться. Он мог бы преуспеть тихо, он мог терпеть неудачу, он может привести к сбою вашей программы, это может привести к синему экрану вашей ОС, или это может привести к стиранию вашего жесткого диска. Некоторые из них маловероятны, но все они являются допустимыми поведением в том, что касается стандарта языка C.

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

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

+0

Спасибо, Адам. Таким образом, это означает, что в моей системе это «определенное» поведение. Но если в той же системе массив находился в конце адресной памяти, это может вызвать проблемы. А в других системах может быть какое-то «аппаратное обеспечение проверки указателя», которое вообще не допускает такой операции, т. Е. Даже арифметику, не говоря уже о ее разыменовании. Правильно ли я понял это? Еще раз, спасибо. – jailed

+0

@jailed Это может быть даже не определенное поведение в вашей системе; вам нужно будет проверить документацию для конкретного компилятора C, который вы используете. – Jonathan

+0

«_Создание неверного адреса памяти не будет иметь вредных эффектов», если вы не используете недопустимый идентификатор сегмента – curiousguy

4

Текст на веб-странице является сбивающим с толку, но технически правильным. В C99 language specification (section 6.5.6) обсуждаются аддитивные выражения, включая арифметику указателя. В подпункте 8 указано, что вычисление указателя за концом массива не должно приводить к переполнению, но помимо этого поведение не определено.

В более практическом смысле компиляторы C, как правило, позволят вам уйти от него, но то, что вы делаете с полученным значением, зависит от вас. Если вы попытаетесь разыменовать полученный указатель на значение, как указано в K & R, поведение не определено.

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

+0

Нет, вы * не можете * вычислить значение. Даже просто вычисление указателя out-of-bounds, как утверждает стандарт C, является неопределенным поведением. –

+1

@Adam Я полагаю, что я запутался в неопределенности с общепринятым. Практически любой компилятор C выполнит математику для вас и даст вам значение, и оставьте разыменование этого значения неопределенным. – Jonathan

+0

@Adam, вы правы. Спецификация C99 (http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf), раздел 6.5.6, пункт 8, явно заявляет, что используя арифметику указателя для вычисления значения one мимо конца массива не должно вызывать переполнение, но дальнейший указатель не определен. Я буду соответствующим образом обновлять свой ответ. – Jonathan

2

поведение будет определено в следующем случае

int a[3]; 
(a + 10) ; // this is UB too as you are computing &a[10] 
*(a+10) = 10; // Ewwww!!!! 
Смежные вопросы