Возвращение заполненного массива строк из функции C - PullRequest
2 голосов
/ 19 марта 2012

Это первая публикация, поэтому я прошу прощения за путаницу:

Я пишу такую ​​функцию:

int myFunc(char* inputStr, int *argCTemp, char** argVTemp[]);

Цель моей функции - взять копию входной строки (практически любого пользовательского ввода), а затем использовать strtok, чтобы преобразовать ее в токены и заполнить массив указателем массива (argV). Когда myFunc закончится, надеюсь, у меня будет количество аргументов и массив строк из моей строки inputStr.

Вот пример того, как я это называю:

int main(int argc, char** argv[])
{
    int argCTemp = -1;
    char** argVTemp; 

    // 1 Do Stuff
    // 2 Get input string from user 
    // 3 then call myfunc like this:

    myFunc(inputStr, &argCTemp, &argVTemp);

    // 4: I get garbage whenever I try to use "argVTemp[i]" 
}

Мои вопросы: как мне лучше всего сделать это безопасно и последовательно. Как профессионалы это делают?


  1. Я не использую malloc, потому что:

    • Я не знаю количества аргументов или длины каждого из них для моего ввода (для динамического распределения пространства). Я понял, поэтому я использую указатели
    • поскольку я объявляю это в основной функции, я думал, что указатели на / memory, используемые argCTemp и argVTemp, будут в порядке / останутся в области видимости, даже если они находятся в стеке.
  2. Я знаю, что когда myFunc завершает работу, он делает недействительными любые созданные в нем ссылки на стек, поэтому я и отправил ему указатели из вызывающей функции. Должен ли я использовать указатели и malloc и тому подобное или *? 1036 *

  3. Последнее: перед выходом myfunc я проверяю, чтобы увидеть значения argCTemp и argVTemp, и они имеют допустимое содержимое. Я устанавливаю argCtemp и argVtemp так:

    (*argCTemp) = argCount;
    (*argVTemp)[0] = "foo";
    

и, кажется, работает нормально, ДО выхода из функции. Так как я устанавливаю указатели где-то в памяти, я запутался, почему ссылка не работает. Я пытался использовать malloc INSIDE myFunc при установке указателей, и он все еще становится мусором, когда myFunc заканчивается и читается вызывающей функцией.

Извините, если что-то из этого сбивает с толку, и заранее благодарю за любую помощь.

Ответы [ 3 ]

1 голос
/ 19 марта 2012

Поскольку "не знаю количество аргументов или длину каждого аргумента для моего ввода", вы также можете использовать malloc.Когда ваш буфер заполнится, вы должны realloc ваш буфер.Лучший способ: вам не нужно хранить весь ввод.Линия, токен или блок лучше.Просто установите статический массив для их хранения.и, может быть, хэш лучше, если ваш ввод больше 100 мб.

Извините за мой плохой английский.

0 голосов
/ 19 марта 2012

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

Вот пример, который выделяет возвращаемый массив с небольшими приращениями:

static
char** myFunc_realloc( char** arr, size_t* elements)
{
    enum {
        allocation_chunk = 16
    };

    *elements += allocation_chunk;
    char** tmp = (char**) realloc( arr, (*elements) * sizeof(char*));

    if (!tmp) {
        abort();  // or whatever error handling
    }

    return tmp;
}

void myFunc_free( char** argv)
{
    free(argv);
}

int myFunc(char* inputStr, int *argCTemp, char** argVTemp[])
{
    size_t argv_elements = 0;
    size_t argv_used = 0;
    char** argv_arr = NULL;


    char* token = strtok( inputStr, " ");

    while (token) {
        if ((argv_used+1) >= argv_elements) {
            // we need to realloc - the +1 is because we want an extra 
            //  element for the NULL sentinel
            argv_arr = myFunc_realloc( argv_arr, &argv_elements);
        }

        argv_arr[argv_used] = token;
        ++argv_used;

        token = strtok( NULL, " ");
    }

    if ((argv_used+1) >= argv_elements) {
        argv_arr = myFunc_realloc( argv_arr, &argv_elements);
    }
    argv_arr[argv_used] = NULL;

    *argCTemp = argv_used;
    *argVTemp = argv_arr;

    return argv_used;
}

Некоторые примечания:

  • в случае сбоя выделения программа завершается.Вам может потребоваться другая обработка ошибок.
  • переданная во входную строку 'повреждена'.Возможно, это не подходящий интерфейс для вашей функции (в общем, я бы предпочел, чтобы такая функция не уничтожала входные данные).
  • пользователь функции должен вызвать myFunc_free(), чтобы освободить возвращаемуюмассив.В настоящее время это простая оболочка для free(), но она дает вам гибкость в выполнении более сложных задач (например, выделение памяти для токенов, чтобы вам не приходилось уничтожать входную строку).
0 голосов
/ 19 марта 2012

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

Вы можете сделать один из двух.

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

static char * arr[MAX SIZE], и отправки его (char **)&arrв вызове функции, или запустите дважды и используйте malloc.

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


Допустим,Вы количество токенов в int n затем

char * arr [] = malloc (sizeof (int *) * n);

это создаст массив указателей, теперь вы передаете егов свою заполненную функцию, вызвав

с (char **)&arr, и используйте его, как вы это делали в своем коде

, например (*argVTemp)[0] = ;.

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

...