C- 符号

本文介绍了C语言中的一些基础概念,包括#ifdef/#endif条件编译,转义字符,逻辑运算符短路,位运算符的用法,特别是异或操作和位移操作。还讨论了取整的不同方法,如0向取整、地板取整、天花板取整和四舍五入。此外,文章还探讨了取模与取余的区别以及运算符优先级。
摘要由CSDN通过智能技术生成

符号

#ifdef-#endif

基于条件编译,代码编译期间处理 gcc -E test1.c -o test.i,生成预处理之后的.i文件查看宏替换,注释去掉的情况.

  • #ifdef-#endif

image-20230412144011453

image-20230412144217157

  • #if-#endif表达式是否为1

image-20230412144459359

\
接续符
  • 使用\之后,后面不要有任何字符,包括空格.
int main()
{
    int a = 1;
    int b = 2;
    int c = 3;
    //试试在\之后带上空格,行不行?
    //试试在\之前带上空格,行不行?
    //建议:不要带
    if (a == 1 &&\
        b == 2 &&\
        c == 3){
        printf("hello world!\n");
    } 
    system("pause");
    return 0;
}
转义
int main()
{
	printf("\""); //特殊转字面
	printf("\n");//字面转特殊
	return 0;
}
  • 回车\r:光标回到当前行的位置

  • 换行\n:光标移动到下一行

旋转光标
int main()
{
	const char* lable = "|/-\\";
	int index = 0;
	while (1)
	{
		index %= 4;
		printf("[%c]\r",lable[index]);
		index++;
		Sleep(300);
	}
}
数字倒计时
int main()
{
	int i = 15;
	for (; i >= 0; i--)
	{
		printf("[%2d]\r",i);
		Sleep(1000);
	}
}
单引号-双引号

变量是帮助我们操纵字面值常量运算的.

  • 不同的语言识别’'的字符常量是不一样的.

image-20230412154456215

  • 为什么这么写也中?

因为C识别’'时就开辟的四字节空间.但是存的只有一个字节.

int main()
{
	//char c1 = 'abscd';//error
	char c2 = 'abcd';
    printf("%c\n",c2);//d
}
逻辑运算符&& ||
短路

当前面的条件成立时执行后面的代码

int show()
{
    printf("you can see me!\n");
    return 1;
} 
int main()
{
    int a = 0;
    scanf("%d", &a);
    a == 10 && show();
    system("pause");
    return 0;
}
位运算符
int main()
{
	printf("%d\n", 2 | 3); //3
	printf("%d\n", 2 & 3); //2
	printf("%d\n", 2 ^ 3); //1
	printf("%d\n", ~0); //-1 按位取反符号位是参与运算的
	system("pause");
	return 0;
}
异或

任何数字和0异或都是他本身,和自己异或都是0.支持交换律和结合律.

  • 交换值(不存在相加进位导致类型溢出的问题)
int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("%d %d\n",a,b);
	system("pause");
}
位运算最好使用定义好的宏
#define SETBIT(x,n) x|=(1<<(n-1))//注意赋值操作
#define RESTBIT(x,n) x&=(~(1<<(n-1)))
void ShowBit(int x)
{
	int num = sizeof(x)*8-1;
	while (num>=0)
	{
		if (x & (1<<num))
		{
			printf("1");
		}
		else
		{
			printf("0");
		}
		num--;
	}
	printf("\n");
}
int main()
{
	int x = 0;
	//设置指定比特位是1
	SETBIT(x,5);
	//显示int的所有比特位
	ShowBit(x);
}
  • 整形提升(不同的编译器方式不同)

image-20230413113542390

在编译期间就已经处理好了,被硬编译为数字.

无论任何位运算符,目标都是要计算机进行计算的,而计算机中只有CPU具有运算能力(先这样简单理解),但计算的数据,都在内存中。

故,计算之前(无论任何运算),都必须将数据从内存拿到CPU中,拿到CPU哪里呢?毫无疑问,在CPU 寄存器中。而寄存器本身,随着计算机位数的不同,寄存器的位数也不同。一般,在32位下,寄存器的位数是32位。

可是,你的char类型数据,只有8比特位。读到寄存器中,只能填补低8位,那么高24位呢?就需要进行“整形提升”。

左移右移
<<(左移): 最高位丢弃,最低位补零
>>(右移):
1. 无符号数:最低位丢弃,最高位补零[逻辑右移](2倍操作)
2. 有符号数:最低位丢弃,最高位补符号位[算术右移]  
补0还是1跟数据是没有关系的,数据正负都需要转化为补码写入内存
向外读取进行补0 or 1是看数据类型的,无符号就补0,有符号就具体来.

image-20230413115751037

丢弃基本理解链:
<< 或者 >> 都是计算,都要在CPU中进行,可是参与移动的变量,是在内存中的。所以需要先把数据移动到CPU内寄存器中,在进行移动。
那么,在实际移动的过程中,是在寄存器中进行的,即大小固定的单位内。那么,左移右移一定会有位置跑到"外边"的情况

++
后置++

image-20230416143445486

前置++

image-20230416143643489

如果没人用于赋值使用,前置和后置没有区别.

  • 初始化有一条汇编语句,赋值是两条汇编语句.
复杂表达式

在不同的编译器中,因为表达式的计算路径不唯一(编译器识别表达式是同时加载到寄存器还是分批加载,时不确定的)导致的.

相同的一份代码:

int main()
{
	int i = 1;
	int j = (++i) + (++i) + (++i);
	printf("%d\n",j);
}

vs环境下:

image-20230416144702342

Linux 环境下:

image-20230416145452687

  • 表达式匹配的规则是贪心算法(根据空格的划分)
int main()
{
	int a = 10;
	int b = 20;
	printf("%d\n",a++ + ++b);//31
}
取整
0向取整(C中默认取整方式)
int main()
{
	//本质是向0取整
	int i = -2.9;
	int j = 2.9;
	printf("%d\n", i); //结果是:-2
	printf("%d\n", j); //结果是:2
	system("pause");
	return 0;
}

image-20230416150821119

floor地板取整

朝向负无穷大取整.-∞

//demo 2
#include <stdio.h>
#include <math.h> //因为使用了floor函数,需要添加该头文件
#include <windows.h>
int main()
{
    //本质是向-∞取整,注意输出格式要不然看不到结果
    printf("%.1f\n", floor(-2.9)); //-3
    printf("%.1f\n", floor(-2.1)); //-3
    printf("%.1f\n", floor(2.9)); //2
    printf("%.1f\n", floor(2.1)); //2
    system("pause");
    return 0;
}
ceil
#include <stdio.h>
#include <math.h>
#include <windows.h>
int main()
{
    //本质是向+∞取整,注意输出格式要不然看不到结果
    printf("%.1f\n", ceil(-2.9)); //-2
    printf("%.1f\n", ceil(-2.1)); //-2
    printf("%.1f\n", ceil(2.9)); //3
    printf("%.1f\n", ceil(2.1)); //3
    system("pause");
    return 0;
}
round 四舍五入
#include <stdio.h>
#include <math.h>
#include <windows.h>
int main()
{
    //本质是四舍五入
    printf("%.1f\n", round(2.1));
    printf("%.1f\n", round(2.9));
    printf("%.1f\n", round(-2.1));
    printf("%.1f\n", round(-2.9));
    system("pause");
    return 0;
}
取模

取模概念:

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r 且0 ≤ r < d。其中,q被称为商,r 被称为余数.

image-20230416154619912

故,大家对取模有了一个修订版的定义:
如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r , q 为整数,且0|r|
< |d|。其中,q 被称为商,r 被称为余数。
有了这个新的定义,那么C中或者Python中的“取模”,就都能解释了。
解释C: -10 = (-3) * 3 + (-1)
解释Python:-10 = (?)* 3 + 2,其中,可以推到出来,'?'必须是-4(后面验证).-10 =-4* 3 + 2,才能满足定义。注意:python中 / 默认是浮点数除法,// 才是整数除法,并进行-∞取整
所以,在不同语言,同一个计算表达式,负数“取模”结果是不同的。我们可以称之为分别叫做正余数 和 负余数.  

由上面的例子可以看出,具体余数r的大小,本质是取决于商q的。而商,又取决谁呢?取决于除法计算的时候,取整规则 .

取余和取模一样吗?
  • 本质 1 取整:

取余:尽可能让商,进行向0取整。
取模:尽可能让商,向-∞方向取整。
故:
C中%,本质其实是取余。
Python中%,本质其实是取模。(后面不考虑python,减少难度)
理解链:

  • 对任何一个大于0的数,对其进行0向取整和-∞取整,取整方向是一致的。故取模等价于取余

  • 对任何一个小于0的数,对其进行0向取整和-∞取整,取整方向是相反的。故取模不等价于取余

同符号数据相除,得到的商,一定是正数(正数vs正整数),即大于0!
故,在对其商进行取整的时候,取模等价于取余。

  • 本质 2 符号:参与取余的两个数据,如果同符号,取模等价于取余
int main()
{
	printf("%d\n", -10 / 3); //结果:-3
	printf("%d\n\n", -10 % 3); //结果:-1 为什么? -10=(-3)*3+(-1)
	printf("%d\n", 10 / -3); //结果:-3
	printf("%d\n\n", 10 % -3); //结果:1 为什么?10=(-3)*(-3)+1
	system("pause");
	return 0;
}
//如果不同符号,余数的求法,参考之前定义。C等向0取整的语言,余数符号,与被除数相同

结论:如果参与取余的两个数据符号不同,在C语言中(或者其他采用向0取整的语言如:C++,Java),余数符号,与被除数相同。

参与取余的两个数据,如果同符号,取模等价于取余

  • 推导
如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r , q 为整数,且0 ≤ |r|< |d|。其中,q 被称为商,r 被称为余数。
a = q*d + r 变换成 r = a - q*d 变换成 r = a + (-q*d)
对于:x = y + z,这样的表达式,x的符号 与 |y||z|中大的数据一致
而r = a + (-q*d)中,|a||-q*d|的绝对值谁大,取决于商q的取整方式。
c是向0取整的,也就是q本身的绝对值是减小的。
如:
-10/3=-3.333.33 向0取整 -3. a=-10 |10|, -q*d=-(-3)*3=9
10/-3=-3.333.33 向0取整 -3. a=10 |10|, -q*d=-(-3)*(-3)=-9  q*d 变小了
python是向-∞取整的,也就是q本身的绝对值是增大的。
-10/3=-3.333.33 '//'向-∞取整 -4. a=-10 |10|, -q*d=-(-4)*3=12 |12|
10/-3=--3.333.33 '//'向-∞取整 -4. a=10 |10|, -q*d=-(-4)*(-3)=-12 |12|
绝对值都变大了,-10+12>0,就和被除数符号不同了.
int main()
{
	printf("%d\n", 5 / (-2)); //-2
	printf("%d\n", 5 % (-2)); //5=(-2)*(-2)+1
	system("pause");
	return 0;
}
运算符优先级
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值