C-操作符详解

1.进制转换

1.1 10进制转2进制

方法:短除法

1.2 2进制转换8进制
8进制的数字每⼀位是0~7的,0~7的数字,各⾃写成2进制,最多有3个2进制位就⾜够了,⽐如7的⼆进制是111,所以在2进制转8进制数的时候,从2进制序列中右边低位开始向左每3个2进制位会换算⼀个8进制位,剩余不够3个2进制位的直接换算。
如:2进制的01101011 ,换成8进制:0153 ,++0开头的数字,会被当做8进制。++

我有一个问题:怎么区别 0111是8进制还是2进制?
1.3 2进制转换16进制
2进制转化16进制和2进制转换8进制十分相似,
16进制的数字每⼀位是0~9,a ~f 的,0~9,a ~f的数字,各⾃写成2进制,最多有4个2进制位就⾜够了,
⽐如 f 的⼆进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进
制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。
如:2进制的01101011,换成16进制:0x6b,16进制表⽰的时候前面加0x
2 原码,反码,补码
整数 的2进制表⽰⽅法有三种,即原码、反码和补码
有符号整数的三种表⽰⽅法均有符号位数值位 两部分,++2进制序列中,最⾼位的1位是被当做符号++
++位,剩余的都是数值位。++
符号位都是⽤0表⽰"正",⽤1表⽰"负"。
正整数的原、反、补码都相同。
负整数的三种表⽰⽅法各不相同。
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
++反码得到原码也是可以使⽤:取反,+1的操作++
对于整形来说:数据存放中其实存放的是补码。

cpp 复制代码
//操作符详解
#include<stdio.h>

int main()
{
	int num1 = 10;
	//10存放在整型变量num1中,占4个字节==32个bit位
	//0000 0000 0000 0000 0000 0000 0000 1010 -原码
	//0000 0000 0000 0000 0000 0000 0000 1010 -反码
	//0000 0000 0000 0000 0000 0000 0000 1010  -补码

	//正数的原码,反码,补码都一样
	int num2 = -10;
	//-10存放在整型变量num2中,占4个字节==32个bit位
	//1000 0000 0000 0000 0000 0000 0000 1010 -原码
	//1111 1111 1111 1111 1111 1111 1111 0101 -反码
	//1111 1111 1111 1111 1111 1111 1111 0110 -补码

	//负数的原码,反码,补码要经过计算
	return 0;
}

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

cpp 复制代码
1-1
 1+(-1)
 0000 0000 0000 0000 0000 0000 0000 0001 -1的补码
 1111 1111 1111 1111 1111 1111 1111 1111 -(-1)的补码
 相加
1 0000 0000 0000 0000 0000 0000 0000 0000
 最高位被丢弃
 结果是0

3 移位操作符(移动的是补码)

3.1左移操作符

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

cpp 复制代码
#include<stdio.h>

int main()
{
	int num = 10;
	int n = num << 1;
	printf("n= %d\n", n);
	printf("num= %d\n", num);
	return 0;
}

3.2 右移操作符

右移操作符比左移操作符稍微复杂一点。
首先他的移位规则就分为两种:

  1. 逻辑右移:左边⽤0填充,右边丢弃
  2. 算术右移:左边⽤原该值的符号位填充,右边丢弃

应用哪种规则取决于编译器,目前大部分编译器上使用的算术右移,

cpp 复制代码
#include <stdio.h>
int main()
{
	int num = -10;
	int n = num >> 1;
	printf("n= %d\n", n);//-5
	printf("num= %d\n", num);//-10
	return 0;
}

逻辑右移:

算术右移

警告⚠️:
对于移位运算符,不要移动负数位,这个是标准未定义的。

cpp 复制代码
int num = 10;
num>>-1;//error
  1. 位操作符:& | ^ ~

注意:他们操作的对象必须是整数

区别: && (逻辑与)||(逻辑或),他们是逻辑操作符

cpp 复制代码
#include <stdio.h>
int main()
{
	int num1 = 3;
	//0000 0000 0000 0000 0000 0000 0000 0011  3的补码
	int num2 = -5;
	//1111 1111 1111 1111 1111 1111 1111 1011   -5的补码
	// 
	//按照(二进制)位进行运算

	printf("%d\n", num1 & num2);//3
	//& 有0就是0,全1为1
	//0000 0000 0000 0000 0000 0000 0000 0011

	printf("%d\n", num1 | num2);//-5
	//| 有1为1,全0为0
	//1111 1111 1111 1111 1111 1111 1111 1011   

	printf("%d\n", num1 ^ num2);//-8
	//^ 相同为0,相异为1
	//1111 1111 1111 1111 1111 1111 1111 1100

	printf("%d\n", ~0);//-1
	//~ 按位取反
	// 1111 1111 1111 1111 1111 1111 1111 1111 -(-1)的补码

	return 0;
}

拓展⾯试题:
不能创建临时变量(第三个变量),交换两个整数
方法:使用^

cpp 复制代码
#include <stdio.h>
int main()
{
	int a = 10;
	//0000 0000 0000 0000 0000 0000 0000 1010
	int b = 20;
	//0000 0000 0000 0000 0000 0000 0001 0100  
	a = a ^ b;
	//a=
	//0000 0000 0000 0000 0000 0000 0001 1110
	//30
	b = a ^ b;
	//b=
	//0000 0000 0000 0000 0000 0000 0000 1010
	//10

	a = a ^ b;
	//a=
	//0000 0000 0000 0000 0000 0000 0001 0100  
	//20
	printf("a = %d b = %d\n", a, b);
	return 0;
}

练习1 :编写代码实现 :求一个整数存储在二进制中1的个数

cpp 复制代码
int main()
{
	//方法1:&的应用
	int num =5;
	int i = 0; int count = 0;
	for (i = 0; i < 32; i++)
	{
		if (((num >> i) & 1) == 1)//不用考虑正负数,移位操作符移动的是补码
			count++;
	}
	printf("%d", count);
	return 0;
}

问题: 什么时候要考虑正负数嘞?

cpp 复制代码
#include <stdio.h>

int count_one_bit(unsigned int n)
//int count_one_bie(int n) 针对负数就可能出错
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
			count++;
		n = n / 2;
	}
	return count;
}

方法3:

cpp 复制代码
//最高效的一种
int count_one_bit(unsigned int n)
{
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	return count;
}
  1. 单目操作符
    单⽬操作符有这些:
    !、 ++ 、 -- 、 & 、 * 、 + 、 - 、 ~ 、 sizeof 、 ( 类型 )

6.逗号表达式
逗号表达式,就是⽤逗号隔开的多个表达式。

逗号表达式,从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。
7.下标访问[]、函数调⽤()
7.1 [ ] 下标引⽤操作符
操作数:⼀个数组名 + ⼀个索引值

cpp 复制代码
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
//[ ]的两个操作数是arr和9。

7.2 () 函数调用操作符

cpp 复制代码
#include <stdio.h>
void test1()
{
 printf("hehe\n");
}
void test2(const char *str)
{
 printf("%s\n", str);
}
int main()
{
 test1(); //这⾥的()就是作为函数调⽤操作符。
 test2("hello bit.");//这⾥的()就是函数调⽤操作符。
 return 0;
}

8.结构体访问操作符
C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类
型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要 名字、年龄、学号、⾝⾼、体重等;描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型。
📌 结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚至是其他结构体
结构体声明

cpp 复制代码
struct Stu
{
	char name[20];
	int age;
	float score;
}stu;//全局变量

结构体的使用

cpp 复制代码
//结构体的声明
struct Stu
{
	char name[20];
	int age;
	float score;
}stu = {"Bob",18,97.5f};//全局变量的初始化

int main()
{
	//结构体变量的初始化
	struct Stu s1 = { "Mary",15,96.5 };//局部变量
	//结构体的直接访问
	printf("%s %d %lf\n", s1.name, s1.age, s1.score);
	printf("%s %d %lf\n", stu.name, stu.age, stu.score);
	//结构体的间接访问
	struct Stu* ps = &s1;
	printf("%s %d %lf\n", ps->name,ps->age,ps->score);
	return 0;
}
  1. 操作符的优先级和结合性

9.1 优先级
优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。
1 3 + 4 * 5 ;
⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。
9.2结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )。
1 5 * 6 / 2 ;
上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 ,
再计算 6 / 2 。 运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概
记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看下⾯表格就可以了。
• 圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符( -- )
• 单⽬运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )
由于圆括号的优先级最⾼,可以使用它改变其他运算符的优先级。

C语言中,可以通过数字的前缀来区分二进制和八进制。如果一个数字以0开头,则表示它是八进制数;如果一个数字以0b或0B开头,则表示它是二进制数。

例如,对于数字0111,根据前缀0,它被视为八进制数。要将其转换为十进制数,可以使用C语言中的atoi()函数或strtol()函数。

下面是一个示例代码:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    char *binary = "0111";
    int decimal = strtol(binary, NULL, 8);
    
    printf("Decimal value: %d\n", decimal); // 输出:73
    
    return 0;
}

在上面的代码中,我们将字符串"0111"转换为八进制数73,并将其打印为十进制数

相关推荐
Elihuss1 小时前
ONVIF协议操作摄像头方法
开发语言·php
lb36363636363 小时前
分享一下arr的意义(c基础)(必看)(牢记)
c语言·知识点
Swift社区4 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht4 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht4 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20244 小时前
Swift 数组
开发语言
南东山人5 小时前
一文说清:C和C++混合编程
c语言·c++
stm 学习ing5 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc6 小时前
《Python基础》之字符串格式化输出
开发语言·python