前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重温C语言,这三十多个细节你把握住了?

重温C语言,这三十多个细节你把握住了?

作者头像
看、未来
发布2021-09-18 11:20:46
4820
发布2021-09-18 11:20:46
举报
文章被收录于专栏:CSDN搜“看,未来”

文章目录

前言

好久不见啦朋友们。

前天参加了软件设计师考试,说实话,有点emmm,但是我也发现很多基础已经忘得差不多了,这就是传说中的手生了吗?

手生到什么地步?前天晚上帮我朋友改代码,甚至连scanf输入double类型数据用什么方式我都想不起来了。

所以,我就整理了一下我自己的学习路线。

江东子弟多才俊,卷土重来未可知!

拿着《C Primer Plus》梳理了一遍,发现还真的有不少细节平时没有注意到,或者是没有刻意的去注意。


基本篇

1、编写代码文档

难度指数:1颗星 / 细节指数:5颗星 / 重要指数:5颗星

(写代码不写文档,拖出去打屎)

最开始接触到代码文档不知道是什么时候了,但是让我想写代码文档绝对是在pycharm上。

很方便,打三个引号,一个回车,什么都给你准备好了。

然后我就想在VS上也找到类似的功能。起初没找到,后来误打误撞试出来了:

代码语言:javascript
复制
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="l1"></param>
        /// <param name="l2"></param>
        /// <returns></returns>

呐,看明白不?画三个杠,不用回车,两个<summary>之间写你的函数功能描述,</param>之间写你的参数释义,也可以写在</param>后面,return我就不多说啥了吧。


C语言相对其他语言的优势

难度指数:1颗星 / 细节指数:1颗星 / 重要指数:3颗星

(连C语言的优势都不清楚,学来干什么?)

1、目前我们所学习的各种语言,基本都离不开C语言的影子,所谓万变不离其宗,就是这个道理。

2、C语言快,不接受反驳。不要说什么汇编更快,写个我看看。

3、可移植性强。就拿嵌入式这一块来说,哪个语言能轻松移植?

4、细节把控到位。C语言给了程序员极大的细节操作权限,连内存分配都给了。只是我们自己把握不住而已,C语言的水太深了。


C语言为什么不内置输入输出语句?

难度指数:2颗星 / 细节指数:4颗星 / 重要指数:3颗星

别说输入输出了,不包任何头文件,我不知道还能写什么C代码。

为什么要这样呢?像Python那样都内置了不好吗?

这也是C语言为什么能做嵌入式,而Python做不了的一个重要原因。

C语言的一个基本设计原则是避免不必要的成分。

我们不可否认,并不是所有项目都需要输入输出的。


儿童节快乐呀各位


int main() 与 void main()的区别

难度指数:1颗星 / 细节指数:3颗星 / 重要指数:2颗星

不知道有多少小伙伴接触过这个 void main(),反正我刚开始学C的时候,那个老师是教我们写这个的,当时就跟我们说,如果没有什么返回值,就写这个就好啦。

后来,遇到我的授业恩师的时候,他叫我们说,不要写void main()了,打开编辑器,写完头文件依赖之后,先把下面这个框架写上:

代码语言:javascript
复制
int main(){
	
	return 0;
}

就算没有什么返回值,也写一个return 0;,错不了的。

有些编译器会允许void main()的形式,但是还没有任何标准考虑接受它,所以编译器可以不接受这种形式,这就是一个在平台移植中存在的一个隐患。

多写一行return 0;很难吗?


可写可不写的花括号,凭什么要我写?

难度指数:1颗星 / 细节指数:2颗星 / 重要指数:2颗星

有的小伙伴可能不知道,在循环语句、分支语句中,如果代码块儿只有一行的情况下(或者循环下面只有一个分支语句),则那个花括号是可写可不写的。

比方说:

代码语言:javascript
复制
while(1)
	printf('1\n')

那这个花括号写不写呢?

不写会有什么好处?

1、代码看起来行数会短一丢丢

2、少写两个括号

不写有什么坏处?

当下基本不会有什么坏处,当下咱的头脑坑定是清醒的,知道为什么不写。

但是修改代码的时候呢?如果要在这种循环下动刀,却又忽略了这个括号呢?

我前两天就遇到了,浪费我五分钟去调试。

所以啊,又不是说什么很必要的,为什么不写?写两个括号会累着?


0==aa==0

难度指数:1颗星 / 细节指数:4颗星 / 重要指数:2颗星

这个又属于那种举手之劳,但是暴雷的时候不知死活的问题了。

一般这个细节老师在讲分支循环的时候都会说吧,如果少写了一个等号,0=a是会报错的,但是a=0是会崩溃的。

不过现在还好,有的编辑器就会警告,前提是要使用够好的编辑器,碧如VS。

像我以前用TXT编程的时候,这个问题就只能靠自己去挖掘了。

细节之处见真章。


T、‘T’、“T”的区别

难度指数:3颗星 / 细节指数:2颗星 / 重要指数:4颗星

这就涉及到字符和字符串的概念了。

这也是一段时间不敲C代码会很容易忘掉的一个点。

首先,T如果经过赋值,它既是一个变量。否则它什么也不是。

其次,‘T’是一个字符,一个char,不是一个字符串。

紧接着,“T”是一个字符串,不是一个char。

什么是字符串,可以理解为char的数组,不过在字符串结尾的时候会带上一个‘\0’。


short、long、unsigned

难度指数:2颗星 / 细节指数:2颗星 / 重要指数:1颗星

不要再觉得这三个很神秘了,它们只不过是修饰词而已了。

short,可能占用比int类型更少的空间,用于仅需小数值的场合,可以简写为short。同int 类型一样,是一种有符号类型。

long,可能占用比int类型更大的空间,用于使用大数值的场合,可简写为long。同int类型一样,是一种有符号类型。

long long,可能占用比long更大的空间,用于更大数值的场合,可简写为long long,同int类型一样,是一种有符号类型。

unsigned,用于只使用非负的场合。将原本分配给负数的空间大小都分配给了正数。


标准输入输出中的占位符

难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星
代码语言:javascript
复制
%d —— 以带符号的十进制形式输出整数
%o —— 以无符号的八进制形式输出整数
%x —— 以无符号的十六进制形式输出整数
%u —— 以无符号的十进制形式输出整数
%c —— 以字符形式输出单个字符
%s —— 输出字符串
%f —— 以小数点形式输出单、双精度实数
%e —— 以标准指数形式输出单、双精度实数
%g —— 选用输出宽度较小的格式输出实数

如果是打印short,用%u,打印long,用%ld,以免在移植过程中造成不必要的麻烦。


scanf读取字符串

和读取单个字符不同,读取字符串的时候,是不需要加上&符号的。


常用ascii码

难度指数:1颗星 / 细节指数:3颗星 / 重要指数:4颗星

其实上网一搜就有了,但是有的比较重要的还是要记一下的。

代码语言:javascript
复制
ESC -- 27
0 -- 48
A -- 65
a -- 97

浮点数的比较大小

难度指数:1颗星 / 细节指数:2颗星 / 重要指数:2颗星

对于浮点数的比较,只能用<和>,舍入误差可能造成两个逻辑上应该相等的数不相等。。



提升篇

刷新输出

难度指数:2颗星 / 细节指数:3颗星 / 重要指数:4颗星

printf()什么时候真正把输出传送给屏幕?printf()语句将输出传送给一个被称为缓冲区的中介存储区域,缓冲区中的数据再不断地被传送给函数。

标准C规定在以下情况下将缓冲区内容输送给屏幕:

1、缓冲区满

2、遇到换行符

3、后面跟了一个scanf语句

可能在平时看来没有什么关系,但是我们在写服务器代码的时候就会有这种问题出来,有时候会导致消息队列被卡死,有时候会导致数据无法及时的被排出。


这里拓展一下缓冲区,为什么需要缓冲区?

首先,将若干个字符作为一个块传输比逐个发送这些字符耗费的时间少。

其次,如果输入有误,就可以使用回删来更正错误。

当最终按下回车简单的时候,就可以发送正确的输入。

缓冲分为两类,完全缓冲I/O和行缓冲I/O,对完全缓冲输入来说,缓冲区满时被清空,这种类型的缓冲常出现在文件传输中。缓冲区的大小取决于操作系统。

对于行缓冲来说,遇到一个换行符就将清空缓冲区,键盘输入是标准的行缓冲,因此按下回车键将清空缓冲区。


out of range

难度指数:4颗星 / 细节指数:5颗星 / 重要指数:5颗星

我就不吭声儿,哪个写C/C++的朋友没有遇到过这个问题。

越界。

一个潜在的问题是:出于执行速度考虑,C并不检查您是否使用了正确的数值下标,当程序运行的时候,这些语句把数据放在可能被其他数据使用到的位置上,因而可能破坏程序的结果,甚至使得程序结果崩溃。


scanf 和 scanf_s

难度指数:3颗星 / 细节指数:1颗星 / 重要指数:2颗星

在使用VS的时候,会发现编译器不通过scanf,给的理由是不安全、即使已经#include<stdio.h>

这时候,它就会推荐我们去使用scanf_s。

解决方法一:

点击项目->项目属性,点开属性页面

点击C/C++ -> 预处理器 -> 预处理器定义 -> 点击右侧的下拉列表 -> 点击下拉列表里的<编辑>

在预处理器定义中添加字段 _CRT_SECURE_NO_WARNINGS

然后点击确定,就可以使用scanf了

但是仅限于这一个项目,其他的项目还是不能使用,因此需要对所有要使用scanf的项目进行逐个修改


方法二:使用scanf_s

scanf()不会检查输入边界,可能造成数据溢出。

scanf_s()会进行边界检查。

因为带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,防止hacker利用原版的不安全性(漏洞)黑掉系统。

scanf_s()参数与scanf()不同:

例如scanf_s(“%s”,&name,n),整形n为name类型的大小,如果name是数组,那n就是该数组的大小。


char* 与 charn

难度指数:1颗星 / 细节指数:3颗星 / 重要指数:3颗星

这二者,最大的区别就在于,一个是动态空间分配,一个是静态空间分配。

如果说,使用了char *a,这时候要使用a,要手动分配空间。

而且,当我们使用sizeof(a)的时候,得出的结果是指针的大小,这是一个坑。

要获取a的大小,使用len()函数。


这里把后面一个问题一并写进来吧,

结构体中是应该放 char* 还是char[] 呢?

要知道,结构体不为字符串分配任何存储空间,所以自己掂量掂量。

如果没有什么特殊需求,还是放char[],如果要定制,那就char*。

反正这俩我都试过,一个是定长包,相对简单,一个是不定长包,虽然困难了点,可以克服。


getchar() 与 putchar()

难度指数:3颗星 / 细节指数:2颗星 / 重要指数:2颗星

getchar的用法

getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了,第一次getchar()时,确实需要人工的输入,但是如果你输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取了。

实际上是 输入设备->内存缓冲区->程序getchar

putchar的用法

(1)输出:putchar函数只能用于单个字符的输出,向终端输出一个字符,且一次只能输出一个字符。

(2)格式:对于变量来说,格式为:putchar(ch);对于常量来说,格式为:putchar(‘ch’),对于转义字符来说,格式为:putchar(’\n’)。


sprintf()做字符串拼接

难度指数:2颗星 / 细节指数:2颗星 / 重要指数:4颗星

字符串的处理一直是很重要的问题,C语言中的字符串拼接又不像Python里面直接一个加号就能解决的。

那么要怎么处理呢?这里采用 sprintf() 的方式来做这件事情。

代码语言:javascript
复制
int sprintf(char *str, const char *format, ...);

参数释义:

代码语言:javascript
复制
str -- 这是指向一个字符数组的指针,该数组存储了 C 字符串。
format -- 这是字符串,包含了要被写入到字符串 str 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags][width][.precision][length]specifier,具体讲解如下:

不多说,上案例:

代码语言:javascript
复制
char s[40]; 
sprintf(s,"%s%d%c","test",1,'2'); /*第一个参数就是指向要写入的那个字符串的指针,剩下的就和printf()一样了

设计原则:“需要知道”原则

难度指数:3颗星 / 细节指数:4颗星 / 重要指数:4颗星

为什么我觉得这个部分值得这么多星星呢?可能有的朋友觉得不值。

写几个项目,再优化,就知道了。

“需要知道”原则,类似于“单一职责原则”,尽可能保持每个函数内部工作对该函数的私密性,只共享那些需要共享的变量。


作用域·链接

难度指数:2颗星 / 细节指数:1颗星 / 重要指数:2颗星

一个C变量具有以下链接之一:外部链接、内部链接或空链接。

具有代码块作用域或者函数原型作用域的变量具有空链接,意味着它们是由其定义所在的代码块或函数原型所私有。

重点来了:具有文件作用域的变量可能有内部链接或外部链接,一个具有外部链接的变量可以在一个多文件的程序的任何地方使用,一个具有内部链接的变量可以在一个文件的任何地方使用。

那怎样知道一个文件作用域变量具有内部链接还是外部链接?可以看看在外部定义中是否使用了存储类说明符static。


extern

难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星

把变量的定义声明放在所有函数之外,即创建了一个外部变量。为了使程序更加清晰,可以在使用外部变量的函数中通过使用extern关键字来再次声明它。

如果变量是在别的文件中定义的,那么使用extern来声明该变量就是必须的。


argc 和argv

难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星

在Linux底下编程的时候,经常会看到如下的一行代码:

代码语言:javascript
复制
int main(int argc,char*argv[]){}

有时候,这个argv还会在main函数实现中被用到,那么就会有小伙伴不知道是干嘛用的,或者说知道是干嘛用的,不知道怎么用。

我也困惑过,所以写下来。

代码语言:javascript
复制
main(int argc,char *argv[ ])

argv为指针的指针

argc为整数

char **argv or: char *argv[] or: char argv[][]

假设程序的名称为CX,

当只输入CX,则由操作系统传来的参数为:

argc=1,表示只有一程序名称。

argc只有一个元素,argv0指向输入的程序路径及名称:./CX

当输入==./CX CanShu_1==,有一个参数,则由操作系统传来的参数为:argc=2,表示除了程序名外还有一个参数。

代码语言:javascript
复制
argv[0]指向输入的程序路径及名称。
argv[1]指向参数para_1字符串。

当输入==./CX CanShu_1 CanShu_2== 有2个参数,则由操作系统传来的参数为:argc=3,表示除了程序名外还有2个参数。

代码语言:javascript
复制
argv[0]指向输入的程序路径及名称。

argv[1]指向参数para_1字符串。

argv[2]指向参数para_2字符串。

以此类推.


C/C++预编译/条件编译指令

难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星

实在不想再长篇大论了,偷个懒吧:讲通C/C++预编译/条件编译指令 #ifdef,#ifndef,#endif,#define,…


指针篇

难度指数:5颗星 / 细节指数:5颗星 / 重要指数:5颗星

开发成长之路(3)-- C语言从入门到开发(讲明白指针和引用,链表很难吗?)

精品专栏打造计划


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/06/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 基本篇
    • 1、编写代码文档
      • 难度指数:1颗星 / 细节指数:5颗星 / 重要指数:5颗星
    • C语言相对其他语言的优势
      • 难度指数:1颗星 / 细节指数:1颗星 / 重要指数:3颗星
    • C语言为什么不内置输入输出语句?
      • 难度指数:2颗星 / 细节指数:4颗星 / 重要指数:3颗星
    • int main() 与 void main()的区别
      • 难度指数:1颗星 / 细节指数:3颗星 / 重要指数:2颗星
    • 可写可不写的花括号,凭什么要我写?
      • 难度指数:1颗星 / 细节指数:2颗星 / 重要指数:2颗星
    • 0==a 与 a==0
      • 难度指数:1颗星 / 细节指数:4颗星 / 重要指数:2颗星
    • T、‘T’、“T”的区别
      • 难度指数:3颗星 / 细节指数:2颗星 / 重要指数:4颗星
    • short、long、unsigned
      • 难度指数:2颗星 / 细节指数:2颗星 / 重要指数:1颗星
    • 标准输入输出中的占位符
      • 难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星
    • scanf读取字符串
      • 常用ascii码
        • 难度指数:1颗星 / 细节指数:3颗星 / 重要指数:4颗星
      • 浮点数的比较大小
        • 难度指数:1颗星 / 细节指数:2颗星 / 重要指数:2颗星
    • 提升篇
      • 刷新输出
        • 难度指数:2颗星 / 细节指数:3颗星 / 重要指数:4颗星
      • out of range
        • 难度指数:4颗星 / 细节指数:5颗星 / 重要指数:5颗星
      • scanf 和 scanf_s
        • 难度指数:3颗星 / 细节指数:1颗星 / 重要指数:2颗星
      • char* 与 charn
        • 难度指数:1颗星 / 细节指数:3颗星 / 重要指数:3颗星
      • getchar() 与 putchar()
        • 难度指数:3颗星 / 细节指数:2颗星 / 重要指数:2颗星
      • sprintf()做字符串拼接
        • 难度指数:2颗星 / 细节指数:2颗星 / 重要指数:4颗星
      • 设计原则:“需要知道”原则
        • 难度指数:3颗星 / 细节指数:4颗星 / 重要指数:4颗星
      • 作用域·链接
        • 难度指数:2颗星 / 细节指数:1颗星 / 重要指数:2颗星
      • extern
        • 难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星
      • argc 和argv
        • 难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星
      • C/C++预编译/条件编译指令
        • 难度指数:2颗星 / 细节指数:2颗星 / 重要指数:3颗星
        • 难度指数:5颗星 / 细节指数:5颗星 / 重要指数:5颗星
    • 指针篇
    相关产品与服务
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档