【问题标题】:Does the treatment of string literals depend on the left hand side of an assignment?字符串文字的处理是否取决于赋值的左侧?
【发布时间】:2021-11-02 22:54:34
【问题描述】:

试图围绕“C 编程语言”中的第 5.7 章到第 5.9 章,处理多维数组、指针数组等,我得出以下观察结果:

如果在下面的代码中 foo 被声明为一个指向 char 数组的指针,并且我稍后想为它分配一个 char 类型的指针,我必须在字符串文字前加上一个 & 符号。但是,如果我将bar 声明为指针,则在没有& 符号的情况下也可以进行相同的操作。

char (*foo)[3]; // creates a single pointer to a char array of size 3
char *bar;      // creates a single pointer to char

int main()
{
    foo = &"AB";
    bar = "AB";
    
    return 0;
}

反汇编(64 位 Macho-O)似乎(至少在初学者看来)对两个任务执行相同的操作:

Disassembly of section __TEXT,__text:

0000000100003f80 _main:
100003f80: 55                           push    rbp
100003f81: 48 89 e5                     mov rbp, rsp
100003f84: 31 c0                        xor eax, eax
100003f86: 48 8d 0d 7b 00 00 00         lea rcx, [rip + 123]        // address of 'bar'
100003f8d: 48 8d 15 6c 00 00 00         lea rdx, [rip + 108]        // address of 'foo'
100003f94: c7 45 fc 00 00 00 00         mov dword ptr [rbp - 4], 0
100003f9b: 48 8d 35 08 00 00 00         lea rsi, [rip + 8]          // address pointing to 'AB'-String
100003fa2: 48 89 32                     mov qword ptr [rdx], rsi    // store address of 'AB' in 'foo'
100003fa5: 48 89 31                     mov qword ptr [rcx], rsi    // store address of 'AB' in 'bar'
100003fa8: 5d                           pop rbp
100003fa9: c3                           ret

Disassembly of section __TEXT,__cstring:

0000000100003faa __cstring:
100003faa: 41 42                        <unknown>
100003fac: 00                           <unknown>


Disassembly of section __DATA,__common:

0000000100004000 _foo:
...

0000000100004008 _bar:
...

由于我正在阅读的这本书是我与 C 的第一次接触,我担心我在这里遗漏了一些明显的东西。如果我在这两种情况下都需要&amp;-symbol,这不是更合乎逻辑吗?

【问题讨论】:

  • "string pointer" 听起来像使用 string 就好像它是一种类型 - 它不是。 “我稍后要为其分配一个字符串指针”,不如“我稍后要为其分配一个 char 指针”更好。

标签: arrays c pointers string-literals


【解决方案1】:

C 中的字符串文字的类型为“char 数组”,其大小等于包括终止空字节在内的字符数。这意味着"AB" 的类型为char [3]

大多数 情况下,当在表达式中使用数组时,它会衰减为指向第一个元素的指针。这就是bar = "AB" 的情况。右侧的字符串常量衰减为类型char *,可以直接赋值给bar

这种衰减不会发生的一种情况是数组是&amp; 运算符的主题。因此,获取char [3] 类型数组的地址会产生一个char (*)[3] 类型的指针,它与foo 的类型匹配。

【讨论】:

  • @attachedy 在表达式**foo+1 中,指针foo 首先被取消引用,产生一个char [3] 类型的数组。然后将该数组转换为char * 类型的指针,该指针再次被取消引用,产生数组中的第一个字符,即'A'。然后将值 1 添加到该字符,产生字符 'B'。例如,如果您将字符串常量更改为 "AX",您仍然会得到 'B'
  • @attachedy 在这种情况下,它与(*foo)[1] 相同,它首先取消引用指针以产生一个数组,然后从数组中检索索引 1 处的元素。
【解决方案2】:

好的,假设"AB" 的每个实例都存储在内存位置0xDEADBEEF。表达式"AB" 是一个数组表达式,在大多数情况下将返回其第一个元素的地址。两个值得注意的例外是 &amp;sizeof 运算符。在&amp;"AB"sizeof "AB" 中,数组将被视为一个数组,而不是其第一个元素的地址。

char (*foo)[3] = &amp;"AB"; 将(在大多数架构上)初始化foo 以具有值0xDEADBEEF,即"AB" 的地址。在表达式&amp;"AB"中,数组"AB"被视为一个数组,&amp;返回它的地址。

char *bar = "AB"; 将初始化bar 以指向A,猜猜看,在大多数架构上,地址与"AB" 相同,即0xDEADBEEF。这里数组"AB" 被转换为指向其第一个元素的指针。

所以foobar 都指向同一个内存位置,但指向不同的对象。 foo 指向 3 个字节的序列,但 bar 指向单个字节。

由于foo 指向一个数组,表达式*foo 就是数组foo 指向的。在大多数情况下,数组表达式产生一个指向数组中第一个元素的指针,因此在表达式**foo 中,子表达式*foo 是数组"AB"(类型为char[3]),但被转换为指针到Achar * 类型)。 *foo 前面的第二个 * 解引用指向 A 的指针,并返回 A

总结一下:

  • foo 是指向char [3]; 的指针,其值为0xDEAFBEEF

  • *foo 是数组foo 指向的"AB",类型为char [3]

  • 在大多数情况下,*foo 被转换为 char *,仍然是 0xDEADBEEF

  • 作为上一点的例外,在表达式&amp;*foosizeof *foo 中,*foo 不会被转换,但仍然是"AB"

  • 像这样**foo取消引用*foo,将返回Afoo==0xDEADBEEF*foo=="AB"-&gt;0xDEADBEEF==bar**foo==*0xDEADBEEF==*bar=='A'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 2022-01-23
    • 2012-03-01
    • 1970-01-01
    • 2019-09-07
    相关资源
    最近更新 更多