c语言进制转换、操作符万字详解------附带多道经典习题

简介: 常用的进制分类 二进制:由0和1组成 八进制:由0~7数字组成 十进制:由0~9数字组成 十六进制:由0~9和A~F组成

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

一、常用的进制分类


    二进制:由0和1组成

          八进制:由0~7数字组成

          十进制:由0~9数字组成

          十六进制:由0~9和A~F组成

二、常用进制的转化


1、二进制转换

二进制转八进制

二进制与八进制对应关系如下图 :
二进制 八进制
000 0
001 1
010 2
011 3
100 4
101 5
110 6
111 7


计算方法: 我们采用取三合一的方法进行进制转换              

               取三合一:将二进制数从右至左依次分组,每三个二进制数为一组,一组代表一个八进制数,不足的在左侧补零

  二进制                                                              十进制


0011 0100    ----------    000  110  100     ------------   0   6   4


0011 1101 0010    ----------    011  111  010  010   -----------    3   7   2   2

二进制转十进制

计算方法:从右至左用二进制的每个数的乘以2的相应位置的次方,最后相加

                         二进制                                                                       十进制


                       00101010                                                                        42


0 * 2^0 + 1 * 2^1 + 0 * 2^2 + 1 * 2^3 + 0 * 2^4 + 1 * 2^5 + 0 * 2^6 + 0 * 2^7  =  42

二进制转十六进制:

二进制和十六进制对应关系如下图:
二进制 十六进制
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 F

计算方法:利用八四二一码进行加法运算。              

 二进制                                                 十六进制


           8   4   2   1     8   4   2   1                0+0+2+0


                                 0   0   1   0      -----------------------------           2


                                                                  8+0+2+1


                                 1   0   1   1      -----------------------------           B                


                                                          8+4+0+1    0+4+0+1


           1   1   0   1     0   1   0   1      ------------------------------          D5      

2、八进制转换

八进制转二进制    

计算方法:利用二进制与八进制关系图进行取一分三

八进制                                                  二进制

 3      --------------------------------------     011


 7      --------------------------------------     111


 5      --------------------------------------     101      


tips:二进制数的每次加一的规则是,满二进一,即 011 + 1 = 100

八进制转十进制

计算方法:从右至左用八进制的每个数的乘以8的相应位置的次方,最后相加

   八进制                                                     十进制


  3 * 8^0


                                        3     ------------------------------------------      3


                                                            1 * 8^1 + 7 * 8^0


                                       17    ------------------------------------------     15


                                                      3 * 8^2 + 2 * 8^1 + 5 * 8^0


                                      325   ------------------------------------------     213

八进制转十六进制

              计算方法:先将八进制转为二进制,然后用二进制利用八四二一码转为十六进制


八进制                                二进制                               十六进制


                                                                                              2+1


                            3    -----------------------      0011         ----------------------     3


                                                                                       4+2+1、2+1    


                        1 6 3  -----------------------   0111 0011  -----------------------    7 3


                                                                                          8+2、8+4


                       2 5 4   -----------------------   1010 1100  ----------------------     A C              

3、十进制转换

十进制转二进制

计算方法:凑或除

除:除而取余,逆序输出

十进制                                                   二进制


                                       168                                                   1010 1000


                             168 / 2 = 84 ...... 0


                               84 / 2 = 41 ...... 0


                               42 / 2 = 21 ...... 0


                               21 / 2 = 10 ...... 1


                               10 / 2 = 5   ...... 0


                                  5 / 2 = 2  ...... 1


                                  2 / 2 = 1  ...... 0           逆序输出


                                  1 / 2 = 0  ...... 1   ---------------------------   1010 1000

凑:利用八四二一的进阶 128 64 32 8 4 2 1进行计算

!!(该进阶其实就是2^n...... 、2^2、2^1、2^0,当你发现不够用时就增加2的次方)!!

十进制                                                    二进制


                                      168      -----------------------------------   1010  1000

                 

                           168 = 128 + 32 +8


                      128  64  32  16  8  4  2  1  


                        1     0    1    0   1  0  0  0    -------------------------------   1010 1000

十进制转八进制

计算方法:除八取于,逆序输出

十进制                                              八进制


                                         145    -----------------------------------     221

                                     

                             145 / 8 = 18 ...... 1


                               18 / 8 = 2   ...... 2            逆序输出


                                 2 / 8 = 0   ...... 2  ----------------------------     221

 十进制转十六进制

计算方法:除十六取余,逆序输出

十进制                                               十六进制


                                       145       --------------------------------         91


                               145 / 16 = 9 ...... 1


                                   9 / 16 = 0 ...... 9     ---------------------         91


                                   


                                      1534      --------------------------------        5FE


                             1534 / 16 = 95 ...... 14  => E


                                 95 / 16 = 5   ...... 15  => F


                                   5 / 16 = 0   ...... 5    => 5      -----------      5FE

4、十六进制转换

十六进制转二进制

计算方法:利用二进制与十六进制对应关系表直接转换

   十六进制                                                            二进制


                                   7E      ----------------------------------------------   0111 1110


                                   8   4   2   1


               7=>7            0   1   1   1               先分开后合并


               E=>14          1   1   1   1    ---------------------------------- 0111  1110  


十六进制转八进制

计算方法:先转为二进制,后二进制利用取三合一的办法转换位为八进制

十六进制                                  二进制                             八进制


                                                     001  111  100


                            7C     -----------------------   0111 1100  -------------------   1 7 4


                                                                                         001  101  011


                            AB    -----------------------   1010 1011  -------------------    2 5 3


                                                                                             000 110


                             6      -----------------------        0110       -------------------      6

十六进制转十进制

计算方式:从右至左用十六进制的每个数的乘以16的相应位置的次方,最后相加

十六进制                                               十进制


                                         6A       -------------------------------------   106


                                      A => 10


                               6A =  6 * 16^1 +  A * 16^0


                                    =  6 * 16^1 + 10 * 16^0


                                    = 106

5、最简单的进制转换工具---计算器

其实还有一种更简单直接的办法,就是利用我们电脑自带的计算器。

在电脑桌面左下角搜索处直接搜索计算器即可。



这就是电脑自带的计算器了,当然你也可以在搜索到时鼠标右击将其固定在任务栏或者开始菜单中以便下次使用。



左侧的四个大写字母表示四种常用的进制,其中四个进制从上至下的排序分别为:

HEX:十六进制(计算机左侧栏的ABCDEF在需要进行十六进制运算时才会可以点击~)

DEC:十进制

OCT:八进制

BIN:二进制

鼠标点击某一个进制,并输入任意数字,即可得到该进制转化为其它进制的结果。



三、原码、反码、 补码


   1、什么是原码、反码、补码?

    整数的三种二进制表示方法叫做原码、反码、补码。


       三种方法均有符号位和数值位两部分,符号位都是用0“表示正”,用1表示“负”,而数值位最高位的一位被当作是符号位,其余的都是数值位。


       正整数的原码、反码、补码均相同,且符号位固定为1。


       负整数的原码、反码、补码均不同,且符号位固定为0。  

      移码的符号位另算!!!

tips:对于整形来说:数据存放内存中其实存放的是补码。 为什么呢?

在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理; 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是 相同的,不需要额外的硬件电路。

!!!所以在内存中的运算都是二进制的补码之间在运算!!!

2、已知真值求其对应原码、反码、补码和移码

    在计算机组成原理这本书中,我们为了区别一般书写表示的数和机器中这些编码表示的数,通常将前者称为真值,将后者称为机器数或者机器码


!!!(如果不是二进制表示形式记得先将其它进制形式转为二进制形式再进行计算)!!!


已知真值求原码

计算方法:x为正,符号位变为0,其余位不变;x为负,符号位变为1,其余位不变

我们设定  [x]原  为机器数, x为真值。

①x为正时,x= + 1001,[x]原 = 0 1001

②x为负时,x=  - 1001,[x]原 = 1 1001

已知真值求反码

计算方法:x为正,真值的反码与原码相同;x为负,符号位变为1,其余位各位取反

①x为正时,      x =(+122) =(+1111010)


                         [x]反 = [x]原 = 0 1111010  


②x为负时,      x =(-122)  =(-1111010)


                         [x]原 = 1 1111010  

                                      ||

                                      v

                         [x]反 = 1 0000101

已知真值求补码

计算方法:x为正,真值的补码与原码相同;x为负,补码等于反码加一

①x为正时,   x = (+122)= (+1111010)


                      [x]补 = [x]原 =  0 1111010


②x为负时,   x = (-122) = (-1111010)


                   


                      [x]原 = 1 1111010  


                                      ||


                                      v


                      [x]反 = 1 0000101


                                      ||


                                      v

                      [x]补 = 1 0000110            求补码得先要求反码

已知真值求移码

计算方法:x为正,符号位变为一其余位不变;x为负,符号位变为0其余各位取反加一

①x为正时,   x = +10101

                     [x]移 = 1 10101

②x为负时,   x = -10101

                      [x]移 = 0 01011

列表总结四种码的变换方法

原码 反码 补码 移码
真值为正 符号位变为0,其余各位不变 同原码 同原码 符号位变为1,其余为不变
真值为负 符号位变为1,其余各位不变 符号位变为1,其余各位取反 反码加一 符号位变为0,其余各位取反加一

四、移位操作符


      !!!记住左移右移操作符在进行与或者其它运算时,绝对不是单纯的把移出去的那一个进行运算,同时也要牢记各类运算的运算规则!!!

1、左移位操作符:<<

箭头朝向:向左

运算方法:该数的二进制形式

移位规则:左边抛弃,右边补0

int num = 10;       00000000  00000000  00000000  00001010  num的二进制表示
int a = num << 1;   00000000  00000000  00000000  00010100  a的二进制表示
//移位后num的值不发生改变

2、右移位操作符:>>

箭头朝向:向右

运算方法:该数的二进制形式  

移位规则:①逻辑右移:左边用0填充,右边丢弃

                 ②算术右移:左边用原该值的符号位填充,右边丢弃

//逻辑右移:
int num = -1;       11111111  11111111  11111111  11111111  num的二进制表示
int a = num >> 1;   01111111  11111111  11111111  11111111  a的二进制表示
//算术右移:
int num = -1;       11111111  11111111  11111111  11111111  num的二进制表示
int a = num >> 1;  1 1111111  11111111  11111111  11111111  a的二进制表示
             //这个被隔开的1就是符号位,末尾其实已经丢弃一个1了   
//移位后num的值不发生改变 

3、小拓展

 对于一个整数,进行左移时结果为原值的两倍,右移时结果为原值的一半,当然,当右移时结果位原值一半时,若原值位奇数那么就向下取整

int main()
{
    int num = 3;
    int a = num << 1;   //当左移一位时 a = 6 ,当左移两位,即num << 2时,a = 12.
    int b = num >> 1;   //当右移一位时 b = 6 ,当右移两位,即num << 2时,b = 0.
    printf("%d", a);
    printf("%d\n", b);
    return 0;
}

注意:移位操作符的操作数只能为整数,同时该数不能为负数否则会报错!!!

num >> 操作数  

num << 操作数  

五、位操作符

1、按位与:&

内存中的运算方式:补码

运算规则:同1为1,其余为0

int a = 3;  
int b = -5; 
int c = a & b; 
              // 00000000 00000000 00000000 00000011   3的补码 
              // 11111111 11111111 11111111 11111011   -5的补码
              // 00000000 00000000 00000000 00000011   c的补码
printf("%d",c);   // c = 3

2、按位或:|

内存中的运算方式:补码

运算规则:同0为0,其余为1

int a = 3;  
int b = -5; 
int c = a | b; 
              // 00000000 00000000 00000000 00000011   3的补码 
              // 11111111 11111111 11111111 11111011   -5的补码
              // 11111111 11111111 11111111 11111011  c的补码
              //负数的补码字符为固定为1,正数的补码字符为固定为0,所以c为负数
              // 11111111 11111111 11111111 11111010   c的反码,负数的补码=反码-1
              // 10000000 00000000 00000000 00000101   c的原码,负数的原码=反码各个位取反
printf("%d",c);   // c = -5    

3、按位异或:^

内存中的运算方式:补码

运算规则:相同为0,不同为1  

int a = 3;  
int b = -5; 
int c = a ^ b; 
              // 00000000 00000000 00000000 00000011   3的补码 
              // 11111111 11111111 11111111 11111011   -5的补码
              // 11111111 11111111 11111111 11111000   c的补码
              //负数的补码字符为固定为1,正数的补码字符为固定为0,所以c为负数
              // 11111111 11111111 11111111 11110111  反码,负数的补码=反码-1
              // 10000000 00000000 00000000 00001000  原码,负数的原码=反码各个位取反
printf("%d",c);   // c = -8

4、按位取反操作符:~

内存中的运算方式:补码

运算规则:二进制是0变为1,二进制是1变为0,符号位是+变为-

int main()
{
int n = 0;
int a = ~n;   //按位取反
printf("%d\n",a);
// 00000000  00000000  00000000  00000000  n的二进制形式
// 11111111  11111111  11111111  11111111  a的二进制形式
}

注意:它们的操作数必须为整数!!!

操作数  ^  操作数

操作数  & 操作数

操作数   |  操作数

六、简单小练


题目一:在不创建第三个变量的前提下,实现两个数的交换

#include <stdio.h>
int main()
{
 int a = 5;
 int b = 10;
 a = a^b;     //将a与b异或运算的值赋值给a
 b = a^b;     //此时a^b等价于a^b^b,由于b^b=0,所以a的值被赋值给了b
 a = a^b;     //由于上一次只是对b进行了赋值所以此时a仍然等于a^b,故a^b就等价于a^b^a=b,实现将b的值
 printf("a = %d b = %d\n", a, b);   //赋值给a,两数交换完成
 return 0;
}

题目二:求一个整数存储在内存中的二进制中1的个数

//因为是内存中所以,进行运算的是二进制的补码
方法一:
int Return(int a)
{
  int count=0;
  for (int i = 0; i < 32; i++)
  {
    if (((a >> i) & 1) == 1)   //一共三十二位,每次向右移位一位并进行位与运算判断结果是否为                
                                   //1,若是则count++,count的最终值代表二进制数中的所有1的个数
    {              //c语言进行按位与运算时系统会默认将数字转为二进制数后再计算
      count++;
  }
}
  return count;
}
int main()
{
int a;
printf("请输入要判断的数字:");
scanf_s("%d",&a);
int count = Return(a);
printf("二进制数中一共右%d个1",count);
}

题目三:修改一个整数二进制的某一个位置的数字(常见于单片机嵌入式)

//将a的二进制中的第五位改为1(从右向左数)
int main()
{
int a = 13;       //00000000  00000000  00000000  00001101   a的二进制形式
a = a | (1 << 4); //00000000  00000000  00000000  00000001   1的二进制形式 
printf("%d\n",a); //00000000  00000000  00000000  00010000   1 << 4后的二进制形式 
                  //1 << 4 后的结果与a进行或运算,同零为零,其余为一
                  //00000000  00000000  00000000  00011101   //a的最后结果
}
//将a的二进制中的第五位改为0(从右向左数)
int main()
{
int a = 13;       
a = a | (1 << 4);    //00000000  00000000  00000000  00011101   //a第一次改变后的二进制形式
                     //11111111  11111111  11111111  11101111   //~(1 << 4)后的结果
a = a & ~(1 << 4);   //二者进行与运算,同一为一,不同为0
                     //00000000  00000000  00000000  00001101   a的最后结果
printf("%d\n",a); 
}

题目四:判断一个整数是否是2的n次方?

//2的n次方这个整数的二进制只有一个1
int main()
{
int n = 0;
scanf_s("%d",&n);
if((n & (n - 1)) == 0)
{
printf("yes\n");
}
else
{
printf("no\n");
}
return 0;
}

题目五:打印整数二进制的奇数位和偶数位

void PrintNum(int i)
{
  int a = 31;       
    //打印a的整个二进制序列
  printf("该数的二进制形式为:");
  while(a>=0)
  {
    printf("%d ", (i >> a) & 1);      //每次都向右移a位,且每次移位后a减一
    a--;                  //按位与操作符&,同一为一,其余为0
  }       
    //打印a偶数位上二进制序列      
  printf("\n偶数位:");
  for (a = 30; a >= 0; a -= 2)        //两个偶数或者两个奇数之间位数相差2
  {
    printf("%d ", (i >> a) & 1);
  }
    //打印a奇数位上二进制序列
  printf("\n奇数位:");
  for (a = 31; a >= 1; a -= 2)       //两个偶数或者两个奇数之间位数相差2
  {
    printf("%d ", (i>> a) & 1);
  }
}                 
int main()
{
  int i;
  printf("确定要输入的整数:>");
  scanf_s("%d", &i);
  PrintNum(i);
  return 0;
}

题目六:求两个数二进制中不同位的个数

Judge(int i, int j)
{
  int a;
  int count = 0;
  for (a = 0; a < 32; a++)
    if (((i >> a) & 1) != ((j>>a) & 1)) 
    {
      count++;          //i和j在该位置上相同count就++记录一次
    }
  return count;
}
int main()
{
  int m, n;
  printf("请输入两个整数:");
  scanf_s("%d %d", &m, &n);
  int count = Judge(m, n);
  printf("%d", count);
}

tips:关于  “if (((i >> a) & 1) != ((j>>a) & 1))” 的解释

 i和j每次都右移a位相当于每次都是这俩数的同一位在做比较,然后再整体与1做与运算,由于与运算是同1为1其余为0,所以每次i和j的二进制位中后面的部分与1与运算后结果都为0,只有最后一位才算的上是真正在和1做与运算,如果两个与运算结果分别为0和1那么证明该位置上两个数字的二进制分别为1和0,即在该位置上两个数的二进制位不同。

七、逗号表达式


       形式:表达式1,表达式2,表达式3,......,表达式n

  逗号表达式就是用逗号隔开的多个表达式,所有表达式从左至右依次执行,整体的结果是最后有一个表达式的结果。

情况一:

int main()
{
int a = 1;
int b = 2;
int c = (a>b,a=b+10,a,b=a+1);
printf("%d",c);                              //c=13
}

情况二:

int main()
{
int a = 1;
int b = 2;
if (a =b + 1, c=a / 2, d > 0)     //如果d>0结果为假,那么就不执行,跟前面的无关
printf("%d",c);   
}

情况三:(了解即可)

//同一种功能实现的方式不同
//方法一:
int main()
{
a = get_val();
count_val(a);
while (a > 0 )
{
// 业务处理                       //代码冗余
a = get_val();
count_val(a);
}
//方法二:
int main()
{
如果使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a> 0 )
{
// 业务处理                   //明显比方法一更加的方便
}
}

八、下标访问[]、函数调⽤()


1、 下标引⽤操作符 [  ]

操作数:数组名+数组下标/索引值

int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",arr[5]);    //下标引用操作符
//[]操作符的操作数是:arr 和  5
//类似于 3 + 5  ,其中3和5是+的操作数
}

2、函数调用操作符 (  )

操作数:函数名+参数名(tips:最少有一个操作数

#include <stdio.h>
int Add(int x,int y)
{
return x + y;
}
void test()
{
printf("hehe\n");
}
int main()
{
 //函数调用
int ret = Add(2,3);    //()的操作数:Add,2,3
printf("%d\n",ret);
 return 0;
}

3、解惑---sizeof是不是函数?

疑惑原因:我们为了计算变量大小的时候会写sizeof(变量名),这里看起来是既有操作符()又有操作数sizeof和变量名。但实际上sizeof不是函数,而是操作符。我们通过代码来解释这个问题:

//sizeof不是函数,是操作符
int main()
{
int a = 10;
int n = sizeof a ;   //如果sizeof是函数那么必须写成sizeof(a)的形式,但是我们发现不用加()也可以得            
                     //到n的值,故不是。
printf("%d\n",n);
}

九、操作符的属性:优先级、结合性

c语言的操作符有两个重要的属性:优先级和结合性,这两个属性在一定程度上决定了表达式求值的计算顺序

1、优先级

优先级是指:如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏,哪个应该后执行。各种运算符的优先级是 不⼀样的。比如:

  3 + 4 * 5 ;

上⾯示例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法

的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4

2、结合性

如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右 结合(从右到左执⾏),⽐如赋值运算符( = )。


 5 * 6 / 2 ;

上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右进行计算,运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排 列),建议⼤概记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看 运算符优先级和结合性关系表即可。

圆括号( () )

⾃增运算符( ++ ),⾃减运算符( -- )

⼀元运算符( + 和 - )

乘法( * ),除法( / )

加法( + ),减法( -

关系运算符( < > 等)

赋值运算符( =

由于圆括号的优先级最⾼,可以使⽤它改变其他运算符的优先级。

3、运算符优先级和结合性关系表

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--


3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<< 

左移

变量<<表达式

左到右

双目运算符

>> 

右移

变量>>表达式

6

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

小于

表达式<表达式

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=


加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

<<=

左移后赋值

变量<<=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

15

逗号运算符

表达式,表达式,…

左到右

--

 双目运算符就是符号两边有两个需要操作的内容,单目同理。符号相同但是有多种意思时要注意它到底是单目还是双目的运算符,就比如&。

十、表达式求值


1、整型提升

C语⾔中整型算术运算 总是⾄少以整数类型(int)的精度 来进⾏,为了获得这个精度,表达式中的字 符类型(char)  短整型(short) 操作数在使⽤之前会被转换为 普通整型(int  /  unsigned int) ,这种转换称为 整型提升 

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀ 般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓度。 通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算。

两个char类型相加

char a = 5;
char b = 8;
char c = a + b;

这是不进行整型提升的时候:



这时进行整形提升之后:


很明显在两个数进行整型提升后,虽然可能前面int型的内存还没用完,但是可以有效避免因为超出内存分配给它的范围导致的结果被丢弃。同时也增加了结果的精确度。

如何进行整型提升?

①: 有符号整数提升是按照变量的数据类型的符号位来提升的,补码符号位为0则整型提升时高位补零,补码符号位为1则整型提升时高位补1.

② :⽆符号整数提升,⾼位补0  

!!!无符号就是有unsigned前缀的!!!

!!!在vs中char==》signed char!!!

!!!不同编译器下char代表的可能是signed char也可能是signed char!!!

正数的整型提升
int main()
{
char a = 5 ;
//5  -  int类型   -   4字节   -   32bit
//00000000  00000000  00000000  00000101       ---  5的补码
//00000101 -  5的补码被截断后的结果赋值给a
char b = 127;
//00000000  00000000  00000000  01111111         ---  127的补码
//01111111  -   127的补码被截断后赋值给b

截断原因:char的二进制位中只有八个比特位,而5和127是整型二进制位中有三十二个比特位,想要分别存入a和b中只能截断前面多余的二进制位 ==》一米的洞塞进四米的棍子

char c = a + b;     
//发生整型提升:a和b的二进制位发生改变
//00000000  00000000  00000000  00000101      ---- a整型提升后的补码
//00000000  00000000  00000000  01111111        ---- b整型提升后的补码
//00000000  00000000  00000000  10000100       ---- a + b 的补码
//10000100     -  c  (a+b的补码被截断后存储到c中)
printf("%d\n",c);
//打印c的时候还是要进行整型提升,因为%d是按照打印10进制的形式打印有符号的整数
//1111111  11111111  11111111  10000100            ---c整型提升后的结果(补码)
//10000000  00000000  00000000  01111100      ---c的原码
//-124    -打印c的结果
}
负数的整型提升

请查看我的另一篇文章《数据在内存中的存储》:http://t.csdn.cn/mmPjb

里面附带了更多的习题能更好的帮你理解整型提升

2、算术转换

  如果某个操作符操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则代码无法执行。

下面的层次体系称为寻常算术转换:

  1. long double
  2. double
  3. float
  4. unsigned long int
  5. long int
  6. unsigned int
  7. int

 如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外一个操作数的类型后执行运算。

3、问题表达式解析

①问题表达式一

a*b + c*d + e*f
我们通过操作符的属性,无法确定这个表达式的唯一计算路径!!
//第一种运算顺序
//a*b 、c*d 、+ 、e*f、+    (只能说相邻的*的计算优先级比+高,但是e*f明显要离c*d还有一段距离所以可以在c*d之后进行+然后再进行e*f)
//第二种运算顺序
//a*b、c*d、e*f、+、+      (同样的也可以先计算e*f然后再计算+)
如果abcdef是变量的情况还好可以用括号进行更改,
但是如果它们是多个表达式,那么就会出问题。

②问题表达式二

操作符的优先级只能决定自减--的运算在+的前面,但是我们无法得知,+操作符的左操作数的获取是在右操作数之前还是之后,所以结果不可预测!!!

int c = 5;
c + --c
//第一种运算顺序
--c、+、c  
4    +   4  =   8
//第二种运算顺序
c、+、--c
5    +    4  =  9

③问题表达式三

函数的调⽤先后顺序⽆法通过操作符的优先级确定!!!

int fun()
{
static int count = 1;         //static修饰局部变量,生命周期延长
return ++count;               //count会有三个结果2、3、4
}                                      //即fun被主函数三次调用时会调用2、3、4但是调用顺序未知
int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d\n",answer);      //虽然结果输出为-10,看起来是顺序调用2、3、4但是其实实际调用顺序依然未知。
return 0;
}

4、总结

!!!即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯⼀的 计算路径,那这个表达式就是存在潜在⻛险的,建议不要写出特别负责的表达式!!!

相关文章
|
2天前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
37 16
|
6天前
|
算法 C语言
【C语言程序设计——循环程序设计】求解最大公约数(头歌实践教学平台习题)【合集】
采用欧几里得算法(EuclideanAlgorithm)求解两个正整数的最大公约数。的最大公约数,然后检查最大公约数是否大于1。如果是,就返回1,表示。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。作为新的参数传递进去。这个递归过程会不断进行,直到。有除1以外的公约数;变为0,此时就找到了最大公约数。开始你的任务吧,祝你成功!是否为0,如果是,那么。就是最大公约数,直接返回。
53 18
|
6天前
|
Serverless C语言
【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】
根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码,求解出数值x的平方根;运用迭代公式,编写一个循环程序,求解出数值x的平方根。注意:不能直接用平方根公式/函数求解本题!开始你的任务吧,祝你成功!​ 相关知识 求平方根的迭代公式 绝对值函数fabs() 循环语句 一、求平方根的迭代公式 1.原理 在C语言中,求一个数的平方根可以使用牛顿迭代法。对于方程(为要求平方根的数),设是的第n次近似值,牛顿迭代公式为。 其基本思想是从一个初始近似值开始,通过不断迭代这个公式,使得越来越接近。
36 18
|
6天前
|
C语言
【C语言程序设计——循环程序设计】统计海军鸣放礼炮声数量(头歌实践教学平台习题)【合集】
有A、B、C三艘军舰同时开始鸣放礼炮各21响。已知A舰每隔5秒1次,B舰每隔6秒放1次,C舰每隔7秒放1次。编程计算观众总共听到几次礼炮声。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。开始你的任务吧,祝你成功!
39 13
|
1天前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
11 3
|
6天前
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
28 10
|
6天前
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
26 10
|
1天前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
7 2
|
6天前
|
存储 C语言
【C语言程序设计——循环程序设计】利用数列的累加和求 sinx(头歌实践教学平台习题)【合集】
项的累加和,一般会使用循环结构,在每次循环中计算出当前项的值(可能基于通项公式或者递推关系),然后累加到一个用于存储累加和的变量中。在C语言中推导数列中的某一项,通常需要依据数列给定的通项公式或者前后项之间的递推关系来实现。例如,对于一个简单的等差数列,其通项公式为。的级数,其每一项之间存在特定的递推关系(后项的分子是其前项的分子乘上。,计算sinx的值,直到最后一项的绝对值小于。为项数),就可以通过代码来计算出指定项的值。对于更复杂的数列,像题目中涉及的用于近似计算。开始你的任务吧,祝你成功!
24 6
|
5天前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
30 1