【问题标题】:Printf output of pointer string explanation from an interviewPrintf 输出的指针字符串解释来自采访
【发布时间】:2018-04-17 17:10:11
【问题描述】:

我进行了一次面试,我收到了这段代码,并询问了这些 printf 语句中的每一个的输出是什么。

我有我作为 cmets 的答案,但我不确定其余的。

谁能解释语句 1、3 和 7 的不同输出以及为什么?

谢谢!

#include <stdio.h>

int main(int argc, const char * argv[]) {

    char *s = "12345";

    printf("%d\n", s);      // 1.Outputs "3999" is this the address of the first pointer?
    printf("%d\n", *s);     // 2.The decimal value of the first character
    printf("%c\n", s);      // 3.Outputs "\237" What is this value?
    printf("%c\n", *s);     // 4.Outputs "1"
    printf("%c\n", *(s+1)); // 5.Outputs "2"
    printf("%s\n", s);      // 6.Outputs "12345"
    printf("%s\n", *s);     // 7.I get an error, why?
    return 0;
}

【问题讨论】:

  • 我必须指出——这次采访是一个悲伤的笑话。你面试的公司招聘的是编译器,而不是软件工程师。
  • 这是一个基本问题,看看我是否了解 printf 以及不同类型和调用的基础知识。
  • 我会建议你尝试另一个工作场所的伙伴。我很乐意详细解释 SO 的原因。

标签: c string pointers printf


【解决方案1】:

这个电话

printf("%d\n", s); 

具有未定义的行为,因为指针使用了无效的格式说明符。

这个电话

printf("%d\n", *s);

输出字符'1'的内部代码(例如ASCII码)。

这个电话

printf("%c\n", s);

由于使用了无效的格式说明符和指针而导致行为未定义。

这些电话

printf("%c\n", *s);
printf("%c\n", *(s+1));

有效。第一个输出字符'1',第二个输出字符'2'

这个电话

printf("%s\n", s);

正确,输出字符串"12345"

这个电话

printf("%s\n", *s);

无效,因为char 类型的对象使用了无效的格式说明符。

【讨论】:

    【解决方案2】:
    1. 此代码是未定义行为 (UB)。您正在传递一个指针,该函数需要一个 int 值。例如,在 64 位架构中,指针是 64 位,int 是 32 位。您可以打印截断的值。

    2. 您正在传递第一个 char 值(由编译器自动转换为 int)并以十进制打印。可能你得到了49'1' 的 ASCII 代码。这是合法使用,但要小心意外,因为如果你的平台 char 实现是 signed,你可能会得到负值。

      李>
    3. 您正在打印重新解释为char 值的传递指针。未定义的行为,因为您无法将指针转换为 char 值。

    4. 您将s 的指定值打印为char,因此您将获得字符串"12345" ('1') 的第一个字符。

    5. 您正在打印由s 指向的第一个char 的下一个,因此您将获得字符串的第二个字符 ('2')。

    6. 您正在打印s 指向的字符串,因此您得到了整个字符串。这是合法的,也是打印字符串的常用方法。

    7. 您传递的字符串的第一个字符被解释为指向要打印的空终止字符串的指针(它不是)。这又是未定义的行为。您正在将 char 值重新解释为指向空终止字符串的指针。在这种情况下,SIGSEGV 很常见,(但不保证 :) )当程序在到达终止字符串的假定空字符之前尝试访问未分配的内存时发送信号(但它可以在方式,只是打印垃圾)。

    【讨论】:

      【解决方案3】:

      第 7 行失败,因为需要 C 样式字符串作为输入,而您正在放置一个字符。

      看看:

      【讨论】:

        【解决方案4】:

        我使用以下online C compiler 来运行您的代码, 结果如下:

        1. 4195988            - undefined behaviour (UB), manifesting here as the address
                                of the char array as you stated (for a 64 bit address you might or
                                might not get truncation)
        2. 49                 - ASCII value of '1'
        3. �                  - undefined behaviour, manifesting here as unsupported ASCII value
                                for a truncation of the address of the array of chars
                                (placing 32-bit address into a char - assuming a 32-bit system)
        4. 1                  - obvious
        5. 2                  - obvious
        6. 12345              - obvious
        7. Segmentation fault - undefined behaviour, trying to place the first char
                                of a char array into a string reserved position
                                (placing char into a string)
        

        注意第 3 点:我们可以推断运行时发生的事情。 在问题中提供的具体示例中 -

        printf("%c\n", s);      // 3.Outputs "\237". What is this value?
        

        这是处理 UB 时与硬件/编译器/操作系统相关的行为。

        为什么?由于输出“\237” -> 这意味着在执行此代码的特定硬件系统下截断

        请看下面的解释(假设——32位系统):

        char *s = "12345"; // Declaring a char pointer pointing to a char array
        char c = s; // Placement of the pointer into a char - our UB
        printf("Pointer to character array: %08x\n", s); // Get the raw bytes
        printf("Pointer to character: %08x\n", c); // Get the raw bytes
        printf("%c\n", s); // place the pointer as a character
                           // display is dependent on the ASCII value and the OS
                           // definitions for 128-255 ASCII values
        

        输出:

        Pointer to character array: 004006e4  // Classic 32-bit pointer
        Pointer to character: ffffffe4        // Truncation to a signed char
                                              // (Note signed MSB padding to 32 bit display)
        �                                     // ASCII value E4 = 228 is not displayed properly
        

        最后的 printf 命令等价于char c = s; printf("%c\n", c);。 为什么?感谢截断。

        一个合法的 ASCII 字符输出的附加示例:

            char *fixedPointer = 0xABCD61; // Declaring a char pointer pointing to a dummy address
            char c = fixedPointer; // Placement of the pointer into a char - our UB
            printf("Pointer to 32-bit address: %08x\n", fixedPointer); // Get the raw bytes
            printf("Pointer to character: %08x\n", c); // Get the raw bytes
            printf("%c\n", fixedPointer);
        

        以及实际输出:

        Pointer to 32-bit address: 00abcd61
        Pointer to character: 00000061
        a
        

        【讨论】:

        • 即使假设为“32 位系统”,char *s = "12345"; printf("Pointer to character array: %08x\n", s); 等代码仍然是未定义的行为,而不是可靠的合法 ASCII 字符输出。这样的代码缺乏未来的正确性和可维护性,即使它最初按需要“工作”。应该明确标识为 UB——尤其是在采访问答中。 IOWs,对 UB 如何发生的详细解释,在采访中没有强调代码从根本上存在缺陷的重要性。
        • @chux :核心原理很简单——不要编码UB。永远不要编写任何源自 UB 的代码。我已经看到了由 UB 引起的可怕错误/副作用,并且一直试图与之抗争。时期。新队。在求职面试中,如果被问及某种行为,在明确表示确实是 UB 之后,可以通过解释 UB 造成的原因来表现出更深入的理解。
        猜你喜欢
        • 2016-04-15
        • 1970-01-01
        • 1970-01-01
        • 2013-01-01
        • 1970-01-01
        • 2011-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多