C memcpy ведет себя не так, как ожидалось

Этот вопрос связан с Созданием массива для хранения Массивы массивов символов в C

Заимствуя код оттуда, у меня есть что-то похожее на это (благодарю luser droog за хороший пример кода):

enum { BUFSZ = 50 };
enum { STRVSZ = 40 };
enum { STRVVSZ = 20 };
char buf[BUFSZ + 1];
char *strv[STRVSZ + 1];
char **strvv[STRVVSZ + 1];

int j;
int i;

while(1){
    fgets(buf, BUFSZ, infile);

    i = 0;
    strv[i] = strdup(buf);
    strv[i+1] = NULL;

    j = 0;
    strvv[j] = calloc(i+1, sizeof *strvv[j]); // assuming i is the count of elements 
    memcpy(strvv[j], strv, i * sizeof *strvv[j]);
    j++;
}

Это может не сразу выйти за дверь, но иллюстрирует нечто похожее на то, что я запускаю. По сути, содержимое strv должно сохраняться в strvv после каждой итерации цикла, а strv со временем изменяется в зависимости от пользовательского ввода.

Использование calloc и memcpy должно было заставить strvv сохранять копии strv на каждой итерации цикла независимо от значений в strv. Однако, когда я распечатываю содержимое strvv, оно выводит одну и ту же строку для каждой записи, подразумевая, что текущий подход по-прежнему перемещает указатели и не создает копии strv в каждой записи strvv.

Я вообще не уверен, почему это происходит и как это исправить. memcpy должен копировать на уровне байтов то, на что указывают указатели в strv =/.


person Dae314    schedule 19.04.2013    source источник
comment
Вы не видите сброс i и j в ноль (0) при каждой итерации и последующую вопиющую перезапись ранее сохраненных данных после этого? Забудьте код и заявите следующее. Что вы пытаетесь сделать? Сделать сетку из динамических строк, где каждая ячейка — это char*, каждая строка — это char*[], а вся сетка — это char **[]?   -  person WhozCraig    schedule 19.04.2013
comment
Я заслужил некоторую ответственность здесь. Ожидалось, что i будет индексом последней записи, добавленной к strv. Поскольку i равно нулю, ноль умножить на что-либо равно нулю, и memcpy ничего не копирует. i здесь должно быть количество копируемых элементов.   -  person luser droog    schedule 19.04.2013
comment
@luserdroog хорошо. Я думаю теперь почти понимаю это. С каждым набором строк новый набор сохраняется в новой строке того, что является псевдоматрицей, где каждая строка фактически может иметь произвольное число (включая нулевое, но не более STRVSZ) строк, заканчивающихся хвостом. Указатель NULL для указания конца списка строк для этой строки. Это примерно так?   -  person WhozCraig    schedule 19.04.2013
comment
@WhozCraig Да. Это то, что я пытался объяснить. Я думаю, что пытается сделать OP. ‹список› ‹списков строк. Где ‹list› реализован как динамический массив.   -  person luser droog    schedule 19.04.2013


Ответы (1)


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

enum { BUFSZ = 50 };
enum { STRVSZ = 40 };
enum { STRVVSZ = 20 };
char buf[BUFSZ + 1];
char *strv[STRVSZ + 1];
char **strvv[STRVVSZ + 1];

int j; // indexes strv slices in strvv
int i; // indexes strings (char *s) in strv

j = 0; // j is index into strvv

while(!feof(infile)) {

    i = 0; // i is index into strv
    while(i < STRVSZ){
        fgets(buf, BUFSZ, infile);
        if (strcmp(buf,"END")==0) break; // end of a set

        strv[i] = strdup(buf);
        strv[i+1] = NULL;
        i++;
    }  // i is count of strv

    // copy strv into strvv
    strvv[j] = calloc(i+1, sizeof *strvv[j]); // assuming i is the count of elements 
    memcpy(strvv[j], strv, (i+1) * sizeof *strvv[j]);  // i+1 to copy the NULL pointer
    j++; // index next element in strvv and a count of strvv

} // j is count of sets in strvv

Это похоже на беспорядок до сих пор.

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

person luser droog    schedule 19.04.2013
comment
Я не заметил проблемы с i в примере, потому что перевел ее в правильную переменную в своем собственном коде, который получает размер массива. Я могу вводить данные в strvv с этим кодом, но все равно все указывает на одно и то же, когда я пытаюсь прочитать массив. - person Dae314; 20.04.2013
comment
Хм. Я не знаю! Все это должно работать, просто следуя одному и тому же шаблону точно для каждого нового уровня. memcpy должен скопировать все указатели в strv. Затем вы должны сбросить i=0;, чтобы заполнить strv следующим набором. Я оставил те плавающие, потому что их положение зависит от того, что еще происходит. - person luser droog; 20.04.2013
comment
Это превращается в кошмар! Я думаю, пришло время поискать лучший способ делать то, что я хочу (надеюсь, тот, который не включает 3D-массивы...), который будет менее запутанным. Большое спасибо за вашу помощь, технически я не могу принять этот ответ, так как на самом деле это не ответ, если кто-то ответит на этот вопрос в будущем, но я поставлю ему +1. - person Dae314; 20.04.2013
comment
Понял. Такой беспорядок и был причиной C++! - person luser droog; 20.04.2013