将多维数组传递给函数时难以理解传递的元素

2024-03-10

我在某处读到以下数组可以以这种方式传递给以下函数,如下所示,但是我不明白数组中的哪些元素被准确传递给函数。
这些是数组

int array[NROWS][NCOLUMNS];
int **array1;
int **array2;
int *array3;
int (*array4)[NCOLUMNS];

这些是功能:

f1(int a[][NCOLUMNS], int m, int n);
f2(int *aryp, int nrows, int ncolumns);
f3(int **pp, int m, int n);

我读到的网站提到我们可以通过以下方式将以下数组传递给以下函数:

f2(&array[0][0], NROWS, NCOLUMNS);
f2(*array2, nrows, ncolumns);
f2(array3, nrows, ncolumns);
f2(*array4, nrows, NCOLUMNS);
f3(array1, nrows, ncolumns);
f3(array2, nrows, ncolumns);

Isn't array1 and array2指针数组?所以当你将它们传递给f3,所有的指针都通过了吗?还有关于array2当传递给f2, f2在形式参数中有一个普通的指针,但是array2是一个指针数组,那么当将指针数组传递给f2?当你通过时如何访问单独的行和列array4这是一个指向一系列一维数组的指针f2?


在 C 语言中你所理解的数组并不总是语言概念中的数组。

C 是 70 年代的语言。与其更抽象的后代相比,它非常接近机器实现。因此,如果您想了解令人困惑的相似语法元素之外的内容,则必须考虑实现。

指针和数组都可以通过以下方式访问(方)括号表示法.

括号表示法无疑是有用的,但就指针和数组之间的混淆而言,它是万恶之源。

f[i]正如我们将看到的,尽管底层机制会有所不同,但它也适用于指针和数组。

指针和数组的关系

让我们从变量声明开始。

Pointers

float * f只是告诉编译器该符号f有一天会引用未知数量的浮点数。

f未初始化。由您决定实际数据的位置并进行设置f指向他们。

指针算术和括号表示法

请记住,当您向指针添加/减去一个值时,单位是指向类型的大小。

float * f;
float * f3 = f+3; // internal value: f + 3 * sizeof (float)

// these two statements are identical
*f3 = 1;
*(f+3) = 1;

自从写下*(f+i)当你想从指针引用连续的数据时,这是很尴尬的,括号表示法可以使用:

f[3] = 1; // equivalent to *(f+3) = 1;

无论使用哪种表示法,f[3] 的地址都按如下方式计算:

@f[3] = f +3* sizeof (float)

你可以考虑f 功能上作为一个(动态)数组,但在 C 看来,它仍然是一个pointer,通过使其看起来像数组的语法进行引用。

Arrays

float f[10]仍然告诉编译器f将引用一些浮点数,但它也

  • allocates the requested number of floats at the appropriate location
    • 在堆栈上,如果f是一个自动局部变量
    • 在静态数据(又名 BSS)中,如果f是全局变量或静态变量
  • 考虑符号f as a constant pointer到这些浮点值中的第一个

尽管数组创建语法可能令人困惑,但数组始终具有编译时已知的固定大小。

例如,float f[] = {2,4,8}声明一个长度为3的数组,相当于float f[3] = {2,4,8}。为了方便起见,可以省略维度:长度反映了初始化器的数量,而不强迫程序员明确地重复它。

不幸的是,[]记法也可以参考pointers在其他一些情况下(稍后会详细介绍)。

括号表示法和数组

括号表示法是访问数组内容的最自然的方式。

当你引用一个数组时,编译器knows它是一个数组。然后它可以根据数组第一个元素访问数据,如下所示:

@f[3] = f +3* sizeof (float)

对于一维数组(但仅在这种情况下!),您可以看到地址计算与指针完全相同。

数组作为指针

由于数组也被视为(常量)指针,因此您可以使用数组来初始化指针,但反过来显然是错误的(因为数组是一个constant指针,因此其值不能更改)。

插图

void test (void)
{
    float* f1;
    float  f2[10];
    float  f3[];         // <-- compiler error : dimension not known
    float  f4[] = {5,7}; // creates float f4[2] with f4[0]=5 and f4[1]=7

    f1[3] = 1234; // <--- write to a random memory location. You're in trouble
    f2[3] = 5678; // write into the space reserved by the compiler

    // obtain 10 floats from the heap and set f1 to point to them
    f1 = (float *) calloc (10, sizeof(float));
    f1[3] = 1234; // write into the space reserved by you

    // make f1 an alias of f2 (f1 will point to the same data as f2)
    f1 = f2;              // f2 is a constant pointer to the array data
    printf ("%g", f1[3]); // will print "5678", as set through f2

    // f2 cannot be changed
    f2 = f1; // <-- compiler error : incompatible types ‘float[10]’ / ‘float *’
}

走向多维

让我们将示例扩展到二维情况:

float    f2[3][10]; // 2d array of floats
float ** f1;        // pointer to pointer to float

f1 = f2; // <-- the compiler should not allow that, but it does!

f2[2][5] = 1234;           // set some array value
printf ("%g\n", f2[2][5]); // no problem accessing it

printf ("%g\n",f1[2][5]);  // bang you're dead

让我们看看这里发生了什么

当你声明float f2[3][10],编译器将所需的 30 个浮点数分配为连续块。前 10 个浮点数代表 f[0],接下来的 10 个浮点数代表 f[1],依此类推。

当你写的时候f2[2][5],编译器仍然知道f is an array,因此它可以计算所需浮点数的有效地址,如下所示:

@f2[2][5] = f + (2* 10 +5) * sizeof (float)

您还可以访问pointers通过多个括号,前提是指针具有适当数量的引用级别:

当引用指针时,编译器简单地连续应用指针算术:

float h = f1[2][5];

相当于:

float * g = f1[2]; // equivalent to g = *(f1+2)
float   h = g[5];  // equivalent to h = *(g +5)

f1[2][5]由编译器处理为*(*(f1+2)+5)。 最终地址将按如下方式计算:

@f1[2][5] = *(f +2* sizeof (float *)) +5* sizeof (float)

你已经要求了,你得到了

除了相同的括号符号之外还有两种截然不同的实现。

显然,当尝试访问f2数据通过f1,结果将是灾难性的。

编译器将从中获得第三个浮点数f2[2],将其视为一个指针,加 20 并尝试引用结果地址。

如果你通过这种写一些值错误初始化的指针,如果您遇到访问冲突而不是默默地破坏某些随机的四个字节内存,请认为自己很幸运。

不幸的是,即使底层数据结构无法正确访问,除非编译器意识到f2是一个数组,f2 is still被视为常数float** pointer

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将多维数组传递给函数时难以理解传递的元素 的相关文章

随机推荐