#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Node {
    int data;
    struct Node *pNext;
}NODE, *PNODE;

PNODE create_list();
void traverse_list(PNODE);
bool is_empty_list(PNODE);
int len_list(PNODE);
void sort_list(PNODE);
bool insert_list(PNODE, int, int);
bool delete_list(PNODE, int, int *);
bool getElem(PNODE, int, int *);
bool clear_list_recommended(PNODE *);
bool clear_list(PNODE *);
bool clear_list_error_usage(PNODE);

bool clear_list_recommended(PNODE *ppHead) {
    PNODE q, p = (*ppHead)->pNext;
    while (p) {
        q = p->pNext;
        free(p);
        p = q;
    }
    (*ppHead)->pNext = NULL;
    return true;
}


/* 此用法头结点的类型与其它节点相同
 * 缺点为清空后头节点的为最后一个节点(数据还在),pNext指向NULL
 */
bool clear_list(PNODE *ppHead) {
    PNODE q;
    while ((*ppHead)->pNext) {
        q = (*ppHead)->pNext;
        *ppHead = (*ppHead)->pNext->pNext;
        free(q);
    }
    return true;
}

/* 错误用法
 * 形参不是PNODE *,并未影响函数外部的指针变量
 * 会导致原头指针指向的头节点还是指向原来的第1个元素(但已经变为垃圾值)
 */
bool clear_list_error_usage(PNODE pHead) {
    PNODE q;
    while (pHead->pNext) {
        q = pHead->pNext;
        pHead = pHead->pNext->pNext;
        free(q);
    }
    return true;
}
bool getElem(PNODE pHead, int i, int *pVal)
{
    PNODE p = pHead->pNext;
    int j = 1;
    while (p != NULL && j < i) {
        p = p->pNext;
        ++j;
    }

    if (p != NULL &&  j == i) {
        *pVal = p->data;
        return true;
    }
    return false;
}

bool delete_list(PNODE pHead, int pos, int * pVal)
{
    int i = 0;
    PNODE p = pHead;
    
    // i等于几就代码i指向第几个节点,我们要让p指向pos前面1个节点
    while (NULL != p && i < pos-1) {
        p = p->pNext;
        i++;
    }

    // 如果下面这行用if (i != pos-1 || p->pNext == NULL) { 则上面也需要改为 while (NULL != p->pNext && i < pos-1) {
    if (i != pos-1 || p == NULL) {
        return false;
    }

    PNODE q = p->pNext;
    *pVal = q->data;

    //p->pNext = p->pNext->pNext;
    p->pNext = q->pNext;
    free(q);
    q = NULL; //函数中的局部变量,个人感觉不需要置空,这里应该是为了保持习惯

    return true;
}


bool insert_list(PNODE pHead, int pos, int val)
{
    int i = 0;
    PNODE p = pHead;
    
    // i等于几就代码i指向第几个节点,我们要让p指向pos前面1个节点
    while (NULL != p && i < pos-1) {
        p = p->pNext;
        i++;
    }

   //if (i > pos-1 || p == NULL)
   if (i != pos-1 || p == NULL)
        return false;


    PNODE pNew = (PNODE)malloc(sizeof(NODE));
    if ( NULL == pNew ) {
        printf("内存分配失败");
        return false;
    }

    pNew->data = val;
    pNew->pNext = p->pNext;
    p->pNext = pNew;
    printf("pNew->data = %d\n", pNew->data);

    return true;
}

void sort_list(PNODE pHead)
{
    PNODE p, q;
    int t;
    for (p = pHead->pNext; p != NULL; p=p->pNext) {
        for (q = p->pNext; q !=NULL; q=q->pNext) {
            if (p->data > q->data) {
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
        }
    }

}
int len_list(PNODE pHead)
{
    int len = 0;
    PNODE p = pHead->pNext;
    while (NULL != p) {
        len++;
        p = p->pNext;
    }

    return len;
}

bool is_empty_list(PNODE pHead)
{
    if (pHead->pNext == NULL)
        return true;
    return false;
}

void traverse_list(PNODE pHead)
{
    PNODE p = pHead->pNext;
    while (p != NULL) {
        printf("%d ", p->data);
        p = p->pNext;
    }
    putchar('\n');
}

PNODE create_list()
{
    int len;
    int val;

    // 头指针指向头节点
    PNODE pHead = (PNODE)malloc(sizeof(NODE));
    if (NULL == pHead) {
        printf("内存分配失败\n");
        exit(-1);
    }
    // 使用一个尾指针始终指向结尾
    PNODE pTail = pHead;
    pTail->pNext = NULL;

    printf("请输入链表长度:");
    scanf("%d", &len);

    for (int i=0; i<len; i++) {
        printf("请输入链表的值:");
        scanf("%d", &val);
        // p为临时节点
        PNODE p = (PNODE)malloc(sizeof(NODE));
        if (NULL == p) {
            printf("内存分配失败\n");
            exit(-1);
        }
        p->data = val;
        p->pNext = NULL;
        // 尾节点的下一个指向刚创建的临时节点
        pTail->pNext = p;
        // 更新尾指针
        pTail = p;
    }

    return pHead;
}

int main(void)
{
    PNODE pHead = NULL;
    pHead = create_list();
    traverse_list(pHead);
    if (is_empty_list(pHead)) {
        printf("链表为空\n");
    }
    else {
        printf("链表不空\n");
    } 

    int len = len_list(pHead);
    printf("链表长度为:%d\n", len);
    
    sort_list(pHead);
    printf("排序后:\n");
    traverse_list(pHead);

    printf("插入结果(成功1,失败0):%d\n", insert_list(pHead, 2, 111));
    printf("插入后:\n");
    traverse_list(pHead);

    int val;
    if (delete_list(pHead, 1, &val)) {
        printf("删除成功:%d\n", val);
    }
    else {
        printf("删除失败\n");
    }
    traverse_list(pHead);
    printf("清空前,头节点的值:%d\n", pHead->data);
    printf("清空前, pHead addr is %p, pHead->pNext addr is %p\n", pHead, pHead->pNext);
    //clear_list(&pHead);
    clear_list_recommended(&pHead);
    //clear_list_error_usage(pHead);
    printf("清空后,头节点的值:%d\n", pHead->data);
    printf("清空后, pHead addr is %p, pHead->pNext addr is %p\n", pHead, pHead->pNext);
    //pHead = create_list();
    printf("清空后遍历:\n");
    traverse_list(pHead);
    return 0;
}

output:

[root@8be225462e66 c]# gcc linklist_1.c && ./a.out
请输入链表长度:4
请输入链表的值:11
请输入链表的值:-2
请输入链表的值:3
请输入链表的值:4
11 -2 3 4
链表不空
链表长度为:4
排序后:
-2 3 4 11
pNew->data = 111
插入结果(成功1,失败0):1
插入后:
-2 111 3 4 11
删除成功:-2
111 3 4 11
清空前,头节点的值:0
清空前, pHead addr is 0x67c2a0, pHead->pNext addr is 0x67cb60
清空后,头节点的值:0
清空后, pHead addr is 0x67c2a0, pHead->pNext addr is (nil)
清空后遍历:

[root@8be225462e66 c]#