Const 数组数组作为函数参数

Const array of arrays as function argument

我不太明白如何正确排列 const 以便将数组的常量数组传递给函数,例如字符串数组:

void f (char **strings);

int main (void)
{
    char strings[][2] = { "a", "b", "c" };
    f (strings);
}

你能告诉我把const放在哪里吗?据我了解,应该有两个 const 并且上面示例中的其中一个应该位于 char.

之前

重复的概率很高,但我找不到类似的问题:(

我认为 this resource 解释得很好。一般来说,这取决于你需要什么样的 const 保护“深度”。如果您希望函数“f”对“外部”指针、它指向的指针(“内部”指针)和“内部”指针指向的字符都具有只读访问权限,则使用

const char *const *const strings

如果你想放松守卫使之合法化:

strings = NULL;

然后使用:

const char *const *strings

进一步放松,以便:

strings = NULL;

*strings = NULL;

只使用一个“常量”:

const char **strings

首先声明不理想。 char strings[][2] 声明了不确定数量的 char[2] 数组,其中数量由初始化程序确定。在大多数情况下,声明一个指针数组更有意义,因为这意味着指向的字符串不需要具有特定的固定长度。

因此您可以将声明更改为 const char* strings[3] = { "a", "b", "c" };。当然,除非意图是 only 允许 1 个字符和 2 个空终止符上的字符串,否则原始代码是正确的。如果您需要 read/writeable 个字符串,那么我们也不能使用指针表示法。

您可以通过将函数声明为

来将 const char* strings[3] 传递给该函数
void f (const char* strings[3]);

就像你声明传递给函数的数组一样。

碰巧,当函数声明的一部分“衰减”为指向第一个元素的指针时,数组。在本例中,指向 const char* 项的指针,写为 const char**.
所以你可以写 void f (const char** strings); 并且它是等价的。

了解数组数组(例如,2 个字符的数组数组)和指针数组之间的区别很重要。顾名思义,它们的元素类型完全不同:

  • 一个数组的每个元素,就像下面的 my_arr_of_arrays,是一个数组!不是地址,也不是指针:每个元素都是一个适当的数组,每个元素的大小都相同(her: 2),由一系列固定数量的子元素组成。 数据就在数组对象中。char my_arr_of_arrays[][2] = { "a", "b", "c", "" };之后,my_arr_of_arrays是一连串字符,成对分组:'a','\ 0', 'b', '\0', 'c', '\0', '\0', '\0'。下图说明了这一点。 my_arr_of_arrays 中的每个元素的大小为 2 个字节。 每个字符串字面量用于复制其中的字符到my_arr_of_arrays对应的数组元素中。数据可以稍后覆盖,副本不是常量。

  • 将其与指针数组进行对比,如下面的 my_arr_of_ptrs!这样的数组中的每个元素都是一个指针。 真正的数据在别处!实际数据可能是用 malloc 分配的,或者是静态数据,如我下面示例中的字符串文字。每个元素(每个地址)在 32 位架构上的大小为 4 字节,在 64 位架构上的大小为 8 字节。 没有从字符串文字中复制任何内容:指针只是指向程序中存储文字本身的位置。数据是常量,不能被覆盖。

令人困惑的是,这些完全不同的数据结构可以用相同的花括号初始化器列表进行初始化;令人困惑的是,字符串文字可以用作将字符复制到数组中的数据源,或者可以获取它们的地址并将其分配给指针:char arr[3] = "12":char *ptr = "12"; 都是有效的,但是对于arr 创建了一个副本,对于 ptr,字符串文字本身的地址 被采用。

你的程序显示 C 允许你将 2 个字符的数组的地址传递给需要指针地址的函数;但这是错误的,如果该函数试图取消引用实际上是一系列字符的“地址”,则会导致灾难。 C++ 禁止这种无意义的转换。

下面的程序和图像可以阐明两个不同数组的数据布局。

#include <stdio.h>

void f_array_of_arrays(char(* const arr_of_arrays)[2])
{
    for (int i = 0; arr_of_arrays[i][0] != 0; i++)
    {
        printf("string no. %d is ->%s<-\n", i, arr_of_arrays[i]);
    }

    const char myOtherArr[][2] = { "1", "2", "3", "4", "" };
    // arr2d = myOtherArr; //  <-- illegal: "const arr2d"!
    arr_of_arrays[0][0] = 'x';     // <-- OK: The chars themselves are not const.
}

void f_array_of_pointers(const char** arr_of_ptrs)
{
    for (int i = 0; arr_of_ptrs[i][0] != 0; i++)
    {
        printf("string no. %d is ->%s<-\n", i, arr_of_ptrs[i]);
    }
    arr_of_ptrs[1] = "87687686";
    for (int i = 0; arr_of_ptrs[i][0] != 0; i++)
    {
        printf("after altering: string no. %d is ->%s<-\n", i, arr_of_ptrs[i]);
    }
}

int main()
{
    char my_arr_of_arrays[][2] = { "a", "b", "c", "[=10=]" };  // last element has two zero bytes.
    const char* my_arr_of_ptrs[] = { "111", "22", "33333", "" }; // "jagged array"

    f_array_of_arrays(my_arr_of_arrays);
    f_array_of_pointers(my_arr_of_ptrs);

    // disaster: function thinks elements are arrays of char but
    // they are addresses; does not compile as C++
    // f_array_of_arrays(my_arr_of_ptrs); 

    // disaster: function thinks elements contain addresses and are 4 bytes,
    // but they are arbitrary characters and 2 bytes long; does not compile as C++
    // f_array_of_pointers(my_arr_of_arrays);
}