【初识C07】 操作符与数据类型 | 16进制 | 大小端字节序

本文详细介绍了C语言中的浮点数除法规则、左移右移操作符的使用,以及负数的原反补码概念。重点讲解了如何利用位操作交换两个整型变量的值,并提供了相关练习题。此外,还探讨了整型提升和截断的原因,以及算术转换规则,包括向上转换和向精度更高的转换。最后,提到了逻辑运算符、三目操作符和逗号表达式的使用,并简单讨论了复杂表达式求值的影响因素。
摘要由CSDN通过智能技术生成

浮点数

  • 除号两边至少要有一个数是浮点数
    • 单精度浮点数:6.0f/5.0f 防止double警告
    • 取模:两端必须是整数

左移右移操作符

  • 不改变原来变量的值。
    • 左移:一个数的二进制位,向左移动一位。
      • 左边丢弃,右边补0。根据二进制位数移动。
      • 有×2的效果。
    • 右移
      • 算术右移
        • 右边丢弃,左边补原符号位
      • 逻辑右移
        • 右边丢弃,左边补0

负数的补码

计算机中存储的是数据的补码。

  • 原码:直接根据数值写出的二进制序列就是原码。
  • 反码:符号位不变,其他位按位取反
  • 补码:反码加一
  • 当前的右移操作符使用的是算数右移。测试验证:-1右移一位。
  • 正整数的原反补相等

位操作符

  • 操作数只能是正数。
  • 按位与 &
    • 对应的二进制位,两个都是1(真),结果与出来才是1.
  • 按位或 |
    • 对应的二进制位,有一个为1(真),结果就是1.
    • 两个为0,结果也是0.
  • 按位异或 ^
    • 对应的二进制位,相同为0(假),相异为 1.(真)

练习题

交换两个整型变量的值,不能创建第三个临时变量

  • 前边的二进制位 0 进行位操作都不影响结果。所以写简化的二进制位。因为两个为假,进行什么位操作,都是假。
    • a 011
    • b 101
  • 异或结果: 110,想象成密码,和b异或就能翻译出来原来的a。
  • 再和 b异或:101,结果就是:a 011
  • 0和任何数异或,都是它本身。两个相同数异或结果是 0.

这个题等于是 a ^b^b = a.

#include <stdio.h>
int main()
{
 int a = 10;     01010
 int b = 20;     10100
 a = a^b; 		a保存的是两个数异或结果			
 b = a^b;		则:b =  a^b^b =a    相同的两个数异或,结果为0.
 a = a^b;		a=a^a^b =b 
 printf("a = %d b = %d\n", a, b);
 return 0;
}

求一个数的二进制位的1的个数

  • 和1按位与。然后每次向右移一位和1,按位与。
#include <stdio.h>
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;  计数
 for(i=0; i<32; i++)
 {
 if( ((num>>i)&1) == 1 )
 count++; 
 }
 printf("二进制中1的个数 = %d\n",count);
 return 0;
}
  • 采用相邻的两个数据进行按位与运算
    • 把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.
    • 那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
int Count(int n)
{
	int count=0;
	while(n)
	{
		n=n&(n-1)
		count++;
	}
	return count;
}

解析

  • 举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。
  • 减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.
  • 我们发现减1的结果是把最右边的一个1开始的所有位都取反了。
  • 这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。
  • 如:1100&1011=1000.。就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.
  • 那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

将某一位的0变成1

  • 和1左移n位后按位或。
  • 要变回去的话:和1左移n位后,取反,然后按位与。

单目操作符

  • 只有一个操作数。
  • sizeof 是一个操作符 sizeof a 可以不加()。内部放的表达式不参与运算。在编译期间已经算过了

整型提升和截断

原因:

  • CPU内整型运算器(ALU)的操作数的字节长度,一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
  • 通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
  • 任何一个表达式都有两个属性:值属性类型属性
    • a+b值属性----类型属性int
    • short(s=a+3),s=5;推导出来a=2.

类型属性决定了以下几个方面:

  • 内存占用: 不同的数据类型占用的内存空间不同。例如,int 通常占用 4 字节,而 char 通常占用 1 字节。
  • 可进行的操作: 不同的数据类型支持不同的操作。比如,整数类型可以进行数学运算,而字符类型支持字符操作。
  • 合法值范围: 每种数据类型都有其合法值的范围。例如,unsigned int 可以存储的值范围是从 0 到最大值(通常为 2^32 - 1,取决于具体的系统架构)。
#include <stdio.h>
int i;  //未初始化,默认为0
int main()
{
    i--;     //-1 		
    if (i > sizeof(i))  		
    //但是sizeof的返回值类型实际为无符号整形,因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形
	//一个非常大的数字,超过4或者8  
    {
        printf(">\n"); //所以输出(>)
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}
  • 整型提升:补符号位
  • 整形截断:如int ->char 只保留最低位的8个比特位。最右边的是二进制是低位,最左边的是高位。

寻常算术转换

  • 向上转换
    • int ->unsigned int->long int
  • 向精度更高的转换
    • int ->folat

&&

  • 只有``a&&b:只有a和b同时为真,才会做什么。。。
    • 结合性:从左到右
  • 左边为假,后面的就不用算了,
  • 左边为真,后面也不用算了。

||

  • 有一个为真后面的就不用算了。

三目操作符

  • max=a>b?a:b
  • a大于b吗?如果大于,max取a的值,否则就取b的值。

逗号表达式()

  • 要从左往右以此计算,但其判断作用的是最后一个表达式的结果。

++、–

#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;
	c = ++a;     ++a://加给a+1,结果为6,用加完之后的结果给c赋值,因此:a = 6  c = 6
	b = ++c, c++, ++a, a++;   
		 	//++c:c为7 c++:c值不变 ++a:a的值为7,a++:a值不变,b取a的值:7
			//表达式结束时,c++和a++会给a和c分别加1,此时c:8,a:8,b:7
	b += a++ + c;   //a先和c加,结果为16,在加上b的值7,比的结果为23,最后给a加1,a的值为9
	printf("a = %d b = %d c = %d\n:", a, b, c); // a:9, b:23, c:8
	return 0;
}

复杂表达式的求值

有三个影响的因素

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序。
  4. 调试,反汇编,可看到对于相同优先级下:复杂表达式是如何运算的。

16进制数字与内存

在计算机中,16进制(十六进制)数字是一种表示方式,用于简化二进制(0和1)数字的书写。十六进制使用0-9表示数字0到9,使用A-F(或a-f)表示数字10到15。

内存模型:

  • 字节(Byte): 计算机内存的基本单位是字节,每个字节由8个二进制位(bit)组成。一个字节可以表示从0到255的数值,即在16进制中,从00到FF。

  • 内存地址: 内存中的每个字节都有一个唯一的地址,通常以16进制表示,这样更紧凑易读。

  • 数据类型: 在内存中,数据的存储形式和大小根据数据类型的不同而异:

    • 整数: 通常以4个字节(32位)或8个字节(64位)存储。比如,整数255在内存中以十六进制表示为FF
    • 字符: 字符串通常以ASCII或Unicode编码存储。ASCII字符在内存中每个字符占1个字节。
  • 十六进制与二进制有直接的对应关系,每一位十六进制数字可以用 4 位二进制数字表示。例如,十六进制数字 0 - F 分别对应二进制的 0000 - 1111。

示例:

  • 数字 255:

    • 在十六进制表示为 FF
    • 在内存中以字节表示为 0xFF
  • 字符 'A':

    • 在 ASCII 编码中,其十六进制值为 41
    • 在内存中以字节表示为 0x41

内存中的表示方式:

  • 假设一个整数变量在内存中占用 4 个字节(32 位),如果这个整数的值用十六进制表示为 0x12345678。
    • 从低位到高位,在内存中(小端序)的存储顺序是:78(二进制为 01111000)存储在最低地址处,然后是 56(二进制为 01010110),接着是 34(二进制为 00110100),最后是 12(二进制为 00010010)存储在最高地址处。
    • 在大端序的计算机系统中,存储顺序则是从高位到低位,即 12 存储在最低地址,78 存储在最高地址。

读取与写入:

在读取内存内容时,计算机根据内存地址从存储中提取数据,并可能转换为十六进制输出进行显示。

  • 总结:十六进制是一种方便的数字表示方式,有助于我们理解和操作内存中的数据,特别是在调试和系统编程时,它提供了一种更直观的查看方式。

大小端字节序

以下是大小端字节序的介绍:

定义

  • 在计算机系统中,对于多字节数据(如整数)在内存中的存储方式有两种不同的字节序,分别称为大端字节序(Big - Endian)和小端字节序(Little - Endian)。

大端字节序(Big - Endian)

  • 概念:大端字节序是指将数据的高位字节(Most Significant Byte,MSB)存储在内存的低地址处,而数据的低位字节(Least Significant Byte,LSB)存储在内存的高地址处。
  • 示例:例如,对于一个 4 字节的整数 0x12345678,在大端字节序的系统中,其在内存中的存储顺序是(假设从地址 0x0000 开始存储):0x0000 存储 0x12,0x0001 存储 0x34,0x0002 存储 0x56,0x0003 存储 0x78。
  • 常见系统:PowerPC、IBM 大型机等系统通常采用大端字节序。

小端字节序(Little - Endian)

  • 概念:小端字节序与大端字节序相反,它将数据的低位字节存储在内存的低地址处,而高位字节存储在内存的高地址处。
  • 示例:对于同样的 4 字节整数 0x12345678,在小端字节序的系统中,其在内存中的存储顺序是(假设从地址 0x0000 开始存储):0x0000 存储 0x78,0x0001 存储 0x56,0x0002 存储 0x34,0x0003 存储 0x12。
  • 常见系统:x86、x64 等常见的 PC 架构系统采用小端字节序。

字节序的影响

  • 数据传输和存储:在不同字节序的系统之间进行数据传输(如网络通信)或读取存储在不同系统上的数据时,需要注意字节序的转换问题,否则可能会导致数据解析错误。
  • 程序编写和调试:在处理多字节数据时,程序员需要清楚所使用的系统的字节序,以确保程序能够正确地处理数据。例如,在进行位操作、指针操作以及与硬件设备交互时,字节序可能会对结果产生影响。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值