【C陷阱与缺陷】----语法陷阱
💯💯💯
要理解一个C程序,必须理解这些程序是如何组成声明,表达式,语句的。虽然现在对C的语法定义很完善,几乎无懈可击,大门有时这些定义与人们的直觉相悖,或容易引起混淆。语法细节决定语义,本篇总结C语法陷阱中的诸多细节,以供参考。
- 导言:
- Ⅰ. 理解函数的声明
- 1.1函数的声明
- 1.2类型转换
- 1.3规则:
- Ⅱ. 运算符的优先级问题
- 2.1不同类型的运算符优先级问题
- 优先级最高:() [ ] .
- 第二高:单目
- 第三高:双目
- 2.2同类运算符之间相对优先级问题
- Ⅲ. 函数调用
- Ⅳ. 注意作为语句结束标志的分号
- Ⅴ. “悬挂”else引发的问题
- Ⅵ. switch语句
导言:
由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷:
①.词法“陷阱”
②.语法“陷阱”
③.语义“陷阱”
④.连接问题
⑤.库函数问题
⑥.预处理器问题
⑦.可移植性缺陷
Ⅰ. 理解函数的声明
(*(void(*)()))0();
你知道这个表达式表示什么吗?
:调用一个首地址为0的函数。
要理解这个表达式我们需要从两个方面入手:函数如何声明的,与类型如何转换的。
1.1函数的声明
任何C变量的声明都是由两部分组成:类型以及变量。
float f,g;
这个声明的含义是:当对其求值时,表达式f和g的类型为浮点数类型
float ff();
这个声明的含义是:表达式ff()求值结果是一个浮点数,也就是说ff是一个返回值为浮点数类型的函数。
float *pf;
这个声明的函数是*pf是一个浮点数,也就是说,pf指向的数是个浮点数。pf是一个指向浮点数的指针。
float *f(), (*h)();
同理,那*f() ,(*h)(),就是浮点表达式
因为函数调用()结合优先级是高于解引用 *
,*f(),也就是 *(f()):f是个函数,返回值是一个指向浮点数的指向。
h呢是一个函数指针,h指向的函数的返回值是浮点类型。
如果假设pf为函数指针,那么如何调用fp所指向的函数呢?
首先pf就是指针指向的函数,那么对它调用就可以了。
不过注意要这样写:(*pf)();
因为函数运算符()的优先级高于单目运算符。如果pf两侧没有括号,那么 pf()就与 (pf())一样了。
那我们如果想调用一个首地址为0的函数应该如何调用呢?
这样?:(* 0)();
上式并不能生效,因为运算符必须要一个指针来做操作数。
而且这个指针还应该是一个函数指针,这样经过运算符作用后的结果才能能作为函数被调用。
所以必须要对上式的0进行类型转换。
转换的发现我们可以描述为:指向函数值为void,参数为void的函数的指针。
也就是这个0必须转换为函数指针,而这个函数指针指向的函数参数为void,返回值也为void。
那该如何转换呢?
1.2类型转换
其实我们一旦找到任何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:只要将声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个封装起来即可。
float (*pf)();
pf表示的是一个指向函数指针,指向的是函数参数是void,返回值为float。
也就是指向返回值为浮点类型的函数的指针。
而float (*)()去掉变量名与分号
再加上一个括号( float (*)() )
这就表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。
所以我们如果将0类型转换为“一个指向返回值为void的函数的指针”类型,就首先要知道一个该类型是如何声明。
如果pf是一个指向返回值为void类型的函数指针,那么(*pf)()的值为void,pf的声明如下:
void (*pf)();
所以该类型的类型转换符为:
( void (*)() )
所以将0强制类型转换为“指向返回值为void的函数指针”则为如下表示
( void (*)() )0
;
而对于该函数指针要是调用该函数指针所指向的函数的话,应该如下表示:
(*( void (*)() )0)();
所以该表达式表达的也就是,调用一个函数,该函数的首地址为0,返回值为0,参数也为0.
1.3规则:
按照使用的方式来声明
Ⅱ. 运算符的优先级问题
2.1不同类型的运算符优先级问题
运算符优先级有那么多,记住它们并不是一件容易的事
所有以我们应该对它们进行恰当的分组,理解各组运算符之间的相对优先级。这样记忆起来就很快了。
优先级最高:() [ ] .
优先级最高的其实并不是真正意义上的运算符,包括:数组下标,函数调用操作符,结构体成员访问操作符。它们都是从左向右结合的。所以a.b.c的含义是(a.b).c
第二高:单目
单目运算符的优先级仅次于前述运算符。
在所有真正意义上的运算符中,它们的优先级最高。因为函数调用的优先级要高于单目运算符的优先级。
类型转换()也是单目运算符,它的优先级和其他单目运算符的优先级一样。单目运算符是自右向左结合,因此*p++会被编译器解释为 *(p++),即p的地址+1,而不是p指向的对象+1
第三高:双目
优先级比单目运算符要低的,接下来就是双目运算符。双目运算符中,算术运算符的优先级最高,移位运算符次之,关系运算符再次之,接着解释逻辑运算符,赋值运算符,最后是条件运算符。
我们需要记住的两点就是
- 任何一个逻辑运算符的优先级低于任何一个关系运算符
- 移位运算符的优先级比算术运算符要低,但是比关系运算符高。
算术>移位>关系>逻辑>赋值>条件
2.2同类运算符之间相对优先级问题
属于同一类型的各个运算符之间的相对优先级,理解起来一般没有什么困难。但是,6个关系运算符的优先级并不相同。
1.运算符==和!=的优先级要低于其他关系运算符的优先级。
因此我们如果要比较a与b的相对大小顺序是否和c与d的相对顺序一样,就可以这样写:
a<b==c<d
2.任何两个逻辑运算符都具有不同的优先级。所有的按位运算符优先级要比顺序运算符的优先级高,每个"与"运算符要比对应的"或"运算符优先级高,而按位异或(^运算符)的优先级介于按位与运算符和按位或运算符之间。
3.在所有的运算符中,三目条件运算符优先级最低。这就可以在条件运算符的条件表达式中包含关系运算符的逻辑组合,因为先处理的是关系运算符,最后再处理三目条件逻辑符。
4.所有的赋值运算符的优先级是一样的,而且它们的结合方式是从右到左。
所以
a=b=0;
与
b=0;
a=b;
表达的意思是一样的。
5.在所有的运算符中,逗号运算符的优先级最低。
Ⅲ. 函数调用
C语言要求:在函数调用的时候,即使函数不带参数,也要将函数参数列表括号写下来。因此,如果 f
是一个函数,f();
则表示一个函数调用语句,而f;
却是一个什么都不做的语句,这个语句虽然计算函数f的地址,但不调用该函数。
Ⅳ. 注意作为语句结束标志的分号
在C语言中如果不小心多写了一个分号可能不会造成什么不良后果:
1.这个分号可能会被视为一个不会产生任何实际效果的空语句
2.或者编译器会因为这个分号,产生警告信息,根据信息去掉这个分号。
但也会有例外发现,有时会造成很大的差别:
1.如果在if或者while语句之后多了一个分号,那么原来紧跟在if或者while之后的语句就是一个单独的语句,与条件判断部分没有任何关系了。
2.当不是多写了一个分号,而是遗漏一个分号,同样会招致麻烦,比如return 语句后面的分号忘记写了,则会将return 后面的语句作为操作数,进行返回。
3.当一个声明的结尾紧跟一个函数定义时,如果声明结尾的分号被省略,编译器可能会把 声明的类型视为函数的返回值类型。
Ⅴ. “悬挂”else引发的问题
C语言中规定:else始终与同一对括号内最近的未匹配的if结合。
也就是就近原则,它会和离它最近的if相结合。当然这必须在同一个括号里。如果在不同的括号里,那么就不遵循了。
int main()
{int a = 0, y = 1;if (a == 0)if (y == 0)printf("正确\n");else{printf("错误\n");}return 0;
}
比如第一个if里面的判断条件为a是否等于0
该代码的本意是else为a不为0时进行的代码,但真正的是else与第二个if相匹配了,
也就是else里的判断条件变成了y不为0时进行的代码。
如果要得到原来的例子中编程者本意的结果,应该这样写:
int main()
{int a = 0, y = 1;if (a == 0){if (y == 0)printf("正确\n");}else{printf("错误\n");}return 0;
}
现在,else与第一个if结合,即使它离第二个if更近也不会改变,因为此时第二个if已经被括号“封装”起来了。
Ⅵ. switch语句
switch语句的特点就是包含break;当遇到break,语句立刻结束。
C语言中switch语句的这种特性,既是它的优势,也是它的一大弱点。说它是弱点,是因为程序员很容易遗漏各个case部分的break语句,造成一些难以理解的程序行为。
说它是优势,是因为如果程序员有意的省略一个break语句,就可以表达出一些采用其他方式很难方便地加以实现的程序控制结构。
特别是对于一些大的swtich,我们经常发现各个分支的处理大同小异:对于某个分支情况的处理只要稍作修改,或者不修改,就也符合程序的要求。
比如这样的一段代码,它的作用是查找符号时跳过程序中的空白字符,在这里,空格键,制表符,换行符的处理都是相同的,除了遇到换行符时程序的代码行计数器需要进行递增。其他都是一样,所以我们可以省略break,程序照样可以运行甚至更好。
case '\n':linecount++;//该处省略break语句case '\t':case ' ':
相关文章:
【C陷阱与缺陷】----语法陷阱
💯💯💯 要理解一个C程序,必须理解这些程序是如何组成声明,表达式,语句的。虽然现在对C的语法定义很完善,几乎无懈可击,大门有时这些定义与人们的直觉相悖,或容易引起混淆…...
虹科分享| 关于TrueNAS十问十答
上一篇文章我们向您介绍了虹科新品HK-TrueNAS企业存储,很多小伙伴会疑问到底什么是NAS存储,之前常用的磁盘、磁带属于什么存储架构,NAS存储好在哪里,什么时候使用NAS?今天我们整理了关于TrueNAS的十问十答,…...
Https 笔记
HTTP TLS TLS 的前身是 SSL 非对称加密的核心: 两个密钥(公私) https 需要第三方CA(证书授权中心)申请SSL证书以确定其真实性 证书种包含了特定的公钥和私钥 密钥交换 自己将私钥上锁后发给对方对方也上锁 在还回来…...
【Python+requests+unittest+excel】实现接口自动化测试框架
一、框架结构: 工程目录 二、Case文件设计 三、基础包 base 3.1 封装get/post请求(runmethon.py) 1 import requests2 import json3 class RunMethod:4 def post_main(self,url,data,headerNone):5 res None6 if heade…...
MySQL终端的使用及其数据类型的使用
什么是数据库?数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。每个数据库都有一个或多个不同的 API 用于创建,访问,管理,搜索和复制所保存的数据。我们也可以将数据存储在文件中,…...
长视频终局:一场考验资金储备的消耗战
赢者通吃,似乎已成为各行各业的常识,但事实真的是这样吗?20世纪70年代,石油价格高涨,在墨西哥湾油田拍卖中高价拍得油田的企业,要么亏损,要么收入低于预期,但仍然有无数企业在高价竞…...
javaEE初阶 — CSS 常用的属性
文章目录CSS 常用的属性1 字体属性1.1 设置字体家族 font-family1.2 设置字体大小 font-size1.3 设置字体粗细 font-weight1.4 文字倾斜 font-style2 文本属性2.1 文本颜色2.2 文本对齐2.3 文本装饰2.4 文本缩进2.5 行高3 背景属性3.1 背景颜色3.2 背景图片3.3 背景位置3.4 背景…...
【面试题】如何取消 script 标签发出的请求
大厂面试题分享 面试题库前后端面试题库 (面试必备) 推荐:★★★★★地址:前端面试题库问题之前在业务上有这样一个场景,通过 script 标签动态引入了一个外部资源,具体方式是这样的const script document.…...
蓝桥杯嵌入式(G4系列):RTC时钟
前言: 关于RTC时钟的HAL库配置我也是第一次,之前都是用库函数的写法,这里写下这篇博客来记录一下自己的学习过程。 STM32Cubemx配置: 首先点击左侧的Timers的RTC,勾选以下选项 进入时钟树配置 进入时间设置࿰…...
Linux——进程间通信1
目录 进程间通信目的 进程间通信标准 管道 匿名管道 管道实现进程间通信 管道的特点 进程池 ProcessPool.cc Task.hpp 习题 进程间通信目的 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件…...
循环语句——“Python”
各位CSDN的uu们你们好呀,今天小雅兰的内容是Python中的循环语句呀,分为while循环和for循环,下面,让我们进入循环语句的世界吧 循环语句 while循环 for循环 continue和break 循环语句小结 人生重开模拟器 设置初始属性 设置性别…...
Python synonyms查找中文任意词汇的同义词近义词
Python synonyms查找中文任意词汇的同义词近义词 作者:虚坏叔叔 博客:https://xuhss.com 早餐店不会开到晚上,想吃的人早就来了!😄 一、安装 对于非专业的开发人员来说可以简单的使用Python一行代码来找到同义词。这…...
三分钟了解http和https
对应测试人员都会听过http请求和响应.在这里给大家介绍http相关的知识 一.http和https基本概念 HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本…...
docker应用:搭建私有云盘
简介:NextCloud是一个开源的云存储解决方案,可以在自己的服务器上搭建个人云存储系统。它提供了与市面上主流云存储服务(如Dropbox、Google Drive)相似的功能,包括文件存储、共享、同步、协作等。NextCloud的主要优势在…...
【C++进阶】面向对象
程序 编写程序是为了让计算机解决现实生活中的实际问题。pascal之父、结构化程序设计先驱Niklaus Wirth提出程序 算法 数据结构。程序是完成一定功能的一些列有序指令的集合。指令 操作码 指令。将指令按一定的顺序进行整合,就形成了程序。 机器语言与汇编语言…...
从ChatGPT与New Bing看程序员为什么要学习算法?
文章目录为什么要学习数据结构和算法?ChatGPT与NEW Bing 的回答想要通关大厂面试,就不能让数据结构和算法拖了后腿业务开发工程师,你真的愿意做一辈子CRUD boy吗?对编程还有追求?不想被行业淘汰?那就不要只…...
SpringBoot-实用开发篇
SpringBoot开发实用篇开发实用篇中因为牵扯到SpringBoot整合各种各样的技术,所以在整合每一个技术之前,都会做一个快速的普及,这样的话内容整个开发实用篇所包含的内容就会比较多。在学习的时候,如果对某一个技术不是很清楚&#…...
Python进阶-----高阶函数->filter() 函数
目录 前言: filter() 函数介绍 filter() 函数使用示例 1.与循环对比 2.与lambda函数综合使用 3.使用None过滤False 4.过滤字典相关数据 前言: 家人们,当你们获取了一个序列的时候,想要把一些内容去掉,保留一部分…...
C/C++面试可能会问三:指针和数组一样吗?
答案:不一样。 哪里不同? 数组名:数组名的值是一个指针常量,也就是数组第一个元素的地址。 它的类型取决于数组元素的类型:如果他们是int类型,那么数组名的类型就是“指向int的常量指针”;如果…...
数字经济新生态,中小企业如何发展营销数字化
五年弹指一挥间,中国数字经济正从尝试探索迈向快速发展,这一趋势,从今年两会的国务院机构改革、总理政府工作报告、部长通道答疑解惑、科技领域大佬提案中都能看出来。 在政府工作报告中,我们可以看到数字经济在不断壮大ÿ…...
【网络】https协议
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
【11】SCI易中期刊推荐——计算机方向(中科院4区)
🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…...
STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式2)
STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式2) 目录STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式2)前言1 环境搭建2 功能描述3 程序编写3.1 BootLoader部分3.2 APP的制作4 修改工程中的内存配置4.1 Bootloader…...
【Spring6】| Bean的生命周期(重要)
目录 一:Bean的生命周期 1. 什么是Bean的生命周期 2. Bean的生命周期之5步 3. Bean生命周期之7步 4. Bean生命周期之10步 5. Bean的scop(作用域)不同,管理方式不同 6. 自己new的对象如何让Spring管理 一:Bean的…...
【C#】单据打印方案(定义打印模板、条形码、二维码、图片、标签)
系列文章 C#项目–业务单据号生成器(定义规则、自动编号、流水号) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/129129787 C#项目–开始日期结束日期范围计算(上周、本周、明年、前年等) 本文链接&…...
前后端身份验证
1、web 开发模式 【】基于服务端渲染的传统 Web 开发模式 【】基于前后端分离的新型 Web 开发模式:依赖于 Ajax 技术的广泛应用。后端只负责提供 API 接口,前端使用 Ajax 调用接口的开发模式 2、身份认证 【】服务端渲染推荐使用 Session 认证机制 【】…...
【蓝桥杯嵌入式】ADC模数转换的原理图解析与代码实现(以第十一届省赛为例)——STM32G4
🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 - 蓝…...
Matlab表示 CDF 时间值
从 CDF 纪元对象中提取日期信息。CDF 表示时间的方式与 MATLAB 不同。CDF 将日期和时间表示为自 1-Jan-0000 以来的毫秒数。这在 CDF 术语中称为纪元。为了表示 CDF 日期,MATLAB 使用一个称为 CDF 纪元对象的对象。MATLAB 还可以将日期和时间表示为日期时间值或日期序列号,即…...
基于Halcon的条码定位与识别【包含 一维码 和 二维码 】
1.针对一维码问题,先列代码: dev_update_off () dev_close_window () dev_open_window (0, 0, 600, 819, black, WindowHandle) dev_set_draw (margin) *读图 read_image (Image, 20221213-174036.png)*获取一维码区域对原图进行抠图 gen_rectangle1 (ROI_0, 2169.33, 1835.…...
每天学一点之多线程
多线程 一、相关概念 并发与并行 并行(parallel):指多个事件任务在同一时刻发生(同时发生)。 并发(concurrency):指两个或多个事件在同一个微小的时间段内发生。程序并发执行可以…...
网站开发 方案 报价单/一句简短走心文案
因为我的时间设置貌似已经是"Y-m-d H:i:s",所以时间戳转换无效。 {$vo.create_time|date"Y-m-d H:i:s",###} 需要截取日期。 https://blog.csdn.net/weixin_43674113/article/details/84956607 TP5时间字符串截取 渡目成书 2018-12-11 16:01…...
建筑人才信息网查询/苏州seo快速优化
1 调高osd的日志等级加上红框那一行就可以了osd的日志路径:/var/log/ceph/ceph-osd.3.log注意:加上了这一行后日志会刷很多,所以要特别注意日志容量的变化,以防把var目录写满了2缺少osdmap或者错误的osdmap从osd日志中发现这两种…...
2345软件管家/小程序seo推广技巧
题目梗概 题意:给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。 思考 颓废了好长时间,终于开始了自己的第二道状压DP题目…...
wordpress 添加字体/aso优化师工作很赚钱吗
Android性能优化典范第4季的课程学习笔记终于在2015年的最后一天完成了(并于2016年1月12日正式发布在CSDN上),文章共17个段落,包含的内容大致有:优化网络请求的行为,优化安装包的资源文件,优化数据传输的效率ÿ…...
网络架构拓扑/网站seo最新优化方法
Ignore 用于告诉Room需要忽略的字段或方法建立索引:在Entity注解的indices属性中添加索引字段。例如:indices {Index(value {"first_name", "last_name"}, unique true), ...}, unique true可以确保表中不会出现{"first_na…...
网站备案真实性核验委托书/百度网首页官网登录
各位客官,实在不好意思,好久没有和大家唠嗑了,最近由于一直忙于虚拟化的各项认证考试工作,于是荒废了客栈的生意,耽误了各位客官的休息与饮食,深表歉意。今天终于闲下来了,刚刚突然想起了制作U盘…...