user-0 提问: c语言位运算的一个问题。

回复:
user: user-1
content: 学习到了，汇编好强大啊。可我不怎么看的很清楚，呜呜呜。。。。

user: user-2
content: 引用 17 楼 halfword1982 的回复:针对32位系统，当算术左移位数大于31时，C标准中对该移位操作是未定义的。  unsigned in i = 1; int n = 32; i <<= n;   上述C代码在Intel x86、PowerPC、ARM架构CPU下对应的汇编指令和移位执行说明如下：  ;;Intel ;;将寄存器eax中的值左移，移动位数为寄存器cl中的值，结果存在eax中 ;;仅取寄存器cl最低5 bits的值作为移位数 ;;当cl寄存器中的值为32时，该操作等价于shl    eax, 0，即移位操作无效 shl    eax,cl   ;;PowerPC ;;将寄存器RS中的值左移，移动位数为寄存器RB中的值，结果存在RA中 ;;仅取寄存器RB的高6 bits作为移位数，当移位数为32-63时，移位结果为0 slw  RA,RS,RB   ;;ARM ;;将寄存器Rm中的值左移，移动位数为寄存器Rm中的值，结果存在Rd中 ;;仅取Rs的最低8 bits作为移位数，当移位数大于31时，移位结果为0 LSL  Rd, Rm, Rs   可以看到，对于C标准中未定义的操作，不同架构CPU有不同的实现和限制，导致计算结果不同。  C标准中哪里来的“算术左移”？

user: user-3
content: 引用 16 楼 sholber 的回复:[quote=引用 9 楼 halfword1982 的回复:] 这个问题的关键在于n的值为32，对于32位系统，算术左移位数的合法值为0-31，超过该范围时，移位操作的结果在C标准中是未定义的，完全取决于芯片厂家的实现。  Intel对于算术左移指令有如下的规定：  SAL/SAR/SHL/SHR—Shift  The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used).  借用楼上的代码，地址0040104F处的shl指令即对应(~0U << n)的计算，寄存器cl中的值为n=32，取最低5 bits的值为0，所以该指令执行后，eax中的值依然为0xffffffff  20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax   完全取决于芯片厂家是什么意思？[/quote] 其实这应该是undefined behavior而不是implementation-defined。标准规定这是UB是考虑到编译器可以对于这类操作做constant folding，这个fold的结果可以和目标cpu的指令效果不同。

user: user-4
content: 针对32位系统，当算术左移位数大于31时，C标准中对该移位操作是未定义的。  unsigned in i = 1; int n = 32; i <<= n;   上述C代码在Intel x86、PowerPC、ARM架构CPU下对应的汇编指令和移位执行说明如下：  ;;Intel ;;将寄存器eax中的值左移，移动位数为寄存器cl中的值，结果存在eax中 ;;仅取寄存器cl最低5 bits的值作为移位数 ;;当cl寄存器中的值为32时，该操作等价于shl    eax, 0，即移位操作无效 shl    eax,cl   ;;PowerPC ;;将寄存器RS中的值左移，移动位数为寄存器RB中的值，结果存在RA中 ;;仅取寄存器RB的高6 bits作为移位数，当移位数为32-63时，移位结果为0 slw  RA,RS,RB   ;;ARM ;;将寄存器Rm中的值左移，移动位数为寄存器Rm中的值，结果存在Rd中 ;;仅取Rs的最低8 bits作为移位数，当移位数大于31时，移位结果为0 LSL  Rd, Rm, Rs   可以看到，对于C标准中未定义的操作，不同架构CPU有不同的实现和限制，导致计算结果不同。

user: user-2
content: 引用 9 楼 halfword1982 的回复:这个问题的关键在于n的值为32，对于32位系统，算术左移位数的合法值为0-31，超过该范围时，移位操作的结果在C标准中是未定义的，完全取决于芯片厂家的实现。  Intel对于算术左移指令有如下的规定：  SAL/SAR/SHL/SHR—Shift  The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used).  借用楼上的代码，地址0040104F处的shl指令即对应(~0U << n)的计算，寄存器cl中的值为n=32，取最低5 bits的值为0，所以该指令执行后，eax中的值依然为0xffffffff  20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax   完全取决于芯片厂家是什么意思？

user: user-2
content: 引用 9 楼 halfword1982 的回复:这个问题的关键在于n的值为32，对于32位系统，算术左移位数的合法值为0-31，超过该范围时，移位操作的结果在C标准中是未定义的，完全取决于芯片厂家的实现。  Intel对于算术左移指令有如下的规定：  SAL/SAR/SHL/SHR—Shift  The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used).  借用楼上的代码，地址0040104F处的shl指令即对应(~0U << n)的计算，寄存器cl中的值为n=32，取最低5 bits的值为0，所以该指令执行后，eax中的值依然为0xffffffff  20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax  不够严谨。 你能告诉我 char c = 1; int i = sizeof (int) * CHAR_BIT - 1; c = c << i;是未定义的吗？为什么？

user: user-5
content: 电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址； 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……

user: user-2
content: 引用 5 楼 zhao4zhong1 的回复:[quote=引用 4 楼 luanxia77 的回复:] 楼上，你这个算什么。。。。？ 人间正道是沧桑， 编程正道是汇编。[/quote] 学C要靠汇编，那你说(char)1<<1LL的结果是啥类型，并用汇编证明。

user: user-4
content: 引用 13 楼 sholber 的回复:不够严谨。 你能告诉我 char c = 1; int i = sizeof (int) * CHAR_BIT - 1; c = c << i;是未定义的吗？为什么？  上述移位操作是否为未定义取决于char类型的符号定义。如果char为signed char，移位计算前，其类型提升为signed int，移位操作的结果（数值结果，正值）超过了signed int的范围，该操作为未定义的；如果char为unsigned char，则该操作的定义是确定的。

user: user-6
content: //32bits反转： x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;  /*circle left shift :y = (x << n) | (x >> (32 - n)); circle right shift:y = (x >> n) | (x << (32 - n)); 由循环移位，第4,5句优化为：*/ x = (x << 24) | ((x & 0xFF00) << 8) |  	((x >> 8) & 0xFF00) | (x >> 24);  //32b，4字节反转： y = x & 0x00FF00FF; (y >> 8) | (y << 24) | (((x << 8) | (x >> 24)) & 0x00FF00FF);

user: user-0
content: 引用 9 楼 halfword1982 的回复:这个问题的关键在于n的值为32，对于32位系统，算术左移位数的合法值为0-31，超过该范围时，移位操作的结果在C标准中是未定义的，完全取决于芯片厂家的实现。  Intel对于算术左移指令有如下的规定：  SAL/SAR/SHL/SHR—Shift  The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used).   借用楼上的代码，地址0040104F处的shl指令即对应(~0U << n)的计算，寄存器cl中的值为n=32，取最低5 bits的值为0，所以该指令执行后，eax中的值依然为0xffffffff  20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax  真的很感谢。

user: user-4
content: 这个问题的关键在于n的值为32，对于32位系统，算术左移位数的合法值为0-31，超过该范围时，移位操作的结果在C标准中是未定义的，完全取决于芯片厂家的实现。  Intel对于算术左移指令有如下的规定：  SAL/SAR/SHL/SHR—Shift  The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used).  借用楼上的代码，地址0040104F处的shl指令即对应(~0U << n)的计算，寄存器cl中的值为n=32，取最低5 bits的值为0，所以该指令执行后，eax中的值依然为0xffffffff  20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax

user: user-0
content: 引用 6 楼 zhao4zhong1 的回复:16: 17:   unsigned int invert( unsigned int x , int p , int n) 18:   { 00401040   push        ebp 00401041   mov         ebp,esp 00401043   sub         esp,48h 00401046   push        ebx 00401047   push        esi 00401048   push        edi 19:       unsigned int r; 20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax 21: 22:       //printf("r = %x\n",r); 23: 24: 25:       unsigned s = r & x; 00401061   mov         ecx,dword ptr [ebp-4] 00401064   and         ecx,dword ptr [ebp+8] 00401067   mov         dword ptr [ebp-8],ecx 26:       //printf("s = %x \n",s); 27: 28:       x = x | r; 0040106A   mov         edx,dword ptr [ebp+8] 0040106D   or          edx,dword ptr [ebp-4] 00401070   mov         dword ptr [ebp+8],edx 29: 30:       x = x & (~s); 00401073   mov         eax,dword ptr [ebp-8] 00401076   not         eax 00401078   mov         ecx,dword ptr [ebp+8] 0040107B   and         ecx,eax 0040107D   mov         dword ptr [ebp+8],ecx 31: 32:       return x ; 00401080   mov         eax,dword ptr [ebp+8] 33: 34:   } 00401083   pop         edi 00401084   pop         esi 00401085   pop         ebx 00401086   mov         esp,ebp 00401088   pop         ebp 00401089   ret  16: 17:   unsigned int invert( unsigned int x , int p , int n) 18:   { 00401040   push        ebp 00401041   mov         ebp,esp 00401043   sub         esp,48h 00401046   push        ebx 00401047   push        esi 00401048   push        edi 19:       unsigned int r; 20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax 21: 22:       //printf("r = %x\n",r); 23: 24: 25:       unsigned s = r & x; 00401061   mov         ecx,dword ptr [ebp-4] 00401064   and         ecx,dword ptr [ebp+8] 00401067   mov         dword ptr [ebp-8],ecx 26:       //printf("s = %x \n",s); 27: 28:       x = x | r; 0040106A   mov         edx,dword ptr [ebp+8] 0040106D   or          edx,dword ptr [ebp-4] 00401070   mov         dword ptr [ebp+8],edx 29: 30:       x = x & (~s); 00401073   mov         eax,dword ptr [ebp-8] 00401076   not         eax 00401078   mov         ecx,dword ptr [ebp+8] 0040107B   and         ecx,eax 0040107D   mov         dword ptr [ebp+8],ecx 31: 32:       return x ; 00401080   mov         eax,dword ptr [ebp+8] 33: 34:   } 00401083   pop         edi 00401084   pop         esi 00401085   pop         ebx 00401086   mov         esp,ebp 00401088   pop         ebp 00401089   ret  我刚刚对着汇编代码一行一行的运行了一下， 就在这一行 dword ptr [ebp-4],eax的时候，eax是0xffffffff，但是我看r的内存地址的时候，赋值并没有成功，但是我在watch整个表达式之后又再，再watch变量r，r的内存就是fffffff了。感觉不知道为什么？

user: user-0
content: 引用 6 楼 zhao4zhong1 的回复:16: 17:   unsigned int invert( unsigned int x , int p , int n) 18:   { 00401040   push        ebp 00401041   mov         ebp,esp 00401043   sub         esp,48h 00401046   push        ebx 00401047   push        esi 00401048   push        edi 19:       unsigned int r; 20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax 21: 22:       //printf("r = %x\n",r); 23: 24: 25:       unsigned s = r & x; 00401061   mov         ecx,dword ptr [ebp-4] 00401064   and         ecx,dword ptr [ebp+8] 00401067   mov         dword ptr [ebp-8],ecx 26:       //printf("s = %x \n",s); 27: 28:       x = x | r; 0040106A   mov         edx,dword ptr [ebp+8] 0040106D   or          edx,dword ptr [ebp-4] 00401070   mov         dword ptr [ebp+8],edx 29: 30:       x = x & (~s); 00401073   mov         eax,dword ptr [ebp-8] 00401076   not         eax 00401078   mov         ecx,dword ptr [ebp+8] 0040107B   and         ecx,eax 0040107D   mov         dword ptr [ebp+8],ecx 31: 32:       return x ; 00401080   mov         eax,dword ptr [ebp+8] 33: 34:   } 00401083   pop         edi 00401084   pop         esi 00401085   pop         ebx 00401086   mov         esp,ebp 00401088   pop         ebp 00401089   ret  16: 17:   unsigned int invert( unsigned int x , int p , int n) 18:   { 00401040   push        ebp 00401041   mov         ebp,esp 00401043   sub         esp,48h 00401046   push        ebx 00401047   push        esi 00401048   push        edi 19:       unsigned int r; 20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax 21: 22:       //printf("r = %x\n",r); 23: 24: 25:       unsigned s = r & x; 00401061   mov         ecx,dword ptr [ebp-4] 00401064   and         ecx,dword ptr [ebp+8] 00401067   mov         dword ptr [ebp-8],ecx 26:       //printf("s = %x \n",s); 27: 28:       x = x | r; 0040106A   mov         edx,dword ptr [ebp+8] 0040106D   or          edx,dword ptr [ebp-4] 00401070   mov         dword ptr [ebp+8],edx 29: 30:       x = x & (~s); 00401073   mov         eax,dword ptr [ebp-8] 00401076   not         eax 00401078   mov         ecx,dword ptr [ebp+8] 0040107B   and         ecx,eax 0040107D   mov         dword ptr [ebp+8],ecx 31: 32:       return x ; 00401080   mov         eax,dword ptr [ebp+8] 33: 34:   } 00401083   pop         edi 00401084   pop         esi 00401085   pop         ebx 00401086   mov         esp,ebp 00401088   pop         ebp 00401089   ret  谢谢。。。。 我先去看一下win32的基本汇编指令。。。

user: user-5
content: 16: 17:   unsigned int invert( unsigned int x , int p , int n) 18:   { 00401040   push        ebp 00401041   mov         ebp,esp 00401043   sub         esp,48h 00401046   push        ebx 00401047   push        esi 00401048   push        edi 19:       unsigned int r; 20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax 21: 22:       //printf("r = %x\n",r); 23: 24: 25:       unsigned s = r & x; 00401061   mov         ecx,dword ptr [ebp-4] 00401064   and         ecx,dword ptr [ebp+8] 00401067   mov         dword ptr [ebp-8],ecx 26:       //printf("s = %x \n",s); 27: 28:       x = x | r; 0040106A   mov         edx,dword ptr [ebp+8] 0040106D   or          edx,dword ptr [ebp-4] 00401070   mov         dword ptr [ebp+8],edx 29: 30:       x = x & (~s); 00401073   mov         eax,dword ptr [ebp-8] 00401076   not         eax 00401078   mov         ecx,dword ptr [ebp+8] 0040107B   and         ecx,eax 0040107D   mov         dword ptr [ebp+8],ecx 31: 32:       return x ; 00401080   mov         eax,dword ptr [ebp+8] 33: 34:   } 00401083   pop         edi 00401084   pop         esi 00401085   pop         ebx 00401086   mov         esp,ebp 00401088   pop         ebp 00401089   ret  16: 17:   unsigned int invert( unsigned int x , int p , int n) 18:   { 00401040   push        ebp 00401041   mov         ebp,esp 00401043   sub         esp,48h 00401046   push        ebx 00401047   push        esi 00401048   push        edi 19:       unsigned int r; 20:       r = (~(~0U << n) << ( p + 1 - n)); 00401049   or          eax,0FFh 0040104C   mov         ecx,dword ptr [ebp+10h] 0040104F   shl         eax,cl 00401051   not         eax 00401053   mov         ecx,dword ptr [ebp+0Ch] 00401056   add         ecx,1 00401059   sub         ecx,dword ptr [ebp+10h] 0040105C   shl         eax,cl 0040105E   mov         dword ptr [ebp-4],eax 21: 22:       //printf("r = %x\n",r); 23: 24: 25:       unsigned s = r & x; 00401061   mov         ecx,dword ptr [ebp-4] 00401064   and         ecx,dword ptr [ebp+8] 00401067   mov         dword ptr [ebp-8],ecx 26:       //printf("s = %x \n",s); 27: 28:       x = x | r; 0040106A   mov         edx,dword ptr [ebp+8] 0040106D   or          edx,dword ptr [ebp-4] 00401070   mov         dword ptr [ebp+8],edx 29: 30:       x = x & (~s); 00401073   mov         eax,dword ptr [ebp-8] 00401076   not         eax 00401078   mov         ecx,dword ptr [ebp+8] 0040107B   and         ecx,eax 0040107D   mov         dword ptr [ebp+8],ecx 31: 32:       return x ; 00401080   mov         eax,dword ptr [ebp+8] 33: 34:   } 00401083   pop         edi 00401084   pop         esi 00401085   pop         ebx 00401086   mov         esp,ebp 00401088   pop         ebp 00401089   ret

user: user-5
content: 引用 4 楼 luanxia77 的回复:楼上，你这个算什么。。。。？ 人间正道是沧桑， 编程正道是汇编。

user: user-0
content: 楼上，你这个算什么。。。。？

user: user-5
content: bc++3.1 帮助里面对每个函数都有一小段能拷贝出来运行看效果的例子程序。非常适合初学C语言。相比较而言VC带的帮助MSDN就没有。http://download.csdn.net/source/2805028 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）

user: user-0
content: 刚刚到ubuntu下测试了一下也是这样的。

user: user-0
content: 刚刚用windows测试了一下也是这样的情况。现在换linux试试看。