【问题标题】:C Differentiating Between Pointer to Char and Pointer to Char ArrayC区分指向Char的指针和指向Char数组的指针
【发布时间】:2015-04-24 20:07:27
【问题描述】:

我正在阅读 K&R,并且在字符指针部分感到有些困惑。 K&R 提供以下作为带有指针的 strcpy 版本:

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

这与使用指向单个字符的指针有何不同?还是 C 只是相信我知道自己在做什么,并且两种情况都使用指向字符的指针?只是当我有一个这些字符的数组时,它被\0终止,而如果它只是一个字符,那么内存中的后续点可能是任何东西......对吗?

【问题讨论】:

  • 在 C 中,指向单个 char 的指针确实无法区分指向正确终止的 chars 序列的指针,也就是字符串。艰难。
  • 上面的 st 确实是指向单个字符的指针。他们还会是什么?它们不是指向字符串的指针,因为 C 没有字符串。是的,如果你碰巧在内存中有一系列以 0 结尾的字符,我们可以用它们做一些类似字符串的事情。
  • 两者相同。程序员应该识别它是一个字符还是一个字符串。如果将 char 指针(不是字符串)作为参数发送给字符串,程序将无法正常工作。 C 会将字符串识别为以 '/0' 字符结尾的字符内存位置集。
  • 只是喜欢通俗的,通俗的说'C'数组其实是指针。好吧,正如我所展示的那样,这实际上根本不是真的。查看我的答案以获取更多信息。

标签: c pointers


【解决方案1】:

区分两者完全是您的责任。

在指向单个字符的指针的情况下,您可以小心地将其作为char * const,这将不允许您增加指针或以其他方式更改它指向的内容并走到某个未定义的地方行为,但您仍然可以修改它指向的值。

C 是非常低级且非常“简单”的语言。当您使用指针时,您必须非常负责。当然,这适用于char * 以及任何其他“数组”——如果不是以空值结尾的,您应该传递数组的长度并将其用作指南。

【讨论】:

    【解决方案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;编辑了那个。后期编辑。遭受“网速慢”之苦!
    • 呃,我主要是在开玩笑。
    • 我总是感谢在投票之前/之后发表评论。
    【解决方案3】:

    你基本上是正确的。指向单个字符的指针没有什么不同。该函数不知道传递单个字符(坏)和传递字符串(好)之间的区别。如果您碰巧传递了单个字符(具有非零值)的地址,那么该函数的行为将是未定义的。在这种类型的场景中,C 不是typesafe

    【讨论】:

      【解决方案4】:

      作为参数传递给函数的数组被隐式转换为指向其第一个元素的指针。因此,指针指向单个标量对象还是指向数组的第一个对象没有区别。指针不具备这样的信息。

      对于名称以str 开头的标准字符串函数,假设它们处理的字符串是包含以零结尾的数据的字符数组。

      所以实际上你可能会犯一个错误,将一个指向单个字符对象的指针传递给字符串函数。但在这种情况下,程序的行为将是未定义的。

      另一方面,如果要完全按照帖子的标题,那么指向字符数组的指针不同于指向单个字符的指针。

      例如考虑以下声明

      char *pc = "Hello";
      char ( *ps )[6] = &"Hello";
      

      如您所见pcps 其中ps 是指向字符数组的指针有不同的声明。:) 如果取消引用指针,您也可以看到差异。例如

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

      【讨论】:

        【解决方案5】:

        应改为使用指向未知字符边界数组的指针。这是每个人都应该做的事情,但 AFAIK 没有人这样做:

        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
        

        生活example.

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

        【讨论】:

        • 当今每个优秀的编译器都会将您的代码优化为与给定代码完全相同的程序集。你认为你的并发症可能会完成什么?它不像 C 那样检查(甚至知道)数组边界。
        • 没有。这样,您将无法传递指向单个字符的指针以及指向固定大小数组的指针,从而阻止您进行 UB 或提醒您有关数组大小的静态信息丢失。这是迄今为止最好的实现,所以请把事情做好。
        • 但我知道你生活在什么样的世界里——你所知道的一切都很流行的有限世界。好吧,正如您所见,流行的东西并不是唯一存在的。如果您不相信我这是一个有效的“C”代码,请检查语言文档或尝试编译它。对于后期 - 我已经提供了在线工作示例的链接。
        • 我从来没有说过你的代码是无效的 C——它是完全有效的。它只是过于复杂而且没用。
        猜你喜欢
        • 2021-01-26
        • 2018-04-05
        • 2012-03-19
        • 2021-11-26
        • 2017-09-03
        • 1970-01-01
        • 2016-01-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多