Как изменить многомерный указатель внутри функции? - PullRequest
1 голос
/ 06 мая 2020

У меня есть функция, которая манипулирует char*** с помощью malloc и memcpy таким образом

// Convert a buffer full line to separated variables
int parseBufferToVariables(char ***variableContainer, char *bufferToParse, int maxVarSize) {
    int i = 0;
    // Get number of rows of the string
    int numberOfRows = 0;
    for (i = 0; bufferToParse[i] != '\0'; i++) {
        if (bufferToParse[i] == '\n')
            ++numberOfRows;
    }
    // Get number of columns of the string
    int numberOfColumns = 1;
    for (i = 0; bufferToParse[i] != '\n'; i++) {
        if (bufferToParse[i] == '\t')
            ++numberOfColumns;
    }
    // Allocate separated variable array
    size_t dim0 = numberOfColumns, dim1 = numberOfRows, dim2 = maxVarSize;
    variableContainer = malloc(sizeof *variableContainer * dim0);
    if (variableContainer) {
        size_t i;
        for (i = 0; i < dim0; i++) {
            variableContainer[i] = malloc(sizeof *variableContainer[i] * dim1);
            if (variableContainer[i]) {
                size_t j;
                for (j = 0; j < dim1; j++) {
                    variableContainer[i][j] = malloc(sizeof *variableContainer[i][j] * dim2);
                }
            }
        }
    }
    // Start parsing string to 3D array
    int init            = 0;
    int numberOfVars    = 0;
    int numberOfLines   = 0;
    int sizeOfVar       = 0;
    int position        = 0;
    char emptyArray[MAXVARSIZE] = {0};
    // Loop trought all lines
    i = 0;
    while (numberOfLines  < numberOfRows) {
        // Every delimiter
        if (bufferToParse[i] == '\t' || bufferToParse[i] == '\n') {
            // Size of the new sring
            sizeOfVar = i - init;
            // Set last \0 character in order to recognize as a proper string
            memcpy(&variableContainer[numberOfVars][numberOfLines], emptyArray, maxVarSize);
            // Copy the string to array
            memcpy(&variableContainer[numberOfVars][numberOfLines], &bufferToParse[position], sizeOfVar);
            // Handle pointers poisition
            init = i + 1;
            position += sizeOfVar + 1;
            // Handle when end of line is reached
            if (bufferToParse[i] == '\n') {
                numberOfVars = 0;
                numberOfLines++;
            }
        }
        i++;
    }
    return numberOfRows;
}

И я пытаюсь вызвать ее по-разному:

char*** container= {0};
parseBufferToVariables (&container, inputString, MAXVARSIZE);

char*** container= {0};
parseBufferToVariables (container, inputString, MAXVARSIZE);

Даже я пытаюсь вызвать char**** в функции:

int parseBufferToVariables(char**** variableContainer, char* bufferToParse, int maxVarSize)

Но у меня всегда есть ошибка сегмента, вызывающая char*** вне функции parseBufferToVariables. Любые идеи?

Ответы [ 2 ]

2 голосов
/ 06 мая 2020

OP использует параметр 4 *, но другие подходы лучше.

Высокая степень * s маскирует отказ ключа заключается в том, что код должен передавать ширину столбца (количество вкладок)

Кроме того, я не вижу определенного нулевого символа завершения в формировании _strings_, поскольку 2-й memcpy() неограничен по размеру - может даже перезаписать границы выделения.


Идея ниже заключается в том, что каждый уровень выделения заканчивается нулем.

csv = parse_file_string(const char *file_string);

При возврате, когда csv[line] == NULL, строк больше нет

Когда csv[line][tab] == NULL, там больше не являются строками.

Этот подход также позволяет использовать другое количество строк в строке.

Скорректированный алгоритм, псевдо C код

// return NULL on error
char ***parse_file_string(const char *file_string) {
  number_lines = find_line_count(file_string);
  char ***csv = calloc(number_lines + 1, sizeof *csv);
  if (csv == NULL) return NULL;

  for (line=0; line < number_lines; line++) {
    tab_count = find_tab_count(file_string);
    csv[line] = calloc(tab_count + 2, sizeof *(csv[line])); 
    // add NULL check

    for (tab=0; tab < tab_count; tab++) {
      char *end = strchr(file_string, '\t');
      csv[line][tab] = malloc_string(file_string, end);
      // add NULL check
      file_string = end + 1;
    }
    char *end = strchr(file_string, '\n');
    csv[line][tab++] = malloc_str(file_string, end); 
    // add NULL check
    file_string = end + 1;
    csv[line][tab] = NULL;

  }
  csv[line] = NULL;
  return csv;
}

Использование

char ***container = parse_file_string(file_string);

for (line=0; container[line]; line++)
  for (tab=0; container[line][tab]; tab++)
     puts(container[line][tab]);

//free
for (line=0; container[line]; line++)
  for (tab=0; container[line][tab]; tab++)
     free(container[line][tab]);
  free(container[line]);
free (container)
1 голос
/ 07 мая 2020

Можно использовать указатель на массив переменной длины, если он поддерживается.
Сначала получите размеры содержимого буфера. Это предполагает, что в каждой строке будет одинаковое количество вкладок.
Объявить указатель и выделить память.
Затем проанализировать буфер в выделенной памяти.

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

void getdimension ( char *buffer, int *rows, int *cols, int *size) {
    int maxsize = 0;
    *rows = 0;
    *cols = 0;
    *size = 0;
    while ( *buffer) {//not the terminating zero
        if ( '\n' == *buffer) {
            if ( ! *rows) {//no rows counted yet
                ++*cols;//add a column
            }
            ++*rows;
            if ( maxsize > *size) {
                *size = maxsize;
            }
            maxsize = 0;
        }
        if ( '\t' == *buffer) {
            if ( ! *rows) {//no rows counted yet
                ++*cols;
            }
            if ( maxsize > *size) {
                *size = maxsize;
            }
            maxsize = 0;
        }
        ++maxsize;
        ++buffer;
    }
    if ( '\n' != *(buffer - 1)) {//last character is not a newline
        ++*rows;
        if ( maxsize > *size) {
            *size = maxsize;
        }
    }
}

void createptr ( int rows, int columns, int size, char (**ptr)[columns][size]) {
    if ( NULL == ( *ptr = malloc ( sizeof **ptr * rows))) {
        fprintf ( stderr, "malloc problem\n");
        exit ( EXIT_FAILURE);
    }
    for ( int line = 0; line < rows; ++line) {
        for ( int tab = 0; tab < columns; ++tab) {
            (*ptr)[line][tab][0] = 0;
        }
    }
}

void parsebuffer ( char *buffer, int rows, int columns, int size, char (*ptr)[columns][size]) {
    int eachrow = 0;
    int eachcol = 0;
    int eachsize = 0;

    while ( *buffer) {
        if ( '\n' == *buffer) {
            ++eachrow;
            eachcol = 0;
            eachsize = 0;
        }
        else if ( '\t' == *buffer) {
            ++eachcol;
            eachsize = 0;
        }
        else {
            ptr[eachrow][eachcol][eachsize] = *buffer;
            ++eachsize;
            ptr[eachrow][eachcol][eachsize] = 0;
        }
        ++buffer;
    }
}

int main ( void) {
    char line[] = "12\t34\t56\t78\t!@#\n"
    "abc\tdef\tghi\tjkl\t$%^\n"
    "mno\tpqr\tstu\tvwx\tyz\n"
    "ABC\tDEF\tGHI\tJKL\tMNOPQ\n";
    int rows = 0;
    int columns = 0;
    int size = 0;

    getdimension ( line, &rows, &columns, &size);
    printf ( "rows %d cols %d size %d\n", rows, columns, size);

    char (*ptr)[columns][size] = NULL;//pointer to variable length array

    createptr ( rows, columns, size, &ptr);

    parsebuffer ( line, rows, columns, size, ptr);

    for ( int row = 0; row < rows; ++row) {
        for ( int col = 0; col < columns; ++col) {
            printf ( "ptr[%d][%d] %s\n", row, col, ptr[row][col]);
        }
    }

    free ( ptr);

    return 0;
}
...