Слишком много элементов в массиве! - PullRequest
5 голосов
/ 28 апреля 2009

Извините, если это нубский вопрос :(.

Часть кода С.

int array[5];
int cnt;

for(cnt = 0; cnt <= 10; cnt+=1)
{
      array[cnt] = cnt;
}

Должен дать ошибку, верно? Нет! Работает отлично! Но почему это так? Кажется, что в первой строке определен массив больше, чем двойной размер (11). Вы даже можете получить доступ к массиву [5-10] позже. И это меня смущает. Он перестает работать, когда вы определяете массив [4 или меньше] ...

Заранее спасибо.

Ответы [ 9 ]

23 голосов
/ 28 апреля 2009

Это может случиться с вашим конкретным компилятором и компьютером, но вы не должны на это рассчитывать.

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

В отличие от языков высокого уровня, таких как Java и C #, C доверяет вам и не выполняет явных проверок границ массивов. Вы должны нести ответственность и не выходить за пределы массива.

16 голосов
/ 28 апреля 2009

Это «работает», только если ваше определение «работает» является синонимом «еще не разбился».

5 голосов
/ 28 апреля 2009

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

4 голосов
/ 28 апреля 2009

Я просто хотел бы отметить, что все это действительно не определено. Ваш пример «работает» в этом конкретном примере, потому что обе переменные расположены в стеке. То есть адрес cnt находится чуть ниже конца массива. Когда cnt достигает cnt == 5 массив операторов [cnt] = cnt; не записывает в память, выделенную для массива, а сразу после него, где лежит адрес cnt. Это просто удача, что это не меняет ваш счетчик. Когда cnt> 5, нет памяти для мусора, и он просто записывает в «стек пустых» (не знаю правильное слово).

еще один пример, иллюстрирующий это:

int main(int ac,char **av)
{
    int a[5];
    int cnt;
    int cnt2=3;

    for(cnt=0;cnt<7;cnt++) {
        a[cnt]=cnt;
        printf("%d %d %d\n", a[cnt], cnt, cnt2);
    }
}

выход:

0 0 3
1 1 3
2 2 3
3 3 3
4 4 3
5 5 5
6 6 5

Последние две записи цикла перезаписывают данные стека после [] и могут привести к очень запутанным ошибкам. В этом случае cnt2 уничтожается.

4 голосов
/ 28 апреля 2009

«Но почему?»

Потому что таков С.

Границы массива не проверяются во время выполнения.

Это "Закон С"

2 голосов
/ 28 апреля 2009

Массивы в Си не проверяются во время выполнения. Другими словами, вы можете «определить» массив размера N и получить доступ к концу границы массива. Если вы выходите из конца массива, то вы удаляете память где-то в стеке (или в куче).

Как только вы где-то очистите память, ваша программа, вероятно, вылетит. Эти сбои может быть трудно отследить, потому что они могут сбои далеко от того места, где вы фактически перекрыли конец массива.

Обычно, когда вы объявляете массивы в C, лучше всего использовать некоторую константу или #define, чтобы отметить размер массива:

#define MAX_ELEMENTS 10
int array[MAX_ELEMENTS];
int cnt;
for(cnt = 0; cnt < MAX_ELEMENTS; cnt+=1) {
   array[cnt] = cnt;
}

Если вы пропустите MAX_ELEMENTS в присваивании массива, вы можете перезаписать значение cnt. Вы можете перезаписать другую переменную. Все зависит от компилятора и структуры кода. Также обратите внимание на использование знака <в цикле for. Массивы C основаны на 0, поэтому вы должны проверять, используя значение меньше или меньше или меньше. </p>

1 голос
/ 01 мая 2009

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

0 голосов
/ 28 апреля 2009

Зависит от того, как упакована стековая память. Кроме того, он с радостью перезапишет эти значения и даже прочитает их, но, скорее всего, вы повредите стек.

0 голосов
/ 28 апреля 2009

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

...