这是一个关于C语言的一般性问题。(我在C语言编程方面没有太多经验)因此,如果我有一个以
char*
作为参数的函数。如何知道它是指向单个char
的指针还是char
数组,因为如果它是char
数组,我可以期望看到\0
,但如果不是char
数组,我就不想搜索\0
。
char*
类型的参数是指向单个char
还是char
数组的指针?
是的。
char*
类型的参数始终是指向一个char
对象的指针(或者如果调用者传递相应的参数为null指针,即不指向任何对象)。
它不是指向数组的指针(那将是例如char(*)[42]
类型的指针),但通常访问数组元素的方式是通过指向元素类型的指针,而不是整个数组的指针。为什么呢?因为实际上指向数组的指针必须始终指定数组的长度(在我的示例中为42
),这是不灵活的,并且不允许同一函数处理不同长度的数组。
char*
类型的参数也可以被视为指向单个char
对象的指针。例如,声明获取输入字符的函数可能如下所示:
bool get_next_char(char *c);
<stdio.h>
已经有几个从输入中读取字符的函数,并且它们不使用这种机制。)strlen
函数计算字符串的长度:size_t strlen(const char *s);
s
指向一个char
数组的第一个元素;在内部,strlen
使用该指针遍历数组,寻找终止符'\0'
。
忽略const
,这两个函数的char*
参数之间没有实质性区别。事实上,在这些情况下,C语言并没有很好地区分:一个指向单个对象的指针与一个指向数组第一个元素的指针之间的区别。
它有一种糟糕的方法来区分这种情况。例如,strlen
可以声明为:
size_t strlen(const char s[]);
但是在C语言中,实际上并没有数组类型的参数。对于参数声明const char s[]
,它会被“调整”为const char *s
;这两种声明是完全一样的。你甚至可以为类似数组参数的东西声明长度:
void foo(char s[42]);
如果出现这种情况,程序会默默地忽略它;上述内容的真正含义与下面的完全相同:
void foo(char *s);
[42]
可能具有一些文档价值,但注释具有相同的价值 - 就编译器而言也具有相同的意义。char*
指针,通常使用空字符'\0'
作为字符串的结束标记-这意味着调用者有责任确保该标记实际存在。否则,您可以将长度作为单独的参数传递,类型可能为size_t
。或者您可以使用任何其他机制,只要所有操作都始终如一。
不,你不能,至少不一定。...因为如果它是
char
数组,我可以期望有一个\0
...
char*
很容易指向未以'\0'
字符为结尾的char
数组(即不包含字符串)。如果您愿意,可以强制执行此要求。操作字符串的标准库函数强制执行该要求-但它们不强制执行该要求。例如,如果将指向未结束数组的指针传递给strlen
,则行为未定义。指针引用的字节数无法确定,您需要自己跟踪这些信息。
\0
结尾,因此您需要知道数组的长度。此外,数组的长度可能为1,这种情况下,您将获得一个没有终止符\0
的字符。
C
的好处是您可以定义数据结构的详细信息,因此您不受限于char数组总是以\0
结尾。\0
)结尾。
sizeof(char[42])
与sizeof(char*)
不同,但是sizeof param
对于param
的任何声明都是相同的。 - Keith Thompsonchar(*)[42]
可能会 和char*
具有二进制兼容性,也可能不具备。除了一些特殊情况(如char*
vs.void*
和struct foo*
vs.struct bar*
)之外,不同的指针类型没有保证拥有相同的表示方式。大多数现代实现使所有指针具有相同的大小,但并非所有实现都是如此。 - Keith Thompsonprintf
的%p
格式要求参数的类型为void*
。例如,传递一个int*
是未定义的行为。你需要将参数转换为void*
:int * ptr; ... printf("%p\n", (void*)ptr);
将int*
转换为void*
,就像将int
转换为double
一样,可能需要一些非平凡的操作,而不仅仅是复制表示。如果你的实现恰好给出所有指针相同的表示(大多数情况下是这样),你可能可以通过将任意类型的指针传递给printf("%p",...)
来摆脱限制;但这并不意味着行为是已定义的。 - Keith Thompson