荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: bakey (深海的鱼爱上会潜水的猫), 信区: Program
标  题: [合集]发现一个很奇怪的C的语法
发信站: 荔园晨风BBS站 (2005年06月01日11:30:53 星期三), 站内信件

☆   1  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 12:22:46 2004

#include <stdio.h>

void main()
{
        int one = 1;

        printf("%d\n", (int)(1<<31));
        printf("%d\n", (int)(one<<31));

        printf("%d\n", (int)(1<<32));
        printf("%d\n", (int)(one<<32));

        printf("%d\n", (int)(1<<33));
        printf("%d\n", (int)(one<<33));

        printf("\n");
}

这个程序在VC下运行结果为:

-2147483648
-2147483648
0
1
0
2

在TC下也有类似的输出:

0
0
0
1
0
2

是不是很奇怪??



☆   2  ──────────── 我是分割线 ─────────────────☆
发信人: jjk (base), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 13:45:49 2004

有warning的, 越界了.

# gcc -Wall -o strange strange.c
strange.c:4: warning: return type of `main' is not `int'
strange.c: In function `main':
strange.c:10: warning: left shift count >= width of type
strange.c:11: warning: left shift count >= width of type
strange.c:13: warning: left shift count >= width of type
strange.c:14: warning: left shift count >= width of type




☆   3  ──────────── 我是分割线 ─────────────────☆
发信人: littlebao (爱拼才会赢), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 15:12:42 2004

应该是对常量进行了左移操作,所以移了32位后为0了

而对变量在四个字节内进行了循环左移




☆   4  ──────────── 我是分割线 ─────────────────☆
发信人: MFC (大四,差不多毕业了!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 16:35:27 2004

VC是32位编译器,左移了31位,1到了最位,而在
二进制中,机器数的最高位为符号位,0表示正数,
1表示负数.故出现了负数.



☆   5  ──────────── 我是分割线 ─────────────────☆
发信人: littlebao (爱拼才会赢), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 16:46:08 2004

这个并不是编译器的问题,而是用int做了转换

int是4个字节即32位,如果用char只有8位




☆   6  ──────────── 我是分割线 ─────────────────☆
发信人: MFC (大四,差不多毕业了!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 16:58:59 2004

但是如果你不用int做转换,结果也是一样的,
如果是int问题,怎么解释在VC与TC结果不同呢?



☆   7  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 17:21:47 2004

应该不是循环左移,因为int two = 2; two<<31是0而不是1,
我想可能是对变量而言,<<右边的参数要对左边参数位数取模,
而对常量而言则不用。



☆   8  ──────────── 我是分割线 ─────────────────☆
发信人: littlebao (爱拼才会赢), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 17:32:57 2004

从以下程序的结果看应该是循环左移的

void main()
{
            int one = 1;
            for(int i = 0 ; i < 64 ; ++i)
            printf("%d\n" , (int)(one << i)) ;
}

? 在 tec (TO BE A BETTER MAN!) 的大作中提到: 】



☆   9  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 17:38:43 2004

                       ~~ 是因为1的关系,你换256看看



☆  10  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 18:05:28 2004

                        这里错了,是固定对32取模的,我试过了
                            而且网上有人说C#也采取这种规则的



☆  11  ──────────── 我是分割线 ─────────────────☆
发信人: jjksam (base), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 18:43:00 2004

是算术左移。

#include <stdio.h>

int main()
{
    int one=1;
    printf("%d\n", (int)(one<<31));
    return 0;
}
----------------------------------------------------
gcc汇编代码
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        movl    $1, -4(%ebp)
        subl    $8, %esp
        movl    -4(%ebp), %eax
        sall    $31, %eax
       ;~~~~~~~~~~~~~~~~~~~~~~~~~~  shift arithmetic left, long
        pushl   %eax
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret



☆  12  ──────────── 我是分割线 ─────────────────☆
发信人: jjksam (base), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 18:46:28 2004

VC 6.0反汇编:

1:    #include <stdio.h>
2:
3:    int main()
4:    {
00401010   push        ebp
00401011   mov         ebp,esp
00401013   sub         esp,44h
00401016   push        ebx
00401017   push        esi
00401018   push        edi
00401019   lea         edi,[ebp-44h]
0040101C   mov         ecx,11h
00401021   mov         eax,0CCCCCCCCh
00401026   rep stos    dword ptr [edi]
5:        int one=1;
00401028   mov         dword ptr [ebp-4],1
6:        printf("%d\n", (int)(one<<31));
0040102F   mov         eax,dword ptr [ebp-4]
00401032   shl         eax,1Fh
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~逻辑左移
00401035   push        eax
00401036   push        offset string "%d\n" (0042001c)
0040103B   call        printf (00401070)
00401040   add         esp,8
7:        return 0;
00401043   xor         eax,eax
8:    }
00401045   pop         edi
00401046   pop         esi
00401047   pop         ebx
00401048   add         esp,44h
0040104B   cmp         ebp,esp
0040104D   call        __chkesp (004010f0)
00401052   mov         esp,ebp
00401054   pop         ebp
00401055   ret




☆  13  ──────────── 我是分割线 ─────────────────☆
发信人: jjksam (base), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 18:57:01 2004

6:        printf("%d\n", (int)(one<<31));
0040B77F   mov         eax,dword ptr [ebp-4]
0040B782   shl         eax,1Fh
0040B785   push        eax
0040B786   push        offset string "%d\n" (0042001c)
0040B78B   call        printf (00401070)
0040B790   add         esp,8
7:        printf("%d\n", (int)(one<<32));
0040B793   mov         ecx,dword ptr [ebp-4]
0040B796   shl         ecx,20h
0040B799   push        ecx
0040B79A   push        offset string "%d\n" (0042001c)
0040B79F   call        printf (00401070)
0040B7A4   add         esp,8
8:        printf("%d\n", (int)(one<<33));
0040B7A7   mov         edx,dword ptr [ebp-4]
0040B7AA   shl         edx,21h
                        ~~~~~~~~~这里并没有取模哦。。
0040B7AD   push        edx
0040B7AE   push        offset string "%d\n" (0042001c)
0040B7B3   call        printf (00401070)
0040B7B8   add         esp,8
9:        return 0;
0040B7BB   xor         eax,eax

                        这里错了,是固定对32取模的,我试过了
                            而且网上有人说C#也采取这种规则的



☆  14  ──────────── 我是分割线 ─────────────────☆
发信人: jjksam (base), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Mon Mar  1 19:02:13 2004

对常量这样做左移操作的话,编译器会自动帮你优化成一个数的,gcc和VC的编译器都
会这样

我只贴gcc的了

main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        movl    $1, -4(%ebp)
        subl    $8, %esp
        pushl   $-2147483648
        ~~~~~~~~~~~~~~~~~~~~~~~~``
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        subl    $8, %esp
        pushl   $0
       `~~~~~~~~~~~~~~~~变成0了
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        subl    $8, %esp
        pushl   $0
        ~~~~~~~~~~~~~~~~~~~~~~变成0了
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret




☆  15  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Wed Mar  3 22:05:30 2004

从这两个贴来看就应该是shl指令已经包含对移位数取模的操作了,
这也难怪对所有类型的左操作数都是对32取模的




☆  16  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Wed Mar  3 22:08:40 2004

这个我也想过,不过没反汇编看过,这点还是你比较醒^_^

                      编译器对算术左移和CPU 指令的理解不一样,呵呵



☆  17  ──────────── 我是分割线 ─────────────────☆
发信人: jjksam (base), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Wed Mar  3 22:29:59 2004

可以再深入研究,这个就要看Intel的指令集了,要看它的实现是怎样的。



☆  18  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Wed Mar  3 22:38:07 2004

钻不了那么深阿,我还是写我的game算了,哈哈




☆  19  ──────────── 我是分割线 ─────────────────☆
发信人: tec (TO BE A BETTER MAN!), 信区: Program
标  题: Re: 发现一个很奇怪的C的语法
时  间: Thu Mar  4 19:47:55 2004

编译器对左移操作的理解和硬件指令的不同亚,所以还是奇怪


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店