【问题标题】:C Pointer help: Array/pointer equivalenceC 指针帮助:数组/指针等价
【发布时间】:2012-12-16 10:11:31
【问题描述】:

在这个玩具代码示例中:

int MAX = 5;

void fillArray(int** someArray, int* blah) {
  int i;
  for (i=0; i<MAX; i++)
    (*someArray)[i] = blah[i];  // segfault happens here
}

int main() {
  int someArray[MAX];
  int blah[] = {1, 2, 3, 4, 5};

  fillArray(&someArray, blah);

  return 0;
}

...我想填充数组 someArray,并让更改在函数之外持续存在。

这是一个非常大的家庭作业的一部分,这个问题解决了这个问题,但不允许我复制解决方案。我得到了一个函数签名,它接受一个 int** 作为参数,我应该编写逻辑来填充该数组。我的印象是在 fillArray() 函数中取消引用 &someArray 会给我所需的数组(指向第一个元素的指针),并且在该数组上使用括号数组元素访问会给我需要分配的必要位置.但是,我无法弄清楚为什么会出现段错误。

非常感谢!

【问题讨论】:

  • blah[5]blah[999] 超出范围。为什么要在fillArray 访问它们?
  • 我很确定指向指针声明的指针也是错误的。
  • 'max' 变量应该定义为 5。我的愚蠢错误。由于边界问题,不会发生段故障。需要 int** 参数的函数声明是作为要求给出的,所以我必须使用它。
  • 我建议为您的代码提供更好的名称/cmets。不管是不是作业。
  • 我想你可能错过了练习的重点。我能想到使用 int** 的唯一原因是引用二维数组。因此,我认为您的讲师希望您填充像 Array2D[X_MAX][Y_MAX] 这样的数组(您需要一个 i 和一个 j 循环),我怀疑他是否希望从堆栈中分配该数组。请参阅下面的我的备注和代码。

标签: c arrays pointers


【解决方案1】:

我想填充数组 someArray,并让更改在函数之外持续存在。

只需将数组传递给函数,因为它会衰减为指向第一个元素的指针:

void fillArray(int* someArray, int* blah) {
    int i;
    for (i=0; i<MAX; i++)
        someArray[i] = blah[i]; 
}

并被调用:

fillArray(someArray, blah);

对元素的更改将在函数外部可见。

如果实际代码是在fillArray() 中分配一个数组,则需要int**

void fillArray(int** someArray, int* blah) {
    int i;
    *someArray = malloc(sizeof(int) * MAX);
    if (*someArray)
    {
        for (i=0; i<MAX; i++)  /* or memcpy() instead of loop */
            (*someArray)[i] = blah[i];
    }
}

并被调用:

int* someArray = NULL;
fillArray(&someArray, blah);
free(someArray);

【讨论】:

  • 这很有效。但是,如果数组已经分配,​​为什么将 &someArray 传递给 fillArray() 并通过 *someArray 访问数组本身会导致分段错误?
【解决方案2】:

当您创建一个数组时,例如 int myArray[10][20],会从堆栈中分配一个保证连续的内存块,并使用普通数组算法来查找数组中的任何给定元素。

如果你想从堆中分配那个 3D“数组”,你可以使用 malloc() 并取回一些内存。那个记忆是“愚蠢的”。它只是一块内存,应该被认为是一个向量。数组的导航逻辑都没有附带,这意味着您必须找到另一种方法来导航所需的 3D 数组。

由于您对 malloc() 的调用返回一个指针,因此您需要的第一个变量是一个指针,用于保存您需要保存一些实际整数数据 IE 的 int* 向量:

int *pArray;

...但这仍然不是您要存储整数的存储空间。你所拥有的是一个指针数组,目前没有指向任何东西。要获取数据的存储空间,您需要调用 malloc() 10 次,每个 malloc() 在每次调用时为 20 个整数分配空间,其返回指针将存储在 *pArray 指针向量中。这意味着

int *pArray

需要改成

int **pArray

正确地表明它是一个指向指针向量基数的指针。

第一次解引用 *pArray[i] 将您置于 int 指针数组中的某个位置,而第二次解引用 *p[i][j] 将您置于 int 数组中的某个位置,由pArray[i] 中的 int 指针。

IE:你有一堆散布在堆上的整数向量,由一组指针指向,跟踪它们的位置。与从堆栈中静态分配的 Array[10][20] 完全不同,后者都是连续存储,并且在任何地方都没有单个指针。

正如其他人所逃避的那样,基于指针的堆方法乍一看似乎没什么用,但事实证明它非常优越。

第一,最重要的是,您可以随时使用 free() 或 realloc() 来调整堆内存的大小,并且在函数返回时它不会超出范围。更重要的是,有经验的 C 编码人员尽可能安排他们的函数对向量进行操作,其中在函数调用中删除了 1 级间接。最后,对于大型数组,相对于可用内存,特别是在大型共享机器上,大块连续内存通常不可用,并且对其他需要内存操作的程序不友好。分配在堆栈上的大型静态数组的代码是维护的噩梦。

在这里您可以看到该表只是一个收集从向量操作返回的向量指针的外壳,其中所有有趣的事情都发生在向量级别或元素级别。在这种特殊情况下,VecRand() 中的向量代码是 calloc() 处理它自己的存储并将 calloc() 的返回指针返回到 TblRand(),但 TblRand 也可以灵活地分配 VecRand() 的存储,只需通过调用 calloc() 替换 VecRand() 的 NULL 参数

/*-------------------------------------------------------------------------------------*/
dbl **TblRand(dbl **TblPtr, int rows, int cols)
{
    int  i=0;

    if ( NULL == TblPtr ){
        if (NULL == (TblPtr=(dbl **)calloc(rows, sizeof(dbl*)))) 
            printf("\nCalloc for pointer array in TblRand failed");
    }
    for (; i!=rows; i++){
        TblPtr[i] = VecRand(NULL, cols);
    }
    return TblPtr;
}
/*-------------------------------------------------------------------------------------*/

dbl *VecRand(dbl *VecPtr, int cols)
{
    if ( NULL == VecPtr ){
        if (NULL == (VecPtr=(dbl *)calloc(cols, sizeof(dbl)))) 
            printf("\nCalloc for random number vector in VecRand failed");
    }

    Randx = GenRand(VecPtr, cols, Randx);
    return VecPtr;
}
     /*--------------------------------------------------------------------------------------*/

static long GenRand(dbl *VecPtr, int cols, long RandSeed)
{
    dbl  r=0, Denom=2147483647.0;

    while ( cols-- )
    {
        RandSeed= (314159269 * RandSeed) & 0x7FFFFFFF;
        r       = sqrt(-2.0 * log((dbl)(RandSeed/Denom)));
        RandSeed= (314159269 * RandSeed) & 0x7FFFFFFF;
        *VecPtr = r * sin(TWOPI * (dbl)(RandSeed/Denom));
        VecPtr++;
    }
    return RandSeed;
}

【讨论】:

    【解决方案3】:

    没有“数组/指针”等价,数组和指针有很大不同。永远不要混淆他们。 someArray 是一个数组&amp;someArray 是一个指向数组的指针,类型为int (*)[MAX]。该函数接受一个指向指针的指针,即int **,它需要指向内存中某处的指针变量。代码中的任何地方都没有指针变量。它可能指向什么?

    对于某些表达式中的第一个元素,数组值可以隐式降级为指针rvalue。像获取地址 (&amp;) 这样需要 左值 的东西显然不能以这种方式工作。以下是数组类型和指针类型的一些区别:

    • 无法分配或传递数组类型。指针类型可以
    • 指向数组的指针和指向指针的指针是不同的类型
    • 数组数组和指针数组是不同的类型
    • 数组类型的sizeof是长度乘以组件类型的大小;指针的sizeof 只是一个大小 指针

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多