【问题标题】:C strings declarations [duplicate]C字符串声明[重复]
【发布时间】:2014-01-03 03:03:47
【问题描述】:

我正在学习 C,今天我坚持使用 C 中的“字符串”。基本上我知道 C 中没有像字符串这样的东西。 在 C 中,字符串是一个以 \0 结尾的数组字符。 到目前为止一切顺利。

char *name = "David";
char name[] = "David";
char name[5] = "David";

这就是混乱的开始。声明“字符串”的三种不同方式。您能否为我提供一个简单的示例,在哪些情况下使用哪种情况。我在网上阅读了很多教程,但仍然无法理解。

我在 stackoverflow 上阅读了这个 How to declare strings in C 问题,但仍然无法区分..

【问题讨论】:

    标签: c string pointers


    【解决方案1】:

    在第一个示例中,您声明了一个指向变量的指针:

    // A variable pointer to a variable string (i.e. an array of 6 bytes). 
    char *pName = "David"; 
    

    此时可以修改'D', 'a', 'v', 'i', 'd', '\0'占用的6个字节:

    pName[0] = 'c';
    *pName = 'c';
    *(pName+0) = 'c';
    strcpy(pName, "Eric"); // Works well
    

    但只有那 6 个字节:

    // BUG: Will overwrite 2 random bytes located after \0 in RAM.
    strcpy(pName, "Fredrik"); 
    

    可以在运行时更改指针以指向另一个变量字符串,例如

    pName = "Charlie Chaplin";
    

    然后可以修改

    pName[0] = 'c';
    *pName = 'c';
    *(pName+0) = 'c';
    // OK now, since pName now points to the CC array
    // which is 16 bytes located somewhere else:
    strcpy(pName, "Fredrik"); 
    

    正如其他人所说,您通常会在指针情况下使用const char *,这也是使用字符串的首选方式。原因是编译器会帮助您解决最常见(也很难找到)的内存垃圾错误:

    // A variable pointer to a constant string (i.e. an array of 6 constant bytes). 
    const char *pName = "David"; 
    // Pointer can be altered runtime to point to another string e.g.
    pName = "Charlie";
    // But, the compiler will warn you if you try to change the string
    // using any of the normal ways:
    pName[0] = 'c';       // BUG
    *pName = 'c';         // BUG
    *(pName+0) = 'c';     // BUG
    strcpy(pName, "Eric");// BUG
    

    其他方式,使用数组,灵活性较低:

    char aName[] = "David"; // aName is now an array in RAM.
    // You can still modify the array using the normal ways:
    aName[0] = 'd';
    *aName = 'd';
    *(aName+0) = 'd';
    strcpy(aName, "Eric"); // OK
    
    // But not change to a larger, or a different buffer
    aName = "Charlie"; // BUG: This is not possible.
    

    同样,常量数组对您的帮助更大:

    const char aName[] = "David"; // aName is now a constant array.
    // The compiler will prevent modification of it:
    aName[0] = 'd';       // BUG
    *aName = 'd';         // BUG
    *(aName+0) = 'd';     // BUG
    strcpy(aName, "Eric");// BUG 
    // And you cannot of course change it this way either:
    aName = "Charlie"; // BUG: This is not possible.
    

    使用指针与数组声明的主要区别在于sizeof()的返回值:sizeof(pName)是指针的大小,通常为4。sizeof(aName)返回数组的大小,即长度字符串+1。 如果在函数内部声明变量,则最重要的是,尤其是当字符串很长时:它占用了更多宝贵的堆栈。因此,通常避免数组声明。 将变量传递给使用sizeof() 的宏时也很重要。此类宏必须提供预期的类型。

    如果你想这样做也很重要。交换字符串。声明为指针的字符串是直截了当的,需要 CPU 访问更少的字节,只需移动指针的 4 个字节:

    const char *pCharlie = "Charlie";
    const char *pDavid = "David";
    const char *pTmp;
    
    pTmp = pCharlie;
    pCharlie = pDavid;
    pDavid = pTmp;
    

    pCharlie 现在是“David”,pDavid 现在是“Charlie”。

    使用数组,你必须为最大的字符串提供足够大的临时存储空间,并使用 strcpy(),它会占用更多 CPU,在字符串中逐字节复制。

    最后一种方法很少使用,因为编译器会自动计算出 David 需要 6 个字节。不需要告诉它什么是显而易见的。

    char aName[6] = "David";
    

    但是,它有时用于数组必须是固定长度的情况,与它的内容无关,例如在二进制协议或文件中。在这种情况下,如果将来有人不小心从字符串中添加或删除字符,手动添加限制可能会有所帮助,以便从编译器获得帮助。

    【讨论】:

      【解决方案2】:
      • 第一个char *name = "David"; 是字符串文字,位于内存的只读部分。你不能对它做任何修改。最好写

        const char *name = "大卫";

      • 第二个char name[] = "David"; 是一串6 字符,包括'\0'。可以修改。

      • char name[5] = "David"; 调用未定义的行为。 "David" 是一个 6 个字符的字符串(包括终止 '\0')。您需要一个包含 6 个字符的数组来存储它。

        字符名[6] = "大卫";

      延伸阅读:C-FAQ 6. Arrays and Pointers.

      【讨论】:

      • 第二个不是5个chars的数组。
      • @H2CO3;真的。现已编辑。
      • @hacks 对于第三个示例,“调用未定义的行为”是什么意思?
      • @moemoe 他的意思是this
      • 如果我理解您对第一个示例的回答,它在字符串不会改变的情况下很有用。例如,为数据库声明主机名 - const char *hostname = "localhost" 我是正确的吗?
      【解决方案3】:

      link 提供了一个很好的解释。

      char[] 指的是数组,char* 指的是指针,它们不是一回事。

      char a[] = "hello"; // array
      char *p = "world"; // pointer
      

      根据标准附件 J.2/1,以下情况为未定义行为:

      - 程序尝试修改字符串文字 (6.4.5)。

      6.4.5/5 说:

      在翻译阶段 7 中,将一个字节或零值代码附加到 由字符串文字产生的每个多字节字符序列 或文字。

      因此,您实际上需要一个包含六个元素的数组来说明 NUL 字符。

      【讨论】:

        猜你喜欢
        • 2021-12-12
        • 2023-03-12
        • 2012-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-03
        • 1970-01-01
        相关资源
        最近更新 更多