Как делает указатель точки к [-1], th индекс массива производит легальный вывод каждый раз. Что на самом деле происходит в присвоении указателя?
#include<stdio.h>
int main()
{
int realarray[10];
int *array = &realarray[-1];
printf("%p\n", (void *)array);
return 0;
}
Код произвел:
manav@workstation:~/knr$ gcc -Wall -pedantic ptr.c
manav@workstation:~/knr$ ./a.out
0xbf841140
Править: Если этот сценарий действителен, то могу я использовать это для определения массива, индекс которого запускают от 1 вместо 0, а именно: массив [1], выстройте [2]...
Вы просто получаете указатель, который содержит адрес этого "воображаемого" места, т.е. местоположение первого элемента &realarray[0]
минус размер одного элемента.
Это неопределенное поведение, и оно может ужасно сломаться, если, например, ваша машина имеет сегментированную архитектуру памяти. Это работает, потому что автор компилятора решил реализовать арифметику так, как описано выше; это может измениться в любой момент, и другой компилятор может повести себя совершенно иначе.
В C и C ++ индексы массива не проверяются во время выполнения. Вы выполняете арифметику указателя, которая может дать или не дать определенные результаты (не здесь).
Однако в C ++ вы можете использовать класс массива, который обеспечивает проверку границ, например boost :: array
или std :: tr1 :: array
(для добавления к стандартному библиотека в C ++ 0x):
#include <cstdio>
#include <boost/array.hpp>
int main()
{
try {
boost::array<int, 10> realarray;
int* p = &realarray.at(-1);
printf("%p\n", (void *)p);
} catch (const std::exception& e) {
puts(e.what());
}
}
Вывод:
array <>: index out of range
Также выдает предупреждение компилятора:
8 test.cpp [Warning] передает отрицательный результат value
-0x000000001 'для преобразования 1 из
T & boost :: array :: at (size_t) [с T = int, unsigned int N = 10u]'
Здесь вы просто выполняете указатель арифметика, он получит первый индексный адрес relarray
Видите, если вы & relarray [+1], вы получите второй адрес элемента массива. поскольку
& relarray [0] указывает на первый индексный адрес.
a[b]
определяется как *(a+b)
поэтому a[-1]
- это *(a-1)
Является ли a-1
действительным указателем и, следовательно, допустимо ли разыменование, зависит от контекста, в котором используется код.
Поведение не определено. Вы можете вычислить только указатель на любой из элементов массива или на один прошлый, но это все. Вы можете разыменовать указатель только на любой из элементов массива (а не на один прошлый указатель). Глядя на имена переменных, похоже, что вы задаете вопрос из этого FAQ по C . Я считаю, что ответ на FAQ очень хороший.
Хотя, как отмечали другие, в данном случае это неопределенное поведение, оно компилируется без предупреждений, потому что в целом , foo [-1]
может быть допустимым.
Например, это нормально:
int realarray[10] = { 10, 20, 30, 40 };
int *array = &realarray[2];
printf("%d\n", array[-1]);
Он просто указывает на адрес элемента, находящегося в памяти непосредственно перед массивом.
Массив можно рассматривать как указатель. Затем он просто уменьшается на единицу.
Вы просто указываете на 4 байта, расположенные перед массивом.
Это прекрасно определено. Ваш код гарантированно будет принят всеми компиляторами и никогда не выйдет из строя во время выполнения. Указатели C / C ++ - это числовой тип данных, подчиняющийся правилам арифметики. Работают сложение и вычитание, а обозначение скобок [] - всего лишь причудливый синтаксис для сложения. NULL - это буквально целое число 0.
И вот почему C / C ++ опасны. Компилятор позволит вам без жалоб создавать указатели, указывающие куда угодно. Разыменование дикого указателя в вашем примере * array = 1234;
приведет к неопределенному поведению, от незначительного повреждения до сбоя.
Да, вы можете использовать его для индексации с 1. Не делайте этого! Идиома C / C ++ заключается в том, чтобы всегда индексировать с 0. Другие люди, которые видели индексирование кода с 1, были бы склонны «исправить» его, чтобы индексировать с 0.
Поведение не определено.
То, что вы наблюдали, могло произойти в вашем конкретном компиляторе и конфигурации, но все может произойти в другой ситуации. На такое поведение вообще нельзя полагаться.
array
указывает на одно место перед начальным адресом realarray
. Однако меня смутило, почему это скомпилировано без каких-либо предупреждений.
Эксперимент мог бы дать немного больше подсказок, если бы он заключался в следующем. Вместо печати значения указателя как
printf("%p\n", (void *)array);
, выведите значение элемента массива
printf("%d\n", *array);
Это потому, что печать указателя с %p всегда даст какой-то результат (без каких-либо ошибок), но из него ничего нельзя будет вывести.