Программа печатает результат, который я хочу, за исключением того, что в одном месте я получаю адрес памяти вместо значения адреса. - PullRequest
0 голосов
/ 25 марта 2012

По сути, упражнение гласит: «Создайте функцию, которая принимает в качестве аргумента массив и его размер и возвращает новый массив, содержащий три самых больших значения массива, заданных в качестве аргумента».Поэтому я подумал, что возьму массив и отсортирую его в функции, а затем передам 3 самых высоких значения новому массиву и верну его.Это мой код.

int *function(int *A, int k){
    int B[3],i,j,temp;

    //Sorting
    for(i=k-1;i>0;i--){
        for(j=1;j<=i;j++){
            if(A[j-1] > A[j])
            {
                temp = A[j-1];
                A[j-1] = A[j];
                A[j] = temp;
            }
        }
    }
    i = 0;
    while(i < 3){
        B[i]= A[k-1];
        i++;
        k--;
    }
    return B;   
} 

int main (int argc, const char * argv[]) {
   int A[5] = {1,8,7,4,6};
    int size = 5;
    int *func,i;

    func = function(A,size);
    for(i=0;i<3;i++)
        printf("%d ",func[i]); 
    return 0;
}

В результате я должен получить 8 7 6, но я получаю 8 -1073743756 6. Я не могу найти ошибку.Я также получаю предупреждение, когда хочу вернуть B. Он говорит: «Адрес возврата функции локальной переменной».Может быть, это как-то связано с этой проблемой.Есть идеи?

Ответы [ 4 ]

3 голосов
/ 25 марта 2012

После возврата из функции объекты автоматического хранения (например, int B[3]) уничтожаются, поэтому обращение к ним недопустимо.На практике вы получите неожиданные значения из-за различных вещей, записанных в стек:

Вы можете:

  • Предоставить статическое хранилище B
  • Использовать malloc (ипозже free)
  • Используйте совершенно другой подход, например, возложите на вызывающего абонента ответственность за передачу B
2 голосов
/ 25 марта 2012

«Время жизни» переменной B соответствует продолжительности функции, в которой она объявлена. Когда он выходит из области видимости, он больше не существует, и его память может использоваться повторно для других целей. Вы должны рассматривать все предупреждения компилятора как ошибки - они обычно являются семантическими ошибками (в отличие от синтаксических ошибок); то есть компилятор понимает код, но маловероятно, что это именно то, что вы намеревались, или может иметь непреднамеренное или неопределенное поведение.

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

Обычный шаблон, который следует использовать, когда вызывающая сторона требует заполнения массива функцией, заключается в том, чтобы вызывающая сторона предоставляла массив функции. Поскольку массивы «распадаются» на указатели при передаче в функцию, также нормально передавать длину, и это полезно, чтобы избежать переполнения буфера. Вы могли бы иметь функцию «знать», что длина буфера в вашем случае всегда равна 3, но она менее обслуживаема и менее безопасна.

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

Пример:

int* fn( int* caller_buffer, int caller_buffer_length )
{
    int i ;
    for( i = 0; i < caller_buffer_length; i++ )
    {
        caller_buffer[i] = ... ;
    }
}

1012 * тогда *

int my_buffer[3] = {0} ;
int my_buffer_length = sizeof(my_buffer) / sizeof(*my_buffer) ;
int* error_check = 0 ;

...

error_check = fn( my_buffer, my_buffer_length ) ;
if( error_check != 0 )
{
    ...
}

Эту проблему можно решить путем статического, глобального или динамического выделения B. Все эти решения я бы назвал «быстрыми и грязными». Они могут решить эту конкретную проблему, но не являются общей моделью, которая хорошо масштабируется для более сложных и больших приложений.

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

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

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

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

Массив B[] только живет , когда function() работает (это local ). После возврата он не ссылается.

Если текст вашей задачи точно указан в кавычках, похоже, вам придется копаться в malloc. И тогда я скажу сейчас:

  • Не разыгрывает malloc
  • free () после завершения

Причина использования malloc после текста :

  1. "Возвращает новый массив"

  2. «Принимает только массив и счетчик» , а не третий параметр или повторное использование A .

И не поддавайтесь искушению использовать глобальный массив;), разве вам разрешено использовать статические? - (Правка: у @Clifford есть хорошие моменты, почему-нет)

Удачи!


Редактировать:
Также: вы не «получаете адрес памяти вместо значения» .

Когда вызывается function(), ему присваивается адрес для возврата. Во-вторых, память для переменных local в этой функции зарезервирована, а их обозначения даны по адресу. То есть. что-то вроде:

/* assuming 4 byte int */
[ 0xA4 ... 0xAF ] <-- int B[3]  -- 3 * 4 = 12 bytes
[ 0xB0 ... 0xB4 ] <-- int i     -- 4 bytes
[ 0xB5 ... 0xB9 ] <-- int j     -- 4 bytes
[ 0xBA ... 0xBE ] <-- int temp  -- 4 bytes

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

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

Итак, когда вы печатаете свои значения func и получаете 2 правильных и одно неправильное, это означает, что два первых еще не были перезаписаны, а последний был перезаписан другим вызовом. Это может даже быть в использовании.


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

Скажи:

B[2] == 6 (on a 4 bytes, int 32, system)

Address: 0xAC 0xAD 0xAE 0xAF       As bit sequence
Value  :    6    0    0    0  ==> [0000 0110 0000 0000 0000 0000]

Возвращение на главную , fun[2] -> [0xC 0xD 0xE 0xF]

Где-то еще функция определяет char (1 байт) и получает теперь свободный адрес 0xD
По этому адресу записывается значение 123.

Теперь у нас есть:

fun[2]:

Address: 0xAC 0xAD 0xAE 0xAF       As bit sequence
Value  :    6  123    0    0  ==> [0000 0110 0111 1011 0000 0000]

Или, если мы переведем это в системе с прямым порядком байтов (например, моей), в целое число, мы получим

          +--------- Least significant byte
          |
Address: 0xAC 0xAD 0xAE 0xAF
Value  :    6  123    0    0  ( == 0x06 0x7B 0x00 0x00 )

Sort to "human Anglo left right order"
0 0 123 6 => 0x00007B06 == 31494 (int, "normal human number"), and the number
                                       you print when doing the for() loop in 
                                       main.

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

                   +----- bytes of fun[2]
                   |
          _________+_________
         [                   ]
char foo 0xAC                |
int  bar       0xAD 0xAE 0xAF  0xB0
               [____+______________]
                    |
                    +---- bytes of bar

Тот факт, что числа 1 и 2 верны, не является ни гарантией того, что байты не были изменены. Такое же значение может быть записано в местоположение. 0 может быть записано в байт 2 целого и т. Д.

Пылесос, чтобы показать:

Надеюсь, вы получите как минимум 1% от этого. Я активно учусь сам, и объяснения часто учатся :)

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

B является локальной переменной и исчезает при выходе из функции.

Объявите B вне функции и передайте ее указателем на функцию.

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