C语言中char指针的澄清

6

我正在阅读K&R第二版的第5章。

在第87页,指向字符数组的指针被介绍为:

char *pmessage;
pmessage = "Now is the time";

如何知道pmessage是指向字符数组的指针,而不是指向单个字符的指针?

更具体地说,在第94页定义了以下函数:

/* month_name: return the name of the n-th month */
char *month_name(int n)
{
    static char *name[] = {
        "Illegal month",
        "January", "February", "March",
        ...
    };

    return (n < 1 || n > 12) ? name[0] : name[n];
}

如果您只收到上述函数的声明,如何知道返回的是单个字符还是字符数组?
如果假设从month_name()返回的是字符数组,并迭代直到遇到NULL,但实际上返回的是单个字符,那么就有可能出现分段错误吗?
请有人演示指向单个字符与字符数组的声明和赋值、它们在函数中的使用以及如何确定返回了哪一个。

2
一个人怎么知道pmessage是指向字符数组的指针,而不是指向单个字符的指针?这是不确定的。但是,人们知道使用的函数。如果一个人不知道使用的函数返回什么,那么他如何能够期望创建一个正常工作的程序呢? - Shahbaz
1
你不需要这样做。更重要的是,你不知道它是否以空字符结尾。这会引起一些问题…! - Nick
4个回答

4

所以你拥有的是一个具有静态存储期的 char 数组字符串字面量:

"Now is the time"

在大多数情况下,数组会衰变成指向第一个元素的指针,这就是这里发生的事情。
pmessage = "Now is the time";

你需要设计和记录接口,确保你知道输入和输出的期望值。在运行时没有信息可以告诉指向的内容的性质。
例如,如果我们看一下 strtok 的man page,它告诉我们:
每次调用 strtok() 都会返回指向包含下一个标记的 null 结尾字符串的指针。
因此程序员知道应该期望什么,并相应地处理结果。
如果你有一个指向单个字符的指针,但却像 C 风格的字符串一样对待它,则会出现 未定义行为,因为你将访问超出边界的内存。段错误是一种可能性,但未定义意味着结果是不可预测的。

2

虽然看起来有些奇怪,但C语言信任程序员的智慧。如果我看到这样一个函数:

/* month_name: return the name of the n-th month */
char *month_name(int n)
{
    static char *name[] = {
        "Illegal month",
        "January", "February", "March",
        ...
    };

    return (n < 1 || n > 12) ? name[0] : name[n];
}

我查看了文档并阅读了它返回一个指向静态分配内存的以NUL结尾的字符串的指针。这已经足够让我正确处理返回值。
如果函数的创建者在未来的版本中更改返回值为其他类型的字符串,他们最好大声宣布,更改函数名或清楚地表明,否则这是他们非常不良的行为。
另一方面,即使文档正确,如果我未能正确处理返回值,那么我就不是那么聪明,也许不适合成为Java开发人员。
最后,如果该函数没有文档,请找到其所有者并烧毁他的房子。1 1如果它是库中的,则已发布!当人们开始编写库时不要立即烧毁他们的房子!^_^


@retrodev,我不否认shafik的回答质量。但是为了明确起见,我绝不想暗示_寻求澄清意味着无能力_。_C信任程序员的智慧_并不是一种侮辱或其他什么东西。这就是事实。C让你打开硬件的每一个细节。这就是许多人害怕它的原因,也是它非常适合系统或嵌入式编程的原因之一。C假设你知道自己在做什么。 - Shahbaz
有时,由于人为错误,有些事情很难控制,而C语言可以帮助你解决这个问题;这就是为什么它具有相对宽松的类型检查系统。然而,我从未听说过或经历过指向数组和指向单个元素(可以视为一个元素数组)的指针之间的混淆,或者特别是C字符串和指向单个字符的指针之间的混淆。由于几乎没有人遇到这种混淆,因此没有理由在C语言中添加额外的语法来防止这种错误。 - Shahbaz
1
@Shahbaz,我反对的不是你的开场白,而是“...我可能不会那么聪明,也许只适合成为Java开发人员。” 我的职业生涯始于C语言问世之前,不幸的是这种精英主义一直存在于行业中,它对行业是有害的。在过去的40年中,出现了许多“难”低级语言,它们都提供了不同的功能。问题就在于,C语言的行为是这样的,但无论一个人有多少经验,都无法预先知道。在这方面,你的评论比你的答案更有用。 - retrodev
1
总之,忽略文档与是否成为Java开发人员无关,因此这不是一个有用的评论。这就是我为什么会点踩的原因。 - retrodev
1
我在那里可能有点刻薄,但其中有一个隐藏的意义。像Java这样进行此类检查的语言希望确保您作为程序员不会因运行时开销而搞砸。这就是为什么可以说编写Java代码需要更少的注意力。例如,在Java或Scala中犯这样的错误,您可能很快就会得到一个异常,如果没有编译错误的话。因此,如果您倾向于不仔细阅读文档,则修复编译错误就可以使事情正常工作。 - Shahbaz
显示剩余3条评论

2

什么是 IT 技术?

char *pmessage;
pmessage = "Now is the time";  

什么是 char *pmessage;?这意味着你将 pmessage 声明为指向 char 的指针。
pmessage = "Now is the time"; 这意味着 pmessage 现在指向字符串字面量 Now is the time第一个字符

当你从函数中返回 pmessage 时,将返回指向字符串字面量的指针。
如果你使用 %c 标识符打印 pmessage,它将打印出 N,如果你使用 %s,它将打印整个字符串字面量。

printf("%c\n", *N);     // 'N' will be printed
printf("%s\n", N);      //  "Now is the time" will be printed 

1

如何知道pmessage是指向字符数组的指针,而不是单个字符的指针?你不能。至少,无法从指针值本身判断它是否指向单个char或char数组的第一个元素。它可以用两种方式。您必须依靠上下文或明确指定指针的使用方式。例如,scanf使用不同的转换说明符来确定指针是否指向单个字符:

char single_char;
scanf( " %c", &single_char );

或者是一个字符数组:

char

char array_of_char[N];
scanf( "%s", &array_of_char[0] );

请记住,当它不是sizeof或一元&运算符的操作数,也不是用于初始化声明中的另一个数组的字符串字面量时,类型为“N个元素的T数组”的表达式将被转换(“衰减”)为“指向T的指针”的表达式,表达式的值将是该数组第一个元素的地址,因此最后一行也可以写成:

scanf( "%s", array_of_char );

由于这个转换规则,每当你将一个数组表达式传递给函数时,函数实际上接收到的是一个指针值。事实上,函数声明。
void foo( char str[N] );

并且

void foo( char str[] );

等同于

void foo( char *str );

所有三个都将str视为指向char的指针。

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