Почему вызов функции изменяет значение массива указателя на функцию, которая не была указана в параметре? - PullRequest
0 голосов
/ 17 февраля 2019

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

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

Используя gdb, мне удалось узнать, когда были сделаны изменения в моем массиве.Кажется, он изменяется после первого printf в функции printSorted.

Мой typedef:

typedef int (*PtrCompFct)(int, int);
typedef int (*PtrSortFct)(int*, int, int, PtrCompFct);

Структура:

typedef struct
{
    int nbFct;
    PtrCompFct compFct;
    PtrSortFct *sortFct;
} SortCompFct_s;

Вот как я вызываю мою функцию (userChoices имеет тип SortCompFct_s):

printSorted(myArr, myArrSize, userChoices->compFct);

И функция, которая меняет мою структуру:

int printSorted(int *arr, int arrSize, PtrCompFct compFct)
{
    for (int i=0; i<(arrSize-1); i++)
    {
        if (compFct(arr[i+1], arr[i]))
        {
            //this is when my array of pointers to function is modified
            printf("The array isn't sorted\n\n");
            return 0;
        }
    }
    printf("The array is sorted\n\n");
    return 1;
}

С GDB до printf у меня есть:

(gdb) print main::userChoices->sortFct[0]
$36 = (PtrSortFct) 0x5555555548ea <quickSort>

и после:

(gdb) print main::userChoices->sortFct[0]
$37 = (PtrSortFct) 0x7fffffffddc0

Как видите, указатель на мою функцию быстрой сортировки был изменен.

РЕДАКТИРОВАТЬ: включает в себя упрощенный и проверяемый код, дело в том, что этот код работает правильно, даже с функцией printSorted

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

typedef int (*PtrCompFct)(int, int);
typedef int (*PtrSortFct)(int*, int, int, PtrCompFct);

typedef struct
{
    int nbFct;
    PtrCompFct compFct;
    PtrSortFct *sortFct;
} SortCompFct_s;

typedef SortCompFct_s *PtrSortCompFct_s;

void initTab(int *arr, int arrSize)
{
    time_t t;
    srand(time(&t));
    for (int i=0; i<arrSize; i++)
    {
        arr[i] = rand();
    }
}

int ascendingSort(int elmt1, int elmt2)
{
    return (elmt1 < elmt2);
}

int descendingSort(int elmt1, int elmt2)
{
    return (elmt1 > elmt2);
}

void switche(int *arr, int ind1, int ind2)
{
    int temp = arr[ind1];
    arr[ind1] = arr[ind2];
    arr[ind2] = temp;
}

int bubbleSort(int *arr, int ind1, int ind2, PtrCompFct fctComp)
{
    int sorted;
    for (int i=ind1; i<ind2; i++)
    {
        sorted = 1;

        for (int j=0; j<ind2; j++)
        {
            if (fctComp(arr[j+1], arr[j]))
            {
                switche(arr, j, j+1);
                sorted = 0;
            }
        }

        if (sorted) return 0;
    }

    return 0;
}

void printArr(int *arr, int arrSize)
{
    for (int i=0; i<arrSize; i++)
    {
        printf("%16d\n", arr[i]);
    }
}

int printSorted(int *arr, int arrSize, PtrCompFct compFct)
{
    for (int i=0; i<arrSize-1; i++)
    {
        if (compFct(arr[i+1], arr[i]))
        {
            //this is when my array of pointers to function is modified
            printf("The array isn't sorted\n\n");
            return 0;
        }
    }
    printf("The array is sorted\n\n");
    return 1;
}

PtrSortCompFct_s menu(void)
{
    PtrSortCompFct_s userChoices;
    PtrSortFct arrSortFct[] = {bubbleSort};

    if ((userChoices = malloc(3*sizeof(int))) != NULL)
    {
        userChoices->nbFct = 1;
        userChoices->compFct = ascendingSort;
        userChoices->sortFct = arrSortFct;
    }

    return userChoices;
}

int main(void)
{
    int arrSize = 10;
    int arr[arrSize];
    initTab(arr, arrSize);
    PtrSortCompFct_s userChoices;

    if ((userChoices = malloc(3*sizeof(int))) != NULL) userChoices = menu();

    printArr(arr, arrSize);
    printSorted(arr, arrSize, userChoices->compFct);

    userChoices->sortFct[0](arr, 0, arrSize-1, userChoices->compFct);

    printArr(arr, arrSize);
    printSorted(arr, arrSize, userChoices->compFct);

    return 0;
}

1 Ответ

0 голосов
/ 17 февраля 2019

С gdb до printf у меня есть: ... и после:

Основная причина вашей проблемы в том, как вы инициализировали userChoices->sortFct (вы не показаликод, который выполняет эту инициализацию).

Этот массив указывает на висящую память кучи или стека, и вызов printf перезаписывает эту память.

if ((userChoices = malloc(6*sizeof(int))) != NULL) userChoices = menu();

Этот код является полностью поддельным: выделение кучи памяти для userChoices, а затем немедленное перезапись userChoices возвращаемым значением из menu() служит только для утечки памяти.Как упомянуто в комментариях, 6*sizeof(int) также является полностью поддельным размером.

Я бы предположил, что ваш menu() выглядит примерно так:

struct SortCompFct_s* menu()
{
   struct SortCompFct_s ret;
   ret.compFct = &SomeFunc;
   ret.sortFct = malloc(...);
   ret.sortFct[0] = &quickSort;
   return &ret;    // Oops: returning address of a local!
}

Если это на самом деле то, что высделал, висячий стек это именно ваша проблема.Вы должны включить максимальное количество предупреждений компилятора (-Wall -Wextra при использовании GCC), чтобы компилятор сообщал вам, что вы что-то делаете неправильно.

Обновление:

Мое предположение былоclose:

PtrSortCompFct_s menu(void)
{
    PtrSortCompFct_s userChoices;
    PtrSortFct arrSortFct[] = {bubbleSort};

    if ((userChoices = malloc(3*sizeof(int))) != NULL)
    {
        userChoices->nbFct = 1;
        userChoices->compFct = ascendingSort;
        userChoices->sortFct = arrSortFct;
    }

    return userChoices;
}

Проблема в том, что userChoices->sortFct указывает на локальную (стековую) переменную arrSortFct.Эта локальная переменная становится недействительной после возврата из menu, и в этот момент userChoices->sortFct указывает на висячий стек (как я догадался).

Вот правильный способ написания этой функции (без проверки ошибок malloc возврат для ясности):

PtrSortCompFct_s menu(void)
{
    PtrSortCompFct_s userChoices;
    PtrSortFct arrSortFct[] = {bubbleSort};

    if ((userChoices = malloc(sizeof(*userChoices)) != NULL)
    {
        userChoices->nbFct = 1;
        userChoices->compFct = ascendingSort;
        userChoices->sortFct = malloc(sizeof(arrSortFct));
        memcpy(userChoices->sortFct, arrSortFct, sizeof(arrSortFct));
    }

    return userChoices;
}

Вам также необходимо исправить main следующим образом:

PtrSortCompFct_s userChoices;

PtrSortCompFct_s userChoices = menu();
... use userChoices ...

free(userChoices->sortFct);
free(sortFct);
return 0;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...