了解 C 中的取消引用、地址和数组下标运算符
Understanding the dereference, address-of, and array subscript operators in C
我将 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 *argv[]
argv
是 array(1) 的 char 指针。所以它是普通数组,只是数组的每个元素都是一个指针。 argv[0]
是一个指针,argv[1]
,等等
argv[0]
- 数组中的第一个元素。由于数组中的每个元素都是字符指针,所以 this 的值也是一个字符指针(正如我们上面已经提到的)。
*argv[1]
- 现在这里 argv[1]
是上述数组中的第二个元素,但 argv[1]
也是一个字符指针。应用 *
只是取消对指针的引用,您将获得 argv[1]
指向的字符串中的第一个字符。
您应该使用 %c
打印它,因为这只是一个字符。
argv[1][0]
已经是数组中第二个字符串的第一个字符 - 因此没有更多的取消引用空间。这与之前的基本相同。
(1) 正如突出显示的那样,严格地说它是指向指针的指针,但也许您可以将其 "think" 作为指针数组。无论如何,这里有更多信息:
最后一行 printf("%c\n",*argv[1]);
是取消引用 argv
和访问数组索引 1
。换句话说,这是在做 argv[1][0]
,就像上一行一样,因为数组下标访问 [1]
比解引用运算符 (*
) 多了一个 higher precedence。
但是,如果您要在最后一行中用括号括起表达式以使取消引用运算符首先被处理,您可以这样做:
printf("%c\n", (*argv)[1]);
现在,当您 运行 程序时,输出的最后一行将是 argv[0][1]
而不是 [1][0]
,即您用来执行的命令行中的第二个字符程序。
如果 argv[1]
是指向 char
的指针,则 *argv[1]
取消引用该指针并获取 argv[1]
处字符串的第一个字符,所以它是相同的作为 argv[1][0]
并使用 "%c"
格式说明符打印。
argv[1][0]
是一个 char
本身,而不是一个指针,所以它不可取消引用。
- 这不是特定于
char *
。
- 你可以通过*ptr和ptr[0]的区别来简化。
- 没有区别,因为 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 *
所以如果你不自己投它就会有未定义的行为。
数组下标操作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] | |
+---+
这可能有助于解释不同表达式的结果。
我将 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 *argv[]
argv
是 array(1) 的 char 指针。所以它是普通数组,只是数组的每个元素都是一个指针。 argv[0]
是一个指针,argv[1]
,等等
argv[0]
- 数组中的第一个元素。由于数组中的每个元素都是字符指针,所以 this 的值也是一个字符指针(正如我们上面已经提到的)。
*argv[1]
- 现在这里 argv[1]
是上述数组中的第二个元素,但 argv[1]
也是一个字符指针。应用 *
只是取消对指针的引用,您将获得 argv[1]
指向的字符串中的第一个字符。
您应该使用 %c
打印它,因为这只是一个字符。
argv[1][0]
已经是数组中第二个字符串的第一个字符 - 因此没有更多的取消引用空间。这与之前的基本相同。
(1) 正如突出显示的那样,严格地说它是指向指针的指针,但也许您可以将其 "think" 作为指针数组。无论如何,这里有更多信息:
最后一行 printf("%c\n",*argv[1]);
是取消引用 argv
和访问数组索引 1
。换句话说,这是在做 argv[1][0]
,就像上一行一样,因为数组下标访问 [1]
比解引用运算符 (*
) 多了一个 higher precedence。
但是,如果您要在最后一行中用括号括起表达式以使取消引用运算符首先被处理,您可以这样做:
printf("%c\n", (*argv)[1]);
现在,当您 运行 程序时,输出的最后一行将是 argv[0][1]
而不是 [1][0]
,即您用来执行的命令行中的第二个字符程序。
如果 argv[1]
是指向 char
的指针,则 *argv[1]
取消引用该指针并获取 argv[1]
处字符串的第一个字符,所以它是相同的作为 argv[1][0]
并使用 "%c"
格式说明符打印。
argv[1][0]
是一个 char
本身,而不是一个指针,所以它不可取消引用。
- 这不是特定于
char *
。 - 你可以通过*ptr和ptr[0]的区别来简化。
- 没有区别,因为 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 *
所以如果你不自己投它就会有未定义的行为。
数组下标操作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] | |
+---+
这可能有助于解释不同表达式的结果。