> 文章列表 > C- 符号

C- 符号

C- 符号

文章目录

    • 符号
        • `#ifdef-#endif`
        • \\
          • 接续符
          • 转义
            • 旋转光标
            • 数字倒计时
        • 单引号-双引号
        • 逻辑运算符&& ||
          • 短路
        • 位运算符
          • 异或
          • 位运算最好使用定义好的宏
          • 左移右移
        • ++
        • 取整
          • 0向取整(C中默认取整方式)
          • floor地板取整
          • ceil
          • round 四舍五入
        • 取模
          • 取余和取模一样吗?
        • 运算符优先级

符号

#ifdef-#endif

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

  • #ifdef-#endif

C- 符号

C- 符号

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

C- 符号

\\

接续符
  • 使用\\之后,后面不要有任何字符,包括空格.
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);}
}

单引号-双引号

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

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

C- 符号

  • 为什么这么写也中?

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

int main()
{//char c1 = 'abscd';//errorchar 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); //3printf("%d\\n", 2 & 3); //2printf("%d\\n", 2 ^ 3); //1printf("%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;//设置指定比特位是1SETBIT(x,5);//显示int的所有比特位ShowBit(x);
}
  • 整形提升(不同的编译器方式不同)

C- 符号

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

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

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

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

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

C- 符号

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

++

后置++

C- 符号

前置++

C- 符号

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

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

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

相同的一份代码:

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

vs环境下:

C- 符号

Linux 环境下:

C- 符号

  • 表达式匹配的规则是贪心算法(根据空格的划分)
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); //结果是:-2printf("%d\\n", j); //结果是:2system("pause");return 0;
}

C- 符号

floor地板取整

朝向负无穷大取整.-∞

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

C- 符号

故,大家对取模有了一个修订版的定义:
如果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); //结果:-3printf("%d\\n\\n", -10 % 3); //结果:-1 为什么? -10=(-3)*3+(-1)printf("%d\\n", 10 / -3); //结果:-3printf("%d\\n\\n", 10 % -3); //结果:1 为什么?10=(-3)*(-3)+1system("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)); //-2printf("%d\\n", 5 % (-2)); //5=(-2)*(-2)+1system("pause");return 0;
}

运算符优先级