您好,这里是limou3434的一篇博客,感兴趣您可以看看我的其他博文系列。本次我主要给您带来了C语言符号相关的知识。
实际上在编译期间,去注释的本质是:将注释替换成空格
C++风格注释是使用“//”将一整行代码行全部注释,而“//注释”还可以使用续行符“\”接替注释另起一行。
/*合法*/#/*合法*/define/*合法*/STR/*合法*/abcd/*合法*/efgh
//这个宏的定义是没有问题的,注意宏的#和define之前可以留有空格
“/**/”不可以嵌套使用,这是因为C语言风格注释遵循就近原则,如果允许嵌套在这里就会出现问题,注意带有指针解引用的除法和C风格注释不能混用。
int x = 10;
int* p = &x;
int y = 20;
y = x/*p;
int x = 10;
int* p = &x;
int y = 20;
y = x/(*p);//或者写成y = x / *p
“\”在C语言中有两个最基本的用途,一是换行符,二是转义符
续行符是为了提高代码的可读性存在的,在续行符后面不要包含空格(加上“\”其实会加强对“换行”的自描述)
使得一些特殊字符不以字面值的形式出现,或者反过来特殊转字面
//1.字面转特殊
printf("n /n");//其中n是字面值,'\n'是转移后的特殊字符
//2.特殊转字面
printf(""");//出错
printf("\"");//打印出双引号符号
#include
#include
int main()
{
const char* lable = "|/-\\";
int index = 0;
while (1)
{
index %= 4;
printf("[%c]\r", lable[index]);
index++;
Sleep(20);
}
return 0;
}
#include
int main()
{
char ch = '1';//这里实际上一直在发生一个现象,就是截断4个字节数据写到1个字节的数据,因为在C99标准规定,'a'叫做整型字符常量,被看作是int类型
printf("%zd", sizeof('1'));//因此这里输出的是4(字节),而不是1(字节)
return 0;
}
但是如果是在C++中运行就会看到这里输出的是1而非4了
#include
int main()
{
printf("%zd\n", sizeof("abcd"));
printf("%zd\n", sizeof("ab"));
printf("%zd\n", sizeof(""));//因为有'\0'的存在,并且每个字符都是按char存储
return 0;
}
//输出5、3、1
计算机是为人类所服务的,计算机只识别二进制,为了和人类产生交互,就存在了ASCII码值表等字符集合
int flag = 0;
scanf("%d", &flag);
flag && function();//其中function()是在别处定义的代码,其是否执行取决于flag的输入值,同样也可以使用“||”来形成判断语句
#include
int main()
{
int i = -1;
//1000 0000|0000 0000|0000 0000|0000 0001(原)
//1111 1111|1111 1111|1111 1111|1111 1110(反)
//1111 1111|1111 1111|1111 1111|1111 1111(补)
int j = -3;
//1000 0000|0000 0000|0000 0000|0000 0011(原)
//1111 1111|1111 1111|1111 1111|1111 1100(反)
//1111 1111|1111 1111|1111 1111|1111 1101(补)
int z = i ^ j;
//1111 1111|1111 1111|1111 1111|1111 1111(补)
//1111 1111|1111 1111|1111 1111|1111 1101(补)
// ^
//-------------------------------------------
//0000 0000|0000 0000|0000 0000|0000 0010(a^b后,补,检测到是正数)
//0000 0000|0000 0000|0000 0000|0000 0010(反)
//0000 0000|0000 0000|0000 0000|0000 0010(原)
printf("%d\n", z);
printf("%d\n", ~(i));
int a = -2;
//1000 0000|0000 0000|0000 0000|0000 0010(原)
//1111 1111|1111 1111|1111 1111|1111 1101(反)
//1111 1111|1111 1111|1111 1111|1111 1110(补)
int b = 3;
//0000 0000|0000 0000|0000 0000|0000 0011(原)
//0000 0000|0000 0000|0000 0000|0000 0011(反)
//0000 0000|0000 0000|0000 0000|0000 0011(补)
int c = a | b;
//1111 1111|1111 1111|1111 1111|1111 1110(补)
//0000 0000|0000 0000|0000 0000|0000 0011(补)
// |
//-------------------------------------------
//1111 1111|1111 1111|1111 1111|1111 1111(a|b后,补,检测到是负数)
//1111 1111|1111 1111|1111 1111|1111 1110(反)
//1000 0000|0000 0000|0000 0000|0000 0001(原)== -1
printf("%d\n", c);
return 0;
}
#include
//置1
#define SETBIT(x, n) (x |= (1<<(n - 1)))
//置0
#define CLRBIT(x, n) (x &= ~(1<<(n - 1)))
void ShowBits(int x)
{
int num = sizeof(x) * 8 - 1;
while (num >= 0)
{
if (x & 1 << num)
{
printf("1 ");
}
else
{
printf("0 ");
}
num--;
}
}
int main()
{
int x = 0;
//1.设置指定比特位为1
SETBIT(x, 3);
SETBIT(x, 5);
CLRBIT(x, 5);
//2.显示int的所有bit位
ShowBits(x);
return 0;
}
int main()
{
char ch = 0;
printf("%zd\n", sizeof(ch));
printf("%zd\n", sizeof(~ch));
printf("%zd\n", sizeof(ch >> 1));
printf("%zd\n", sizeof(ch << 1));
printf("%zd\n", sizeof(!ch));
return 0;
}
//可以看到输出了1,4,4,4,1(最后一个数字在linux中是4)
这样输出的原因是无论是何种运算符,在进行计算的时候,目标都是要计算机进行计算。而计算机中只有CPU具有运算能力,计算的数据都要从内存中拿取到CPU的寄存器中。寄存器的位数和计算机的位数是一样的,因此char类型的数据读到寄存器中,也只能填补8个位,高24位只能进行整型提升。并且sizeof不是函数是关键字,sizeof在编译期间就硬编码进程序,而正是在整型提升这个阶段进行计算的,而后续ch变量又被截断(这样仅仅是简单理解,忽略了很多细节)。
另外通过最后一个数也可以看到,不同编译器对“整型提升”可能还存在不同的解释(Linux的可能会比较准确)
最重要的是右移操作符,分为“逻辑右移”和“算术右移”两种,前者补0,后者补符号位(这会影响到是否“*”或“/”2的问题)。
而在VS中右移是算术右移动,而如何右移则取决于自身的类型,和保存什么数据是没有关系的。
在使用位移操作的时候也需要注意优先级的问题。
unsigned i = -1;
i = i >> 1;//补0而不是补1,因为-1的数据类型是unsigned
0x01 << 2 + 3;//加法运算符的优先级大于右移操作符,注意不能移动负数或者超出计算机位数的整数
花括号的作用是“打包”
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
00007FF7D73D1750 push rbp
00007FF7D73D1752 push rdi
00007FF7D73D1753 sub rsp,128h
00007FF7D73D175A lea rbp,[rsp+20h]
00007FF7D73D175F lea rcx,[__79481D6E_main@c (07FF7D73E1008h)]
00007FF7D73D1766 call __CheckForDebuggerJustMyCode (07FF7D73D1343h)
int a = 10;
00007FF7D73D176B mov dword ptr [a],0Ah//在内存中开辟空间a,放入值0Ah
int b = a++;
00007FF7D73D1772 mov eax,dword ptr [a]//把内存的数据a移动到CPU里的eax寄存器
00007FF7D73D1775 mov dword ptr [b],eax//把eax的内容放到b里面
00007FF7D73D1778 mov eax,dword ptr [a]//把内存的数据a移动到CPU里的eax寄存器
00007FF7D73D177B inc eax//将eax自增
00007FF7D73D177D mov dword ptr [a],eax//将计算结果从寄存器移动到内存
return 0;
00007FF7D73D1780 xor eax,eax
}
00007FF7D73D1782 lea rsp,[rbp+108h]
00007FF7D73D1789 pop rdi
00007FF7D73D178A pop rbp
00007FF7D73D178B ret
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
00007FF6A29A1B70 push rbp
00007FF6A29A1B72 push rdi
00007FF6A29A1B73 sub rsp,108h
00007FF6A29A1B7A lea rbp,[rsp+20h]
00007FF6A29A1B7F lea rcx,[__79481D6E_main@c (07FF6A29B1008h)]
00007FF6A29A1B86 call __CheckForDebuggerJustMyCode (07FF6A29A1343h)
int a = 10;
00007FF6A29A1B8B mov dword ptr [a],0Ah
a++;
00007FF6A29A1B92 mov eax,dword ptr [a]
00007FF6A29A1B95 inc eax
00007FF6A29A1B97 mov dword ptr [a],eax
return 0;
00007FF6A29A1B9A xor eax,eax
}
00007FF6A29A1B9C lea rsp,[rbp+0E8h]
00007FF6A29A1BA3 pop rdi
00007FF6A29A1BA4 pop rbp
00007FF6A29A1BA5 ret
C语言、C++、Java默认都是向零取整
#include
int main()
{
int i = 2.9;
int j = -2.9;
printf("%d %d", i, j);
return 0;
}//可以看到输出了“2 -2”
#include
#include
int main()
{
printf("%d %d", (int)trunc(2.9), (int)trunc(-2.9));
return 0;
}//也是输出零向取整的结果“2 -2”
floor(-2.9);//-3.0
floor(-2.1);//-3.0
floor(2.9);//2.0
floor(2.1);//2.0
ceil(-2.9);//2.0
ceil(-2.1);//2.0
ceil(2.9);//3.0
ceil(2.1);//3.0
round(2.1);//2.0
round(2.9);//3.0
round(-2.1);//-2.0
round(-2.9);//-3.0
以下是python中代码结果
C:\Users\DELL>python
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> -10//3
-4
>>> -10%3
2
以下是C语言中代码结果
#include
int main()
{
int a = -10;
int b = 3;
printf("%d\n", a / b);//-3
printf("%d\n", a % b);//-1
return 0;
}
在考虑运算符和优先级之前还会做一个事情,即“符号匹配”。C的符号在进行匹配的时候,使用了贪心算法:每一个符号从左到右应该包含尽可能多的字符。
例如“==”中间不能加入空白、“a+++10”应该理解为“a++ + 10”、“i+++++j”应该写成“i++ + ++j”,因此在平时写代码的时候,还是要要注意适当空格(这有可能会影响到语法使用!而不仅仅是为了美观)。
甚至在有的编译器没有对贪心实现完全,哪怕是正确的语法没有给与空格,还会进行报错。
其本质是“数据在寄存器中的存储顺序不一样”,例如:对于“a * b + c * d”,光从语法层次是无法判断先进行"a * b"还是“c * d”,具体先计算谁由编译器决定,我们唯一可以确定地是:两个乘法运算永远比中间的加法运算优先。
今天我带您了解了:注释、续行符/转义符、单双引号等有关符号的使用细节,也许这些扣得比较细,您只需了解即可,或者至少不会因为这些细小琐碎得点而犯错。本节中提到的关于取余/取模运算,这里也仅作复习,更加详细的取整细节,您可以去我的上一篇博文C语言32个关键字看看,那里会更加详细……
最后,望与君共勉。