【问题标题】:How to allocate memory using a pointer to pointer in C如何在C中使用指向指针的指针分配内存
【发布时间】:2017-10-10 17:02:25
【问题描述】:

我正在尝试理解代码 sn-ps free a double pointer

Why use double pointer? or Why use pointers to pointers?

我想了解以下内容的区别。两个sn-ps都来自上面的url

int** pt; 

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

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

你能用一些例子和图纸详细说明

【问题讨论】:

  • 请不要将指针指向指针称为“双指针”。后者听起来太像一个指向双精度的指针,这是一种完全不同的动物。
  • 不要在 C++ 中使用 malloc。
  • @PeteBecker 已修复。感谢您的建议
  • @manni66。我使用的是 C 而不是 C++。我正在寻找答案

标签: c pointers malloc


【解决方案1】:

首先,sn-p 代码不好有几个原因 - 第一个是将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],但与真正的二维数组不同,行在内存中并不相邻,并且每一行的长度可能不同。你通常会这样写

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]
                                           +---+---+     +---+

【讨论】:

  • 结果的转换是一个非常好的主意,因为它允许编译器为此语句发出消息 pt = (int*) malloc(sizeof(int)*10);
  • @VladfromMoscow:使用sizeof *pt 而不是sizeof(int) 消除了该特定问题。 T *p = malloc( sizeof *p * N ); 将永远做正确的事,不管T
  • 你确定吗?考虑例如 char *p = malloc( sizeof( struct A ) );和 char *p = malloc( sizeof *p );
  • @VladfromMoscow:我不确定你要表达什么意思——如果我的目标是p,我将使用sizeof *p,而不是sizeof some_arbitrary_type_that_isn't_the_same_type_as_*p。跨度>
  • 并非总是如此。例如,您需要分配一个字符缓冲区来复制结构,或者您需要分配一个可以容纳二维数组的一维数组等等。
【解决方案2】:

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

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 可能会失败,应该针对失败测试返回值。

【讨论】:

    【解决方案3】:

    使用强制转换帮助编译器在此代码 sn-p 中找到错误

    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 **类型的对象,并且没有从@987654328类型的隐式转换@ 类型为int **

    这段代码sn-p

    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;
    

    并且想在一个函数中初始化它。在这种情况下,您必须通过引用将指针传递给函数。否则,函数将处理指针的副本,在这种情况下,函数中不会分配原始指针。

    所以对应的代码sn-p可以看成演示程序中的样子

    #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
    

    【讨论】:

    • 你不能像从 main() 中的 f(pt) 一样将指针传递给 f()。如果不是,为什么不呢?
    • @user2979872 事实上,我在回答中描述了为什么这是不可能的。你能详细说明一下不清楚的地方吗?
    • 不清楚的是f(pt)和f(&pt)的区别。当您将 pt 声明为 int *pt 时,我想知道 pt 和 &pt 之间有什么区别。如果我理解正确,&pt 表示 pt 的地址,而 pt 指向指针的地址(仅当像 pt = &a 一样分配时,其中 a 被声明为 int a = 5)。否则 pt 指向无效地址。如果我错了,请纠正我
    • @user2979872 pt 和 &pt 是不同的类型,这写在答案中。所以编译器会报错,因为参数的类型与参数的类型不对应。我们将更改函数中的原始 pt。所以我们必须通过引用传递它,否则函数将处理 pt 的副本,并且副本(不是原始 pt)将在函数中发生变化。
    • @user2979872 很抱歉,但我不明白您在评论中的意思。表达式 &pt 产生变量 pt 本身的地址。指针是与任何其他对象一样的对象。所以你可以应用运算符 & 来获取它的地址。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    • 2016-02-21
    相关资源
    最近更新 更多