2012-01-21 2 views
2

При переходе с языков более высокого уровня я пытаюсь переустановить себя, изучая c. Я пытаюсь понять указатели и распределение памяти (о чем я никогда не думал раньше).На месте и обратно. Новые шаблоны дизайна в C

Код Пытаюсь

#include <stdio.h> 

int main() 
{ 

    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 }; 

    printf("\nShuffling...\n\n"); 
    shuffle(array, sizeof(array)/sizeof(int)); 

    int i; 
    for(i = 0; i < sizeof(array)/sizeof(int); i++) 
    { 
     printf("%d\n", array[i]); 
    } 

} 

int shuffle(int *shuffle_array, int length) 
{ 
    int i, j, newArray[length]; 
    for(i = 0, j = length; i < length; j--, i++) 
    { 
     newArray[i] = shuffle_array[j]; 
    } 

    shuffle_array = newArray; 
} 

Первый, это не работает, и я пытаюсь выяснить, почему массив не переворачивается на печать.

Второго, сравнивая концепцию редактирования на месте против возвращения нового пункта в с помощью malloc() и free().

+0

1st: newArray выделяется в стеке и будет освобожден после оставления shuffle -> shuffle_array укажет на мусор. Во-вторых, вы передаете указатель, а не ссылку на указатель (или указатель на указатель), поэтому ваш результат не будет возвращен на главную -> без изменений после тасования. – Till

+0

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

+1

Поскольку вы используете C99 (иначе вам не будет разрешено объявлять переменную в основе функции), вы также должны объявить свои счетчики циклов таким образом: 'for (int i = 0, j = length; ...) '. Кроме того, 'shuffle' ничего не возвращает, хотя вы объявили его возвратом' int'. – Gandaro

ответ

4

Когда ваша функция завершается, ваш локально определенный массив выходит из области действия и, следовательно, больше не существует.

Вы должны тетсру назад над shuffle_array ...

memcpy(shuffle_array, newArray, length * sizeof(int)); 

Кроме того, вы также начинают свой читать с конца shuffle_array. Последний элемент в массиве - длина -1.

Редактировать: Чтение с конца массива не обязательно вызовет проблемы. Просто, что вы делаете, это неопределенное поведение.

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

TBH для реверсирования массива гораздо лучший алгоритм должен начинаться с обоих концов и обмениваться данными. Вы можете сделать это следующим образом:

for(i = 0, j = (length - 1); i < j; j--, i++) 
{ 
    int temp = shuffle_array[i]; 
    shuffle_array[i] = shuffleArray[j]; 
    shuffle_array[j] = temp; 
} 

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

+0

Звучит разумно, не могли бы вы привести пример? – jondavidjohn

+0

, и если это так, почему он печатает его в оригинальном порядке и не дает мне ошибку области? – jondavidjohn

+0

@jondavidjohn Вы не делаете что-то незаконное с языком, просто вы не делаете то, что думаете, что делаете. –

7

Внутри shuffle вы создаете в стеке массив, называемый newArray (что означает, что он будет освобожден при завершении функции).

Затем вы помещаете каждый предмет из shuffle_array в newArray назад.

Затем вы создаете указатель shuffle_array, локальную переменную (что означает, что изменения в ней не отражаются вне функции), укажите на первый элемент newArray. Это ничего не меняет за пределами shuffle, потому что shuffle_array является локальной переменной.

После возвращения функции ничего не произошло за пределами функции. Это связано с тем, что вы просто изменяете локальные данные с помощью shuffle.

Чтобы перетасовать массив (или отменить его или сделать что-нибудь с ним), вы должны изменить его непосредственно, а не изменять копию. Таким образом, это должно быть

int tmp = shuffle_array[i]; 
shuffle_array[i] = shuffle_array[j]; 
shuffle_array[j] = tmp; 

Чтобы вы обменяли два значения элементов массива, на котором вы находитесь.Хотя shuffle_array является временной переменной, он указывает на блок памяти в main, так что изменение этой памяти возможно даже после возвращения функции.

Вы можете представить себе это так:

в main:

array - - - - 
       \ 
       \ 
       1, 2, 3, 4, 5, ..., 52 }; 

когда shuffle называется:

array - - - - 
       \ 
       \ 
       1, 2, 3, 4, 5, ..., 52 
       /
      /
      /
shuffle_array 

newArray - - - 
       \ 
       52, ..., 5, 4, 3, 2, 1 

Затем, когда вы делаете

shuffle_array = newArray; 

Это выглядит как

array - - - - 
       \ 
       \ 
       1, 2, 3, 4, 5, ..., 52 



shuffle_array - 
       \ 
newArray - - - | 
       \/
       52, ..., 5, 4, 3, 2, 1 

Затем shuffle возвращается, и все возвращается только

array - - - - 
       \ 
       \ 
       1, 2, 3, 4, 5, ..., 52 
+0

отличная информация, очень цените это, многому научились. – jondavidjohn

+0

+1 для диаграммы :) – asaelr

-3

1) Вы назначаете из массива в стеке. Эти данные не будут присутствовать, когда функция вернется.

2) Мне интересно, почему

shuffle_array = newArray 

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

3) Вам нужно будет присвоить значения локальных массивов обратно массиву, переданному с использованием цикла for или memcpy.

+3

Никто не должен вам комментировать для downvoting. Это не то, как система предназначена или предназначена для работы. – meagar

+0

Ваш первый абзац (о значении указателя массива) очень неясен. Я не уверен, в чем ваш смысл. Также неясно, что «вы не можете сделать это в C» относится к предыдущему абзацу или следующему сегменту кода. Цитируемый код не присваивает элементу 0. Кроме того, вы не можете назначать массивы в C++. – sth

+0

Он не присваивает первому элементу массива, но указатель, хранящийся в переменной 'shuffle_array', заменяется указателем на массив, хранящийся в' newArray'. – sth

2

newArray выделяется в стеке и будет освобожден после оставления shuffle -> shuffle_array укажет на мусор.

Вы передаете указатель, а не ссылку на указатель (или указатель на указатель), поэтому ваш результат не будет возвращен на главную -> без изменений после тасования.

Как насчет этого решения:

//note, the returned array needs to be freed using free(); 
int *newShuffledArray(int *shuffle_array, int length) 
{ 
    int i, j; 
    int *newArray = malloc(length * sizeof(int)); 
    for(i = 0, j = length-1; i < length-1; j--, i++) 
    { 
     newArray[i] = shuffle_array[j]; 
    } 
    return newArray; 
} 
+0

Хотя мне не нравится идея управления памятью, если я могу ее избежать, я ценю альтернативный подход, полезную информацию, многому научился! – jondavidjohn

+0

@jondavidjohn, что является абсолютно правильным и правильной идеей подхода к проблеме. Я просто хотел указать на эту альтернативу, чтобы убедиться, что вы видите ее механику. Лучшие решения для вашей конкретной проблемы были даны Сет и Гозом. – Till

+0

Я действительно ценю это, имея дело с необработанной памятью и указателями, иногда меня обертывают, но в хорошем смысле! – jondavidjohn

0

Вы ничего не изменилось. Вы задали только локальную переменную, и что вы ожидали? ;)

#include <stdio.h> 

void foo(int* a) { 
    ++a; 
} 

int main(void) { 
    int array[3] = {1,2,3}; 
    foo(array); 
    for (int i = 0; i < 3; ++i) 
     printf("%d\n", array[i]); // results in 1\n2\n3\n, not 2\n3\nSegmentation fault\n 
} 
+0

Мне интересно, как мой (правильный) ответ получает downvoted, в то время как неправильный ответ принимается запросчиком и 4 раза увеличивается. – Gandaro

+0

Ты не одинок. Я думаю, что голоса должны быть напечатаны, кто проголосовал. Я видел этот вопрос - по крайней мере, я думаю, что видел это - раньше в тот же день, когда Java-код с тем же вопросом, но я не могу вспомнить, был ли он одним и тем же плакатом. Вы, вероятно, проголосовали за то, что ваши и мои ответы могут быть истолкованы как комментарии. – octopusgrabbus

1

FWIW, это будет намного эффективнее (я предпочитаю кодовые решения).

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

void shuffle(int *shuffle_array, int length) 
{ 
     int i, j, tmp; 
     for (i = 0, j = length - 1; i < j; ++i, --j) { 
       tmp = shuffle_array[i]; 
       shuffle_array[i] = shuffle_array[j]; 
       shuffle_array[j] = tmp; 
     } 
} 

int main(void) 
{ 
     int i, array[] = { 
       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
       11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 
       21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 
       31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 
       41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 
       51, 52 
     }, length = sizeof(array)/sizeof(int); 
     shuffle(array, length); 
     for (i = 0; i < length; ++i) { 
       printf("%d\n", array[i]); 
     } 
     return EXIT_SUCCESS; 
} 

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

Смежные вопросы