【C语言】深度讨论使代码更严谨,更优雅的方式

news2024/12/29 10:57:33

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页:June-Frost
🚀专栏:C语言

🔥该篇将从多个部分探讨如何写出更严谨,更优雅的代码。
🌏该文章借鉴《高质量 C++/C 编程指南》—— 林锐。

目录:

  • 🌟命名
  • 🌟 空行
  • 🌟 语句
    • ✉️ 表达式语句
    • ✉️ 函数调用语句
    • ✉️ 控制语句
      • 📬一些共性
      • 🔭分支语句
      • 🔭循环语句
  • 🌟指针
  • 🌟 函数
  • ❤️ 结语

🌟命名

 📗在编程中,命名是非常重要的,一个好的命名可以帮助别人更好地理解代码,提升代码的可读性和可维护性,常见的命名规则有: ①驼峰命名法(又叫小驼峰法,单词的第一个字母小写,后面的单词的首字母大写,例如:int myAge = 0),②下划线命名法(所有字母小写,单词之间用下划线连接,例如:int first_name = 0),③帕斯卡命名法(又叫大驼峰法,将第一个单词的首字母和其他单词的首字母均大写,例如:int FirstName = 0),④匈牙利命名法(将变量名的前缀指定为变量的数据类型,例如:int iname = 0//这里的i代表int)。
 但是,随着不同的语言以及不同IDE的发展,其所推崇的规则是不同的,况且命名对程序的影响无足轻重,不同的项目和团队可能也有着自己的命名风格。所以我们应该关注命名的共性规则。

  • 标识符应该可以清晰表达含义,最好使用英文单词组合,切记不可以使用拼音
  • 标识符最好能以最短的长度描述出最多的信息,避免使用缩写,除非是广泛使用的行业标准缩写,例如SL //水利行业标准 ,此外单个字母也并非无用,有时可以用于循环变量。
  • 避免通过大小写来区分。例如:int o; int O;,这种写法极容易混淆。
  • 变量名称使用 形容词+名词或者 名词,例如: int flag;int newFlag;
  • 函数名称使用 动词+名词或者动词 ,例如:Init();InitBoard();
  • 避免使用编号(除非逻辑必要)和特殊字符,例如int num1; int num2;

Windows环境下的一些建议:

  • 常量名和宏定义使用大写字母和下划线,例如MAX_LENGTH
  • static 修饰的静态变量加前缀 s_ , 例如:static int s_newFlag = 0
  • 若要使用全局变量,变量需要加前缀 g_ (表示 global) ,例如: int g_flag;//全局变量

🌟 空行

📗适当的空行可以帮助分隔不同的代码块从而提高可读性,并且阅读者可以很容易地区分不同的代码段,更轻松地理解代码逻辑。在总体的布局上也会显得更加美观。

  •  在函数声明或者定义之间增加空行,以区分不同的函数,这样可以快速的找到特定的代码块。
//空行
int calculateAverage(...)
{
	//业务处理
}
//空行
void GetCustomerDetails(...)
{
	//业务处理
}

  •  逻辑密切相关的地方不加空行,其它地方加空行,以分隔不同的逻辑部分,有利于阅读者更好地理解代码的结构和逻辑关系。

  •  在代码的不同部分之间增加空行,例如,在循环语句,条件语句,变量声明之间添加以助于分隔不同的代码块。

//空行
while (...)
{
	description1;
    //空行
	if (...)
	{
       //业务处理
	}
	else
	{
       //业务处理
	}
	//空行
	description2;
}

但是需要注意,空行固然有着诸多好处,但是过度添加空行可能会使得代码过于稀疏,反而影响了可读性,所以一定要确保在合适的位置增加。


🌟 语句

✉️ 表达式语句

 优雅的表达式应该是简洁、易读、清晰和易于理解的。应该使用适当的命名、合适的语法和惯用的风格来书写表达式,以便使代码更易于维护和扩展。以下是一些建议:

  • 避免冗长和复杂的表达式,尽量不书写多用途的表达式,例如:可以将d = (a = b + c) + r ; 改为 a = b + c; d = a + r;;
  • 在适当的情况下使用括号可以明确表达式的优先级和分组,避免混淆和误解。例如:求一个闰年,return ((year%4 == 0)&&(year%100!=0))||(year%400 == 0);
  • 避免使用全局变量。全局变量可以使代码难以理解和维护,因为它们的作用域不明确,并且可能会被意外地修改。尽可能使用局部变量或参数,这可以使代码更安全和可预测。
  • 表达式的一些双目操作符,例如赋值操作符、比较操作符、算术操作符等,前后应该加空格。一些单目操作符,例如逻辑反操作( !),按位取反(~)等,前后不加空格。还有一些操作符,例如下标引用( [ ] )、结构成员( -> 和 . ) 前后不加空格 。

✉️ 函数调用语句

  • 函数名之后不要留空格,紧跟左括号‘(’ 。
  • ‘,’之后要留空格

✉️ 控制语句

📬一些共性

  • 关键字之后要留空格。像const、virtual、inline、case 等关键字之后至少要留一个空格,否则无法辨析关键字。像 if、for、while 等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。
  • 对于表达式比较长的 for 语句和 if 语句,为了紧凑起见可以适当地去掉一些空格,如 for (i=0; i<10; i++)和 if ((a<=b) && (c<=d))
  • 如果一行的字符过多,则需要长句拆分,长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首,如果可能,使表达式的每一行都不超过限制字符数(通常是80个字符)。
    例如:
              if((*p==')'&&STTop(&s1)!='(')
                ||(*p==']'&&STTop(&s1)!='[')
                ||(*p=='}'&&STTop(&s1)!='{'))
  • if、for、while、do 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{}。这样可以防止书写失误。并且程序的分界符‘{’和‘}’应独占一行并且位于同一列(如果出现嵌套的{},则使用缩进对齐) ,这些做法也可以防止产生悬空else,

悬空else:

#include<stdio.h>
int main()
{
	int a = 0;
	int b = 2;
	if (a == 1)
		if (b == 2)
			printf("点赞\n");
	else
		printf("收藏\n");
	return 0;
}

注意:else 会和 最近的if 匹配 。 最好加入{} ,这样可以使得代码中的匹配更加明确。

🔭分支语句

  • if 语句的条件判断中,如果是与0比较,最好能让读者一眼看出类型,例如,布尔类型:BOOL value=0;......;if(value)//表示为真时执行 ,整型类型:int value = 0;........;if(value!=0), 指针类型:int* value = NULL;........;if(value !=NULL)
  • 在switch语句中,即使程序真的不需要 default 处理,也应该保留语句 default 。甚至可以加个break 。

🔭循环语句

  • 如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。

例如:
如果N(循环次数)大,可以采用该方式,不会打断“流水线”作业,能提高效率。

if (condition) 
{ 
   for (i=0; i<N; i++) 
   {
    //业务处理
   }
} 
else 
{ 
   for (i=0; i<N; i++) 
   {
    //业务处理
   }
} 

如果N比较小,两者差别不明显。以下这种更加简洁。

for (i = 0; i < N; i++)
{
	if (condition)
	{
       //业务处理
	}
	else
	{
      //业务处理
	}
}
  • 不可在 for 循环体内修改循环变量,防止 for 循环失去控制。循环变量的调整只放在表达式3中可以防止出现问题。
  • 建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法。虽然for (i=0; i<=9; i++)和for (i=0; i<10; i++)都是循环10次,但是第二个更加直观。

🌟指针

 想要将代码写的更严谨,那么必须杜绝野指针(指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)),野指针可能会导致数据错误,程序崩溃等一系列问题。
野指针是如何形成的呢?
1.指针未初始化

#include <stdio.h>
int main()
{
	int *p;//局部变量指针未初始化,默认为随机值
	*p = 20;
	return 0;
}

2.指针越界访问

#include <stdio.h>
int main()
{
	int arr[10] = {0};
	int *p = arr;
	int i = 0;
	for(i=0; i<=11; i++)
	{
	  //当指针指向的范围超出数组arr的范围时,p就是野指针
	  *(p++) = i;
	}
	return 0;
}

3.指针指向的空间释放:指针被 free 或者 delete 之后,没有置为 NULL,让人误以为是个合法的指针。

解决方案:

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性
#include <stdio.h>
int main()
{
	int *p = NULL;
	//....
	int a = 10;
	p = &a;
	if(p != NULL)
	{
	  *p = 20;
	}
	return 0;
}

🌟 函数

  • 参数命名要恰当,顺序要合理。例如要实现strcpy,我们就可以将源字符串起名为strSource ,目的字符串为strDestination ,这样写代码就会很清楚从哪里拷贝到哪里,而且,一般地,应将目的参数放在前面,源参数放在后面。这样就得到了函数声明 :char * StringCopy(char* strDestination, const char* strSource );
  • 如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。

例如:

#include<stdio.h>
struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(const struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

 如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降,传递指针则不会。

  • 函数名字与返回值类型在语义上不可冲突。

 这一点很重要,例如可能人们会误以为 getchar 的返回类型是char,其实不然。这个函数的机制是:如果正确读取到一个字符,则返回ASCll码值,如果读取不正常,则会返回EOF(end of file,文件结束标志),EOF其实就是 -1 。🌈对于char类型,一些编译器定义的范围为 -128 ~ 127,还有一些为 0~255 。如果是后者,在返回 -1 时就会出错,另外,对于ASCll码表的扩展字符,就算时 -128 ~ 127 也无法表示,所以采取大范围的 int 更加合适。
  所以当我们自己设计函数时,要尽量避免出现误导的情况。

  • 在进入函数体后,使用assert(断言)对参数的有效性进行检查。如果传入的参数是非法的,那么assert就会报出错误,对检查bug有一定的帮助。
  • 如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该指针在函数体内被意外修改。

针对这两点,就可以模拟实现一个大致合格的 strcpy 函数。

char* StringCopy(char* strDestination, const char * strSource)
{
	char* ret = strDestination;
	//断言
	assert(strDestination != NULL);
	assert(strSource != NULL);

	while (*strDestination++ = *strSource++)
	{
		;//空语句
	}

	return ret;
}

❤️ 结语

 文章到这里就结束了,如果对你有帮助,你的点赞将会是我的最大动力,如果大家有什么问题或者不同的见解,欢迎大家的留言~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970424.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电梯SIP-IP五方对讲管理系统

电梯SIP-IP五方对讲管理系统 是深圳锐科达精心打磨的一款IP数字信号对讲设备&#xff0c;是在传统电梯对讲系统基础上的一次全新升级&#xff0c;突破了模拟、FM调频系统存在的技术障碍&#xff0c;实现联网;在模/数交替的过程中&#xff0c;继承了模拟、FM调频系统的优点&…

Spring Cloud Alibaba-@SentinelResource的使用

1 SentinelResource的使用 在定义了资源点之后&#xff0c;我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能 通过SentinelResource来指定出现异常时的处理策略。 SentinelResource 用于定义资源&#xff0c;并提供可选的异常处理和 fallback 配置项。…

Rockchip RK3399 - DRM子系统

从开始接触音频子系统到如今已经两个多月&#xff0c;说实话花费的时间的确有点长了。从今天起我们开始接触DRM&#xff0c;网上已经有很多优秀的关于DRM的文章了&#xff0c;因此我们学习直接去学习一些优秀的文章即可。后面有关DRM相关的文章我们会大量参考[1] DRM (Direct R…

每日刷题(回溯法经典问题之组合)

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 ♈️今日夜电波&#xff1a;保留—郭顶 1:33 ━━━━━━️&#x1f49f;──────── 4:30 &#x1f504; ◀️ ⏸ ▶️ ☰ …

华为数通方向HCIP-DataCom H12-821题库(单选题:261-280)

第261题 以下关于IPv6过渡技术的描述,正确的是哪些项? A、转换技术的原理是将IPv6的头部改写成IPv4的头部,或者将IPv4的头部改写成IPv6的头部 B、使用隧道技术,能够将IPv4封装在IPv6隧道中实现互通,但是隧道的端点需要支持双栈技术 C、转换技术适用于纯IPv4网络与纯IPv…

一文速览嵌入式六大出口

嵌入式行业的前景确实十分广阔&#xff0c;并且在许多领域都发挥着重要作用。以下是一些关键点&#xff0c;说明嵌入式系统的发展潜力和前途&#xff1a; 1. 物联网&#xff08;IoT&#xff09;&#xff1a;嵌入式系统是实现智能家居、智能城市、智能工厂等物联网设备的核心。物…

【网络教程】群晖轻松设置钉钉机器人使用Webhook发送通知消息,分分钟搞定!

文章目录 准备设置相关链接准备 演示环境:群晖DSM7.2(其他版本操作雷同)需要提前准备好你的钉钉机器人webhook链接,如果你还不会设置/获取,请点击 参考这篇文章 或自行某度设置 打开群晖,进入控制面板 —> 通知设置 —> Webhooks,如下图 然后点击新增,提供商选择…

深兰科技再次荣登“全球独角兽企业500强排行榜”

9月1日&#xff0c;由青岛市人民政府、中国人民大学中国民营企业研究中心共同主办&#xff0c;青岛市民营经济发展局、崂山区人民政府、北京隐形独角兽信息科技院承办的&#xff0c;以“塑造产业发展新优势&#xff0c;激发经济发展新活力”为主题的“2023第五届全球独角兽企业…

vue router进行路由跳转并携带参数(params/query)

在使用router.push进行路由跳转到另一个组件时&#xff0c;可以通过params或query来传递参数。 1. 使用params传参&#xff1a; // 在路由跳转时传递参数 router.push({ name: targetComponent, params: {paramName: paramValue // 参数名和值 } });// 在目标组件中通过$r…

Vue3回到顶部(BackTop)

效果如下图&#xff1a;在线预览 APIs 参数说明类型默认值必传bottomBackTop 距离页面底部的高度number | string40falserightBackTop 距离页面右侧的宽度number | string40falsevisibilityHeight滚动时触发显示回到顶部的高度number180falsetoBackTop 渲染的容器节点 可选 元…

05. 逻辑门和加法器等原理探究

1. 二极管 1.1 什么是二极管 二极管是一种电子元件&#xff0c;它的主要特点是只允许电流在一个方向通过&#xff0c;而另一个方向电流将被阻止。 下面是二极管的示意图: 电流往箭头指向的地方流 1.2 二极管的作用 下面第一个图&#xff1a;给灯泡加上正负电压&#xff0c;…

pcapng 文件转 pcap 文件

pcap 是早期计算机网络抓包格式,几乎所有抓包工具都支持pcap&#xff1b;pcapng 是下一代抓包格式&#xff0c;支持不同路线以寻求标准化&#xff0c;pcapng格式通过使用标准化块和字段来实现可扩展性需求。 在tcpreplay重放数据包的时候&#xff0c;手里只有pcapng文件&#…

【牛客网题目】合并k个已排序的链表

目录 描述 题目分析 描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围&#xff1a;节点总数 0≤n≤5000&#xff0c;每个节点的val满足∣val∣<1000 要求&#xff1a;时间复杂度 O(nlogn) 示例1 输入&#xff1a;[{1,2,3},{4,5,6,7}]返回值…

地质灾害监测方案(地质灾害监测原理与方法)

我国坡地较多,地质灾害时有发生,给人民生命财产安全和经济建设造成严重威胁。采用工业物联网技术进行地质灾害监测,可以实现对山体移动、边坡变形等地质灾害的预警和实时监测,保护人民生命财产安全。现提出如下地质灾害监测方案: 1. 监测场景:针对易发地质灾害的区域,如矿山边坡…

有效的价格管理手段

品牌产品的价格定位是非常严肃的事情&#xff0c;尤其像新品发售期间&#xff0c;价格的波动会对销量影响非常大&#xff0c;所以产品定价的稳定性关系到品牌发展&#xff0c;同时也会影响经销商的销售热情&#xff0c;所以品牌需要对价格进行管理&#xff0c;维持住价格的定价…

分支创建查看切换

1、初始化git目录&#xff0c;创建文件并将其推送到本地库 git init echo "123" > hello.txt git add hello.txt git commit -m "first commit" hello.txt$ git init Initialized empty Git repository in D:/Git/git-demo/.git/ AdministratorDESKT…

elementUI textarea可自适应文本高度的文本域

效果图; 通过设置 autosize 属性可以使得文本域的高度能够根据文本内容自动进行调整&#xff0c;并且 autosize 还可以设定为一个对象&#xff0c;指定最小行数和最大行数。 <el-inputtype"textarea"autosizeplaceholder"请输入内容"v-model"te…

书单文案素材哪里找?怎么做成视频?

在当今信息爆炸的时代&#xff0c;越来越多的人开始关注书单并希望分享自己的阅读经验。但是&#xff0c;很多人可能不知道如何寻找书单文案素材以及如何将其制作成视频。本文将为大家介绍一些寻找书单文案素材和制作书单视频的方法。 寻找书单文案素材 1.书单推荐网站 除了书…

前端如何将后台数组进行等分切割

前端如何切割数组 目标&#xff1a;前端需要做轮播&#xff0c;一屏展示12个&#xff0c;后端返回的数组需要进行切割&#xff0c;将数据以12为一组进行分割 环境&#xff1a;vue3tselement plus 代码如下&#xff1a; function divideArrayIntoEqualParts(array, chunkSiz…

输出归一化位置式PID(COTRUST完整梯形图代码)

SMART PLC单自由度和双自由度位置式PID的完整源代码,请参看下面文章链接: 位置式PID(S7-200SMART 单自由度、双自由度梯形图源代码)_RXXW_Dor的博客-CSDN博客有关位置型PID和增量型PID的更多详细介绍请参看PID专栏的相关文章,链接如下:SMART PLC增量型PID算法和梯形图代码…