区分字符指针和字符数组指针

3

我正在阅读K&R,在字符指针章节有些困惑。K&R提供了以下使用指针的strcpy版本:

void strcpy(char *s, char *t) {
    while ( (*s = *t) != '\0') {
        s++;
        t++;
    }
}

这与使用指向单个字符的指针有何区别? 或者说C语言只是相信我知道自己在做什么,并且两种情况都使用指向字符的指针? 只是当我有一组这些字符时,它以\0结尾,而如果只有一个字符,则内存中的后续位置可以是任何东西...是这样吗?


5
在C语言中,指向单个字符的指针与指向正确终止的字符序列(也称为字符串)的指针实际上是无法区分的。虽然如此。 - EOF
1
st实际上是指向单个字符的指针。它们不是指向字符串的指针,因为C语言没有字符串。是的,如果您恰好在内存中有以0结尾的一系列字符,我们可以对它们执行某些类似于字符串的操作。 - Lee Daniel Crocker
程序员应该区分字符和字符串。如果您将一个 char 指针(而非字符串)作为参数传递给字符串,程序将无法正常运行。C 将字符串识别为一组以 '/0' 字符结束的字符内存位置。 - Ramesh-X
人们喜欢流行的事物,而流行的说法是'C'数组实际上是指针。但实际上这完全不正确,正如我所展示的那样。请查看我的答案以获取更多信息。 - AnArrayOfFunctions
5个回答

4
区分这两者完全是你的责任。
在单个字符指针的情况下,您可以小心地将其作为 char * const,这将不允许您增加指针或以其他方式更改它所指向的内容,从而导致未定义的行为,但您仍然可以修改它指向的值。
C语言非常低级且非常“裸骨”。使用指针时必须非常负责任。自然地,这也适用于char *以及任何其他“数组”——如果没有以null结尾,则一定要传递数组的长度并将其用作指南。

3
作为函数参数传递的数组会被隐式转换成指向它们第一个元素的指针。因此,一个指针指向单个标量对象或数组的第一个对象之间没有区别。指针不具有这样的信息。
至于名称以str开头的标准字符串函数,则假定它们处理包含由零终止的数据的字符数组,即字符串。
因此,实际上您可能会犯错误,并将指向单个字符对象的指针传递给字符串函数。但是,在这种情况下,程序行为将未定义。
另一方面,如果要严格按照您帖子的标题,则指向字符数组的指针与指向单个字符的指针不同。
例如,请考虑以下声明。
char *pc = "Hello";
char ( *ps )[6] = &"Hello";

正如您所看到的,pc和指向字符数组的指针ps有不同的声明方式:) 如果您对这些指针进行解引用操作,您也可以看到它们之间的区别。例如

printf( "%zu\n", sizeof( *pc ) );
printf( "%zu\n", sizeof( *ps ) );

2
你基本上是正确的。它与指向单个字符的指针没有区别。函数不知道你传递单个字符(不好)和传递字符串(好)的区别。如果你恰好传递了一个非零值的单个字符的地址,则此函数的行为将是未定义的。在这种类型的场景中,C并不是类型安全的。

2
这与使用指向单个字符的指针有何不同?
区别在于当您使用指向单个字符的指针时,您应该只使用一次指针且不能解除引用,而不能增加该指针。但对于字符串中的单个字符指针,则可以将指针递增至超过最后一个元素。
6.5.6 加法运算符:
如果指针操作数和结果都指向同一数组对象的元素,或者指向该数组对象的最后一个元素之后,则评估不会产生溢出; ...
实际上,指向单个元素的指针的行为类似于指向包含一个元素的数组的指针。
char c = 'A';
char *p = &c;  // Pointer to char

这类似于(除了在这种情况下,c 是一个数组)。

char c[1] = {'A'};
char *p = c;   // Pointer to the first element of array c.

或者说C语言只是相信我知道我在做什么,两种情况都使用字符指针吗?

是的。你需要确保指针指向数组/字符串对象的元素或其之后的位置,但在后一种情况下不要进行解引用操作。


嘿!你完全可以对单个char指针进行一次递增。但是之后你不能对其进行解引用。但是你可以递增! - EOF
@EOF; 编辑了一下。晚点编辑。遭受“网络连接缓慢”的困扰! - haccks
额,我主要是开玩笑的。 - EOF
我总是很感激在点踩之前/之后留下评论。 - haccks

-2

应该使用指向未知大小字符数组的指针。这是每个人都应该做的,但据我所知没有人这样做:

void str_cpy(char (*s)[], char (*t)[]) {{

    char *str_cpy[2] = { *s, *t }, *s = *str_cpy, *t = str_cpy[1];

{
    void str_cpy(char (*s)[], char (*t)[]);


    while ( (*s = *t) != '\0') s++, t++;
}
}}

请注意,对于未知大小的数组指针进行递增是被禁止的,而且在这里也不是期望的操作,因此需要更改函数代码(就像我上面所做的那样)。
然后,您可以像这样传递字符串:
char str[20], str1[20];

strcpy(&str, &str1);

这样你就可以诊断意外地将指针传递给单个字符:

char singleChar = 'H';

strcpy(&singleChar, &singleChar); //warning

生活例子

然而,问题在于使用char *变量是访问任何“char数组”中的每个单独char最实用的方法,因为您可以直接增加这样的变量以使其引用下一个数组元素(使用指向数组的指针无法做到这一点-增加这样的指针将使其引用内存中的下一个数组而不是所指数组的下一个字符)。此外,使用指向数组的指针的语法更加复杂。


今天制作的每个好编译器都会将您的代码优化为与给定代码完全相同的汇编代码。你认为你的复杂化可能会实现什么?就像C语言不检查(甚至不知道)数组边界一样。 - Lee Daniel Crocker
不,它是这样的。这种方式,您将无法传递单个字符指针以及指向固定大小数组的指针,从而防止您发生 UB 或提醒您关于数组大小的静态信息已丢失。这是目前最好的实现,请确保正确使用。 - AnArrayOfFunctions
1
但我知道你生活的世界——一个所有你所知道的都很流行的有限世界。但是正如你所看到的,流行的事物并不是唯一存在的。如果你不相信我,这是一个有效的“C”代码,请查看语言文档或尝试编译它。对于晚来者——我提供了在线工作示例的链接。 - AnArrayOfFunctions
我从未说过你的代码不是有效的C语言——它是完全有效的。只是太复杂并且没有用处。 - Lee Daniel Crocker

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