繁体   English   中英

如何在C中使用指向指针的指针分配内存

[英]How to allocate memory using a pointer to pointer in C

我试图免费阅读双指针中的代码段

为什么使用双指针? 或为什么使用指向指针的指针?

我想了解以下内容之间的区别。 这两个片段均来自上述网址

int** pt; 

pt = (int*) malloc(sizeof(int)*10);

*pt = (int*) malloc(sizeof(int)*10);

您能否详细说明一些示例和绘图

首先,由于以下几个原因,代码段很糟糕-首先是将malloc的结果转换为错误的类型,并使用错误的类型来计算内存量。 解决演员和类型问题,我们有:

int **pt;

 pt = malloc( sizeof  *pt * 10 );   // allocate space for 10 int *
*pt = malloc( sizeof **pt * 10 );   // allocate space for 10 int

执行第一行之后,您将具有以下内容:

     int **                int *
    +---+                 +---+
pt: |   | --------------->|   | pt[0]
    +---+                 +---+       
                          |   | pt[1]
                          +---+
                          |   | pt[2]
                          +---+
                           ...
                          +---+
                          |   | pt[9]
                          +---+

您已经为10个int *对象留出了空间,并且pt指向它们中的第一个。

下一行

*pt = malloc( sizeof **pt * 10 ); // allocate space for 10 int

为10个int对象分配空间,并设置pt[0]指向它们:

     int **                int *                int 
    +---+                 +---+                +---+
pt: |   | --------------->|   | pt[0] -------->|   | pt[0][0]
    +---+                 +---+                +---+
                          |   | pt[1]          |   | pt[0][1]
                          +---+                +---+
                          |   | pt[2]          |   | pt[0][2]
                          +---+                +---+
                           ...                  ...
                          +---+                +---+
                          |   | pt[9]          |   | pt[0][9]
                          +---+                +---+

这说明了分配“锯齿状”数组的一种方法。 您仍然可以将其索引为pt[i][j] ,但是与真正的2D数组不同,行在内存中并不相邻,并且每行的长度可能不同。 您通常将其写为

pt = malloc( sizeof *pt * ROWS );
if ( pt )
{
  for ( size_t r = 0; r < ROWS; r++ )
  {
    pt[r] = malloc( sizeof *pt[r] * COLS );
  }
}

完成这些后,您将获得以下内容:

     int **           int *                 int
    +---+            +---+                 +---+---+     +---+
pt: |   | ---------> |   | pt[0] --------> |   |   | ... |   | pt[0][0] - pt[0][COLS-1]
    +---+            +---+                 +---+---+     +---+
                     |   | pt[1] ------+
                     +---+             |   +---+---+     +---+
                     |   | pt[2] ---+  +-> |   |   | ... |   | pt[1][0] - pt[1][COLS-1]
                     +---+          |      +---+---+     +---+
                      ...           | 
                                    |      +---+---+     +---+
                                    +----> |   |   | ... |   | pt[2][0] - pt[2][COLS-1]
                                           +---+---+     +---+

以下是错误的,编译器应抱怨类型:

int** pt;     
pt = (int*) malloc(sizeof(int)*10);

由于另一个原因,这也是错误的(这里pt实际上并不指向任何可用的东西):

int** pt;     
*pt = (int*) malloc(sizeof(int)*10);

指向T的指针是类型T *的变量,它可能包含某些可能包含类型T元素的内存地址:

+------+
|      | pointer to T 
+------+
    |
    v
+-------------+-------------+-------------+
|             |             |             | elements of type T
+-------------+-------------+-------------+ 

例如,在C中,要获取绘制的内容,可以编写:

int *pi;
pi = malloc(sizeof(int)*3);

如果您有一个指向T指针,则图表可能类似于:

+------+
|      | pointer to pointer to T 
+------+
    |
    v
+------+------+------+
|      |      |      | pointers to T 
+------+------+------+
    |      |      |     +-------------+-------------+-------------+
    |      |      +---->|             |             |             | elements of type T
    |      |            +-------------+-------------+-------------+ 
    |      |     +-------------+-------------+
    |      +---->|             |             | elements of type T
    |            +-------------+-------------+ 
    |
    v
+-------------+-------------+-------------+-------------+
|             |             |             |             | elements of type T
+-------------+-------------+-------------+-------------+ 

代码可能是:

int **ppi;
ppi = malloc(sizeof(int *)*3);
ppi[0] = malloc(sizeof(int)*3);
ppi[1] = malloc(sizeof(int)*2);
ppi[2] = malloc(sizeof(int)*4);

当然, malloc可能失败,并且应该测试返回值是否失败。

使用强制转换可以帮助编译器在此代码片段中找到错误

int** pt; 

pt = (int*) malloc(sizeof(int)*10);

例如,错误消息可能看起来像

 error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
  pt = (int*) malloc(sizeof(int)*10);
     ^

如果不进行强制转换,则编译器可以接受此明显无效的代码,因为函数malloc的返回类型为void *且类型void *的指针可以分配给任何其他类型的对象的指针。

也就是说,在赋值的右侧,求值表达式的类型为int *而在赋值的左侧,则为int **类型的对象,并且没有从int *类型到int **类型的隐式转换。 int **

此代码段

int** pt; 

*pt = (int*) malloc(sizeof(int)*10);

因其他原因无效。 指针pt未被对象的有效地址初始化。 如果指针具有自动存储持续时间,则其值为不确定;如果指针具有静态存储持续时间,则其值为NULL。 在任何情况下,其取消引用都会导致未定义的行为。

所以写是正确的

int* pt; 
^^^^^^^
pt = (int*) malloc(sizeof(int)*10);

但是这个构造

int** pt; 

//...

*pt = (int*) malloc(sizeof(int)*10);

在某些情况下可以有效。

假设您声明了一个指针

int *pt;

并希望在函数中对其进行初始化。 在这种情况下,您必须通过引用将指针传递给函数。 否则,函数将处理指针的副本,在这种情况下,原始指针将不会在函数中分配。

因此,相应的代码段可以像在演示程序中所示

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

size_t f( int **pt )
{
    const size_t N = 10;

    *pt = (int*) malloc( sizeof( int ) * N );

    if ( *pt )
    {
        int value = 0;
        for ( size_t i = 0; i < N; i++ ) ( *pt )[i] = value++;
    }

    return *pt == NULL ? 0 : N;
}

int main( void )
{
    int *pt;

    size_t n = f( &pt );

    if ( n )
    {
        for ( size_t i = 0; i < n; i++ ) printf( "%d ", pt[i] );
        putchar( '\n' );
    }

    free( pt );
}

程序输出为

0 1 2 3 4 5 6 7 8 9

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM