Указатель на [-1] -й индекс массива - PullRequest
11 голосов
/ 02 марта 2010

Как указатель указывает на [-1] -й индекс массива, каждый раз получая допустимый вывод. Что на самом деле происходит при назначении указателя?

#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], ...

Ответы [ 12 ]

13 голосов
/ 02 марта 2010

Вы просто получаете указатель, который содержит адрес этого "мнимого" местоположения, то есть местоположения первого элемента &realarray[0] минус размер одного элемента.

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

9 голосов
/ 02 марта 2010

a[b] определяется как *(a+b)

следовательно a[-1] равно *(a-1)

Является ли a-1 действительным указателем и, следовательно, разыменование является действительным, зависит от контекста, в котором используется код.

3 голосов
/ 02 марта 2010

Хотя, как отмечали другие, в данном случае это неопределенное поведение, оно компилируется без предупреждений, поскольку в общем , foo[-1] может быть допустимым.

Например, это нормально:

int realarray[10] = { 10, 20, 30, 40 };
int *array = &realarray[2];

printf("%d\n", array[-1]);
3 голосов
/ 02 марта 2010

Поведение не определено. Вы можете рассчитать только указатель на любой из элементов массива или одного прошлого, но это все. Вы можете только разыменовать указатель на любой из элементов массива (а не один предыдущий указатель). Глядя на имена переменных, похоже, что вы задаете вопрос из этого C FAQ . Я думаю, что ответ на FAQ очень хороший.

3 голосов
/ 02 марта 2010

Поведение не определено.

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

2 голосов
/ 02 марта 2010

В 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());
    }
}

Выход:

массив <>: индекс вне диапазона

Также выдает предупреждение компилятора:

8 test.cpp [Предупреждение] прохождение отрицательного значение -0x000000001' for converting 1 of T & boost :: array :: at (size_t) [с T = int, без знака int N = 10u] '

1 голос
/ 02 марта 2010

Здесь вы просто выполняете арифметику с указателем. Он получит первый индексный адрес для Relarray

Смотрите, если вы & relarray [+1], вы получите адрес второго элемента массива. с

& relarray [0] указывает на первый индексный адрес.

1 голос
/ 02 марта 2010

Он просто указывает на адрес элемента прямо перед массивом в памяти.

Массив можно просто рассматривать как указатель. Затем это просто уменьшается на единицу.

0 голосов
/ 02 марта 2010

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

printf("%p\n", (void *)array);

, выведите значение элемента массива

printf("%d\n", *array);

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

0 голосов
/ 02 марта 2010

Это прекрасно определено. Ваш код гарантированно будет принят всеми компиляторами и никогда не потерпит крах во время выполнения. Указатели C / C ++ - это числовой тип данных, который подчиняется правилам арифметики. Работа сложения и вычитания, а скобочная запись [] - это просто причудливый синтаксис для сложения. NULL буквально целое число 0.

И именно поэтому C / C ++ опасны. Компилятор позволит вам создавать указатели, которые указывают куда угодно без жалоб. Разыменование дикий указатель в вашем примере, *array = 1234; приведет к неопределенному поведению, от незначительного повреждения до сбоя.

Да, вы можете использовать его для индексации от 1. Не делайте этого! Идиома C / C ++ заключается в том, чтобы всегда индексировать с 0. Другие люди, которые видели индексирование кода с 1, будут склонны "исправить" его индексирование с 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...