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

网友投稿 729 2022-05-29

文章目录

前言

基本篇

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* 与 char[n]

难度指数: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颗星

前言

好久不见啦朋友们。

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

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

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

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

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

基本篇

1、编写代码文档

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

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

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

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

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ///

/// /// /// /// ///

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

1

2

3

4

5

6

7

呐,看明白不?画三个杠,不用回车,两个

之间写你的函数功能描述,之间写你的参数释义,也可以写在后面,return我就不多说啥了吧。

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

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

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

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

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

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

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

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

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

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

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

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

儿童节快乐呀各位

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

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

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

int main(){ return 0; }

1

2

3

4

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

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

多写一行return 0;很难吗?

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

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

比方说:

while(1) printf('1\n')

1

2

那这个花括号写不写呢?

不写会有什么好处?

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

2、少写两个括号

不写有什么坏处?

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

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

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

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

0==a 与 a==0

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

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

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

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

细节之处见真章。

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

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

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

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

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

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

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

short、long、unsigned

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

short,

可能

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

long,

可能

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

long long,

可能

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

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

标准输入输出中的占位符

%d —— 以带符号的十进制形式输出整数 %o —— 以无符号的八进制形式输出整数 %x —— 以无符号的十六进制形式输出整数 %u —— 以无符号的十进制形式输出整数 %c —— 以字符形式输出单个字符 %s —— 输出字符串 %f —— 以小数点形式输出单、双精度实数 %e —— 以标准指数形式输出单、双精度实数 %g —— 选用输出宽度较小的格式输出实数

1

2

3

4

5

6

7

8

9

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

scanf读取字符串

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

常用ascii码

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

ESC -- 27 0 -- 48 A -- 65 a -- 97

1

2

3

4

浮点数的比较大小

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

提升篇

刷新输出

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

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

1、缓冲区满

2、遇到换行符

3、后面跟了一个scanf语句

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

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

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

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

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

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

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

out of range

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

越界。

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

scanf 和 scanf_s

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

这时候,它就会推荐我们去使用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* 与 char[n]

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

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

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

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

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

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

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

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

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

getchar() 与 putchar()

getchar的用法

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

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

putchar的用法

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

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

sprintf()做字符串拼接

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

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

int sprintf(char *str, const char *format, ...);

1

参数释义:

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

1

2

不多说,上案例:

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

1

2

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

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

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

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

作用域·链接

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

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

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

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

extern

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

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

argc 和argv

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

int main(int argc,char*argv[]){}

1

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

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

main(int argc,char *argv[ ]) argv为指针的指针 argc为整数 char **argv or: char *argv[] or: char argv[][]

1

2

3

4

5

6

7

假设程序的名称为CX,

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

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

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

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

argv[0]指向输入的程序路径及名称。 argv[1]指向参数para_1字符串。

1

2

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

argv[0]指向输入的程序路径及名称。 argv[1]指向参数para_1字符串。 argv[2]指向参数para_2字符串。

1

2

3

4

5

以此类推.

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

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

指针篇

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

精品专栏打造计划

C 语言 Python

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:浅谈HBase的数据分布
下一篇:Unix基础之环境变量
相关文章