努力理解如何在 scanf 中调用指向指针的指针

Struggling to understand how to call a pointer to pointer in scanf

我正在用 C 构建一个程序,以便 运行 为我的博士研究进行一些模拟。基本上,该程序使用一个函数来读取具有一些重要值的输入文件,然后将这些值分配给 main 函数中的变量。其中一些值需要分配给数组 xpar,其大小也在输入文件中声明。为此,我需要在函数内部使用一些动态分配的内存,指向指针 *x*par.

的地址

我设法使用二维数组表示法为这些指针赋值,但我不完全理解为什么需要这样,因为函数指向的指针被初始化为 NULL 指针。

这里以最小功能代码为例:

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

void read_input(char *name, int *dim, int *npar, int *np, int *ndiv, double *t, double **par, double **x);

int main(void) {
    
    int DIM;                        
    int nP;                         
    int nDiv;                       
    int nPar;                       

    // Assign values for program parameters, system parameters and initial conditions
    char *input_filename = "input.txt";
    double t;
    double *par = NULL;
    double *x = NULL;
    read_input(input_filename, &DIM, &nPar, &nP, &nDiv, &t, &par, &x);
 
    for (int i = 0; i < DIM; i++) {
        printf("x[%i] = %.15lf\n", i, x[i]);
    }
    for (int i = 0; i < nPar; i++) {
        printf("par[%i] = %lf\n", i, par[i]);
    }

    free(x);
    free(par);
}

void read_input(char *name, int *dim, int *npar, int *np, int *ndiv, double *t, double **par, double **x) {
    // Open input file
    FILE *input = fopen(name, "r");
    if (input == NULL) {
        printf("Input File Not Found...\n");
        return;
    }
    // Read and assign system constants
    fscanf(input, "%i", dim);
    fscanf(input, "%i", npar);
    // Read and assign program parameters
    fscanf(input, "%i %i", np, ndiv); 
    // Read and assign initial time
    fscanf(input, "%lf", t);
    // Allocate memory for x[dim] and par[npar] vectors
    *x = malloc((*dim) * sizeof(double));
    *par = malloc((*npar) * sizeof(double));
    // Security check for pointers
    if (*x == NULL || *par == NULL) {
        free(*x);
        free(*par);
        printf("Memory allocation for *x or *par did not complete successfully");
        return;
    }
    // assign IC to x[dim] vector
    for (int i = 0; i < *dim; i++) {
        fscanf(input, "%lf ", &x[i][i]);     
    }

    for (int i = 0; i < *dim; i++) {
        for (int j = 0; j < *dim; j++) {
            printf("x[%i][%i] = %lf (address: %p)\n", i, j, x[i][j], &x[i][j]);
        }
    }
    printf("==============================\n");
    // Assign values to par[npar] vector
    for (int i = 0; i < *npar; i++) {
            fscanf(input, "%lf\n", &par[i][i]);
    }
    for (int i = 0; i < *npar; i++) {
        for (int j = 0; j < *npar; j++) {
            printf("par[%i][%i] = %lf (address: %p)\n", i, j, par[i][j], &par[i][j]);
        }
    }
    printf("==============================\n");
        
    // Close input file
    fclose(input);
}

此外,我检查了双指针x[i][j]par[i][j]指向的地址,它们对于[i][0], ..., [i][4]和[=22=的每个组合都是相同的]:

x[0][0] = 0.707107 (address: 0000020B01C33FC0)
x[0][1] = 0.000000 (address: 0000020B01C33FC8)
x[1][0] = 0.707107 (address: 0000020B01C33FC0)
x[1][1] = 0.000000 (address: 0000020B01C33FC8)
==============================
par[0][0] = 1.000000 (address: 0000020B01C34580)
par[0][1] = 0.150000 (address: 0000020B01C34588)
par[0][2] = 0.025000 (address: 0000020B01C34590)
par[0][3] = -0.500000 (address: 0000020B01C34598)
par[0][4] = 1.000000 (address: 0000020B01C345A0)
par[1][0] = 1.000000 (address: 0000020B01C34580)
par[1][1] = 0.150000 (address: 0000020B01C34588)
par[1][2] = 0.025000 (address: 0000020B01C34590)
par[1][3] = -0.500000 (address: 0000020B01C34598)
par[1][4] = 1.000000 (address: 0000020B01C345A0)
par[2][0] = 1.000000 (address: 0000020B01C34580)
par[2][1] = 0.150000 (address: 0000020B01C34588)
par[2][2] = 0.025000 (address: 0000020B01C34590)
par[2][3] = -0.500000 (address: 0000020B01C34598)
par[2][4] = 1.000000 (address: 0000020B01C345A0)
par[3][0] = 1.000000 (address: 0000020B01C34580)
par[3][1] = 0.150000 (address: 0000020B01C34588)
par[3][2] = 0.025000 (address: 0000020B01C34590)
par[3][3] = -0.500000 (address: 0000020B01C34598)
par[3][4] = 1.000000 (address: 0000020B01C345A0)
par[4][0] = 1.000000 (address: 0000020B01C34580)
par[4][1] = 0.150000 (address: 0000020B01C34588)
par[4][2] = 0.025000 (address: 0000020B01C34590)
par[4][3] = -0.500000 (address: 0000020B01C34598)
par[4][4] = 1.000000 (address: 0000020B01C345A0)
==============================
x[0] = 0.707106781186547
x[1] = 0.000000000000000
par[0] = 1.000000
par[1] = 0.150000
par[2] = 0.025000
par[3] = -0.500000
par[4] = 1.000000

input.txt 文件的格式如下:

2
5

1000 1000

0.0
0.707106781186547 0.0

1.0
0.15
0.025
-0.5
1.0

我在这里错过了什么?

我设法在一些文章中找到了为指针赋值的正确方法:

为此,应通过一维数组表示法访问指针,方法是将指针本身调用为:

scanf("%lf", &(*x)[i]);

而不是

scanf("%lf", &x[i][i]); 

那么,就可以正确赋值了。

在二维索引中的第一个索引不是 0 的所有情况下,发布的代码都有未定义的行为。例如:

    fscanf(input, "%lf ", &x[i][i]); 
  • xmain函数中指向int的指针地址。
  • x[i] 仅在 i0 时才定义,否则您正在从内存中超出 main 局部变量的位置读取,该位置未定义行为,但可能会或可能不会导致问题。
  • x[i][i] 取消引用值 x[i],这可能是一个无效指针,因此极有可能导致崩溃。仅将此地址作为 &x[i][i] 可能仍未引起注意,但是当 fscanf() 尝试将 int 存储在那里时,取消引用 &x[i][i] 将是一个问题。

令人惊讶的是,您的程序可以毫无问题地产生输出。

这个循环的正确语法是

// assign IC to x[dim] vector
for (int i = 0; i < *dim; i++) {
    fscanf(input, "%lf ", &(*x)[i]);
}

&(*x)[i]*x 指向的数组的第 i 个元素的地址。还有其他的可能性,none其中令人满意的:

  • &x[0][i]
  • (*x) + i
  • *x + i
  • x[0] + i
  • i + *x
  • &0[x][i]
  • &i[*x]
  • &i[0[x]]
  • i+0[x]

是的,以上都是等价的...

更好的做法是在read_input中定义正确类型的局部变量,运行成功后传回给调用者。您还可以将所有这些变量分组在一个结构中,并将指针传递给 read_input:

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

int read_input(const char *name, int *pdim, int *pnpar,
               int *pnp, int *pndiv, double *pt,
               double **ppar, double **px)
{
    // use regular variables
    int dim, npar, np, ndiv;
    double t;
    double *par = NULL;
    double *px = NULL;

    // Open input file
    FILE *input = fopen(name, "r");
    if (input == NULL) {
        printf("Cannot open file %s: %s\n", name, strerror(errno));
        return -1;
    }
    // Read and assign system constants
    if (fscanf(input, "%i %i", &dim, &npar) != 2)
        goto invalid;
    // Read and assign program parameters
    if (fscanf(input, "%i %i", &np, &ndiv) != 2)
        goto invalid;
    // Read and assign initial time
    if (fscanf(input, "%lf", &t) != 1)
        goto invalid;
    // Allocate memory for x[dim] and par[npar] vectors
    x = calloc(dim, sizeof(double));
    par = calloc(npar, sizeof(double));
    // Security check for pointers
    if (x == NULL || par == NULL) {
        printf("Memory allocation for x or par did not complete successfully\n");
        free(x);
        free(par);
        fclose(input);
        return -1;
    }
    // assign IC to x[dim] vector
    for (int i = 0; i < dim; i++) {
        if (fscanf(input, "%lf", &x[i]) != 1)
           goto invalid;
    }

    // Assign values to par[npar] vector
    for (int i = 0; i < npar; i++) {
        if (fscanf(input, "%lf", &par[i]) != 1)
            goto invalid;
    }
        
    // Close input file
    fclose(input);

    // update valid data to the caller
    *pdim = dim;
    *pnpar = npar;
    *pnp = np;
    *pndiv = ndiv;
    *pt = t;
    *px = x;
    *ppar = par;
    return 0;

invalid:
    printf("Invalid or missing input\n");
    free(x);
    free(par);
    fclose(input);
    return -1;
}