Члены массива перезаписаны последним элементом - PullRequest
1 голос
/ 22 мая 2009

Со следующим фрагментом кода я получаю очень странный результат. Почему значение последнего элемента перезаписывает все предыдущие элементы массива? я подозреваю, что есть более серьезная проблема, чем эта непосредственная проблема.

#include <stdio.h>

main()
{
    int i, cases;
    char num[1000000];

    scanf("%d", &cases);
    char* array[cases];

    //store inputs in array
    for(i=0; i<cases; i++)
    {
        scanf("%s", &num);
        array[i] = &num;
    }

    //print out array items and their memory addresses
    for(i=0; i<cases; i++)
    {
        printf("%d %s\n", i, array[i]);  //print (array index) (array value) 
        printf("%d %p\n", i, &array[i]); //print (array index) (array address) 
    }
}

Inputs:
3 <-- number of lines to follow
0   <-- put in array[0]
1   <-- put in array[1]
2   <-- put in array[2]

Outputs
0 3         <-- why is this being overwritten with the last element?
0 0013BCD0
1 3         <-- why is this being overwritten with the last element?
1 0013BCD4
2 3
2 0013BCD8

Ответы [ 7 ]

3 голосов
/ 22 мая 2009

В результате получается строка array[i] = &num;, в которой вы устанавливаете значение элемента array[i] для адреса массива num; поскольку array является массивом символов, я подозреваю, что он усекает ваш адрес num массива, а младший байт просто равен 3.

Тем не менее. Тем не менее, ваш char [num] [1000000] - отвратительная форма, и вы вообще не должны этого делать. Выделите в кучу и выберите меньшее число, ради всего святого. Кроме того, scanf ("% s", & num) фактически не даст вам то, что вы хотите. Вот подсказка; используйте цикл getc () для чтения чисел; это позволяет избежать необходимости какого-либо предварительного выделения массива для scanf ().

2 голосов
/ 22 мая 2009

Это потому, что вы вводите в каждый индекс массива один и тот же адрес (адрес char num [1000000];).

Это ошибка, которая приведет вас к динамическому распределению (calloc, malloc, new и т. Д.).

Ура! * * 1005

1 голос
/ 22 мая 2009

Внутри вашего первого цикла вы должны (но не) записывать каждый вход в отдельный элемент массива num; вместо этого вы всегда пишете в одно и то же место, т.е. на &num.

0 голосов
/ 22 мая 2009

Именно для таких вещей C ++, кажется, сделан. Разбор пользовательского ввода и динамическое распределение выполняются более безопасно и быстро. Я не могу вспомнить систему, в которой у вас есть такой пользовательский интерфейс, где вы не можете переключиться на C ++.

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


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

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

Вы подготавливаете массив для хранения всех (количество дел) указателей на строки, но вы резервируете память только для одной строки. Вы должны сделать это для каждой строки, так что бывает. Чтобы упростить задачу с точки зрения урока «динамическое выделение памяти», я рекомендую сделать это следующим образом: char* array[cases][10000]; Это дает вам строки падежей по 10 тысяч символов.

Вы, вероятно, также не хотите иметь отдельные указатели на элементы массива. Это начинает иметь смысл, если вы хотите отсортировать элементы массива, когда эти элементы больше, чем сами указатели. В этом случае ваш выигрыш в производительности состоит не в том, чтобы перемещать (копировать) большие куски, а только в указателях (обычно 4 байта). В вашем случае int также имеет длину 4 байта. И вы все равно не сортируете:)

scanf() опасно, если не сказать больше. Во втором приложении вы указываете записать строку по адресу массива. Это кажется простой ошибкой, но может привести ко многим проблемам. Вы, вероятно, хотите сделать это следующим образом: scanf("%d", &array[i]); (К сожалению, у меня нет компилятора под рукой, поэтому я не уверен на 100%). оставьте следующую строку:)


Вопрос к специалистам по уценке: почему так чертовски невозможно объединить СПИСКИ с блоками КОД?

0 голосов
/ 22 мая 2009

Заменить

//store inputs in array
for(i=0; i<cases; i++)
{
    scanf("%s", &num);
    array[i] = &num;
}

с

array[0] = num;
//store inputs in array
for(i=0; i<cases; i++)
{
    scanf("%s", array[i]);
    array[i+1] = array[i] + strlen(array[i]) + 1;
}

для сканирования каждой строки в первое доступное пространство в num[] и установите следующий элемент в array[], чтобы он указывал на следующее доступное пространство. Теперь ваши printf() строк будут работать. Оригинал сканировал каждую строку в начале num[].

Примечание: scanf() с неукрашенными %s столь же плох, как и gets(), потому что он не ограничивает объем данных, которые будут украдены. Не используйте его в реальном коде.

Заменить

    printf("%d %p\n", i, &array[i]); //print (array index) (array address) 

с

    printf("%d %p\n", i, (void*)(array[i])); //print (array index) (array address) 

для фактической печати адресов, хранящихся в a[], а не адресов элементов a[]. Приведение требуется, потому что %p ожидает указатель на void, поэтому вы должны указать его.

0 голосов
/ 22 мая 2009

Это ваш код, который исправлен:

#include <stdio.h>

main(void)
{
    int i, cases;

    scanf("%d", &cases);
    char* array[cases];

    //store inputs in array
    for(i=0; i<cases; i++)
    {
        char *num = malloc(100000);
        scanf("%s", num);
        array[i] = num;
    }

    //print out array items and their memory addresses
    for(i=0; i<cases; i++)
    {
        printf("%d %s\n", i, array[i]);  //print (array index) (array value)
        printf("%d %p\n", i, (void*)&array[i]); //print (array index) (array address)
    }
    return 1;
}

Вы также можете использовать

char *num = calloc(100000, sizeof(char));

, что немного оборонительно. Я не знаю, зачем вам нужно 100000. Вы можете сделать это динамически, используя malloc. Это потребует больше работы, но очень надежно.

Что происходит в вашем коде, так это то, что вы сохраняете строку% s по адресу num, который не изменяется, а затем присваиваете элемент array [i] этому адресу. Назначение в C - это не что иное, как сохранение ссылки, вы не сохраняете сам элемент - это было бы пустой тратой пространства. Таким образом, так как все элементы массива указывают на адрес (хранится только ссылка), значение в адресе изменяется, поэтому ссылка также изменяется, поэтому все они меняются на 2 (а не на 3, как вы указали в своем посте).

0 голосов
/ 22 мая 2009

char * array [case];

Это выделяется во время компиляции, а не во время выполнения. И случаи не инициализируются (хотя я думаю, что вы все равно хотите, чтобы он работал динамически). Поэтому вам нужно либо предварительно выделить память, либо ознакомиться с семейством библиотечных функций malloc.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...