为什么使用 char agrv 而不是 char **argv 作为 main 的参数会导致以下输出

Why does using char agrv instead of char **argv as the argument of main cause the following output?

本文关键字:char 参数 输出 main argv agrv 为什么 作为      更新时间:2023-10-16

当我这样做时:

int main(int agrc, char argv)
{
    printf("%d", argv);
    return 0;
}

当我从命令行运行程序时,我得到以下输入:

$ prog_name 0
0
$ prog_name (from 0-7 characters)
48
$ prog_name 12345678
56
$ prog_name 1234567812345678
64
// and so on...

那么这些值从何而来,为什么它们会增加 8?

当我有这个时会发生什么:

int main(int agrc, char argv[])

您的输出可能是"普通"argv参数的地址,该地址是隐式转换的解释,请参阅下面的注释作为char。换句话说,我怀疑你所拥有的相当于:

int main(int agrc, char **argv)
{
    printf("%d", (char) argv);
    return 0;
}

在我的机器(CentOS 6 32 位)上,拆解的目标代码如下:

   0x080483c4 <+0>: push   %ebp
   0x080483c5 <+1>: mov    %esp,%ebp
   0x080483c7 <+3>: and    $0xfffffff0,%esp
   0x080483ca <+6>: sub    $0x10,%esp
   0x080483cd <+9>: mov    0xc(%ebp),%eax
   0x080483d0 <+12>:    movsbl %al,%eax
   0x080483d3 <+15>:    mov    %eax,0x4(%esp)
   0x080483d7 <+19>:    movl   $0x80484b4,(%esp)
   0x080483de <+26>:    call   0x80482f4 <printf@plt>

以及您发布的原始代码:

   0x080483c4 <+0>: push   %ebp
   0x080483c5 <+1>: mov    %esp,%ebp
   0x080483c7 <+3>: and    $0xfffffff0,%esp
   0x080483ca <+6>: sub    $0x20,%esp
   0x080483cd <+9>: mov    0xc(%ebp),%eax
   0x080483d0 <+12>:    mov    %al,0x1c(%esp)
   0x080483d4 <+16>:    movsbl 0x1c(%esp),%eax
   0x080483d9 <+21>:    mov    %eax,0x4(%esp)
   0x080483dd <+25>:    movl   $0x80484b4,(%esp)
   0x080483e4 <+32>:    call   0x80482f4 <printf@plt>

在这两种情况下$0x80484b4 "%d"格式说明符存储为字符串文本,0xc(%ebp)负责printf()使用的实际值:

(gdb) x/db 0xbffff324
0xbffff324: -60
(gdb) p $al
$3 = -60

请注意,AL(一个字节累加器,即.part EAX的一个字节)只"获取"$ebp+0xc地址的第一个字节(我的CPU是小字节序,所以它实际上是LSB)。这意味着(char)转换会"切断"argv地址。

因此,您可能会观察到这些数字中的每一个都有log2(n)未设置的最低有效位。这是由于指针类型对象的对齐要求。通常对于 32 位 x86 计算机alignof(char **) == 4 .

正如评论中已经指出的那样,您违反了 C 标准,因此这是 UB 的一个例子。

来自 C 标准,关于main()的签名

该实现没有声明此函数的原型。

因此,如果您传递不同类型的参数,编译器不会有任何问题。

在您的代码中,

int main(int agrc, char argv)

不是建议用于main()的签名。它应该是

int main(int agrc, char* argv[])

或者,至少

int main(int agrc, char** argv)

否则,在托管环境中,行为未定义。您可以在C11标准第 5.1.2.2.1 章中查看更多内容。

如您所见,在您的例子中,您正在使第二个参数成为char类型。根据标准规范,

如果 argc 的值大于零,则argv[0]argv[argc-1](含)的数组成员应包含指向字符串的指针,....

因此,在这里,提供的0作为指向字符串的指针传递给main(),该字符串在char中被接受,这不是定义的行为。

堆栈上有一个字符串指针,但您在那里用字符声明了main,然后将其打印为小数。 该字符串的内存地址不可预测,因此您将获得不可预测的输出。

试试这个:

int main( int argc, char* argv[] )
{
    printf( "%s", argv[1] );
    return 0;
}

我想这会给你你想要的。