【问题标题】:Understanding the dereference, address-of, and array subscript operators in C了解 C 中的解引用、寻址和数组下标运算符
【发布时间】:2017-01-15 17:57:11
【问题描述】:

我将 argv[] 定义为 char *。使用以下 printf 语句:

     printf("%s\n",argv[1]);   // prints out the entire string
     printf("%p\n",&argv[1]);  // & -> gets the address
     printf("%c\n",argv[1][0]);// prints out the first char of second var
     printf("%c\n",*argv[1]);  //  

这是我不明白的最后一个。打印*argv[1] 是什么意思?为什么和*argv[1][0] 不一样,为什么不能打印出printf("%s\n",*argv[1]);。另外,为什么&*argv[1]&argv[1] 的地址不同?

【问题讨论】:

  • 阅读char* tag page可能会对您有所帮助。关于argcargv,也许还有this question
  • 理解char*不是问题,而是指针问题
  • @user3655463:看到 OP 想取消对argv[1][0] 的引用,我不同意。

标签: c pointers c-strings argv char-pointer


【解决方案1】:

数组下标操作a[i] 定义为*(a + i) - 给定地址a,从该地址偏移i 元素(不是字节)并取消引用结果。因此,给定一个指针p*p等价于*(p + 0),也就是等价于p[0]

argv 的类型是char **;鉴于此,以下所有内容都是正确的:

    Expression         Type            Value
    ----------         ----            -----
          argv         char **         Pointer to a sequence of strings
         *argv         char *          Equivalent to argv[0]
        **argv         char            Equivalent to argv[0][0]
       argv[i]         char *          Pointer to a single string
      *argv[i]         char            Same as argv[i][0]
    argv[i][j]         char            j'th character of i'th string
      &argv[i]         char **         Address of the pointer to the i'th string

由于argv[i][j] 的类型是char*argv[i][j] 不是一个有效的表达式。

这是argv 序列的糟糕可视化:

     +---+              +---+                                         +---+
argv |   | ---> argv[0] |   | ---------------------------> argv[0][0] |   |
     +---+              +---+                     +---+               +---+
                argv[1] |   | -------> argv[1][0] |   |    argv[0][1] |   |
                        +---+                     +---+               +---+
                         ...           argv[1][1] |   |                ...
                        +---+                     +---+               +---+
             argv[argc] |   | ---|||               ...   argv[0][n-1] |   |
                        +---+                     +---+               +---+
                                     argv[1][m-1] |   |
                                                  +---+

这可能有助于解释不同表达式的结果。

【讨论】:

    【解决方案2】:
    char *argv[]
    

    argv 是 char 指针的数组(1)。所以它是普通数组,只是数组的每个元素都是一个指针。 argv[0]是指针,argv[1]

    argv[0] - 数组中的第一个元素。由于数组中的每个元素都是 char 指针,因此 this 的值也是一个 char 指针(正如我们上面已经提到的)。

    *argv[1] - 现在argv[1] 是上述数组中的第二个元素,但argv[1] 也是一个字符指针。应用* 只是取消对指针的引用,您将获得argv[1] 指向的字符串中的第一个字符。 您应该使用%c 来打印它,因为这只是一个字符。

    argv[1][0] 已经是数组中第二个字符串的第一个字符 - 因此没有更多的取消引用空间。这与之前的基本相同。


    (1) 严格地说它是指向指针的指针,但也许你可以“认为”它是指针数组。无论如何,这里有更多关于它的信息:https://stackoverflow.com/a/39096006/3963067

    【讨论】:

    • @HolyBlackCat 看起来更好?
    【解决方案3】:

    如果argv[1] 是指向char 的指针,那么*argv[1] 会取消引用该指针并在argv[1] 处为您获取字符串的第一个字符,因此它与argv[1][0] 相同,并带有@ 987654326@ 格式说明符。

    argv[1][0] 本身是一个char,而不是指针,因此它不可解引用。

    【讨论】:

    • 顺便说一句,我不太确定“dereferensable”的正确拼写是什么,因为“dereferensable”看起来很难看,并且 macOS 自动更正下划线为不正确,但“dereferensable”感觉更好,尽管下划线为好。有谁知道什么拼写是正确的?
    【解决方案4】:
    1. 这不是特定于char *
    2. 您可以通过 *ptr 和 ptr[0] 之间的区别来简化。
    3. 没有区别,因为 ptr[0] 是 *(ptr + 0) 或 *ptr 的糖,因为 + 0 没用。

    // printf("%p\n", &argv[1]); is wrong you must cast to (void *)
    printf("%p\n", (void *)&argv[1]);
    

    因为%p 说明符期望void *,在正常情况下,C 会自动将您的指针提升为void *,但printf() 使用变量参数列表。这有很多规则,如果你愿意,我让你阅读doc。但是char * 不会被提升为void *,就像我说的printf() 除了void * 所以如果你不自己施放它,你会有一个未定义的行为。

    【讨论】:

    • @GovindParmar 需要强制转换来确保兼容的指针类型。 "p 参数应该是一个指向 void 的指针。" C11dr §7.21.6.1 8
    • 无论我使用 (void *)&argv[1] 还是 &argv[1],我都会得到相同的地址
    • @DCR 是的,这很正常,我从不使用指针大小不同的系统,但在 C 中这是可能的。例如,某些系统有 `sizeof (int *) != sizeof (void * )。这只是一个注释,但它是您想要编写正确且可移植的 C 重要的是要知道您必须转换它。
    【解决方案5】:

    最后一行printf("%c\n",*argv[1]); 既取消引用argv 又访问数组索引1。换句话说,这是在做argv[1][0],就像上一行一样,因为数组下标访问[1]比解引用运算符(*)多了一个higher precedence

    但是,如果您要将最后一行中的表达式括起来以使取消引用运算符首先被处理,您可以这样做:

    printf("%c\n", (*argv)[1]);
    

    现在,当您运行程序时,输出的最后一行将是 argv[0][1] 而不是 [1][0],即您用于执行程序的命令行中的第二个字符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-29
      • 1970-01-01
      • 1970-01-01
      • 2013-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多