【深度解刨C语言】符号篇(全)

2023-05-16

文章目录

  • 一.注释
  • 二.续行符与转义符
    • 1.续行符
    • 2.转义符
  • 三.回车与换行
  • 四.逻辑操作符
  • 五.位操作符和移位操作符
  • 六.前置++与后置++
  • 七.字符与字符串
  • 八./和%
    • 1.四种取整方式
    • 2.取模与取余的区别和联系
    • 3./两边异号的情况
      • 1.左正右负
      • 2.左负右正
  • 九.运算符的优先级

一.注释

注释的两种符号:

  1. // +内容
  2. /*+内容 */
  3. 说明:/*只会与最近的 */进行匹配。
int main()
{
	//这是一段注释
	/*这是一段注释*/
	return 0;
}

说明:注释在预处理阶段(准确的来说是预编译)就被删除了,而与之代替的是空格,所以注释的本质上其实是空格。注释的意义在于提高代码的可读性
4. 补充:
1.空格:C语言中空格是用来当做符号识别的分割符
2:预处理器识别符号的基本方式是贪心法,所谓贪心指的是,编译器会尽可能的识别多的字符,(也就是说编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么就再读一个字符,判断已经读入的两个字符组成的字符串是否可能是一个字符的组成部分;如果可能,继续读入下一个字符,判断已经读入的两个字符是否可能是一个符号的组成部分;如果可能继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已经不再可能组成一个有意义的字符)——<<C陷阱与缺陷>>,这样做是为了满足大多数的情况,而少数的情况需要我们自己对符号进行分割,那具体如何分割,当然用空格了。

例1:

#include<stdio.h>
int main()
{	
	int a = 1;
	int* p = &a;
	int b = a/ *p;//注意:这里的/*之间需加一个空格,否则会被编译器判断为注释符号
	return 0;
}

例2:

int main()
{
	int a = 1;
	int b = 2;
	int c = a-- - b;//是a后置减减再减去b
	printf("%d\n", c);//答案是-1
	a = 1;
	b = 2;
	c = a - --b;//是b前置--,然后a再减去b
	printf("%d\n", c);//答案:0
	return 0;
}

说明:像这类准二义性的问题,有时候会给我们带来麻烦。

不好的注释方式:
例1:

#if 0
int main()
{
	printf("hello world\n");
	return 0;
}
#endif

例2:

int main()
{
	if (0)
	{
		printf("hello world\n");
	}
	return 0;
}

说明:这样注释的方式并不明显,所以阅读代码时,并不确定这段代码是否是一段注释,所以不建议这样进行注释。

二.续行符与转义符

1.续行符

逻辑表达式:

#include<stdio.h>
int main()
{
	int a = 1;
	int b = 1;
	int c = 0;
	int d = 1; 
	if (a == 1 &&\
		b == 1 &&\
		c == 0 &&\
		d == 1)
	{
		printf("hello world\n");
	}
	return 0;
}

说明:不带续行符也是可以的,但这就好像不写函数参数默认为int一样,所以严格一点,还是建议带上续行符号
注意:续行符后不能带空格

字符串:

int main()
{
	char* p = "abcd\
efg";//这是为了处理比较长的字符串而准备的,我们这里就暂且用一下。
	printf("%s\n", p);//字符串的续行符是不会被打印出来的
	return 0;
}

2.转义符

在这里插入图片描述

说明:\在这里是改变原字符的意思,具体有以上14种。
注意:单个\出现是不能被打印,要想打印需把\转义也就是\\。

例1:

#include <stdio.h>
int main()
{
	 printf("c:\code\test.c\n");
	 int len = strlen("c:\code\test.c\n");//这里的\是打不出来的
	 printf("%d",len);//len是13
    return 0;
}

解释:

1——c,
2——:
3——\c(注意在vs 2019中 \加上字符被编译器认为是一个转义字符),
4——0,
5——d;
6——e,
7——\t,
8——e,
9——s,
10——t,
11—— .
12——c,
13——\n,

例2:

#include<stdio.h>
#include<string.h>
int main()
{
	printf("%d\n",strlen("c:\\code\\test.c\\n"));
	//这样能打印出\了,但是字符的个数比前面多了3个——总共16个字符
	printf("c:\\code\\test.c\\n");
	return 0;
}

效果:
在这里插入图片描述
例3:

#include<stdio.h>
int main()
{
	printf("\"");//这里\是改变"的意思,而能被打印出来
	return 0;
}

说明:"会自动与最近的”进行匹配

三.回车与换行

1.回车

符号:\r
说明:指的是回到当前行的首元素

2.换行

符号:\n
说明:准确的是回到当前行的下一行的相同位置
注意:一般编译器实现的换行是:回车+换行(上面提及的换行)

一个有意思的旋转光标的实现:

#include<stdio.h>
#include<Windows.h>
int main()
{
	while (1)
	{
		printf("\\\r");
		Sleep(100);
		
		printf("|\r");
		Sleep(100);

		printf("-\r");
		Sleep(100);

		printf("/\r");
		Sleep(100);
	}
	return 0;
}

四.逻辑操作符

逻辑与:&&
说明:一假即为假,全真才为真(串联)
逻辑或: ||
说明: 一真即为真,全假才为假(并联)
运算顺序:从左向右

#include<stdio.h>
int my_print()
{
	printf("hello\n");
	return 1;
}
int main()
{
	int judge = 0;
	scanf("%d", &judge);
	judge && my_print();
	//输入0时,不执行my_print
	//输入1时,执行my_print
	//judge || my_print();
	//输入1时,不执行
	//输入0时,执行
	return 0;
}

五.位操作符和移位操作符

位运算符是对内存的数直接进行运算的(补码)
按位与:&
说明:同1才为1,其余都为0
按位或:|
说明:同0才为0,其余为1
按位异或:^
说明:相同为0,相异为1
按位取反: ~
说明:1变0,0变1,包括符号位。

简单运用:

int main()
{
	printf("%d\n", 1 & 2);//0
	//  01(二进制)
	//& 10
	//  00
	//答案是:0
	printf("%d\n", 1 | 2);//3
	//  01
	//& 10
	//  11
	//答案:3
	printf("%d\n", 1 ^ 2);//3
	//  01
	//^ 10
	//  11
	//答案:3
	printf("%d\n", ~-1);//0
	//  11111111 11111111 11111111 11111111
	//~
	//  00000000 00000000 00000000 00000000
	//答案:0
	return 0;
}

移位操作符
1.移位操作符运算后不会影响操作数本身
2.移位操作符运算的范围为整数

  1. 左移操作符
    符号:<<(双箭头向左)
    功能:将补码整体左移n位舍去,右边补0。
int a =1;
int b = a<<1;//这是将a的补码向左移动一位,

a的补码:00000000000000000000000000000001
b的补码:00000000000000000000000000000010
在这里插入图片描述
2. 右移操作符
符号:>>(双箭头向右)

1.算数右移
功能:将补码整体向右移n位,最左边补符号位
例子:

int a =-1;
int b = a>>1:

在这里插入图片描述
a的补码:11111111111111111111111111111111
b的补码:11111111111111111111111111111111
1.逻辑右移
功能:将补码整体向右移n位,最左边补0
在这里插入图片描述

一般右移都是算数右移(补符号位

关于sizeof的整形提升问题:

#include<stdio.h>
int main()
{

	char a = 0;
	printf("%d\n", sizeof(~a));//4
	printf("%d\n", sizeof(!a));//1
	printf("%d\n", sizeof(a&a));//4
	printf("%d\n", sizeof(a|a));//4
	printf("%d\n", sizeof(a^a));//4
	printf("%d\n", sizeof(a >>1));
	printf("%d\n", sizeof(a <<1));
	return 0;
}

说明:位运算符合移位操作符,都是在整形的大小进行计算的。
注意:!a在VS下是1,在Linux下是4,这里我们推荐当做4进行理解。

经典运用:
1.用异或和按位与实现加法

说明:CPU也是通过异或和按位与进行加法运算的。

#include<stdio.h>
int main()
{
	int begin = 0;
	int process = 0;
	int end = 0;
	scanf("%d%d", &begin, &end);
	process = end;
	while (process)//当没有进位信息,就停止循环
	{
		//先进行异或不保留进位信息
		end = begin ^ process;
		//获取进位信息,左移之后是进位
		process = (begin & process) << 1;
		//更新下一轮的加数信息
		begin = end;
	}
	printf("%d\n", end);
	return 0;
}

2.用异或实现两个数交换

#include<stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	printf("a==%d,b==%d\n", a, b);
	a = a ^ b;
	b = a ^ b;
	// b =a^(b^b)=a^0=a
	a = a ^ b;
	//a = b^(a^a)=b^0=b
	printf("a==%d,b==%d\n", a, b);
	return 0;
}

说明:
1.异或支持交换律和结合律
2.0与任何数异或等于本身
3.两个相同的数异或为0

3.用按位与将二进制序列输出

void ShowBites(int x)
{
	for (int i = 31; i >= 0; i--)
	{
		if (((x>>i) & 1)== 1)
		{
			printf("1");
		}
		else
		{
			printf("0");
		}
	}
}

4.用异或将指定的数变为1

#include<stdio.h>
#define SPECIALBIT(x,y) ((x)|=(1<<(y-1))) 
int main()
{
	int a = 0;
	SPECIALBIT(a, 5);
	ShowBites(a);
	return 0;
}

5.用按位与将指定位置变为0

#include<stdio.h>
#define SPECIALBITS(x,y) ((x)^=(1<<(y-1)))
int main()
{
	int a = 0xFFFFFFFF;
	SPECIALBITS(a, 5);
	ShowBites(a);
	return 0;
}

六.前置++与后置++

我们一般说前置++是先自增一,再使用,后置++是先使用,然后再加1,真的是这样吗?
答案:应该说大多数情况下是这样的,这样只是为了方便我们记忆,这种情况并不是绝对的。
满足情况的例子:

int main()
{
	int a = 0;
	int b = 1;
	b = a++;
	return 0;
}

b=a++的反汇编代码

在这里插入图片描述

int main()
{
	int a = 0;
	int b = 1;
	b = ++a;
	return 0;
}

在这里插入图片描述
不满足情况的例子:

int main()
{
	int a = 0;
	++a;
	a++;
	return 0;
}

在这里插入图片描述
一个典型的问题表达式:

所谓的问题表达式,并不是说表达式的语法有问题,而是说表达式的计算路径具有二义性,也就是有多种计算路径。

#include<stdio.h>
int main()
{
	int a = 1;
	a = (++a) + (++a) + (++a);
	printf("%d\n", a);
	return 0;
}

说明:这里是++与+的计算顺序与优先级的问题
有两种可能:
1.先计算全部的++,也就是把a的值先自增三次,再把自增过后的a相加,答案为12,这是VS下运行的结果
2.先计算前两个++,也就是先把a自增两次,再把自增过后的前两个a相加,最后再对a自增一,再把前两次相加的结果再相加此时a自增的结果,也就是3+3+4=10
这是Linux下的运行结果。

七.字符与字符串

1.字符串
说明:C语言并没有字符串类型,但是有字符类型

字符串在C语言中主要以两种形式存在:
1.数组
2.字符指针
但是字符串应用的场景很多。

int main()
{
	char* p ="abcdef";//p是字符串首字符的地址
	//说明:这里的字符串是不能进行修改的。
	char arr[]="abcdef";//字符串被放在数组中,字符串里面的字符是能修改的
	char c = "0123456"[1];//这是字符指针的形式。
	printf("%d\n",sizeof("abcdef");//这是以数组形式存在的,其大小是
	//字符串的字符个数
	printf("%d\n", sizeof(""));//里面有一个字符\0,所以是1
	return 0;
}

2.字符
说明:字符常量是以整形存在的,字符常量被放在字符变量中是要发生截断的。
注意:
1.一个字符里面最多包含4个字符(‘abcd’),对应4个字节
2.一个字符里面不能为空

int main()
{
	printf("%d\n", sizeof('c'));//这是4个字节
	char c = 'c';
	printf("%d\n", sizeof(c));//这是1个字节
	printf("%d\n",sizeof(+c));//整形提升,4个字节
	//CPU运算是以整形大小的
	return 0;
}

为什么存在ASCII码表?

因为代码是外国人发明的,外国的字符由26个英文字符组成,这也就照应了ASCII码表里面,为啥有26个大小写字符,那与计算机怎么交互信息呢?自然是由ASCII码表翻译出的字符啦!

八./和%

1.四种取整方式

1.向0取整
在这里插入图片描述

函数:trunc()
参数:double
返回值:double

#include<stdio.h>
#include<math.h>
int main()
{
	printf("%f\n", trunc(5.4));//5.0
	return 0;
}

2.向负无穷取整
在这里插入图片描述

#include<stdio.h>
#include<math.h>
int main()
{
	printf("%f\n", floor(5.4));//5.0
	return 0;
}

3.向正无穷取整
在这里插入图片描述

函数:ceil()
参数:double
返回值:double

#include<math.h>
int main()
{
	printf("%f\n", ceil(5.4));//6.0
	return 0;
}

4.四舍五入取整

函数:round()
参数:double
返回值:double

#include<math.h>
int main()
{
	printf("%f\n", round(5.4));//5.0
	return 0;
}

2.取模与取余的区别和联系

数学中余数的定义为:

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

计算机中余数的定义

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = qd + r其中,q被称为商,r 被称为余数。
注意:这里的余数是可以小于0的,商的结果是向0取整
比如:5/-2=-2.5,向0取整为-2,这里的 /准确的来说是取余
余数的求法:r=a-q
d=5-(-2)*(-2)=5-4=1,也就是用公式求。

C语言的 / 就是取余

计算机中取模的定义

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = qd + r其中,q被称为商,r 被称为余数。
说明:a/d=q余r,这里的/准确的来说是取模
注意:这里的余数是可以小于0的,商的结果是向负无穷取整
比如:5/-2=-2.5,向负无穷取整为-3
余数的求法:用公式求,r=a-q
d=5-(-3)*(-2)=5-6=-1.

Python的 / 就是取模

3./两边异号的情况

1.左正右负

这里的r=a-qd,a是被除数,q是商,d是除数,
也就是a>0,d<0,q<0,q * d > 0,所以可以这样写r=|a|-|q*d|,
1.当为取模时,商实际上会比较小(负数),绝对值比较大,所以|q*d|>|a|,因此r是小于0的
2.当为取余时,商实际上会比较大(负数),绝对值比较小,所以|q
d|<|a|,r是大于0的。

2.左负右正

这里的r=a-qd,a是被除数,q是商,d是除数,
也就是a<0,d>0,q<0,q*d<0,所以可以这样写r=|q*d|-|a|
1.当为取模时,商实际上会比较小(负数),绝对值比较大,所以|q*d|>|a|,因此r是大于0的
2.当为取余时,商实际上会比较大(负数),绝对值比较小,所以|q
d|<|a|,r是小于0的。

九.运算符的优先级

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【深度解刨C语言】符号篇(全) 的相关文章

  • Ubuntu Linux服务器安装图形化界面并用VNC Viewer连接

    文章目录 1 流程2 服务器开启VNC使用的端口3 服务器安装Ubuntu桌面与VNC Server4 客户端安装VNC Viewer xff08 略 xff09 5 连接 x1f517 1 流程 服务器开启VNC使用的端口服务器安装Ubu
  • 利用JavaScript写一个简易地学生管理系统

    不多说上代码 span class token keyword var span studentNoArr span class token operator 61 span span class token punctuation spa
  • 轻松掌握 JS 删除数组中指定的对象或者删除数组中某一项

    前言 关于 JS 删除数组中指定的对象或某一项的话题 xff0c 它是在实际开发中经常会遇到的问题 xff0c 也是需要掌握的基本技能 在这篇文章中 xff0c 我们将深入探讨如何使用 JS 来删除数组中指定的对象或者删除数组中某一项 删除
  • ubuntu能ping通却ssh不上(connect to localhost port 22: Connection refused)解决办法

    1 问题描述 xff1a ssh 192 168 144 130 ssh connect to host 192 168 144 130 port 22 Connection refused 2 原因 因为Ubuntu默认没有安装opens
  • mysql增加用户并赋予、删除、查看各种权限

    数据库系统表概述 mysql数据库下存储的都是权限表 xff0c 重要的有user表 db表 host表还有tables priv表 columns priv proc priv表 1 user表 xff1a 有用户列 权限列 安全列 资源
  • 数据库上云?这些事你不得不知

    您的业务适合哪种数据库 xff0c 真的知道吗 xff1f 互联网 43 人工智能 物联网等新兴技术的迅猛发展 xff0c 让如何处理大量 复杂 多变的数据问题再次浮出水面 xff0c 成为新时代企业用户面临的技术挑战之一 关系型数据库 x
  • 【STM32技巧】STM32 PWM互补输出设置

    本例程是使用STM32CubeMX 6 5版本生成带代码 测试单片机型号 xff1a STM32F103VET6 时钟设置72MHZ 使用定时器1的通道1 和 通道2 配置PWM互补输出 分频72分频 定时器频率为 72MHZ 72 61
  • js删除键值对的方法

    1 对于Map 增set k v 删delete k 查 get k 改set 43 get span class token keyword const span graph span class token operator 61 sp
  • java一路走来

    记得第一次学编程 xff0c 那都是高二时 xff0c 那时因为电脑很狂热 xff0c 刚好有一个哥就给我介绍了编程 xff0c 并给我演示了下vb语言 xff0c 用vb做了个计算器 xff0c 当时我惊呆了 xff0c 才知道编程有多神
  • Python基础教程之列表

    八仙过海是一个中国民间故事 xff0c 讲了汉钟离 张果老 韩湘子 铁拐李 吕洞宾 何仙姑 蓝采和和曹国舅八位神仙各显神通渡海的故事 我们都知道变量只能被赋值为一个值 xff0c 现在我们想把八位神仙的名字同时赋值 xff0c 该如何实现呢
  • move_base参数配置

    现在我们已经可以构建当前环境地图 xff0c 可以实现机器人在当前地图中的定位 那接下来就可以在地图上给定目标点 xff0c 开始规划路径控制机器人移动过去 xff0c 这样就可以完成在已知地图中进行自动导航的任务了 在已知地图中进行路径规
  • 计算节点nova服务启动失败

    在计算节点启动openstack nova compute服务的时候 xff0c 服务无法正常启动 xff0c 查看nova的日志发现如下报错 xff1a 2019 04 25 00 02 26 481 24682 ERROR nova T
  • openstack通过dashboard页面创建实例,实现实例与外部通信

    配置完所有节点 xff0c 通过dashboard页面来进行下一步的配置 首先通过admin用户登录页面 在管理员下 xff0c 创建一个外部网络 注意供应商类型为flat xff0c 之前修改配置文件时有提到过 etc neutron p
  • 防火墙经过地址转换抓包详解

    R表示收到 xff0c X表示发出 xff0c R 64 eth1是从eth1口收到数据包 X 64 eth2是从2口发出数据包 16 1 1 1 gt 18 1 1 2 表示IP 16 1 1 1发给IP 18 1 1 2 Seq后面的数
  • PyTorch复现SRGAN算法核心代码(带注释)

    train py import argparse import os from math import log10 import pandas as pd import torch optim as optim import torch u
  • saltstack部署OpenStack rocky版

    通过saltstack工具 xff0c 来自动部署OpenStack 基本的yaml 源码放在github上了 有兴趣可以查看 自行测试三台虚拟机安装没有问题 需要注意的是安装cinder slave xff08 从节点 xff09 的时候
  • openstack登录dashboard无法获取实例,无法获取镜像

    在部署配置完openstack基础服务以及dashboard后 登录页面发现很多功能都不正常 xff0c 无法获取实例 xff0c 也无法获取镜像 查看日志 xff1a grep ERROR var log nova var log nov
  • 实例 "test-1" 执行所请求操作失败,实例处于错误状态。: 请稍后再试

    openstack创建实例的时候出现报错 xff1a 实例 test 1 执行所请求操作失败 xff0c 实例处于错误状态 请稍后再试 错误 Build of instance 6ec6e8b1 9300 4be4 95fe 20434ea
  • Pycharm报错:Process finished with exit code -1066598274 (0xC06D007E)

    问题描述 pycharm运行到np linalg slogdet 时报错Process finished with exit code 1066598274 0xC06D007E xff0c 检查发现该函数输入shape超过 xff08 3
  • 你能区分数字化与信息化吗?

    近几年 xff0c 数字化 悄然无息地取代了 信息化 xff0c 从各种定义中 xff0c 我们可以知道数字化是指将任何连续变化的输入如图画的线条或声音信号转化为一串分离的单元 xff0c 在计算机中用0和1表示 xff0c 通常用模数转换

随机推荐