2017-09-22 5 views
2

행렬 곱셈을 위해 동적으로 유형화 된 3 개의 행렬 (int, float, double)을 만들려고합니다. 메인에있는 각 행렬에 대해 void ** 형식의 컨테이너를 만든 다음 void (***)를 init 함수에 전달하여 해당 유형에 대한 사용자 선택에 따라 malloc됩니다. 아래의 코드는 컴파일되지만 j 루프를 한 번 반복하면 세그멘테이션 오류가 발생합니다. 왜 이런 일이 생기는지 내 인생에 대해 알 수는 없습니다. 마찬가지로 별도의 2-deep 루프 (malloc 루프에서 j-loop를 가져옴)에서 초기화를 수행하면 i-loop가 한 번 반복 된 후에 세그먼트 화 오류가 발생합니다.C : 다른 함수 안에 동적으로 입력 된 2D 행렬 초기화

다이내믹 형 행렬 곱셈의 목표를 달성하기위한 좋은 방법일까요? 도움을 주셔서 대단히 감사합니다.

void initMat(int type, int matSize, void ***matA, void ***matB, void ***matC) 
{ 
    int i, j, k; 

    switch(type) { 
     case 0 : 
     *matA = malloc(matSize * sizeof(int*)); 
     *matB = malloc(matSize * sizeof(int*)); 
     *matC = malloc(matSize * sizeof(int*)); 
     for (i = 0; i < matSize; i++) { 
      *matA[i] = malloc(matSize * sizeof(int)); 
      *matB[i] = malloc(matSize * sizeof(int)); 
      *matC[i] = malloc(matSize * sizeof(int)); 
      for (j = 0; j < matSize; j++) { 
       *(int*)matA[i][j] = rand()/RAND_MAX * 10; 
       *(int*)matB[i][j] = rand()/RAND_MAX * 10; 
       *(int*)matC[i][j] = 0; 
      } 
     } 
     break; 

     case 1 : 
     // with float, double, etc. 
     break; 

     default : 
     printf("Invalid case.\n"); 
    } 
} 

int main() 
{ 
    int type = 0; 
    int size = 0; 
    void **matA, **matB, **matC; 

    int sizes[6] = {3, 4, 5}; 
    int matSize = sizes[size]; 
    printf("The selected matrix size is: %d. \n", matSize); //allows user to select matrix size 

    initMat(type, matSize, &matA, &matB, &matC); 

// displayMat(matSize, matA); 

} 
+0

필요'* (INT *)'? –

+0

GDB를 사용하여 프로그램이 원하는 출력을 제공하지 않거나 충돌이 발생하는 곳을 확인하십시오 –

+0

[this] (https://ideone.com/igYfQQ) – BLUEPIXY

답변

0

동적으로 할당 된 2 차원 배열을 사용하려면 올바른 포인터 유형을 사용해야합니다. (int **)은 서로 다른 할당을 가리키는 포인터 배열의 첫 번째 요소를 가리키는 포인터에 대한 포인터입니다. 이런 종류의 코드 결과는 들쭉날쭉 한 배열이지만 2 차원 배열은 아닙니다. 할당 된 메모리는 (배열 할당이 있어야합니다으로) 연속이 보장되지 않습니다

size_t num_rows = 3; 
size_t num_cols = 5; 

int **jagged_arr = malloc(sizeof *jagged_arr * num_rows); 

for (size_t i = 0; i < num_rows; i++) { 
    jagged_arr[i] = malloc(sizeof *jagged_arr[i] * num_cols); 
} 

하나의 가능성은 단순히 1 차원 배열에 대한 스토리지를 할당하고, 2 차원 배열 인덱스에서이 배열에 오프셋을 계산하는 것입니다.이것은 잘 작동하지만, 그 결과는 2 차원 배열이 아닌 :

size_t num_elems = num_rows * num_cols; 
int *simulated_2d_arr = malloc(sizeof *simulated_2d_arr * num_elems); 

이 2 차원 배열로 인덱스 될 수 있지만, 1 차원 인덱스 열의 수와 2 차원 배열의 인덱스로부터 계산 될 수있다 :

for (size_t i = 0; i < num_rows; i++) { 
    for (size_t j = 0; j < num_cols; j++) { 
     simulated_2d_arr[i * num_cols + j] = i * num_cols + j; 
    } 
} 

두 가지 방법 모두 용도가 있지만 결과 배열을 2 차원 배열로 작업 할 수있는 함수로 전달할 수 없다는 단점이 있습니다.

void print_2d_arr(size_t rows, size_t cols, int arr[][cols]) 
{ 
    for (size_t i = 0; i < rows; i++) { 
     for (size_t j = 0; j < cols; j++) { 
      printf("%5d", arr[i][j]); 
     } 
     putchar('\n'); 
    } 
} 

이 기능은 같은 뭔가를 작동합니다 :

int real_2d_arr[2][3] = { { 1, 2, 3 }, 
          { 4, 5, 6 } }; 

하지만 jagged_arr 이전에 대해 작동하지 않습니다 :

즉, 같은 2 차원 배열을 인쇄하는 기능을 고려한다

예상 'INT (*) (sizetype) (COLS)]하지만 인수 형'INT ** '

이다,691 363,210

또는 simulated_2d_arr 위해 동적으로 할당 할 때

예상 'INT (*) (sizetype) (COLS)]하지만 인수 형인'INT * '

올바른 유형 사용 2d 배열은 위의 오류 메시지에서 볼 수 있습니다. int의 2 차원 배열의 경우 int (*)[]이됩니다. 이것은 함수 호출을 포함하여 대부분의 표현식에서 2 차원 배열이 붕괴되는 유형입니다. 따라서, 동적 int s의 2 차원 배열을 할당이 작동합니다 :

size_t num_rows = 3; 
size_t num_cols = 5; 
int (*array_2d)[num_cols] = malloc(sizeof *array_2d * num_rows); 

num_colsint의의 num_rows 배열을위한 공간을 할당합니다. VLA는 생성되지 않지만 VLA 유형이 사용됩니다. 물론 VLA는 C99에서 다시 소개되었지만 C11에서는 선택 사항으로 지정되었습니다 (여전히 널리 지원되지만).

질문의 동적 유형 부분에 대해서는 유형 식별자를 보유하기 위해 enum을 만들고 이러한 열거 상수 중 하나를 필요로하는 함수에 전달하는 옵션이 있습니다. 이 함수는 (void *) 인수를 받아 들여야하며,이 인수는 형식 열거 형 상수를 기반으로 적절히 변환됩니다. 이것은 좀 더 복잡하지만 여기 예제 프로그램이 있습니다. print_array() 함수는 동적으로 할당 된 배열과 정적으로 크기가 지정된 배열 모두에서 작동합니다. 또한 트리플, 또는 두 배 간접 표시가 필요 없음을 유의하십시오.

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

enum Type { CHAR, 
      INT, 
      FLOAT, 
      DOUBLE }; 

void * get_array(enum Type type, size_t rows, size_t cols); 
void init_array(enum Type type, size_t rows, size_t cols, void *arr); 
void print_array(enum Type type, size_t rows, size_t cols, void *arr); 

int main(void) 
{ 
    char (*arr_char)[5] = get_array(CHAR, 4, 5); 
    int (*arr_int)[5] = get_array(INT, 4, 5); 
    double (*arr_double)[5] = get_array(DOUBLE, 4, 5); 

    int arr_static[][3] = { { 1, 2, 3 }, 
          { 4, 5, 6 }, 
          { 7, 8, 9 } }; 

    if (arr_char) {       // check for null pointer 
     init_array(CHAR, 4, 5, arr_char); 
     puts("4x5 array of char"); 
     print_array(CHAR, 4, 5, arr_char); 
     putchar('\n'); 
    } 

    if (arr_int) {       // check for null pointer 
     init_array(INT, 4, 5, arr_int); 
     puts("4x5 array of int"); 
     print_array(INT, 4, 5, arr_int); 
     putchar('\n'); 
    } 

    if (arr_double) {       // check for null pointer 
     init_array(DOUBLE, 4, 5, arr_double); 
     puts("4x5 array of double"); 
     print_array(DOUBLE, 4, 5, arr_double); 
     putchar('\n'); 
    } 

    puts("Statically sized 3x3 array of int"); 
    print_array(INT, 3, 3, arr_static); 
    putchar('\n'); 

    /* Cleanup */ 
    free(arr_char); 
    free(arr_int); 
    free(arr_double); 

    return 0; 
} 

/* Returns null pointer on allocation failure */ 
void *get_array(enum Type type, size_t rows, size_t cols) 
{ 
    size_t array_sz = 0; 
    void *ret = NULL; 

    switch (type) { 
    case CHAR: 
     array_sz = sizeof (char) * rows * cols; 
     break; 
    case INT: 
     array_sz = sizeof (int) * rows * cols; 
     break; 
    case FLOAT: 
     array_sz = sizeof (float) * rows * cols; 
     break; 
    case DOUBLE: 
     array_sz = sizeof (double) * rows * cols; 
     break; 
    default: 
     fprintf(stderr, "Unrecognized type in get_array()"); 
    } 

    if (array_sz) { 
     ret = malloc(array_sz); 
    } 

    return ret; 
} 

void init_array(enum Type type, size_t rows, size_t cols, void *arr) 
{ 
    for (size_t i = 0; i < rows; i++) { 
     for (size_t j = 0; j < cols; j++) { 
      int offset = i * cols + j; 
      switch (type) { 
      case CHAR: 
      { 
       char (*array_char)[cols] = arr; 
       array_char[i][j] = 'a' + offset; 
       break; 
      } 
      case INT: 
      { 
       int (*array_int)[cols] = arr; 
       array_int[i][j] = 0 + offset; 
       break; 
      } 
      case FLOAT: 
      { 
       float (*array_float)[cols] = arr; 
       array_float[i][j] = 0.0 + offset; 
       break; 
      } 
      case DOUBLE: 
      { 
       double (*array_double)[cols] = arr; 
       array_double[i][j] = 0.0 + offset; 
       break; 
      } 
      default: 
       fprintf(stderr, "Unrecognized type in get_array()"); 
      } 
     } 
    } 
} 

void print_array(enum Type type, size_t rows, size_t cols, void *arr) 
{ 
    for (size_t i = 0; i < rows; i++) { 
     for (size_t j = 0; j < cols; j++) { 
       switch (type) { 
      case CHAR: 
      { 
       char (*array_char)[cols] = arr; 
       printf("%3c", array_char[i][j]); 
       break; 
      } 
      case INT: 
      { 
       int (*array_int)[cols] = arr; 
       printf("%5d", array_int[i][j]); 
       break; 
      } 
      case FLOAT: 
      { 
       float (*array_float)[cols] = arr; 
       printf("%8.2f", array_float[i][j]); 
       break; 
      } 
      case DOUBLE: 
      { 
       double (*array_double)[cols] = arr; 
       printf("%8.2f", array_double[i][j]); 
       break; 
      } 
      default: 
       fprintf(stderr, "Unrecognized type in get_array()"); 
      } 
     } 
     putchar('\n'); 
    } 
} 

프로그램 출력 :

4x5 array of char 
    a b c d e 
    f g h i j 
    k l m n o 
    p q r s t 

4x5 array of int 
    0 1 2 3 4 
    5 6 7 8 9 
    10 11 12 13 14 
    15 16 17 18 19 

4x5 array of double 
    0.00 1.00 2.00 3.00 4.00 
    5.00 6.00 7.00 8.00 9.00 
    10.00 11.00 12.00 13.00 14.00 
    15.00 16.00 17.00 18.00 19.00 

Statically sized 3x3 array of int 
    1 2 3 
    4 5 6 
    7 8 9 
1

저는 실제로 이와 같은 것을 독자적으로 연구했습니다. 이것을하는 방법에 대한 나의 충고는 실제로 당신이 그것을하는 방식을 포기하는 것입니다. 그것의 모양에서 당신은 포인터의 배열을 가질 수 있습니다 (int 포인터의 배열을 말하게하십시오). 그리고 그 포인터 각각은 자신의 배열을 가지며, 기본적으로는 int **exampleMatrix이라고 선언 된 변수를 가지고 있습니다. 실제로 캐시 미스 (cache miss)와 같은 문제가 있습니다. 더 좋은 방법은 다음과 같습니다.

#include <stdlib.h> 

void *initMat(size_t rows, size_t cols, int type); 

int main(void){ 
    int *matrix = initMat(); 
    free(matrix); 
    return 0; 
} 

void *initMat(size_t rows, size_t cols, int type){ 
    //check for what type to allocate here 
    void *matrix = malloc(sizeof(int)*rows*cols); 
    //check for errors here 
    return matrix; 
} 

물론 매트릭스가 행 또는 열 메이저인지 결정해야합니다. 나는 이것이 의미가 있기를 바랍니다, 영어는 제 첫 언어가 아니며 때로는 설명하는 것이 최선이 아닙니다. 나를 더 잘 설명 할 필요가 있다면 그냥 말해주세요.

+0

네,이게 합리적입니다. 저는 이것이 2D 매트릭스를 할당하는 한 가지 방법이라는 것을 알고 있습니다. (단 하나의 레벨 포인터와 모든 후속 get/sets는 올바른 값을 얻기 위해 포인터 산술 연산을 수행하면됩니다.) 내 이전 방식으로 무리를 잡은 후에 나는 이미 당신과 비슷한 새로운 솔루션을 시작했습니다. 또한 메모리 액세스 계층이 하나 더 적기 때문에 단일 행렬이 큰 행렬 크기에서 더 잘 수행된다는 또 다른 이점을 생각했습니다. 제안 해 주셔서 감사합니다.(비록 기술적으로 * 더블 포인터 메쏘드가 작동해야하지만, 왜 여전히 그렇지 않은지에 대해 관심이 있습니다.) –

-1

메모리를 할당하는 방식이 이상합니다.

일부 메모리를 할당 한 다음 사용자 메모리를 탈곡하는이 기능을 외부에서에 matA 강조했다하지 않는 한, matA 점을 ?? 않습니다

*matA = malloc(matSize * sizeof(int*));

.

Malloc은 할당 된 메모리에 대한 포인터를 반환하지만 임의 포인터가 가리키는 곳에서는 할당하지 않습니다.

간접 참조 수준이 세 가지가 필요하지 않습니다. ***matA 두 개이면 충분합니다. 호출 할 때

void initMat(int type, int matSize, void ***matA, void ***matB, void ***matC) 
{ 
    int i, j, k; 

    switch(type) { 
     case 0 : 
      *matA = malloc(matSize * sizeof(int*)); 
      *matB = malloc(matSize * sizeof(int*)); 
      *matC = malloc(matSize * sizeof(int*)); 
      for (i = 0; i < matSize; i++) { 
       (*matA)[i] = malloc(matSize * sizeof(int)); 
       (*matB)[i] = malloc(matSize * sizeof(int)); 
       (*matC)[i] = malloc(matSize * sizeof(int)); 
       for (j = 0; j < matSize; j++) { 
        (*matA)[i][j] = rand()/RAND_MAX * 10; 
        (*matB)[i][j] = rand()/RAND_MAX * 10; 
        (*matC)[i][j] = 0; 
       } 
      } 
     break; 

     case 1 : 
     // with float, double, etc. 
     break; 

     default : 
      printf("Invalid case.\n"); 
    } 
} 

과 : - 이것은 내가 제안 된 변경 코드입니다

면책 조항 : 나는 실제로이 시도하지 않은하지만 난 그게 워크 것 같아요

int type,matSize; 
//whatever type you like 
Type **matA,**matB,**matC; 
//your code here 
initMat(type,matSize,&matA,&matB,&matC); 
//the rest of your code here 
+1

그래서 두 레벨이 작동하지 않는 이유는 함수에 전달할 때 c에서는 모든 것이 값으로 전달되므로 복사본이 만들어집니다. 즉, 함수의 값을 변경하는 것은 (같은 이름을 가지고 있음에도 불구하고) 메인에있는 값을 변경하지 않는다는 것을 의미합니다. 그래서 우리는 항상 "하나의 포인터 레이어 위로"전달합니다. 이것이 init 함수에서 3 레벨을 사용해야하는 이유입니다. 그래서 실제 2 레벨 행렬이 값을 유지합니다 (int인지 포인터 이건간에). 나는 이것을 사실로 시험했다. –

+1

이것은 BLUEPIXY의 코드가 작동하는 이유입니다. 왜냐하면 그가 2 개의 레벨만을 사용하더라도 메인의 원래 matA는 하나의 포인터이지만 무효화되기 때문에 무효 레벨을 바꿀 때 사라지는 것이 중요하지 않습니다 int에 대한 포인터. 그래서 그는 init 함수에서 2 레벨 인 명시 적으로 형식화 된 int 행렬을 작성하고 전달 된 "void"행렬 변수의 첫 번째 레벨에 포인터를 링크했습니다. 기본적으로 전달 된 변수보다 3 레벨 낮지 만 명시적인 형식의 2 단계 수준에 대한 로컬 참조 –

+1

원래의 코드가 틀린 곳은 내가 * matA [i] = malloc (matSize * sizeof (int))을 설정할 때입니다. 두 번째 계층 (i- 루프)에서. C는 * matA [i]를 matA [i]에서 값을 역 참조하는 것으로 해석하지만, i * matA의 i 번째 구성 요소를 의미합니다 (아이디어와 (* matA) [i]). 역 참조가 먼저 발생하기를 원하면 역 참조와 인덱스 검색을 함께 사용할 수없는 것 같습니다 (BLUEPIXY의 코드가 * matA를 일시적으로 대체하기 위해 해당 레벨에서 다른 변수를 사용하는 이유를 설명합니다). 모두가 함께 클릭하는 것 같습니다. –

0

기본적인 문제는 후위 연산자는 접두사 연산자보다 우선 순위가 있다는 것입니다.당신이

*matA[i] = malloc(matSize * sizeof(int)); 

을 말할 때 당신이 원하는 것은

(*matA)[i] = malloc(matSize * sizeof(int)); 

그래서 당신이 그것을 작동하게하기 위해 명시 적으로 괄호가 필요하다 그래서 당신은

*(matA[i]) = malloc(matSize * sizeof(int)); 

을 얻고있다. 이와 유사하게, 대신

*(int*)matA[i][j] = rand()/RAND_MAX * 10; 

당신은 왜 당신이 사용하는

((int**)*matA)[i][j] = rand()/RAND_MAX * 10;