周末闲来无事,(哗),好久之前买的C专家编程一直没看,翻起来看了一下

尽量不使用unsigned

尽量不要在代码中使用unsigned,尤其是一些看起来是无符号类型的数字,比如年龄等,因为难免要使用unsignedsigned混用,这时候可能会发生-1被转换为一个大数字的错误情况。StackExchange 上有一篇很好地讨论

有一种情况,在使用2进制操作的时候请一定要使用unsigned

注意缓冲区溢出攻击

不要使用gets等没有指定缓冲区大小的函数,有安全隐患,使用fgets等新函数。(P42)

fgets(char* buffer, size_t n, FILE* f);

Shell 小知识

使用"--"的约定:从这里开始没有参数是选项开关,即使他是以"-"开头(P45)

管理内存的建议

错误的用法

char* fn() {
char buffer[128];
// logic here
return buffer;
}

buffer是局部变量,函数执行结束之后会被自动释放。

解决

void fn(char* buffer, int n) {
// do something with buffer
};

应该让用户来解决内存分配问题

使用Lint

lint程序是软件的道德准。当你做错事时,他会告诉你哪里不对,应该始终使用lint,按照他的道德准则办事。

现存的可以用的应该是splint了,vim用户可以使用syntatics插件

结构中的无名字段

struct pid {
unsigned inactive : 1;
unsigned : 1; // 匿名字段 主动填充
unsigned refcount : 6;
unsigned : 0; // 匿名零长度字段,强制对齐
short pid_id;
...
};

在考虑到 C 结构中会填充满一个 int 长度,如下程序会有什么结果呢?

#include <stdio.h>

struct X {
int i : 3, j : 5, k : 24;
}; struct Y {
int i : 3, j : 5, k : 25;
}; struct Z {
int i : 3, : 0, j : 5; // nice syntax huh ?
}; int main() {
// 结果分别是 4 8 8
printf("%d %d %d", sizeof(struct X), sizeof(struct Y), sizeof(struct Z));
}

结构与传参数

  1. int型参数往往会被传递到寄存器中

  2. 在结构中放置数组会实现传参数一样的传递数组的效果

     struct s { int a[100]; };
    sturct s orange, lime, lemon; struct s two(struct s e) {
    for (int i = 0; i < 100; i++)
    e.a[i] *= 2;
    return e;
    } int main() {
    for (int i = 0; i < 100; i++)
    lime.a[i] = 1;
    lemon = two(lime);
    oragne = lemon;
    }

联合的使用

  1. 联合往往是作为大的结构的一部分,当两个属性互斥的时候用于节约内存

  2. 联合可以用于把一样东西解释成两种东西

     union color {
    int val;
    struct { char r, g, b, a; } byte;
    };
  3. 枚举的好处在于枚举的名字在编译以后依然可见,而宏则不可见

数组并非指针

这两个是完全不同的

extern int* x; // 指针
extern int x[]; // 数组

动态链接库为什么会使程序性能提高

  1. 动态链接可执行文件的体积小
  2. 操作系统内核保证同一个动态链接库能够被所有使用它们的进程共享

如何生成动态链接库(.so)

编译不包含 main 的文件到.o文件,然后使用 ld 生成动态链接库,使用 ar 生成静态链接库

或者使用-G 参数生成so 文件,cc -o libfruit.so -G fruit.c

使用-L 指定编译时路径,-R 指定运行时路径,-l 指定想要连接的库

值得注意的是,如果你先要连接的是libx.so,只需要添加-lx 参数就好了

a.out 的构成

内存映射/虚拟内存

对于每个进程,进程本身都认为它独自占有了4GB的内存,虚拟内存通过『页(Page)』来组织,页就是操作系统在磁盘和内存之间进行 swap 的单位,通常为几 k 大小。当访问的地址不在实际内存中的时候,MMU 就会产生一个页错误,如果有效,内核从磁盘中读入这一页。如果发生了错误,内核通过向违规的内存发布合适的信号来处理对无效地址的引用,每个程序可以通过 signal 注册函数来处理不同的信号。

内存映射硬件还会确保你无法访问操作系统分配给你的进程之外的内存。

Cache

全写型缓存 Write-through 每次写入时同时写入内存

写回式内存 Write-back 第一次写入时只对 Cache 写入,再次写入 Cache时,把上一次写入内存。

Cache 进行访问的单元是行(line),一个数据部分(块)和一个标签(内存中的地址),行和块两个术语有时候可以通用。

memcpy 等库函数往往为缓存优化过而比手工使用 for 循环快很多。

alloc

malloc 简单地分配内存,calloc 会把分配好的内存都清零,realloc 用于重新分配内存。

总线错误

总线错误机会都是由于未对齐的读或者写完成的,段错误则有可能是由多种不同错误引起的。通常的原因有

  1. 解引用一个包含非法值得指针(NULL 等)
  2. 未获得正确权限就使用(访问只读区域,越界,指针已经被 free)
  3. 栈或者堆满了

隐式类型提升

char short会被提升为 int,sizeof('A')返回4,但是 C++中又会返回1.

有限状态机

它的基本思路使用一张表表示所有可能的状态,并列出进入每个状态时可能执行的所有动作,其中最后一个动作就是计算下一个应该进入的状态,通常基于当前的状态和下一次输入的基础计算。绝大多数基于函数指针数组。直到结束函数调用或者遇到一个错误的状态。状态函数返回一个后续函数的指针,使用 switch 语句放在循环内部等都可以实现有限状态机。当状态函数有多个不同的参数的时候,可以使用类似 main 函数的 argc 和 argv 来传递参数。

调试

可以编写一个函数,用于遍历并打印整个数据结构。这个函数在程序中不会被调用,只有在 debug 的时候才会被调用。

可调试的编码意味着把系统分为几个部分,先让程序总体结构运行,只有在基本的程序能够运行之后,在把复杂的细节完善没性能调优和算法优化,有时候,花点时间把变成问题分解为几个部分往往是解决它的最快方法。

如果我搁浅到一个荒岛上,并只被允许带一个数据结构,那我毫无疑问选择散列表。

库函数和系统调用的区别

库函数是语言或者应用程序的一部分,而系统调用是操作系统的一部分,通过 trap 来实现,需要切换到到内核态执行,开销大,当然许多 C 库函数是通过系统调用实现的,比如 system 就是一个库函数。

从文件中完全随机提取一行,要求只能遍历一次文件

如果不限定遍历文件的次数,那么,全部读取完毕后,随机选择一行即可。

如果只能读取一次,那我们首先读取一行,然后读取第二行,50%的几率选择原来的或者新的,然后第三行,2/3的概率选择原来的或者1/3选择新的。直到最后一行,(n-1)/n的概率选择原来的(1/n)的概率选择新的。

多维数组

多维数组的不同纬度可能指向的是同一个地址,但是他们的类型不一样,+1时候的效果也不一样

int A[10][10][10];
printf("%d\n", (void*)(A+1) - (void*)A); // 400
printf("%d\n", (void*)(A[0]+1) - (void*)A[0]); // 40
printf("%d\n", (void*)(A[0][0]+1) - (void*)A[0][0]); // 4

多维数组做函数参数时也很蛋疼,只有第一个维度的数字可以省略

int process(int a[][10][10]);

对于程序中出现了a[0][0] = 1等语句,我们并不能知道 a 到底是个什么类型,因为C语言只保证了a[i][j]((a+i) + j)是等价的。

// a 可能是
int a[10][10];
int** a;
int* a[10];
int (*a)[10];

C专家编程阅读笔记的更多相关文章

  1. c专家编程读书笔记

    无论在什么时候,如果遇到malloc(strlen(str));,几乎可以直接断定他是错误的,而malloc(strlen(str)+1):才是正确的: 一个L的NUL哟关于结束一个ACSII字符串: ...

  2. 类型解释器——C专家编程读书笔记

    对于声明,应该按下面的步骤来进行解释: 1) 声明从它的名字开始读取,然后按照优先级顺序依次读取 2) 优先级顺序 a) 括号括起来的部分 b) 后缀操作符,()表示函数,[]表示数组 c) 前缀操作 ...

  3. Javascript_Dom编程 阅读笔记(1)

    寻找html页面中的节点 所有文档节点getElementsByTagName("*"); document.getElementsByTagName("*") ...

  4. Java并发编程阅读笔记-Java监视器模式示例

    1.前言 书中在解释Java监视器模式的时候使用了一个车辆追踪器例子,根据不同的使用场景给出了不同的实现和优化. 2.监视器模式示例 实现一个调度车辆的"车辆追踪器",每台车使用一 ...

  5. Java并发编程阅读笔记-锁和活跃性问题

  6. Java并发编程阅读笔记-同步容器、工具类整理

  7. 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

    本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O ...

  8. 【unix网络编程第三版】阅读笔记(二):套接字编程简介

    unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...

  9. 《Java编程思想》阅读笔记二

    Java编程思想 这是一个通过对<Java编程思想>(Think in java)进行阅读同时对java内容查漏补缺的系列.一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会忽略或 ...

随机推荐

  1. http的几种请求的方式(Get、Post、Put、Head、Delete、Options、Trace和Connect)

    http的这几种请求方式各有各的特点,适用于各自的环境.下面我就说说这些方式的各自特点: 1.Get:它的原理就是通过发送一个请求来取得服务器上的某一资源.获取到的资源是通过一组HTTP头和呈现数据来 ...

  2. Freemaker配置文件详解

    classic_compatible=true              ##如果变量为null,转化为空字符串,比如做比较的时候按照空字符做比较 whitespace_stripping=true  ...

  3. spring项目中dubbo相关的配置文件出现红叉的问题

    近来在eclipse中导入了一个web项目,但是发现项目上有红色的叉号. 原来是spring中关于dubbo的配置文件报错了. Multiple annotations found at this l ...

  4. jquery实现名单滚动

    转:http://www.qdfuns.com/notes/25341/917d9cb031f835a086dd445b77b6e04e.html 介绍:记录滚动特效.就是那一排文字不停地滚啊滚啊滚得 ...

  5. Python Number(数字)

    ---Number类型的细节,其包含的基本数字类型 ---Number基本数字类型之间的数值转换 ---Number上面的数学函数,有些可以直接调用,有些需要导入库 参见http://www.runo ...

  6. 【树莓派】Linux应用相关:自动删除n天前日志

    linux是一个很能自动产生文件的系统,日志.邮件.备份等.虽然现在硬盘廉价,我们可以有很多硬盘空间供这些文件浪费,让系统定时清理一些不需要的文件很有一种爽快的事情.不用你去每天惦记着是否需要清理日志 ...

  7. java安装1335错误解决办法(亲测)

    心血来潮想了解一下java,结果一开始就碰到了让心"恶心"的1335错误. 废话不多说,直接看下面: 你可以先尝试在这个链接下载java.exe文件 http://www.orac ...

  8. 老李分享:大数据框架Hadoop和Spark的异同

    poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-845052 ...

  9. Android自定义View之音频条形图

    2016-04-12 17:52 76人阅读 评论(2) 收藏 举报  分类: Android(26)  版权声明:本文为博主原创文章,未经博主允许不得转载. 新建项目,新建MusicRectangl ...

  10. poj 3270 Cow Sorting (置换入门)

    题意:给你一个无序数列,让你两两交换将其排成一个非递减的序列,每次交换的花费交换的两个数之和,问你最小的花费 思路:首先了解一下什么是置换,置换即定义S = {1,...,n}到其自身的一个双射函数f ...