【问题标题】:printing with pointers and strings (C)使用指针和字符串打印 (C)
【发布时间】:2021-01-22 09:50:21
【问题描述】:

谁能解释为什么会这样打印出来:“d ce” 我想我明白为什么它打印出“d”但我不明白“ce”

#include <stdio.h>

int main(void){
    char s1[]="abcd",*cp;

    cp=s1+2;
    printf("%c %s\n",(*(++cp))++,s1+2);

    return 0;
}

【问题讨论】:

  • 将其写为多个表达式,然后使用调试器单步执行代码。混淆代码在这里是边界题外话。
  • 能否在标题中更好地描述问题。您当前的“使用指针和字符串打印”的标题非常通用,可能适用于数千个问题。

标签: c string pointers


【解决方案1】:

TL;DR

为了理解这段代码,您需要对序列点、未定义行为和其他“语言律师”内容有相当深入的 C 知识。如果不是这种情况,那么只需忘记编写或理解这样的人工垃圾代码,您就可以在此处停止阅读。下面是高级解释。


C11 6.5 状态:

如果标量对象上的副作用相对于不同的副作用是无序的 在同一标量对象或使用同一标量的值的值计算上 对象,行为未定义。如果有多个允许的排序 表达式的子表达式,如果这样一个未排序的一面,则行为未定义 效果发生在任何排序中。

通常这会呈现奇怪的代码,例如这种未定义的行为。但在这种情况下,1) s 不是标量而是聚合。标量是s[3] 左值。并且 2) 更改值的 )++ 增量不是未排序到使用同一表达式中的对象进行的另一个值计算。

(*(++cp))++s1+2 彼此之间是无序的,但这对于为未定义行为提出理由而言并不重要。

  • cp=s1+2; 这里cp 指向'c'
  • 这个表达式单独(*(++cp))++ 首先将指针增加一级以指向包含'd's[3]。然后它取消引用指针,以便我们获得值'd'
  • 然后使用后缀 ++ 将值加 1,在大多数符号表中表示 'e'
  • 相对于s1+2 期间发生的值计算,值的 ++ 更改没有排序,但这并不重要。
  • 在调用函数之前评估所有参数,并且在参数列表的完整评估之后有一个序列点。
  • printf 然后发现 %s 并取消引用字符串,这是一个值计算,但相对于前一个 )++ 进行排序。这将打印ce
  • 最后在函数返回之前还有另一个序列点。

所以代码实际上是定义良好的,尽管写得很糟糕。

【讨论】:

    【解决方案2】:

    希望内联 cmets 说明清楚。

    #include <stdio.h>
    
    int main()
    {
        char s1[]="abcd",s2[]="cdef",s3[5],*cp;
    
        printf("s1 = %p\n", (void*)s1); // prints start address of s1
        
        cp = s1+2; // cp points to 2 chars advanced of s1 i.e 'c'
        
        printf("cp = %p\n", (void*)cp); // prints address at 'c'
        //first increment cp to point to next location, which now points at 'd' from 'c'
        //the outer ++ increments 'd' to 'e' post-fix, so first prints 'd'
        printf("(*(++cp))++ = %c\n", (*(++cp))++); 
                                                   
        printf("s1 = %s\n", s1); // you will see "abce"
        printf("s1+2 = %s\n", s1+2); // 2 chars advanced of s1, i.e "ce"
        
        //printf("%c %s\n",(*(++cp))++,s1+2);
    
        return 0;
    }
    

    【讨论】:

    • 但请注意,函数参数的评估不是按照明确定义的顺序进行的。编译器不妨先评估s1+2 参数。所以你在这个例子中的执行顺序不一定与实际代码的执行顺序相对应。关于为什么这段代码几乎没有避开许多错误的解释是有序的。
    • 所以输出也可以是ce d
    • 不,代码是可移植的。但如果它是类似printf("%c %c\n",(*(++cp))++,s1[2]); 的东西,那么它会突然变成非常未定义的行为。因此,为什么我怀疑编写此代码的人只是出于运气才避开了错误。
    猜你喜欢
    • 2023-03-15
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 2012-02-06
    • 2019-06-08
    • 2013-03-09
    • 2013-05-31
    • 1970-01-01
    相关资源
    最近更新 更多