圆括号中的 C 函数

C functions in round brackets

我刚刚注意到,在 C 中可以将 运行 函数放在以逗号分隔的圆括号中。我想知道它是否等同于正常地 运行ning 它们(没有外部圆括号,用分号分隔)以及这种语法或行为的起源(这是作为功能添加的,它是否有特殊名称,是有想要的副作用,这是否实际使用等)。多年来,作为函数调用的一部分,我如何每天编写数千次这个序列,这很有趣,但我什至不记得曾经看过这个独立的。

example.c

#include <stdio.h>

int main(int argc, char *argv[])
{
    (printf("Hello "), printf("World!\n"));

    return 0;
}

非常感谢您分享您的知识,Moritz

where this syntax or behavior originates from

看看comma operator

printf("Hello ")

首先计算,但其结果被丢弃。

printf("World!\n")
接下来计算

,它的return值决定了整个表达式return的值

(printf("Hello "), printf("World!\n"));

与此同时,您可以看到 "Hello World!" 打印到控制台,这本可以通过更简单的方式完成:

printf("Hello World!\n");

does this have a special name

我会使用逗号运算符将其称为分组语句

are there wanted side-effects

当您想要创建序列点时它会很有用。

is this actually used

例如,如果您尝试

int success = (printf("Hello "), printf("World!\n"));

您无法真正知道第一个 printf 是否成功。这是以多种方式使用逗号运算符的糟糕示例。

Comma operator:(Comma operator)

逗号运算符表达式的形式为

lhs , rhs
哪里

lhs - 任何表达式 rhs - 除另一个逗号运算符以外的任何表达式(换句话说,逗号运算符的结合性是从左到右) 首先,计算左操作数 lhs 并丢弃其结果值。

然后,一个序列点发生,这样lhs的所有副作用就完成了。

然后,对右操作数 rhs 求值,其结果由逗号运算符 return 编辑为非左值。

Code 1:

#include <stdio.h>

int main()
{
    int n;

    n= (printf("Hello"),printf(", World!"));

    printf("\nn = %d",n);

    return 0;
}

此处 prints 的 return 值将是 n=(5,8); 因此逗号运算符丢弃 5 并且 8 将分配给 n。

code 2(without parentheses):

#include <stdio.h>

int main()
{
    int n;

    n= printf("Hello"),printf(", World!");

    printf("\nn = %d",n);

    return 0;
}

这里第一个n =5,8会被赋值给5,因为赋值运算符的优先级高于逗号,右边的printfreturn会被丢弃

if you want to get both printf return value

#include <stdio.h>

int main()
{

    int n,m;

    n= printf("Hello"),m=printf(", World!");

    printf("\nn = %d,m = %d",n,m);

    return 0;
}

这里n=5,m=8;

谢谢。

"I wonder whether it is equivalent to just running them normally..."

由于逗号分隔的子表达式和逗号分隔的表达式从左到右求值之间有一个sequence point,因此该方法与单独语句(使用时)不应有显着差异就像你的例子一样)。

6.5.17 Comma operator

2 The left operand of a comma operator is evaluated as a void expression; there is a sequence point between its evaluation and that of the right operand. Then the right operand is evaluated; the result has its type and value.

Source: ISO/IEC 9899:2011 (C11)


Annex C

1 The following are the sequence points described in 5.1.2.3:

...

Between the evaluations of the first and second operands of the following operators: logical AND && (6.5.13); logical OR || (6.5.14); comma , (6.5.17).

Source: ISO/IEC 9899:2011 (C11), Annex C


两种方法的汇编代码也是等价的:

逗号分隔的子表达式:

puts@plt:
 jmp QWORD PTR [rip+0x2fe2] # 404018 <puts@GLIBC_2.2.5>
 push 0x0
 jmp 401020 <.plt>
printf@plt:
 jmp QWORD PTR [rip+0x2fda] # 404020 <printf@GLIBC_2.2.5>
 push 0x1
 jmp 401020 <.plt>
_dl_relocate_static_pie:
 repz ret 
 nop WORD PTR cs:[rax+rax*1+0x0]
 nop DWORD PTR [rax+0x0]
main:
 push rbp
 mov rbp,rsp
 sub rsp,0x10
 mov DWORD PTR [rbp-0x4],edi
 mov QWORD PTR [rbp-0x10],rsi
 mov edi,0x402004
 mov eax,0x0
 call 401040 <printf@plt>
 mov edi,0x40200b
 call 401030 <puts@plt>
 mov eax,0x0
 leave 
 ret 
 nop WORD PTR cs:[rax+rax*1+0x0]
 nop DWORD PTR [rax+rax*1+0x0]

代码:

#include <stdio.h>

int main(int argc, char *argv[])
{
    (printf("Hello "), printf("World!\n"));

    return 0;
}

单独的语句:

puts@plt:
 jmp QWORD PTR [rip+0x2fe2] # 404018 <puts@GLIBC_2.2.5>
 push 0x0
 jmp 401020 <.plt>
printf@plt:
 jmp QWORD PTR [rip+0x2fda] # 404020 <printf@GLIBC_2.2.5>
 push 0x1
 jmp 401020 <.plt>
_dl_relocate_static_pie:
 repz ret 
 nop WORD PTR cs:[rax+rax*1+0x0]
 nop DWORD PTR [rax+0x0]
main:
 push rbp
 mov rbp,rsp
 sub rsp,0x10
 mov DWORD PTR [rbp-0x4],edi
 mov QWORD PTR [rbp-0x10],rsi
 mov edi,0x402004
 mov eax,0x0
 call 401040 <printf@plt>
 mov edi,0x40200b
 call 401030 <puts@plt>
 mov eax,0x0
 leave 
 ret 
 nop WORD PTR cs:[rax+rax*1+0x0]
 nop DWORD PTR [rax+rax*1+0x0]

代码:

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Hello ");
    printf("World!\n");

    return 0;
}