“char*”作为参数是指向单个字符还是字符数组的指针?

4
这是一个关于C语言的一般性问题。(我在C语言编程方面没有太多经验)因此,如果我有一个以char*作为参数的函数。如何知道它是指向单个char的指针还是char数组,因为如果它是char数组,我可以期望看到\0,但如果不是char数组,我就不想搜索\0
3个回答

9

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,则行为未定义。
推荐阅读: comp.lang.c FAQ的第6节。

除了 char s[42] 具有文档价值外,也具有一些编译时的价值,因为 sizeof(char[42]) 与 sizeof(char*) 不同。 - Mike Housky
1
@MikeHousky:文档价值可以通过注释来实现,这对编译器具有同样的意义。是的,sizeof(char[42])sizeof(char*)不同,但是sizeof param对于param的任何声明都是相同的。 - Keith Thompson
@MikeHousky: char(*)[42] 可能会char* 具有二进制兼容性,也可能不具备。除了一些特殊情况(如 char* vs. void*struct foo* vs. struct bar*)之外,不同的指针类型没有保证拥有相同的表示方式。大多数现代实现使所有指针具有相同的大小,但并非所有实现都是如此。 - Keith Thompson
我不明白如何传递不同大小的数据指针,仍然可以使用printf(“%p”)。 C99 / C11 6.2.3.2 [7]要求对象指针可转换为任何其他对象指针类型,并且不会丢失信息内容(因此任何两个数据指针类型中的信息内容必须相同)。唯一的限制是新类型的对齐可能不正确。 char不能有对齐问题,因此这不是问题。实际上,指针数组二元性和“&产生其操作数的地址”的要求意味着类型T的每个标量都对齐为T [1]数组。 - Mike Housky
@MikeHousky:printf%p 格式要求参数的类型为 void*。例如,传递一个 int* 是未定义的行为。你需要将参数转换为 void*: int * ptr; ... printf("%p\n", (void*)ptr);int* 转换为 void*,就像将 int 转换为 double 一样,可能需要一些非平凡的操作,而不仅仅是复制表示。如果你的实现恰好给出所有指针相同的表示(大多数情况下是这样),你可能可以通过将任意类型的指针传递给 printf("%p",...) 来摆脱限制;但这并不意味着行为是已定义的。 - Keith Thompson
显示剩余5条评论

6

指针引用的字节数无法确定,您需要自己跟踪这些信息。


啊哈,这正是我想的!谢谢! - Adroit

1
有可能char数组没有\0结尾,因此您需要知道数组的长度。此外,数组的长度可能为1,这种情况下,您将获得一个没有终止符\0的字符。 C的好处是您可以定义数据结构的详细信息,因此您不受限于char数组总是以\0结尾。
用于描述C数据结构的一些术语是同义词。例如,数组是数据元素的顺序系列,字符数组是字符串,并且字符串可以以空字符(\0)结尾。

对啊,我懂了这一部分!谢谢! - Adroit
3
虽然这个提及确实值得一提,但它并不是问题的答案,最好将其作为对问题的评论。 - alk
@alk,答案并未探讨字符串的这个方面。 - JackCColeman
2
但是谁说它是一个字符串?这就是问题的关键。它也不一定是一个数组。 - Chris Stratton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接