HFC读书笔记

发布时间 2023-06-18 15:18:24作者: Captain_Amazing

数组变量可以当成指针使用, 但有不同的地方:

  1. sizeof(数组) 是数组的大小, 而用在指针上返回4或8

  2. &数组变量 == 数组变量, 而&指针不同

  3. 数组变量并没有分配存储空间, 所以不能指向其它地方

scanf()的用法可参考printf()的用法, 需要设置长度, 例如字符数组, 需要减1, 留给\0, 避免缓冲区溢出, 对于字符串更好的是用fgets(),

  配合sizeof运算符使用  备注 scanf -> scan formatted

char指针设置为字符串字面值, 确保使用const关键字: const char *s = "some string";  因为字面值是不可修改的

---------------------------------------------------------------------------------------------------------------------------------------------------:-)

熟悉使用string.h标准库, 处理字符串

---------------------------------------------------------------------------------------------------------------------------------------------------:-)

使用小工具: 

程序结束时检查错误状态: UNIX用 echo $?    windows用 echo %ERRORLEVEL%

进程有标准输入和标准输出, 标准错误  stdin, stdout, stderr

当调用printf()时, 其实是调用了fprintf()函数的特殊  fprintf(stdout, "我喜欢乌龟!");

  可以用fscanf()来读取标准输入, 用法和scanf()很像

  可以用2>重定向标准错误

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

符号 | 表示管道(pipe), 它能连接一个进程的标准输出与另一个进程的标准输入

例如: (./bermuda | ./geo2json) < gpsdata.csv > output.json  数据流由管道从左到右流动

除了标准输入, 标准输出, 标准错误, 还可以自建数据流. 使用fopen()函数

FILE *in_file = fopen("input.txt", "r");

w = 写; r = 读; a = 追加  (注意, 全是小写, 按照书上的例子, 不小心大写了, 运行时报"段错误")

创建数据流后, 可以使用fscanf()函数读数据, fprintf()函数写数据

使用完后需要关闭流: fclose(in_file);

如果出现错误, fopen()会返回0, 最好检查一个有没有错误发生!

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

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

可以向函数传递参数, 前面参数个数从程序本身算起; 后面是char指针数组, 实际也就是字符串指针数组的意思

熟悉POSIX库中的unistd.h头文件中的函数getopt(), 用于处理命令行选项(即以"-"开头的命令行参数)

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

创建头文件只需要在目录中新建.h文件, 写上内容, 保存, 在程序的开头加上预处理命令#include "xxx.h"

  注意引号是相对路径查找头文件, 而尖括号是绝对路径查找头文件, 函数声明通常放在头文件中

  头文件的作用: 一是函数顺序问题; 另一个是共享代码, 到编译后期链接时有用

共享变量: 在头文件中声明, 在使用时加上extern, 例如: extern int passcode;

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

gcc -c *.c 此命令会为所有源文件创建目标文件, 但不会链接成完整的可执行文件

gcc *.o -o launch 然后, 把目标文件链接起来

总结来说, 如果只修改了一个文件, 只需要重新编译这一个文件, 再重新链接成程序即可

make是如何自动化代码的构建过程的:

  写出Makefile文件, 描述目标,依赖项和生成方法, 再用命令make launch

 其实原理非常简单, 只是要使用才能记住, 不然明天就忘得一干二净:-)

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

为结构变量赋值相当于叫计算机复制数据

嵌套的结构, 在初始化数据时, 要使用多层花括号的写法

可以使用typedef为结构(/类型)创建别名, 就可以当成类型名使用, 其实只写类型名, 结构名省略, 编译器也没有意见:-)

  对了, 这样的话叫做匿名结构哦, 其实也没啥

要更新结构的值, 需要传递结构的指针, 而且可以使用简洁易阅读的语法更新字段: t->age 代表 (*t).age

注意, 上面是指针时用->, 非指针的情况下, 还是要用.号访问字段的!

联合: 声明后设置它的值常用方法 C89方式 quantity q = {4};  它会设置联合的第一个字段的值

  指定初始化器方法: quantity q = {.weight=1.5};

  "点"表示法: quantity q;  q.volume = 3.7; 这里使用了分行

c语言的位字段: typedef struct { unsigned int lowPassVcf:1; }  必须是unsigned int, 表示该字段只占用一位存储空间

  也即是说C语言的位是可以自定义的, 1位或多位空间占用

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

如果一个结构包含一个链向同种结构的链接, 那个这个结构就被称为递归结构.

另外递归结构要有名字, 因为里面有个相同类型的指针, 而C语言的语法不允许用typedef别名来声明它.

在C语言中, NULL的值实际为0, NULL实际专门用来把某个指针设为0

申请存储器(堆空间)的函数叫malloc(), 是memory allocation (存储器分配的意思), 通常与sizeof运算符一起使用

  malloc(sizeof(island)); 当然需要头文件 #include <stdlib.h>

  malloc()返回通用指针(void*类型), island *p = malloc(sizeof(island));

  用完需要调用free()函数释放存储器: free(p);

  注意, 使用strdup()之后, 也要调用free(), 因为它通常调用了malloc(), 这要看标准库是如何实现的

堆之所以叫做堆: 计算机不会自动组织它, 它只是一大堆数据而已

 可能安装valgrind来检测是否存在内存泄漏, 加上选项, 并把程序传给valgrind

  valgrind --leak-check=full ./spies

当然, 可以在可执行文件中加入调试信息, 记录要编译代码的行号 -g 开关

  gcc -g spies.c -o spies

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

在C语言中, 函数名就是指身函数的指针, 有了函数指针, 就能把函数传给函数

但还是需要一个函数指针变量: 返回类型 (*指针变量)(参数类型)

  例如: char** (*names_fn)(char*, int)  多个参数用逗号隔开

  函数指针使用中, 省略* 和&, 编译器也能识别它们, 而且更好读

   names_fn = album_names;

  char** results = names_fn("Sacha Distel", 1972);

char** 并没有打错, 这是一个指什, 通常用来指向字符串数组

qsort()函数来自于标准库stdlib.h, 用于排序, 接收指向比较器函数的指针, 比较器函数可以比较两个值的大小

  比较器函数接收2个指针, 分别指向待排序数组中的两项

函数指针数组写法: 返回类型 (*指针变量)(参数类型) 例如: void (*replies[])(response);

  函数指针数组为调用一些相关函数提供了方便, 让代码易于管理和扩展, 从而可以伸缩.

参数可变的函数就称为可变参数函数, 需要包含stdarg.h, 函数写法固定如下

double total(int args, ...)
{
    double total = 0.0;
    va_list ap;
    va_start(ap, args);
    int i;
    for (i = 0; i < args; i++)
    {
        enum drink d = va_arg(ap, enum drink);
        total += price(d);
    }
    va_end(ap);
    return total;
}

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

通常类UNIX操作系统会在 /usr/local/include 和 /usr/include 目录查找头文件

  只要头文件在标准目录中, 就可以使用尖括号包含它们: #include <stdio.h>

  也可以使用完整路径名: #include "/my_header_files/encrypt.h"

  也可以告诉编译器去哪里找头文件: gcc -I/my_header_files test_code.c ... -o test_code

用完整路径名共享.o目录文件, 跟指定头文件所在路径差不多写法

只要创建目标文件存档, 就可以一次告诉编译器一批目标文件(应该就是说的压缩包吧:-)

用ar 命令创建存档: ar -rcs libhfsecurity.a encrypt.o checksum.o

  r表示存在就更新, c表示创建时不显示反馈, s表示要在开头建立索引, 后面是存档文件名, .a文件

  务必把存档命名为libXXX.a, 否则编译器找不到它们, 存档是静态库

nm命令可以查看存档中的内容, 它列出存档中保存的文件的名字: nm libhfsecurity.a

  另外 ar -t 文件名  会列出存档中的目标文件

  ar -x libhfsecurity.a encrypt.o  该命令可以从存档中提取文件

当存储安装到标准目录: gcc test_code.c -lhfsecurity -o test_code

其它目录: gcc test_code.c -L/my_lib -lhfsecurity -o test_code

由上面笔记及练习题指明: -I 用于指定头文件所在 -L用于指定存档文件所在 如果在当前目录, 后面跟点 .

----------------------------------------------------------------------------------------------------------------------------------------------------:-)

创建动态链接库

  创建目标文件  gcc -I. -fPIC -c hfcal.c -o hfcal.o  -fPIC意思是创建位置无关代码(事实上不加也没关系)

  -shard选项告诉gcc把.o目标文件转化为动态库, 一个平台一个叫法, 然后扩展名也不同, 如上图

  Linux叫共享目标文件, 里面还保存了库名, 所以编译后不能修改文件名, 除非重新编译

  不同操作系统寻找动态库的方式并不相同, 有点复杂, 在Linux中, 通常保存在/usr/lib或/usr/local/lib中

    不知道为何, 当动态库放在标准目录中, 还是不能运行程序, 试了好久, 网上查也没有解决:-(

    经过不懈努力, 终于找到了, 原因是我装的64位Linux(CentOS7), 相应需要把动态库拷贝到lib64目录中,Yes! Cool!

  而在Window中, 通常把.dll和可执行文件放在同一个目录

在Linux中, 需要设置LD_LIBRARY_PATH变量, 这样程序才能发现动态库

  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/libs  这里libs是共享目标文件所在目录,  上图是放在这个libs文件夹, 所以

  下面是编译和链接程序命令

  gcc -I. -c elliptical.c -o elliptical.o

  gcc elliptical.o -L. -lhfcal -o elliptical

 ----------------------------------------------------------------------------------------------------------------------------------------------------:-)

操作系统内核管理进程, 存储器, 硬件, 而系统调用是程序用来与内核对话的函数

system()函数, 操作系统解释命令, 然后才运行