【问题标题】:Why does calling a function modify the value of an array of pointer to function that weren't given in parameter?为什么调用函数会修改参数中未给出的指向函数的指针数组的值?
【发布时间】:2019-07-10 17:36:16
【问题描述】:

我有一个结构,它包含一个指向函数的指针和一个指向函数的指针数组。我将第一个指针(不是数组)作为函数的参数传递,该函数应该告诉我整数数组是否已排序(它可以按升序或降序排列,这是由 compFct 定义的,即参数中给定的函数指针)。

不幸的是,该函数正在更改我的结构中指针数组中的值(没有更改我指向参数中给定函数的指针的值)。

使用 gdb 我设法知道我的数组中的更改是什么时候发生的。它似乎在 printSorted 函数中的第一个 printf 之后被修改。

我的类型定义:

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;
}

在 printf 之前使用 gdb:

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

之后:

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

如您所见,指向我的 quickSort 函数的指针已被修改。

编辑:包括简化和可验证的代码,问题是这段代码可以正常工作,即使使用 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;
}

【问题讨论】:

  • 旁白:为什么 -1for (int i=0; i&lt;(arrSize-1); i++) 中? IAC,您需要将呼叫发布到printSorted(),作为minimal reproducible example 的一部分。我怀疑你的排序功能有问题。
  • main::userChoices-&gt;sortFct 在哪里/如何获得它的价值?
  • 是的,不应该是 -2 以避免在数组边界之外读取吗?
  • @chux -1 是因为我的参数是数组中元素的数量,而不是最后一个索引。我的排序功能正在运行,我尝试在修改后使用 gdb 将 userChoices-&gt;sortFct[0] 的值设置为 0x5555555548ea,并且我的程序的其余部分运行良好。
  • 没有minimal reproducible example,这篇文章不足以给出一个好的答案。

标签: c arrays pointers gdb


【解决方案1】:

在 printf 之前使用 gdb,我有:... 和之后:

问题的根本原因在于您如何初始化userChoices-&gt;sortFct(您没有显示执行此初始化的代码)。

该数组指向悬空堆或堆栈内存,并且对printf 的调用会覆盖该内存。

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

该代码完全是伪造的:为userChoices 分配内存,然后立即用menu() 的返回值覆盖userChoices,这只会导致内存泄漏。正如 cmets 中提到的,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),那么编译器会告诉您您做错了什么。

更新:

我的猜测很接近:

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-&gt;sortFct 指向一个本地(堆栈)变量arrSortFct。从menu 返回后,该局部变量变为无效,此时userChoices-&gt;sortFct 指向悬空堆栈(正如我所猜测的那样)。

这是编写此函数的正确方法(为了清楚起见,省略了malloc return 的错误检查):

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 用户选择;

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

free(userChoices->sortFct);
free(sortFct);
return 0;

【讨论】:

  • 感谢您的回答!我想知道我应该如何初始化这样的结构?那么我添加到帖子中的代码呢?根据您所说,它应该像上一个一样以段错误结束,不是吗?
  • 您的解决方案运行良好,谢谢。 malloc(sizeof(arrSortFct) 用于创建一个以后不会被 printf 覆盖的非局部变量,对吧?
  • @FloXire malloc 在堆上分配空间。当函数返回时,该空间(与堆栈不同)不会变得无效。只有当你明确地调用 free 时它才会失效。
猜你喜欢
  • 2016-11-07
  • 2018-12-11
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
相关资源
最近更新 更多