Для ошибки цикла с двумерным динамически размещаемым массивом - PullRequest
0 голосов
/ 16 сентября 2018

У меня проблема с записью значений в мой 2D-массив, который я выделил динамически. Кажется, что он записывает значения в другие места в массиве, когда это не должно быть.

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

Когда я пытаюсь определить массив как двойной KAB[3][15]={0.0}, у меня нет этой проблемы.

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

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <string.h>



int main( ) 
{

int ms=0,i=0,j=0,n=0;
double value=0;
    double **KAB;
    KAB = (double **) malloc(3 * sizeof(double **));//makes array of pointers
    for(i = 0; i < 15; i++)
    {
        KAB[i] =(double *) malloc(3 *sizeof(double*));//each pointer points to a certain number of doubles
    }

        for(i=0;i< 3;i++)
        {
            for(j=0;j< 15;j++)
            {
                KAB[i][j]=0.0;//each value is set to 0.
            }
        }

for(ms=0; ms < 3; ms++)
{   
    for(i=0; i<15;i++)
    {       
        value=0;
        for(n=0; n<5 ;n++)
        {
                value+=ms*1.0+1;    
        }
        KAB[ms][i]=value;

        printf("Step:%d MS:%d\n",i,ms);
        printf("KAB[0][7]=%lf KAB[1][7]=%lf KAB[2][7]=%lf\n",KAB[0][7],KAB[1][7],KAB[2][7]);
    }
    }

return 0;
}//ends main    

Я включил соответствующий вывод с некоторыми аннотациями.

MS:0 Step:0
KAB[0][7]=0.000000, KAB[1][7]=0.000000, KAB[2][7]=0.000000
MS:0 Step:1

Все начинается с 0. И первое значение помещается в правильное место.

MS:0 Step:7
KAB[0][7]=5.000000, KAB[1][7]=0.000000, KAB[2][7]=0.000000

Но до конца цикла ms = 0 что-то записывается во вторую строку массива

MS:0 Step:11
KAB[0][7]=5.000000, KAB[1][7]=5.000000, KAB[2][7]=0.000000

Во время третьего шага цикла ms = 1 первая строка перезаписывается

MS:1 Step:3
KAB[0][7]=10.000000, KAB[1][7]=5.000000, KAB[2][7]=0.000000

На соответствующем шаге во второй строке, столбце седьмого значения вводится правильное значение

MS:1 Step:7
KAB[0][7]=10.000000, KAB[1][7]=10.000000, KAB[2][7]=0.000000

Но до того, как остальная часть строки будет закончена, то же значение будет помещено в следующую строку в том же столбце.

MS:1 Step:11
KAB[0][7]=10.000000, KAB[1][7]=10.000000, KAB[2][7]=10.000000

вторая строка заменяется некоторыми значениями из третьей строки

MS:2 Step:3
KAB[0][7]=10.000000, KAB[1][7]=15.000000, KAB[2][7]=10.000000

Третий ряд получает правильное значение. Эти значения остаются до конца, но ясно, что первая и вторая строки имеют некоторые неправильные значения.

MS:2 Step:7
KAB[0][7]=10.000000, KAB[1][7]=15.000000, KAB[2][7]=15.000000

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

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

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

    KAB = (double **) malloc(3 * sizeof(double **));//makes array of pointers
    for(i = 0; i < 15; i++)

3 & 15 являются магическими числами (то есть, если что-то меняется, и вам нужно отрегулировать пределы выделения или цикла, вам остается выбирать код, чтобы найтикаждое распределение и корректировать каждый предел цикла, каждый размер декларации и т. д.

1014 * Не используйте магические числа , например
#define ROW     3   /* if you need a constant, #define one (or more) */
#define COL    15
#define NVAL    5
...
    for (int ms = 0; ms < ROW; ms++) {              /* for each row */

        kab[ms] = calloc (COL, sizeof *kab[ms]);    /* allocate storage */

В вашем случае выиметь неправильное магическое число в распределении строк, например,

for(i = 0; i < 15; i++)
{
    KAB[i] =(double *) malloc(3 *sizeof(double*));
}

Вы выделяете только 3-указатели , но затем пытаетесь выделить и назначить хранилище для 3double* до 15-указателей . Начиная с KAB[3], вы вызываете Неопределенное поведение (поскольку вы уже использовали 3 выделенных указателя), и определенная операция вашего кода завершена.

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

Распределение указателей иХранилище для значений

Когда вы выделяете, используя указатель-указатель-на-тип в качестве базового типа, вы должны

  1. выделитьуказатель для хранения адреса памяти для каждой строки;и
  2. выделяет хранилище для каждой строки для хранения значений

(вы также должны проверять каждое выделение, malloc, calloc & realloc может и потерпеть неудачу при исчерпании памяти)

Далее, поскольку вы циклически обнуляете свои значения строк kab до нуля, просто используйте calloc для выделения, он одновременно выделяет и устанавливает память в ноль.

(не используйте UPPPER имена переменных регистра (зарезервированные для констант и макросов) и не используйте camelCase или MixedCase переменную - (оставьте их для Java или C ++))

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

...
int main (void) {

    double **kab = NULL;

    /* calloc allocates and initializes memory all zero */
    kab = calloc (ROW, sizeof *kab);    /* use dereference pointer to
                                         * determine typesize */

    if (!kab) {     /* validate every allocation by checking the return */
        perror ("calloc-kab");  /* handle error */
        exit (EXIT_FAILURE);
    }

    for (int ms = 0; ms < ROW; ms++) {              /* for each row */

        kab[ms] = calloc (COL, sizeof *kab[ms]);    /* allocate storage */
        if (!kab[ms]) {                             /* validate allocation */
            perror ("calloc-kab[ms]");              /* handle error */
            exit (EXIT_FAILURE);
        }
...

(Нет необходимости приводить к возвращению malloc, это не нужно. См .: DoЯ приведу результат malloc? )

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

Собрав все части вместе, вы можете сделать что-то похожее на следующее:

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

#define ROW     3   /* if you need a constant, #define one (or more) */
#define COL    15
#define NVAL    5

int main (void) {

    double **kab = NULL;

    /* calloc allocates and initializes memory all zero */
    kab = calloc (ROW, sizeof *kab);    /* use dereference pointer to
                                         * determine typesize */

    if (!kab) {     /* validate every allocation by checking the return */
        perror ("calloc-kab");  /* handle error */
        exit (EXIT_FAILURE);
    }

    for (int ms = 0; ms < ROW; ms++) {              /* for each row */

        kab[ms] = calloc (COL, sizeof *kab[ms]);    /* allocate storage */
        if (!kab[ms]) {                             /* validate allocation */
            perror ("calloc-kab[ms]");              /* handle error */
            exit (EXIT_FAILURE);
        }

        for (int i = 0; i < COL; i++ ) {    /* for each column */
            double value = 0;

            for (int n = 0; n < NVAL; n++)  /* loop NVAL times */
                value += ms * 1.0 + 1;      /* build value */

            kab[ms][i] = value;             /* assign value to kab[ms][i] */
        }
    }

    for (int ms = 0; ms < ROW; ms++) {      /* for each row */
        for (int i = 0; i < COL; i++)       /* for each col */
            printf (" %4.1lf", kab[ms][i]); /* output value */
        putchar ('\n');     /* tidy up */
        free (kab[ms]);     /* free row */
    }
    free (kab);             /* free pointers */

    return 0;
}

Пример использования / Вывод

Как создать value для хранения в каждом столбце, немного неинтересно, но для целей примера это хорошо.

$./bin/arraydyn2d
  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0
 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0
 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0

Использование памяти / проверка ошибок

В любом написанном вами коде, который динамически распределяет память, у вас есть 2 отклика относительно любого выделенного блока памяти: (1) всегда сохраняет указатель на начальный адрес для блока памяти, поэтому (2) он может быть освобожден , когда онбольше не требуется.

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

Для Linux valgrind - нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через нее.

$ valgrind ./bin/arraydyn2d
==15774== Memcheck, a memory error detector
==15774== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==15774== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==15774== Command: ./bin/arraydyn2d
==15774==
  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0  5.0
 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0
 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0 15.0
==15774==
==15774== HEAP SUMMARY:
==15774==     in use at exit: 0 bytes in 0 blocks
==15774==   total heap usage: 4 allocs, 4 frees, 384 bytes allocated
==15774==
==15774== All heap blocks were freed -- no leaks are possible
==15774==
==15774== For counts of detected and suppressed errors, rerun with: -v
==15774== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

Посмотрите вещии дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 16 сентября 2018

Вы неправильно делаете malloc.

KAB = (double **) malloc(3 * sizeof(double **));

Это утверждение неверно и будет выделять массив double *, но вам нужен массив double *, потому что каждый элемент внутри KAB должен быть double *, чтобы указывать на массив double, а не double **

Аналогично случаю со следующим утверждением

KAB[i] =(double *) malloc(3 *sizeof(double*));

Здесь вы выделяете массив типа double *, вместо него должен быть массив значений типа double.

Следующий код будет работать правильно.

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <string.h>



int main() 
{

    int ms=0,i=0,j=0,n=0;
    double value=0;
    double **KAB;
    KAB = (double **) malloc(3 * sizeof(double *));//makes array of type double pointers
    for(i = 0; i < 15; i++)
    {
      KAB[i] =(double *) malloc(3 *sizeof(double));//each element in KAB points to an array of doubles
    }

      for(i=0;i< 3;i++)
      {
        for(j=0;j< 15;j++)
        {
            KAB[i][j]=0.0;//each value is set to 0.
        }
      }

    for(ms=0; ms < 3; ms++)
    {   
        for(i=0; i<15;i++)
        {       
          value=0;
          for(n=0; n<5 ;n++)
          {
                  value+=ms*1.0+1;    
          }
          KAB[ms][i]=value;

          printf("Step:%d MS:%d\n",i,ms);
          printf("KAB[0][7]=%lf KAB[1][7]=%lf KAB[2][7]=%lf\n",KAB[0][7],KAB[1][7],KAB[2][7]);
        }
    }
    return 0;
}//ends main 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...