繁体   English   中英

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

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

当我这样做:

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"格式说明$0x80484b4存储为字符串文字, 0xc(%ebp)负责printf()使用的实际值:

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

请注意, AL (一个字节累加器,即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;
}

我想这会给你你的意图。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM