У меня проблема с записью значений в мой 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
1
JMC 16 Сен 2018 в 02:40

2 ответа

Лучший ответ

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

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

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

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

Не используйте магические числа , например

#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 указателя , но затем пытаетесь выделить и назначить хранилище для 3 double* до 15 указателей . Начиная с KAB[3] вы вызываете Undefined Behavior (потому что вы уже использовали 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, в этом нет необходимости. См. Приведение результата malloc? )

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

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

#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)

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

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

1
David C. Rankin 17 Сен 2018 в 17:59

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

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

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

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

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 
0
Anupam 16 Сен 2018 в 00:46