LINUX 入门 2.1

LINUX 入门 2.1

day4 20240425 耗时:60min

day5 20240428 耗时:120min

课程链接地址

第2章 linux c 通讯录

0 产品+架构设计

考虑做产品的思维+ 架构(注意按从上往下的顺序考虑:用什么数据结构,存储格式)

一般功能:增删查,打印,保存,加载调用

在这里插入图片描述

业务逻辑: 输入用户名name+phone等,最贴近上层用户的操作

在这里插入图片描述

最底层: 文件需要read write; 数据存用链表或者其他都行

1 实现数据存储——双向链表

从最底层往上开始实现,双向链表:用struct——val+指针(指下一个*next和前一个*pre)
在这里插入图片描述

  1. 封装over

    #define NAME_LENGTH 16
    #define PHONE_LENGTH 32
    
    
    struct person{
        char name[NAME_LENGTH]; 
        char phone[PHONE_LENGTH];
    
        struct person *next;
        struct person *pre;
    }
    
    struct contacts{
        struct person *people;
        int count; //数多少人
    }
    
  2. 头添加item

    双向的第一个节点前面插入, 顺序:item指①,①指item, 再 people指item,但是代码逻辑不一样:itemnext指1,prev指空,再item是新的头!!!!

    没调函数,用宏实现,注意加\ ,要么放一行里;

    #define LIST_INSERT(item, list) do{ \
        item->prev= NULL;               \
        item->next= list;               \
        list = item;                    \
    } while(0)                          \
    
  3. 删除

    传入头list+当前指向item

    应该可以不分先后的!!!

    ​ item prev的next指向 item的next,把item跳了

    ​ item next的prev 指向item 的prev

    特殊情况:被删的是第一个头节点,item=list,这种比较简单

    ​ 最后释放掉item,类似C++delete(item) 防止内存泄漏,野指针

    #define LIST_REMOVE(item, list) do{ \
        if(item->next != NULL)  item->prev->next= item->next; \
        if (item->prev !=NULL)  item->next->prev= item->prev;\
        if( item == list) list = item->next; \
        item->next= item->prev= NULL;   \
    }while(0)  
    
思考题

业务层和支持层未分离,对链表操作会影响name和Phone

//业务层
char name[NAME_LENGTH]; 
char phone[PHONE_LENGTH];

//支持层
struct person *next;
struct person *pre;

2 架构接口层的实现

interface层:person 结构体的4种操作,不需要知道底层person如何实现,里面是什么,实现业务和数据结构隔离

  1. insert

  2. delete

  3. search

    用前面define过的宏操作

    strcmp要include <string.h>

  4. traversal

// define interface
int person_insert(struct person **ppeople, struct person *ps){
    if(ps  == NULL)return -1;
    LIST_INSERT(ps, *ppeople); //ps插到people里, **people是指针的指针
    return 0;
}

int person_delete(struct person **ppeople, struct person *ps){
    if(ps == NULL) return -1;
    if (ppeople ==NULL) return -2;
    LIST_REMOVE(ps, *ppeople);
    return 0;
}

struct person* person_search(struct person *people, const char *name){

    struct person *item = NULL; //next= null
    for(item = people; item != NULL; item = item->next){
        if(!strcmp(name, item->name)) break;//一样返回0,!strcmp = 非0就break
    }
    return item;
}

int person_traversal(struct person *people){
    struct person *item = NULL;
	for (item = people;item != NULL;item = item->next) {
        INFO("name: %s, phone: %s\n", item->name, item->phone);
    }
	return 0;
}

3业务逻辑的分析与实现

用枚举enum(默认递增1)把操作定义出来,不在case写比较丑,而且不知道那个操作对应哪个数字

enum{ //4个链表操作+2个文件操作
    OPER_INSERT = 1,
    OPER_PRINT,
    OPER_DELETE,
    OPER_SEARCH,
    OPER_SAVE,
    OPER_LOAD    
}

能明显看到套了三层,一层一层包上去

数据结构(直接define宏操作)——接口层——业务层(entry)


// 业务层入口
int insert_entry(struct contacts *cts){
    if(cts == NULL) return -1;

    struct person *p = (struct person*)malloc(sizeof(struct person));
    if(p== NULL) return -2;
    //name
    INFO("please input name: \n");
    scanf("%s", &p->name); //scanf可能有溢出问题,最长16位
    
    //phone
    INFO("please input phone: \n");
    scanf("%s", &p->phone); 
    
    //add people
    if(0!= person_insert(&cts->people, p)){
        //异常了,就释放
        free(p);
        return -3;
    }

    cts->count ++;
    INFO("inset success \n");
    return 0;
}


int print_entry(struct contacts *cts){
    if(cts == NULL) return -1;
    // cts->people
    person_traversal(cts->people);
}

int delete_entry(struct contacts *cts){
    if(cts == NULL) return -1;
    //name
    INFO("please input name \n");
    char name[NAME_LENGTH] = {0};
    scanf("%s", name); //指向字符数组的第一个位置的地址

    
    //person
    struct person *ps = person_search(cts->people, name);
    if(ps == NULL) {
        INFO("person don't exist\n");
        return -2;
    }
	INFO("name: %s, phone: %s\n", ps->name, ps->phone);

    //delete
    person_delete(&cts->people, ps);
    free(ps);
    
    return 0;
}

int search_entry(struct contacts *cts){
    if(cts == NULL) return -1;
    //name
    INFO("please input name \n");
    char name[NAME_LENGTH] = {0};
    scanf("%s", name); //指向字符数组的第一个位置的地址

    
    //person
    struct person *ps = person_search(cts->people, name);
    if(ps == NULL) {
        INFO("person don't exist\n");
        return -2;
    }
    INFO("name : %s, phone: %s\n", ps->name, ps->phone);

    // 上面和删除一样
    return 0;
}


int main(){
    struct contacts *cts=(struct contacts *)malloc(sizeof(struct contacts));
    if(cts == NULL) return -1;

    memset(cts, 0 , sizeof (struct contacts);
    
    while(1){
        int select = 0;
        scanf("d", &select);
        
        switch (select){
            case OPER_INSERT:
                insert_entry(cts);
                break;
            case OPER_PRINT:
                print_entry(cts);
                break;
            case OPER_DELETE:
                delete_entry(cts);
                break;
            case OPER_SEARCH:
                search_entry(cts);
                break;

                
            case OPER_SAVE:
                break;
            case OPER_LOAD:
                break;
        }
    }
}

c基础

  1. #define 用于定义预处理宏,而预处理宏的命名约定是使用大写字母。这有助于与普通变量和函数名区分开来

    printf不直接用好处:

    #define  INFO printf
    
    1. 所有关于接口层的都用define宏定义,因为这样可以排除出是哪一层的实现出现了问题!!!!
    2. 上线产品版:直接改成 #define INFO 不打印没必要的输出
  2. 操作请查阅:C语言文件操作函数大全(扫完了一遍)

  3. struct person *p = (struct person*)malloc(sizeof(struct person));

    1. struct person *p:声明了一个指向结构体类型 person 的指针变量 p。这意味着 p 可以存储结构体类型 person 的地址。
    2. (struct person*)malloc(sizeof(struct person)):这部分代码是一个动态内存分配的语句。malloc 函数用于在堆上分配一块内存空间,并返回指向该内存空间起始位置的指针。sizeof(struct person) 计算了结构体类型 person 所需的字节数,以便确保分配的内存空间足够存储一个 person 结构体的数据。
    3. (struct person*):这是一个类型转换操作,将 malloc 返回的 void* 类型的指针转换为指向 struct person 类型的指针,以便与 p 的类型相匹配。
  4. memset要include标准库<stdlib.h>

    void* memset(void* s, int c, size_t n);
    c 是要设置的值,它被解释为一个 unsigned char,所以通常用于设置字节值。
    n 是要设置的字节数。

    使用 malloc 函数分配内存时,得到的内存块中的内容是未定义的,即内存中的数据可能是任意值。为了确保在使用内存之前具有确定的初始状态,可以使用 calloc 函数来分配内存,该函数会将分配的内存块中的每个字节都初始化为零。

    所以malloc出来的东西要Memset置零

  5. 二级指针:指针的指针 什么时候用

    当形参people内容空的,无法执行函数的操作,用二级指针:指针的指针

    **people(c比c++呆): 指向people这个指针的指针

    传指针的地址 不会空 肯定有地址

  6. linux下scanf输入字符串(>16位会溢出) 数组溢出

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值