【问题标题】:Query with Double Pointers in C在 C 中使用双指针查询
【发布时间】:2016-05-18 14:01:14
【问题描述】:

在大学里我们做了这个代码:

int cargaArreglo(int ** A);

int main(){

int * A = NULL;
int i = 0;
int dimension = cargaArreglo(&A);

for( i = 0; i < dimension; i++ )
     printf( "[%d]", A[i] );

free( A );
return 0;
}

int cargaArreglo(int ** A){

int i = 0;
char bandera = 's';
(*A) = malloc( 1*sizeof(int) );

while( bandera != 'n' ){
     printf( "Ingrese un numero" );
     scanf( "%d", &(*A)[i] );
     printf( "Desea seguir ingresando?" );
     i++;
     fflush( stdin );
     scanf( "%c", &bandera );
     if( bandera != 'n' )
     (*A) = realloc( (*A), (i+1)*sizeof(int) );
}
return i;
}

我们尝试加载一个动态数组,并无限制地添加值。代码工作得很好,但是在我们需要用双指针声明参数的函数中,是否存在将其转换为简单指针的可能性?我的意思是,老师说这是不可能的,因为没有双指针我不能改变全局变量的值。这让我对指针的尊重感到困惑。

感谢您的宝贵时间!

PD:如果您发现我的英语有错误,请告诉我,因为我会在学习上帮助我很多。

【问题讨论】:

  • Nitpick:它不是真正的 双指针,它是一个指向指针。 :)
  • fflush( stdin ); 是未定义的行为。
  • fflush( stdin ); 是 UB。
  • 呈现的代码中没有全局变量。 main() 中声明的变量是该函数的局部变量。

标签: c pointers double-pointer


【解决方案1】:

如果您不喜欢指向指针的指针,您可以随时修改函数的签名,使其返回新的指针值以分配给 A,并改为通过指针设置维度值:

[...]
int dimension;
int * A = cargaArreglo(&dimension);
[...]


int * cargaArreglo(int * retDimension){
   int i = 0;
   char bandera = 's';
   int * B = malloc( 1*sizeof(int) );

   while( bandera != 'n' ){
        printf( "Ingrese un numero" );
        scanf( "%d", &B[i] );
        printf( "Desea seguir ingresando?" );
        i++;
        fflush( stdout ); /* fflush( stdin ) is UB */
        scanf( "%c", &bandera );
        if( bandera != 'n' )
           B = realloc( B, (i+1)*sizeof(int) );
   }
   *retDimension = i;

   return B;
}

我的意思是,老师说那是不可能的,因为没有 双指针我不能改变全局变量的值。这 让我对指针的尊重感到困惑。

老师的意思是,当你在 C 中调用一个函数时,每个参数的临时副本会被压入堆栈以在函数内部使用。因此,如果您在被调用函数中更改参数的值,它不会将值更改回调用函数中......例如,运行以下代码:

void foo(int x, int * A)
{
   x = 6;
   A = NULL;
}

int x = 5;
int * A = &x;
printf("BEFORE x=%i A=%p &x=%p\n", x, A, &x);
foo(x, A);
printf("AFTER x=%i A=%p &x=%p\n", x, A, &x);

...将打印出来:

BEFORE x=5 A=0x2345678 &x=0x2345678
AFTER x=5 A=0x2345678 &x=0x2345678

... 也就是说 foo() 实际上并没有改变调用函数中 x 或 A 的值,因为 foo() 只修改了 x 的副本和传递给它的 A 的副本,而不是原始变量。

但是,foo() 可以使用 A 指针来(间接)修改调用函数的 x 的值,因为 A 的函数副本仍然指向调用函数的 x,所以你可以实现foo 这样做:

void foo(int * A)
{
   *A = 666;
}

int x = 5;
printf("BEFORE x=%i\n", x);
foo(&x);
printf("AFTER x=%i\n", x);

...你会得到这个输出:

BEFORE x=5
AFTER x=666

...如果上面的内容对您有意义,那么指向指针的情况实际上与上面相同,除了指向 int 的不是 A,而是它的值的类型is 指向的是一个指针:

void foo(int ** x)
{
    *x = NULL;
}

int y = 5;
int * x = &y;
int ** A = &x;
printf("BEFORE x=%p\n", x);
foo(A);
printf("AFTER x=%p\n", x);

... 会打印出来:

BEFORE x=0x2345845  (actual value will vary depending on your computer)  
AFTER x=NULL

【讨论】:

    【解决方案2】:

    是否有可能将其转换为简单的指针?

    一个简单的指针。 C 没有其他种类。

    理解这一点是掌握 C 的关键之一。另一种看待它的方式是,指针只是 C 支持的一种值。有效的指针值作为程序中函数或对象的地址(在某种意义上)具有重要意义,但这并不能使它们变得神奇。它们是一流的价值观。特别是,作为语言类型系统的自然结果,指针可以指向指针类型的对象。一个指针指向另一个指针并不意味着它不简单。

    我认为有些人对指针感到困惑,因为他们从左到右读取类型,所以他们首先看到某些基本类型的名称(在您的情况下为int)并将最重要的意义归于这一点。但是虽然int *int ** typesint相关typevalues em> 这些指针类型与ints 根本没有太大关系。与其从左到右阅读类型,不如从内向外阅读,在许多常见情况下是从右到左。将int **A 解读为“A 是指向int 的指针”强调它的值是一个指针,这是它最重要的一点。

    至于重组程序的具体方式,@JeremyFriesner 已经很好地介绍了这个问题。在这方面要理解的关键是您(可能)首先要使用指针参数的原因。

    一般而言,您可能想要这样做的原因有多种,但主要的原因和适用于您的特定代码的原因是您的讲师给您的:以便函数可以修改以不同方式声明的对象范围或动态分配。在这种情况下,该对象是指向动态分配数组地址的指针。 (记住:指针本身就是对象。)

    此外,C 函数只有一个返回值。如果您希望一个函数将多条数据传递给它的调用者,那么您必须提供指针参数以便它可以这样做,或者您必须返回一个合适的结构类型的值(您可能还没有研究过),或者您必须返回一个指向包含所有数据的结构或数组的指针。

    【讨论】:

      【解决方案3】:

      如果您不喜欢语法的外观,您可以在函数内部执行工作时使用局部指针变量,并在最后将其分配回取消引用的指针:

      int cargaArreglo(int **A){
      
          int i = 0;
          char bandera = 's';
          int *B;
          B = malloc( 1*sizeof(int) );
      
          while( bandera != 'n' ){
               printf( "Ingrese un numero" );
               scanf( "%d", &B[i] );
               printf( "Desea seguir ingresando?" );
               i++;
               fflush( stdin );
               scanf( "%c", &bandera );
               if( bandera != 'n' )
               B = realloc( B, (i+1)*sizeof(int) );
          }
          *A = B;
          return i;
      }
      

      【讨论】:

        猜你喜欢
        • 2022-11-14
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        • 2013-03-31
        • 1970-01-01
        • 2012-04-20
        • 2019-04-20
        相关资源
        最近更新 更多