【问题标题】:In C/C++, is char* arrayName[][] a pointer to a pointer to a pointer OR a pointer to a pointer?在 C/C++ 中,char* arrayName[][] 是指向指针的指针还是指向指针的指针?
【发布时间】:2011-04-24 15:48:46
【问题描述】:

我将多维数组理解为指向指针的指针,但也许我错了?

例如,我认为:

char * var = char var[]

char ** var = char* var[]char var[][]

char *** var = char var[][][]char* var[][]char** var[]

这是不正确的吗?我很困惑,因为我在一个简单的教科书示例中看到一个 char*[][] 转换为 char**。

我粘贴了下面的示例。谁能帮我解决这个问题?谢谢!


/* A simple dictionary. */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

/* list of words and meanings */

char  *dic[][40] = {
    "atlas", "A volume of maps.",
    "car", "A motorized vehicle.",
    "telephone", "A communication device.",
    "airplane", "A flying machine.",
    "", ""  /* null terminate the list */
};

int main(void)
{
    char word[80], ch;
    char **p;

do {
    puts("\nEnter word: ");
    scanf("%s", word);
    p = (char **)dic;
    /* find matching word and print its meaning */
    do {
        if(!strcmp(*p, word)) {
            puts("Meaning:");
            puts(*(p+1));
            break;
            }

        if(!strcmp(*p, word)) break;

        p = p + 2;  /* advance through the list */
        } while(*p);

    if(!*p) puts("Word not in dictionary.");
    printf("Another? (y/n): ");
    scanf(" %c%*c", &ch);

    } while(toupper(ch) != 'N');

return 0;

}

【问题讨论】:

  • 请注意,在这种特定情况下,在 C++ 中,最好使用 std::map 而不是这里的。
  • 上面的代码来自 Herbert Schildt 在 C 中的完整参考 页码 212 但我不明白 if(!strcmp(*p, word)) break; 我认为那句话是多余的。因为如果单词与 *p 处的当前元素匹配,那么它应该已经导致内部 do while 中断。请就此问题赐教

标签: c++ c arrays pointers multidimensional-array


【解决方案1】:

C的规则如下:

6.3.2.1 左值、数组和函数指示符
...
3 除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串字面量,否则为 ''array 类型的表达式of type'' 被转换为类型为 ''pointer to type'' 的表达式,它指向数组对象的初始元素并且不是左值。如果数组对象具有寄存器存储类,则行为未定义。

C++ 的语言有点不同:

4.2 数组到指针的转换[conv.array]

1 “N T 的数组”或“T 的未知边界数组”类型的左值或右值可以转换为“指向 T 的指针”类型的右值。结果是指向数组第一个元素的指针。
...
8.3.4 数组 [dcl.array]
...
7 多维数组遵循一致的规则。如果 E 是秩为 i × j × ... × k 的 n 维数组,则表达式中出现的 E 转换为指向 (n−1) 维数组的指针,秩为 j × ... × k。如果将 * 运算符作为下标的结果显式或隐式应用于此指针,则结果是指向的 (n-1) 维数组, 它本身立即转换为指针。

所以以下都成立:

Declaration        Expression        Type             Decays to
-----------        ----------        ----             ---------
     T a[N]                 a        T [N]            T *
                           &a        T (*)[N]     
                           *a        T
                         a[i]        T

  T a[M][N]                 a        T [M][N]         T (*)[N]
                           &a        T (*)[M][N]  
                           *a        T [N]            T *
                         a[i]        T [N]            T *
                        &a[i]        T (*)[N]      
                        *a[i]        T
                      a[i][j]        T

T a[M][N][O]                a        T [M][N][O]      T (*)[M][N]
                           &a        T (*)[M][N][O]
                           *a        T [M][N]         T (*)[N]
                         a[i]        T [M][N]         T (*)[N]
                        &a[i]        T (*)[M][N]  
                        *a[i]        T [N]            T *
                      a[i][j]        T [N]            T *
                     &a[i][j]        T (*)[N]
                     *a[i][j]        T
                   a[i][j][k]        T

对于高维数组,模式应该是清晰的。

让我们分析一下你的字典:

/* list of words and meanings */         

char  *dic[][40] = {         
    "atlas", "A volume of maps.",         
    "car", "A motorized vehicle.",         
    "telephone", "A communication device.",         
    "airplane", "A flying machine.",         
    "", ""  /* null terminate the list */         
};

这不会按照您想要的方式设置您的字典;您基本上已将其设置为 40 个指向 char 的指针的 1 元素数组。如果你想要一个字符串对数组,那么声明应该是这样的:

char *dic[][2] = 
{
  {"atlas", "A volume of maps"},
  {"car", "A motorized vehicle"},
  {"telephone", "A communication device"},
  {"airplane" , "A flying machine"},
  {NULL, NULL} // empty strings and NULLs are different things.  
}; 

dic 的类型是“指向 char 的 2 元素数组的 5 元素数组”,或 char *[5][2]。按照上面的规则,表达式dic 应该衰减为char *(*)[2]——一个指向 char 指针的 2 元素数组的指针。

搜索此字典的函数如下所示:

char *definition(char *term, char *(*dictionary)[2]) // *NOT* char ***dictionary
{
  while ((*dictionary)[0] != NULL && strcmp((*dictionary)[0], term) != 0)
    dictionary++;
  return (*dictionary)[1];
}

你会从你的主函数中调用它,比如

char *def = definition(term, dic);

请注意,我们必须在函数中的 *dictionary 表达式周围使用括号。数组下标运算符[] 的优先级高于解引用运算符*,我们不想直接下标dictionary,我们要下标dictionary指向的数组 em>。

【讨论】:

    【解决方案2】:

    我将多维数组理解为指向指针的指针,但也许我错了?

    是的,你错了。数组和指针是有区别的。数组可以衰减为指针,但指针不携带有关其指向的数组的大小或配置的状态。不要将这种自动衰减与数组和指针相同的想法混为一谈——它们不是。

    char ** 是指向包含字符指针的内存块的指针,字符指针本身指向字符的内存块。 char [][] 是包含字符的单个内存块。

    如果您有一个char ** 并使用ptr[x][y] 访问它,编译器会将其更改为*(*(ptr + x)+y)。如果您有char [][],编译器会将arr[x][y] 更改为*(ptr + rowLength*y + x)。 (注意:我对 Xs 和 Ys 的顺序不是 110% 肯定,但这对于我在这里提出的观点并不重要)请注意,给定指针,编译器不会'对数组的大小或维度一无所知,如果将指针视为多维数组,则无法确定实际地址。

    char *dic[][40]an array of arrays of size forty, which contain character pointers。因此,它根本不符合你的任务。

    p = (char **)dic; dic 做的事情没有任何意义。但由于您可以将指针强制转换为任何其他指针,因此强制转换成功,即使尝试以这种方式读取数据会导致未定义的行为。

    【讨论】:

      【解决方案3】:

      您需要参考'right left rule'。或者,您可以在 here

      处解读大部分 C 式声明

      所以,

      char *p[2][3] 被解析为

      p 是一个由 2 个元素组成的数组,其中每个元素是一个由 3 个元素组成的数组,因此每个元素都是一个指向字符的指针。([] 的绑定比 * 强)

      char (*p)[2][3] 被解析为

      “p 是一个指向 2 元素 char 数组的指针,其中每个元素都是 3 个元素的 char 数组。” (括号绑定最强)

      【讨论】:

        【解决方案4】:

        没有看得太详细,但我认为作者是在依赖 c 来布置这样的二维字符串数组:

        连续内存中的键、值、键、值、键、值。然后将这个数组作为一维字符串数组遍历p = (char **)dic;

        这是 C 语言的优点和潜在问题之一 - 它具有许多低级功能,但您可以使用体面的代码保护自己以防止副作用。

        【讨论】:

        • 这正是他想要的。我相信 sn-p 来自 C: The Complete Reference。那本书似乎有很多不清楚和糟糕的 C 实践。尽管书中的示例可能确实适用于大多数平台,但我通常会避免这种对内部 C 机制的滥用。亲吻
        【解决方案5】:

        我对@9​​87654321@ 和[] 组合的记忆规则之一是main 的签名。作品! :-)

        您的 dic 是一个由 40 个元素组成的数组,其中每个元素都是指向 char 的指针。

        #include <iostream>
        #include <typeinfo>
        using namespace std;
        
        template< class Type, unsigned N >
        void tellMeAbout( Type const (&)[N] )
        {
            cout << "Well, it's an array of " << typeid( Type ).name() << ".\n";
        }
        
        int main()
        {
            char  *dic[][40]    = { 0 };
            tellMeAbout( dic );
        }
        

        使用 Visual C++ 我得到...

        嗯,它是一个 char * [40] 的数组。

        干杯,

        --阿尔夫

        【讨论】:

        • 该代码无效。 typeid 只需要对多态类型有效。 char *[40] 不是多态的。仅仅因为它在 MSVC 中工作并不意味着它符合标准。此外,该问题同时标记为 C 和 C++,但 OP 的代码是 C,而不是 C++,因此发布 C 解决方案可能会更好。
        • @Billy:很高兴代码是有效的。在 C++0x N3092 中,typeid 应用于非多态类型的结果由 §5.2.8/3 定义。关于用于检查类型的语言,C 不提供该功能,但 C++ 提供。以上是标准C++。我提到了 Visual C++,因为输出取决于编译器。干杯&hth
        • @Alf:C++0x 还不是标准。
        • @Billy:它在 C++98 中的位置大致相同。啊,你强迫我安装一个不可靠的 USB 驱动器!好的,它由 C++98 §5.2.8/3 定义。呵呵,谈巧合! :-) 顺便说一句,比利,是你给我的答案打分了吗?那又要怎么加分呢? ;-)
        • @Billy:这是与您的想法不兼容的 C++98 §5.2.8/3 措辞:“当 typeid 应用于表达式时,表达式不是多态类的左值类型,结果引用代表表达式静态类型的type_info对象。”直接涵盖上述代码中用法的措辞在 C++98 §5.2.8/4 中,“当 typeid 应用于 type-id 时,结果指的是 @ 987654333@ 对象表示 type-id 的类型。"请注意,对多态类型的限制将与 §5.2.8/3 冲突。干杯&hth.,
        猜你喜欢
        • 2021-11-26
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        • 1970-01-01
        • 2021-08-14
        • 2016-11-13
        • 2019-09-10
        • 2018-04-05
        相关资源
        最近更新 更多