三、复习C语言
复习C语言项目
项目源地址
自主学习代码
需要完成:
练习0到练习18, 练习32,练习33,练习42,练习44 需要完成编程算法以及附加题.
练习1
在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后运行它看看发生了什么。
由于ex1是一个二进制可执行文件,修改其中的内容会导致很多可能发生的错误。我尝试删除了它的第一行,这让它很快发现了错误类型:
./ex1
-bash: ./ex1: cannot execute binary file: Exec format error
再多打印5行文本或者其它比"Hello world."更复杂的东西。
随便写了一些东西:
#include <stdio.h>
int main(int argc, char *argv[]){
puts("hello world");
printf("I'm a little fish of C\n");
const char* name = "zhywyt";
const char* email = "zhywyt@hdu.edu.cn";
const char* url = "zhywyt.me";
printf("My name is %s\n",name);
printf("My email is %s\n",email);
printf("My blog url is %s\n",url);
return 0;
}
执行man 3 puts来阅读这个函数和其它函数的文档
自行完成。
练习2
创建目标all:ex1,可以以单个命令make构建ex1。
CFLAG=-Wall -g
all:ex1
cc ${CFLAG} ex1.c
clean:
rm -f ex1
阅读man make来了解关于如何执行它的更多信息。
阅读man cc来了解关于-Wall和-g行为的更多信息。
在互联网上搜索Makefile文件,看看你是否能改进你的文件。
在另一个C语言项目中找到Makefile文件,并且尝试理解它做了什么。
练习3
找到尽可能多的方法使ex3崩溃。
执行man 3 printf来阅读其它可用的'%'格式化占位符。如果你在其它语言中使用过它们,应该看着非常熟悉(它们来源于printf)。
由于我对C语言比较熟悉了,这里就不进行这些操作了。
将ex3添加到你的Makefile的all列表中。到目前为止,可以使用make clean all来构建你所有的练习。
将ex3添加到你的Makefile的clean列表中。当你需要的时候使用make clean可以删除它。
练习4
按照上面的指导,使用Valgrind和编译器修复这个程序。
这个很有意思,我之前几乎没有接触过这种东西。很快修改好了代码,看着valgrind没有任何报错还是很欣慰的。
在互联网上查询Valgrind相关的资料。
下载另一个程序并手动构建它。尝试一些你已经使用,但从来没有手动构建的程序。
看看Valgrind的源码是如何在目录下组织的,并且阅读它的Makefile文件。不要担心,这对我来说没有任何意义。
对于CMake 和 Makefile 我接触的比较多,所以这里只快速地学习一下valgrind。
练习5-17
这部分知识我简单的温习了一下,直接来到了后面的课程。
练习18 函数指针
用十六进制编辑器打开ex18,接着找到函数起始处的十六进制代码序列,看看是否能在原始程序中找到函数。
0 1 2 3 4 7 8
f30f1efa554889e5897dfc8975f88b45fc2b45f85dc3f30f1e
8 7 4 3 2 1 0
f30f1efa554889e5897dfc8975f88b45f82b45fc5dc3f30f1e
3 4 2 7 1 0 8
f30f1efa554889e5897dfc8975f8837dfc007406837df80075
这是三个比较函数的地址,接下来从起始地址出发,寻找他们。
我使用vim来查看十六进制文件,打开ex18后,:%!xxd即可把二进制文件转化为16进制可读文件,再使用:%!xxd -r恢复二进制,或者:q!选择不保存。然后使用/f30f 1efa 5548 89e5 897d fc89 75f8 8来寻找,最终找到的结果为:
00001400: 75f8 8b45 fc2b 45f8 5dc3 f30f 1efa 5548 u..E.+E.].....UH
00001410: 89e5 897d fc89 75f8 8b45 f82b 45fc 5dc3 ...}..u..E.+E.].
00001420: f30f 1efa 5548 89e5 897d fc89 75f8 837d ....UH...}..u..}
00001430: fc00 7406 837d f800 7507 b800 0000 00eb ..t..}..u.......
00001440: 098b 45fc 99f7 7df8 89d0 5dc3 f30f 1efa ..E...}...].....
但是貌似并不能找到有意义的内容。
在你的十六进制编辑器中找到更多随机出现的东西并修改它们。重新运行你的程序看看发生了什么。字符串是你最容易修改的东西。
我尝试将USAGE修改为usage但是vim似乎不能直接进行类似的修改,只能正常的查看十六进制文件。然后使用tweak进行修改。结果发现不如vim方便,在使用vim的时候应该修改十六进制表示,而不是后面的可视化字符。
首先找到USGE所在
00002030: 6571 7565 7374 6564 2e00 2564 2000 2530 equested..%d .%0
00002040: 3278 0055 5341 4745 3a20 6578 3138 2034 2x.USAGE: ex18 4
00002050: 2033 2031 2035 2036 0000 0000 011b 033b 3 1 5 6.......;
然后修改字符串内任意一处十六进制值就好了,这里我把5341修改为了5342也就是A变成B则为正确,修改会二进制文件运行可知:
./ex18
ERROR: USBGE: ex18 4 3 1 5 6
这里的修改是正确的。
将错误的函数传给compare_cb,并看看C编辑器会报告什么错误。
我添加了一个类型不匹配的函数:
void error_function(){
printf("error function run!\n");
}
编译得到警告:
ex18.c:115:34: warning: passing argument 3 of ‘test_sorting’ from incompatible pointer type [-Wincompatible-pointer-types]
115 | test_sorting(numbers, count, error_function);
| ^~~~~~~~~~~~~~
| |
| void (*)()
将NULL传给它,看看程序中会发生什么。然后运行Valgrind来看看它会报告什么。
==81810== Jump to the invalid address stated on the next line
==81810== at 0x0: ???
==81810== by 0x10949E: test_sorting (ex18.c:76)
==81810== by 0x109694: main (ex18.c:115)
==81810== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==81810==
==81810==
==81810== Process terminating with default action of signal 11 (SIGSEGV)
==81810== Bad permissions for mapped region at address 0x0
==81810== at 0x0: ???
==81810== by 0x10949E: test_sorting (ex18.c:76)
==81810== by 0x109694: main (ex18.c:115)
可以看到这里提到了没有权限访问0x0地址,也就是我的给到的NULL。
编写另一个排序算法,修改test_sorting使它接收任意的排序函数和排序函数的比较回调。并使用它来测试两种排序算法。
练习20
练习20学习了如何使用调试宏,dbg.h。这个非常好用,在我自己以前写项目的时候也经常会使用到一些debug信息,但是这个是我见过最为优雅的一种实现方式。这里记录一下如此优雅的宏实现:
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; }
#endif
练习32
练习33
练习42
练习44
本文来自博客园,作者:zhywyt,转载请注明原文链接:https://www.cnblogs.com/zhywyt/articles/18364343

浙公网安备 33010602011771号