PA0:关于剩余练习1
练习16: 结构体里,如果用的是指针,那引用对应结构体的成员时要用->箭头,如果用实际变量名,那使用成员时用.点号即可。
例子里面是用malloc来手动在堆里申请了一块内存来创建结构体,但没这个必要,结构体定义好以后,可以作为一个类型来使用,这样创建出来的结构体就在栈里面。
typedef struct test { int age; char sex; } Test; //这里用了typedef关键字,Test就是给test起的别名,之后可以用Test t1={40,‘m’}这样的格式来创建结构体实例 //或者,不用typedef,直接在结构体声明后实例 struct test { int age; char sex; } t1, t2;
原题里用的是malloc:
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
who->name = strdup(name);
who->age = age;
who->height = height;
who->weight = weight;
return who;
} 附加题要求不用malloc,那连这个函数其实都可以不需要。就按照上面的,直接实例化,用大括号赋值即可。
----------------练习17------------
关于C语言里结构体的内存分配: 基本原则是这个变量当前的地址要能被自己这个类型的大小整除
errno是一个全局变量,可以保存程序在运行中的错误代码和对应错误信息。程序运行时,errno会被设置为0,出现错误时会被设为其他值。perror是一个函数,大致相当于打印错误信息,它的参数就是message。
rewind函数:文件操作相关,作用是让文件指针回到文件开头,以便重新读取,或写入。
fflush函数:文件操作相关,刷新stream的缓冲区,确保缓冲区内容都写入了文件或显示在屏幕。
附加题要的find操作:这个find的功能是找出所有同时满足名字和Email的数据库表项。
void Database_find(struct Connection *conn,const char *name,const char *email) { int i=0; struct Database *db=conn->db; for(i=0;i<MAX_ROWS;i++) { if(db->rows[i]->set) { if( !strcmp(name,db->rows[i)->name) && !strcmp(name,db->rows[i)->name)) { struct Address *cur = &db->rows[i]; Address_print(cur); } } else { die("Cannot find target name&email"); } } } //按照输入的名字或邮箱进行搜索, //只有名字和邮箱二者都匹配才会提示存在,否则即报错 //这里是基于原版数据库写的 //strcmp(const char*s1,const chat *s2) 返回0才表示二者相等
用脚本做自动化测试:
#!/bin/bash echo "Compiling ex17.c..." make ex17 echo "Creating database file db.dat..." ./ex17 db.dat c echo "Storing users in db.dat..." ./ex17 db.dat s 1 zed zed@zedshaw.com ./ex17 db.dat s 2 frank frank@zedshaw.com ./ex17 db.dat s 3 joe joe@zedshaw.com echo "Listing users in db.dat..." ./ex17 db.dat l echo "Deleting user 3 in db.dat..." ./ex17 db.dat d 3 echo "Listing users in db.dat again..." ./ex17 db.dat l echo "Getting user 2 in db.dat..." ./ex17 db.dat g 2
--------------------练习18-------------
函数指针的基本格式:
int (*POINTER_NAME)(int a, int b)
int (*p) (int, int); // 定义一个函数指针变量
这样定义出来的p就是一个指针变量,可以指向一个同样有 两个int参数,返回值为int的函数。
让p=compare,这样以来p(10,20)和compare(10,20)效果就一样了。
typedef这个关键字的功能不只是起别名,它还可以定义新的类型名。
typedef
int
(*compare_cb)
(int, int) 这样就定义了一个新类型名,叫compare_cb,就和int 、char一样。注意,typedef后面的int不是修饰typedef的,它是完整的函数指针定义的一部分,表示返回值是int。
这样定义新类型以后,用法就和别的类型一样:
compare_cb p2=compare 声明了一个函数指针变量p2,指向了compare函数。
练习18 的例子就是一个函数调用其他函数。为什么不直接在函数里写明要调用哪个函数?因为用函数指针可以放入任何输入输出匹配的函数。
从某些用法上来说,函数指针是不是也是间接实现多态的一种方法?
练习18的例子,在test_sorted里调用冒泡排序,冒泡排序又分别调用了三种sort。前两种分别对应升序和降序,根据sort的结果来冒泡。
附加题:
如果把NULL加进去,被当作0来进行排序了,程序没有崩溃。用valgrind,并没有发现报错,程序正常地执行了。
我尝试加入了一个有三个int参数的假排序函数fake,编译器会提示expected 'compare_cb'{aka 'int(*)(int,int)'} but argument is of type 'int (*)(int,int,int)