Неправильные значения внутри массива

Что вызывает ArrayIndexOutOfBoundsException?

Если вы думаете о переменной как о «поле», где вы можете поместить значение, то массив представляет собой ряд ящиков, расположенных рядом с eachother, где число Ячейки представляют собой конечное и явное целое число.

Создание такого массива:

final int[] myArray = new int[5]

создает строку из 5 полей, каждая из которых имеет int. Каждый из ящиков имеет индекс, позицию в ряду ящиков. Этот индекс начинается с 0 и заканчивается на N-1, где N - размер массива (количество ящиков).

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

myArray[3]

Который даст вам значение 4-го ящика в серии (так как в первом поле есть индекс 0).

ArrayIndexOutOfBoundsException вызвано попыткой извлечь «ящик», который не существует, передав индекс, который выше индекса последнего «поля» или отрицательный.

В моем примере работы эти фрагменты кода приведут к такому исключению:

myArray[5] //tries to retrieve the 6th "box" when there is only 5
myArray[-1] //just makes no sense
myArray[1337] //waay to high

Как избежать ArrayIndexOutOfBoundsException

Чтобы предотвратить ArrayIndexOutOfBoundsException, необходимо рассмотреть некоторые ключевые моменты:

Looping

При переходе по массиву всегда убедитесь, что индекс, который вы извлекаете, строго меньше длины массива (количество ящиков). Например:

for (int i = 0; i < myArray.length; i++) {

Обратите внимание на <, никогда не смешивайте там =.

Возможно, вам захочется сделать что-то вроде этого:

for (int i = 1; i <= myArray.length; i++) {
    final int someint = myArray[i - 1]

Просто нет. Придерживайтесь одного выше (если вам нужно использовать индекс), и это сэкономит вам много боли.

По возможности используйте foreach:

for (int value : myArray) {

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

Когда вы выполняете цикл, что бы вы ни делали, НИКОГДА не изменяйте значение итератора цикла (здесь: i). Единственное место, которое должно изменить значение, это сохранить цикл. Изменение в противном случае просто рискует исключением и в большинстве случаев не является обязательным.

Retrieval / update

При извлечении произвольного элемента массива всегда проверяйте его действительность индекс по длине массива:

public Integer getArrayElement(final int index) {
    if (index < 0 || index >= myArray.length) {
        return null; //although I would much prefer an actual exception being thrown when this happens.
    }
    return myArray[index];
}
0
задан Defender 25 June 2019 в 19:33
поделиться

4 ответа

Этот дополнительный ответ в ответ на комментарий OP к моему более раннему ответу. Защитник спрашивает:

"Я имею некоторые вопросы в запасе. Если бы это ожидает массив интервала типа, и я отправил дважды, почему это изменило бы результат в 360 градусах. Я все еще не получил ответ на это".

будет легче проиллюстрировать это со столкновением типа между short int и int, но та же идея относится int по сравнению с double.

Смотрят на этот код:

#include <stdlib.h>
#include <stdio.h>

void functionA(short int[], int size);

int main(void) {

    printf("sizeof short int : %lu\n", sizeof(short int));
    printf("sizeof int       : %lu\n", sizeof(int));
    printf("\n\n");

    printf("====  sending short int to functionA() ====\n");
    short int shortdata[4] = {10, 20, 50, 100};
    functionA(shortdata, 4);

    printf("====  sending int to functionA() ====\n");
    int intdata[4] = {10, 20, 50, 100};
    functionA(intdata, 4);
}

void functionA(short int arr[], int size) {

   int i;

   char* ptr;

   for (i = 0; i < size; ++i) {
      ptr = &arr[i];
      printf("bytes of 'arr[%d]' : %x %x\n", i, *ptr, *(ptr+1));

      printf("value of 'arr[%d]' : %d\n", i, arr[i]);

      printf("\n");
   }
}

, который производит этот вывод:

sizeof short int : 2
sizeof int       : 4


====  sending short int to functionA() ====
bytes of 'arr[0]' : a 0
value of 'arr[0]' : 10

bytes of 'arr[1]' : 14 0
value of 'arr[1]' : 20

bytes of 'arr[2]' : 32 0
value of 'arr[2]' : 50

bytes of 'arr[3]' : 64 0
value of 'arr[3]' : 100

====  sending int to functionA() ====
bytes of 'arr[0]' : a 0
value of 'arr[0]' : 10

bytes of 'arr[1]' : 0 0
value of 'arr[1]' : 0

bytes of 'arr[2]' : 14 0
value of 'arr[2]' : 20

bytes of 'arr[3]' : 0 0
value of 'arr[3]' : 0

первые две строки вывода показывают, что на моей машине, short int берет 2 байта памяти, и int берет 4 байта.

functionA() ожидает short int массив, и когда я отправляю его short int[], мы видим ожидаемый вывод. Байты, которые составляют первый элемент массива, 0x0a 0x00, который в десятичном числе равняется "10"; байты, которые составляют второй элемент массива, 0x14 0x00, который в десятичном числе равняется "20"; и так далее.

, Но когда я отправляю functionA() int[], я отправляю 4 байта за элемент, поэтому когда это выполняет итерации через массив, это не видит элементы правильно. Это делает хорошо с первым элементом, но только потому, что это - небольшое число; когда это ищет второй элемент, это на самом деле смотрит на последние два байта первого элемента, таким образом, это видит 0x00 0x00, или "0"; когда это ищет третий элемент, это смотрит на первые два байта второго элемента и видит 0x14 0x00, или "20"; и так далее.

Другой способ показать случается так, что байты [1 120] являются этим:

0a 00 14 00 32 00 64 00

и байты [1 121] это:

0a 00 00 00 14 00 00 00 32 00 00 00 64 00 00 00

, поскольку functionA() ожидает short int[], это рассматривает intdata тот путь - два байта за элемент - и видит как его элементы:

arr[0] : 0a 00
arr[1] : 00 00
arr[2] : 14 00
arr[3] : 00 00

Это - подобная история с Вашим кодом. Ваш getAverage() ожидает int[], поэтому когда Вы отправляете его double[], это не видит байты путем, Вы предназначаете.

(Столкновение между [1 128] и double является еще более решительным, чем между [1 130] и int; это вызвано тем, что в дополнение к тому, чтобы быть различными размерами (на большинстве современных машин, int 4 байта и double, 8 байтов), числа с плавающей запятой хранятся как экспонента и мантисса - таким образом, значения байта имеют совершенно другое значение.)

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

0
ответ дан landru27 25 June 2019 в 19:33
поделиться

Объекты, определенные внутри функций без static или _Thread_local, не инициализируются автоматически и имеют неопределенные значения. Чтобы обеспечить инициализацию массива mrr, определите его с помощью int mrr[2][3] = { 0 };, который инициализирует его всеми нулями.

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

1
ответ дан Eric Postpischil 25 June 2019 в 19:33
поделиться

По умолчанию [значения] массива, определенного внутри функции (то есть локального массива), являются неопределенными. Глобальные (то есть статические) переменные по умолчанию инициализируются равными 0. Смотрите этот пост: Начальное значение массива int в C

. Для решения вашей проблемы просто инициализируйте ваш массив вручную, например,

for (int r=0;r<2;r++)
{
    for (int c=0; c<3; c++) {
        mrr[r][c] = 0;
        printf("[%d],[%d]:%d\n",r,c,mrr[r][c]);
    }
}
.
0
ответ дан b-frid 25 June 2019 в 19:33
поделиться

Ваш вопрос о неинициализированных переменных хорошо освещен в комментариях и других ответах; Этот ответ для вашего второго вопроса, о неожиданном выводе из getAverage().

Вы определяете balance как массив double, но getAverage() ожидает массив int. Когда я изменяю:

double getAverage(int arr[], int size) { ... }

на:

double getAverage(double arr[], int size) { ... }

, я получаю ожидаемый результат: 215.2. Неявное преобразование типов [1] в вашем исходном коде вызывает неожиданное поведение.

Кроме того, я не знаю, какой компилятор вы используете, но когда я скомпилировал ваш оригинальный код [2], gcc выдал это предупреждение:

warning: incompatible pointer types passing 'double [5]' to parameter of type 'int *'

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

[1] «преобразование типов», возможно, не лучший термин здесь; преобразование типов обычно не вызывает затруднений, хотя есть некоторые ошибки; здесь происходит то, что байты, составляющие значение double, читаются как int, и структура байтов этих двух типов данных очень различна; вот почему при выводе вашего исходного кода создается впечатление, что он имеет дело с такими сумасшедшими большими числами: например, байты, используемые для представления «17» в виде двойного числа, когда читается как int, становятся очень, очень разными числами. Хуже того, проходя через байты, составляющие массив значений double, как если бы это был массив значений int, он, вероятно, даже не смотрит на элементы массива на соответствующих границах элементов. Короче говоря, несоответствие типов вызывает хаос.

[2] Под «исходным кодом» я подразумеваю ваш код с достаточным количеством служебного кода для его компиляции, например, include прагмы и тело main(); Я в целом согласен с другими комментариями о предоставлении полного, минимального примера.

0
ответ дан landru27 25 June 2019 в 19:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: