【问题标题】:Passing a string literal as a function parameter defined as a pointer将字符串文字作为定义为指针的函数参数传递
【发布时间】:2013-01-04 22:11:43
【问题描述】:

我正在阅读 Kernighan 和 Richie 的The C Programming Language中关于数组和指针的章节。

他们举了一个例子:

/* strlen:  return length of string s */
int strlen(char *s)
{
    int n;

    for (n = 0; *s != '\0'; s++)
        n++;
    return n;
}

然后说:

“由于s是一个指针,递增它是完全合法的; s++ 对调用 strlen 的函数中的字符串没有影响,而只是增加 strlen 指针的私有副本。这意味着像这样的调用

strlen("hello, world");  /* string constant */
strlen(array);           /* char array[100]; */
strlen(ptr);             /* char *ptr; */

一切正常。”

除了第一个调用示例之外,我觉得我理解了所有这些:为什么或如何将字符串文字 "hello, world" 视为 char *s?这是一个怎样的指针?该函数是否将此字符串文字分配为其局部变量*s 的值,然后使用s 作为数组名称/指针?

【问题讨论】:

  • 关于字符串、数组、指针及其关系的一些非常好的问题/答案。 Just one example。恭喜你至少对这个主题进行了一些研究,这比几乎任何其他人在发布有关该主题的问题之前所做的都要大得多。写得很好的问题。
  • 另请注意,K&R 在良好的编程实践方面缺乏很多。将字符串传递给不修改它的函数时,应将参数声明为const char* s。这在传递字符串文字时尤其重要。如果您的函数尝试写入 *s,您将调用未定义的行为并且程序可能会崩溃。
  • 感谢大家把这篇文章写成这么棒的帖子!很难/不可能选择一个接受的答案。带着你的答案,我回去阅读了本书中这个例子之前的部分。我认为我之前没有注意到的关键句子是By definition, the value of a variable of expression of type array is the address of element zero of the array。因此,当我在函数中使用字符串文字作为参数时,我传递的不是字符串文字本身,而是“类型数组”,它是指向字符串文字中第 0 个索引的指针,它是一个数组。跨度>
  • 谢谢伦丁!实际上,我在在这里发帖之前尝试自己回答这个问题时遇到了这个问题。更多关于你提到的内容(使用const char *s而不是char *s)可以在securecoding.cert.org/confluence/display/seccode/…找到。

标签: c pointers


【解决方案1】:

要了解像“Hello World”这样的字符串是如何转换为指针的,重要的是要了解,字符串实际上是十六进制数据,从地址开始一直移动直到找到NULL

也就是说,每个字符串常量,例如“Hello World”,都存储在内存某处

可能性是:

0x10203040 : 0x48 [H]
0x10203041 : 0x65 [e]
0x10203042 : 0x6C [l]
0x10203043 : 0x6C [l]
0x10203044 : 0x6F [o]
0x10203045 : 0x20 [' ']
0x10203046 : 0x57 [W]
0x10203047 : 0x6F [o]
0x10203048 : 0x72 [r]
0x10203049 : 0x6C [l]
0x1020304A : 0x64 [d]
0x1020304B : 0x00 [\0]

所以,当使用内存中的上述值调用此函数时,[左侧是地址后跟':',右侧是字符的ascii值]

int strlen(const char *s)
{
    int n;

    for (n = 0; *s != ′\0′; s++)
        n++;
    return n;
}

strlen("Hello World");

那时,传递给strlen的是值0x10203040,它是字符数组的第一个元素的地址。

注意,地址是按值传递的。因此,strlen 拥有自己的“Hello World”地址副本。从n = 0 开始,直到我在内存中找到\0,我递增n 以及s 中的地址(然后递增到0x10203041)等等,直到找到\0在地址0x1020304B 并返回字符串长度。

【讨论】:

  • 谢谢 Aniket。将参数签名更改为const char *s 是否会阻止此函数或具有此类签名的任何函数在本地操作指针变量? const 本质上是否将此指针转换为数组? K&R 指出“必须牢记数组名称和指针之间的一个区别。点是变量,因此 pa=a 和 pa++ [其中 pa 是指针] 是合法的。但数组名称不是变量; 像 a=pa 和 a++ 这样的结构是非法的[其中 a 是一个数组名]。"
  • 不,const 仅帮助您防止意外修改数组@dimadima 的内容。数组在内部被视为指针,但指针在内部不被视为数组。数组名称是数组开头的标签。下标运算符 [i] 从数组的开头取消引用数组中的 ith 值。
  • ****打印并贴在墙上****
  • 存储在某处是什么意思?是存放在栈中还是代码段中?
【解决方案2】:

"hello, world"

char 的数组(类型为char[13])。表达式中char 数组的值是指向char 的指针。指针指向数组的第一个元素(即"hello, world"的值为&"hello, world"[0])。

【讨论】:

    【解决方案3】:

    注意:

    • 指针(基本上)是指向内存地址的值。
    • "hello, word" 这样的静态字符串存储在内存中的某处

    因此,指针可以像指向存储在内存中的任何其他(动态)结构(如字符数组)一样简单地指向静态字符串。与其他提供的示例确实没有区别。

    【讨论】:

      【解决方案4】:

      函数是否将此字符串文字分配为其局部变量 *s 的值 然后使用 s 作为数组名/指针?

      是的

      【讨论】:

      • "Yes",从某种意义上说,字符串文字是一个数组。由于by definition, the value of a variable of expression of type array is the address of element zero of the array,数组的值是它的第一个元素的地址。第一个地址作为局部函数变量分配给s
      【解决方案5】:

      正如它在同一页的第一段(第 99 页,K&R2)中所说:

      "根据定义,数组类型的变量或表达式的值是 数组元素零的地址。"

      “hello, world”的值是“h”的地址。

      【讨论】:

      • 或者换句话说:C 中的文字字符串是用'\0' 分隔的字节,并通过它们的地址找到。因此,当您将"abc" 传递给函数时,实际上是将地址(char * 指针)作为参数传递给函数。在 C 中,字符串文字也应该被视为只读。
      【解决方案6】:
      char str[] = "Hello, world";
      strlen(str);
      

      C 中的字符串是以 NULL('\0')结尾的字符数组,这意味着它应该在内存中的某个位置。

      那么上面发送存储的字符串和下面直接发送有什么区别

      strlen("Hello, World");
      

      答案都是一样的,但是字符串的存储位置和处理方式,这里是编译器和堆栈。

      编译器在编译时将字符串压入堆栈并发送堆栈中的起始地址(char*),调用函数看到指针并访问字符串。

      编译器还在函数存在后添加代码以将堆栈恢复到正确的位置,从而删除创建的临时字符串 注意:以上是依赖于编译器的实现,但大多数编译器都是这样工作的

      【讨论】:

        猜你喜欢
        • 2019-04-24
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        • 2020-11-08
        • 1970-01-01
        • 1970-01-01
        • 2020-05-01
        • 1970-01-01
        相关资源
        最近更新 更多