如何在 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** 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

相关内容

  • 没有找到相关文章

最新更新