【推荐书籍】C语言深度解剖

2023-05-16

“她想要统治,同时又要享受;她想要王后的权柄,还要女人的自由;她伸出玉手,抓起王冠,就像拿起一件意想不到的礼物。她那时还太年轻,不知道所有命运赠送的礼物,早已在暗中标好了价格。”

– 斯蒂芬·茨威格茨威格《断头王后》

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BgzpxaYQ-1677686386468)(null)]

推荐理由

又到了一年一度的金三银四换坑的日子,这本书虽然不敢说对得起“深度”二字,但是对于即将去笔试面试的求职者给予不少的帮助,重要地是短时间内能够知道很多要考的细节,有很多你平时没有注意到的很多知识点都能够从中获取。

你好,好运的 3 月!

笔记

  • 二进制中数据是以补码的形式存储,补码是绝对值的原码取反得到反码,然后+1得到。

    对于-0和+0,

    十进制数原码反码补码
    +0000000000000000000000000
    -0100000001111111100000000

    之所以补码相同是应为 -0 的反码 +1 后溢出了,最高位会被去掉,所以补码相同。

  • 有符号和无符号之间运算,会将有符号转换成无符号再运算。

  • float变量与0比较,不能采用 ==,因为浮点数是有精度,应该采用范围比较,比如

    if((i<0.000001) && (i> -0.000001))

    同样也不要在一个很大的浮点数和很小的浮点数之间运算。

  • 判断指针是否为空,比较好的写法:if(NULL ==p); if(NULL != p);

  • 空语句建议这样写,能突出该语句为空语句:NULL; // ARM中NOP指令

  • if 语句在做判断的时候,将出现概率比较高的语句放在前面,可读性和性能都会提高。

  • case 语句的排列选择,为了可读性,按数字大小排,为了性能,按出现概率排。

  • switch 中的 default 语句处理默认情况,通常是用来处理错误,所以不要偷懒去把 case 的语句放在default 处理。

  • 在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。

  • 空指针类型void*定义的指针,任何类型的指针无需强制类型转换即可赋值给它(赋值后还是void*型,还可以被别的指针类型赋值)。但是空指针不能直接赋值给其他指针。(可以说 void 涵盖的范围更大)

  • 函数默认返回值为 int 型。

  • ANSI标准,不能对 void 指针进行算法操作,比如:

    void *i;
    i++;
    
  • 如果函数的参数可以是任意类型指针,那么应声明其参数为 void *。

  • void不能代表一个真实的变量(C语言要求类型),是一个抽象的概念,可以理解为一个可以动态变换的盒子,你可以赋值各种指针类型,但是要使用它,你必须强制类型转换成固定的类型。

  • return语句不可返回指向“栈内存”(比如局部定义数组地址)的“指针”,因为该内存在函数体结束时被自动销毁。

  • const定义只读变量,具有不可变性,但其仍是变量,所以不能用来定义数组,下面的写法是错误的。

    const int i = 10;

    int a[i];

  • 若使用 const 修饰函数的参数和返回值,当为指针时,有意义,一般数值没有意义(因为传入的是副本内容)当返回为 const 指针时,表示对返回指针所指向的数据内容不要进行修改,有修改则程序会报错!

  • volatile 关键字是一种类型修饰符,用它修饰的变量表示可以被某些编译器,未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

    volatile int i;

    volatile 关键字告诉编译器 i 是随时可能发生变化的,每次使用它的时候必须从内存中取出i的值,因而编译器生成的汇编代码会重新从i的地址处读取数据放在 i 中。这样看来,如果是一个寄存器变量或者表示一个端口数据或者是多个线程的共享数据,就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。

    如果不加可能会被优化,int i = 10; int j = i; int k = i; // 在后两句中,由于i没有被用作左值,所以两次赋值会可能被优化使用同一个数据(而不是每次重新从内存中取),这样的做法提高了效率,若是在两个语句之间该值被改变,就不能得到新的值。

  • const volatile int i=10;中的i是什么属性?存在的意义何在?

    使用 const 告诉编译器,你的程序不应该修改他的值。(并不代表别的非你的程序的东西不会修改)

    volatile 告诉编译器,其值是很容易发生改变,不要做太多优化。

    两者并不矛盾,因为该值可能会被非你的程序改变,比如被一些硬件设备改变(单片机中寄存器),这个值也是容易改变的,但是不应该被你的程序改变。

  • 一个空的结构体变量的大小是1byte。(因为编译器认为每个变量应该有大小,而 1byte 是最小的)

  • unionstruct 类似,但是所有的成员共用一个结构体,其大小等于最大成员的大小。注意大小端模式的影响。

  • 储存模式:

    大端模式(Big endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。

    小端模式(Little-endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。

  • enum color_type
    {
      RED,
      G,
      B,
      F,
      H,
      K
    }color_variabnle;
    

    color_variabnlecolor_type 类型的一个变量,花括号内是该变量取值范围,color_variabnle只能取花括号内任何一个值。

    括号内的成员都是常量(枚举常量)。所以sizeof(color_variabnle)为4个字节(就是int型)

    可以想宏定义的常量一样使用枚举常量(不用写枚举变量名。直接用)。

    #define 在预编译时替换,而 enum 则是在编译的时候确定其值。使用结构体成员的时候,可以

  • const stu stu3; // stu是结构体类型名,判断 const 修饰的是那个,stu也是忽略(不管是不是指针)后,再看修饰哪个。

  • typedef不支持如下的用法(类型扩展):

    typedef int INT32;
    
    unsigned INT32 i = 0;
    
  • C语言中的注释内容在编译的时候会使用空格代替,所以分析一些写在语句间的注释时,用空格代替就可以知道结果了。

  • 如下的写法会出现错误,/*会被当做注释的开始

    y = x/*p
    
  • 出色的注释

    • 规则1

      注释应当准确、易懂,防止有二义性。错误的注释不但无益反而有害。

    • 规则2

      边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要及时删除。

    • 规则3

      注释是对代码的“提示”,而不是文档。程序中的注释应当简单明了,注释太多了会让人眼花缭乱。

    • 规则4

      一目了然的语句不加注释。例如: i++; /*i加1*/多余的注释

    • 规则5

      对于全局数据(全局变量、常量定义等)必须要加注释。

    • 规则6

      注释采用英文,尽量避免在注释中使用缩写,特别是不常用缩写。因为不一定所有的编译器都能显示中文,别人打开你的代码,你的注释也许是一团乱码。还有,你的代码不一定是懂中文的人阅读。

    • 规则7

      注释的位置应与被描述的代码相邻,可以与语句在同一行,也可以在上行,但不可放在下方。同一结构中不同域的注释要对齐。

    • 规则8

      当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。

    • 规则9

      注释的缩进要与代码的缩进一致。

    • 规则10

      注释代码段时应注重“为何做(why)“,而不是“怎么做(how)”.说明怎么做的注释一般停留在编程语言的层次,而不是为了说明问题。尽力阐述“怎么做”的注释一般没有告诉我们操作的意图,而指明“怎么做”的注释通常是冗余的。

    • 规则11

      数值的单位一定要注释。注释应该说明某数值的单位到底是什么意思。比如:关于长度的必须说明单位是毫米,米,还是千米等;关于时间的必须说明单位是时,分,秒,还是毫秒等。

    • 规则12

      对变量的范围给出注释。

    • 规则13

      对一系列的数字编号给出注释,尤其在编写底层驱动程序的时候(比如管脚编号)

    • 规则13

      对于函数的入口出口数据给出注释。

  • C语言里以反斜杠()表示断行。编译器会将反斜杠剔除掉,跟在反斜杠后面的字符自动接续到前一行。但是注意:反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。

    但是对于一些完整的语句,比如以下是不需要的\

    if(a
      )
    {
        b=3;
    }
    

    有时间再看看编译器是如何处理空格的(或许得看编译原理)。

  • 逻辑运算符,在判断语句中,|| 一边为真即结束,&& 一边为假,即结束。

  • 右移运算符作用在负数时,最高位补 0 还是 1 由编译器决定。

  • char d[10]= {"abcdefg"}; // 是可以的,{}相当于有打包的作用,但是char换成int,就会报错。

  • 预处理命令

    #undef 撤销已定义的宏名

    #line 改变当前行数和文件名称,它们是在编译程序中预先定义的标识符命令的基本形式如下:

    #line number["filename"]

    #error 编译程序时,只要遇到 #error 就会生成一个编译错误提示消息,并停止编译.(比如可以用来判断某些宏定义是否定义等等)

    #pragma 为实现时定义的命令,它允许向编译程序传送各种指令例如,编译程序可能有一种选择,它支持对程序执行的跟踪。可用#pragma语句指定一个跟踪选择。

  • ANSI 标准C还定义了如下几个宏:

    _LINE_ 表示正在编译的文件行号

    _FILE_ 表示正在编译的文件的名字

    _DATE_ 表示编译时刻的日期字符串,例如: “25 Dec 2007”

    _TIME_ 表示编译时刻的时间字符串,例如: “12:30:55”

    _STDC_ 判断该文件是不是定义成标准C程序

  • const 修饰的只读变量不能用来作为定义数组的维数,也不能放在 case 关键字后面。

  • 编译器对注释的处理先于预处理指令被处理,所以使用 define 来定义注释符号,然后在程序中使用新定义的注释符号会报错。

  • 使用 define 宏定义表达式,记住别吝啬括号。

  • #include ".h" ,先在当前路径找,找不到再按系统指定路径。<>系统规定的路径找

    #include 也支持相对路径,.代表当前目录,…代表上层目录。

  • #pragma 的指令比较复杂,也会根据带参数的不同有不同功能。

    #pragma message(“hello,V00”) // 可以使用其在编译输出端口输出括号内的信息

  • #pragma code_seg另一个使用得比较多的pragma参数是code seg。

    格式如:#pragma code seg( [“section-name”[,“section-class”]]它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

  • #pragma once (比较常用)只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在Visual C++6.0中就已经有了,但是考虑到兼容性并没有太多的使用它。

  • #运算符可以把语言符号转换成字符串。

    #define SQR(x) printf(“The square of “#x” is %d.\n”,(x)*(x))

    SQR(8) 输出 The square of 8 is %d.

  • ##运算符将两个语言符号组结合成一个。

    #define X(n) x##n

    则X(8) 会展开成x8

  • 指针在定义的时候一定要赋值,不然就赋值为NULL,否则会存储任意的地址(如果通过这个任意地址访问,可能会导致系统崩溃)

  • 数组名作为右值,代表的是数组首元素的首地址。数组名不能作为左值。

  • &a代表的是数组的首地址,&a[0]代表的是数组首元素的地址(等同于a)。(值是一样,但意义不一样,运算的结果就不同)

  • 函数指针、函数指针数组、函数指针数组的指针

  • 野指针很可怕,不用的时候把它置为NULL地址处(初始化和使用完)

  • 静态区:保存自动全局变量和static变量(包括static全局和局部变量)。静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。

    栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容,也会自动被销毁。其特点是效率高,但空间大小有限。

    堆: 由malloc系列函数或new操作符分配的内存。其生命周期由free或delete决定。在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。

  • 容易出现野指针的地方:

    1、结构体内有指针变量,在定义的时候初始化(赋值NULL或者分配内存),防止野指针。

    2、给结构体指针分配了内存,但是结构体内的指针并没有分配,也是野指针

  • 一般在函数入口处使用assert(NULL != p)对参数进行校验。在非参数的地方使用if (NULL!=p)来校验。但这都有一个要求,即p在定义的同时被初始化为NULL了。比如上面的例子,即使用if (NULL !=p)校验也起不了作用,因为name指针并没有被初始化为NULL,其内部是一个非NULL的乱码。

    assert是一个宏,仅在Debug版本有用,Release无效。用来调试用。

  • 使用malloc分配的内存使用完后一定要使用free释放,一定要一一对应,否则会造成内存泄漏或者别的错误。

    free的作用是斩断指针对内存的所有权关系,并不会改变指针的值和内存的值。(所以需要手动把指针赋值为NULL)

  • 编码风格

    1、函数要有注释(说明创建时间、作者、函数作用、参数说明、修改历史等)

    2、函数间空格隔开

    3、变量定义与执行语句隔开

    4、逻辑不相关的语句用空行隔开

    5、修改别人的代码时不要轻易删除,先注释

    6、缩进采用四个空格(可以考虑IDE定义tab键为空格)

  • 一般来说,函数的入口参数,目的参数在前,源参数在后(和#define的使用类似)

  • 如果传入的是指针,且只是读,加const

  • 函数入口处,使用assert检查指针,而不推荐使用if

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

【推荐书籍】C语言深度解剖 的相关文章

随机推荐

  • 【RT-Thread】使用 Finsh 查看线程状态中的 sp 代表什么意思?

    佛说 xff1a 一切有为法 xff0c 如梦幻泡影 xff0c 如露亦如电 xff0c 应作如是观 金刚经 详解 sp 含义 最近使用 RT Thread 的 Finsh 输入 list thread 命令查看线程状态时 xff0c 突然
  • Qt学习:常用数学函数

    C语言中 Qt中 xff0c 都没有以任意为底数的对数函数 xff0c 所以log5 3 以5为底 是没有的 但是可以用logx y 61 ln y ln x 来代替 xff0c 修改代码如下 1 Qt中对数 xff0c 通过自然对数qLn
  • 【好书推荐】-- 《以太网权威指南》(第2版)

    书籍的全部意义是使人善用自己的孤独 匡扶 纳闷集 稍微点题外话 xff0c 最近换了份跟汽车电子相关的工作 xff0c 突然强度就上来了 xff0c 以前5天8小时还能摸鱼的幸福时光还甚是想念 话扯远了 xff0c 回到文章内容 因为最近手
  • 【神器】截图+贴图工具 Snipaste

    生活就好像一大杯 xff0c 一直在续着热水的茶 一天天过下去就像一次次开水 xff0c 到后来越来越淡 xff0c 根本没味道了 匡扶 纳闷集 1 推荐理由 今天介绍的这款神器 xff0c 名唤 Snipaste 毫不夸张地说 xff0c
  • 【好书推荐】第一本无人驾驶技术书

    炮制虽繁必不敢省人工 xff0c 品味虽贵必不敢减物力 xff0c 修合无人见 xff0c 存心有天知 同仁堂古训 推荐理由 智能驾驶行业如火如荼 xff0c 和半导体 xff08 在中国 xff09 元宇宙等新兴行业一样有着醉人的科技元素
  • 【神器】MarkDown-沉浸写作的利器

    最好的销售方法 xff0c 就是真诚地相信你所销售的东西 出售你真正相信的东西感觉很棒 xff0c 而试图出售你不相信的东西 xff0c 感觉很糟糕 Sam Altman 如何成功 1 缘起 话说天下文档格式各有不同 xff0c 富文本格式
  • 【好书推荐】C语言程序设计:现代方法(第二版)

    待到秋来九月八 xff0c 我花开后百花杀 冲天香阵透长安 xff0c 满城尽带黄金甲 唐 黄巢 不第后赋菊 推荐理由 C 语言作为嵌入式开发的必备语言 xff0c 重要性不言而喻 基于此 xff0c 本次推荐的书籍是 C语言程序设计 xf
  • 【好书推荐】网络是怎样连接的

    游山五岳东道主 xff0c 拥书百城南面王 万人丛中一握手 xff0c 使我衣袖三年香 龚自珍 投宋于庭翔凤 xff08 最后两句 xff0c 我愿称之为目前所见最强 夸夸 词 xff09 推荐理由 今天推荐的这本书想必很多搞计算机网络的人
  • 【好书推荐】程序是怎样跑起来的

    一位护士问临终的病人 xff0c 他们有什么遗憾 这个护士后来总结了 5 个最常见的回答 xff1a 不要忽视你的梦想 xff0c 不要工作太久 xff0c 说出你内心所想 xff0c 结交朋友 xff0c 要开心 推荐理由 之前有推荐过两
  • 【好书推荐】图解TCP-IP(第5版)

    有一个公式 xff1a 幸运 61 你做的事情 X 知道的人数 你做的事情越多 xff0c 知道的人越多 xff0c 越可能幸运 发表作品会增加你的幸运 推荐理由 由于工作现在与网络技术密切相关 xff0c 所以工作之余会重点补充这方面的知
  • 【神器】Adobe Illustrator-作图利器

    但我们却不加留意地度过我们美好的日子 xff0c 只有到了糟糕的日子真正来临的时候 xff0c 我们才会想念和渴望曾经有过的美好日子 我们脸带愁容 xff0c 许多欢乐愉快的时光未加品尝和咀嚼就过去了 xff0c 直到以后日子变得艰难和令人
  • 嘉立创EDA的一些使用技巧

    立创EDA专业版 使用教程 lceda cn https prodocs lceda cn cn faq editor index html绘制板框 xff1a https blog csdn net gutie bartholomew a
  • Halcon —— 图像像素类型与转换

    图像类型 就目前工业领域主流的图像处理工具halcon来讲 xff0c 有以下几种图像类型 xff1a byte complex cyclic direction int1 int2 int4 int8 real uint2 xff0c 具
  • 【神器】嘉立创EDA推荐及一些技巧

    食肉何曾尽虎头 xff0c 卅年书剑海天秋 文章幸未逢黄祖 xff0c 襆被今犹窘马周 自是汝才难用世 xff0c 岂真吾相不当侯 须知少日拏云志 xff0c 曾许人间第一流 清代 吴庆坻 题三十计小象 背景 最近因为需要 xff0c 所以
  • 【Keil】编译选项设置 Warning 为 error

    死亡是一座永恒的灯塔 xff0c 不管你驶向何方 xff0c 最终都会朝它转 一切都将逝去 xff0c 只有死神永生 刘慈欣 三体 前言 众所周知 xff0c 一般而言 xff0c 编译程序过程中的 warning 警告并不会影响可执行文件
  • 【好书推荐】计算机网络:自顶向下方法(第七版)

    人生的美妙之处在于迷上一样东西 人生苦短 xff0c 少做些虚无缥缈的事 刘慈欣 三体 推荐理由 自计算机网络诞生以来 xff0c 经过数十年的发展 xff0c 计算机的体系已经非常庞大 xff0c 同时计算机网络也大大促进了人类社会的发展
  • 【C语言内功心法】__DATE__和__TIME__帮你构建更完善的软件版本信息

    弱小和无知 xff0c 都不是生存的障碍 xff0c 傲慢才是 刘慈欣 三体 何为 DATE 和 TIME xff1f DATE 和 TIME 是 C 语言中的两个内置宏 xff0c 你可以理解为两个字符串值 xff0c 这两个宏用于记录编
  • 【好书推荐】车载以太网权威指南

    20年后 xff0c 会令你失望的不是做过的事 xff0c 而是你没做过的 xff0c 所以解开帆索 xff0c 从安全的港湾出发 xff0c 乘风而行 xff0c 去探索 去梦想 去发现 xff01 Twenty years from n
  • 【荐书】C程序设计语言(第二版)

    在大多数人眼中 xff0c 我是个一事无成 乖僻古怪 令人作呕的人 我毫无社会地位可言 xff0c 也永远不会有 总之 xff0c 我是底层人中的底层人 好吧 xff0c 就算这些看法都完全正确 xff0c 我也想有那么一天 xff0c 通
  • 【推荐书籍】C语言深度解剖

    她想要统治 xff0c 同时又要享受 xff1b 她想要王后的权柄 xff0c 还要女人的自由 xff1b 她伸出玉手 xff0c 抓起王冠 xff0c 就像拿起一件意想不到的礼物 她那时还太年轻 xff0c 不知道所有命运赠送的礼物 xf