Skip to content

C语言基础

1. 关键词

关键字 应用场景
static 在函数内部:该变量在多次函数调用之间保持其值不变
② 使得变量或函数的作用域局限于定义它的文件或函数内部,外部无法访问 。
extern 告诉编译器变量或函数在别的地方定义,可以跨文件寻找并使用变量或函数
volatile 所修饰的对象不能被编译器优化
register 建议编译器将变量存储在寄存器中以提高访问速度
typedef 给数据类型取别名
const 修饰只读变量,变量一旦赋初值就不能被修改

2. 全局变量与局部变量

变量类型 作用域 生命周期 存储位置 默认值
局部变量 函数或代码块内部 函数或代码块执行期间 栈(Stack) 未定义(随机值)
全局变量 整个程序 程序运行期间 静态区(Data Segment) 0
静态局部变量 函数内部 程序运行期间 静态区(Data Segment) 0
静态全局变量 当前文件 程序运行期间 静态区(Data Segment) 0

3. 预处理器

3.1 指令汇总

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的代替方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

3.2 宏定义

C
// 无参宏
#define DEAD_ZONE_ARR           1850
//有参宏
#define M1A(duty)               __HAL_TIM_SetCompare(&htim8, TIM_CHANNEL_1, duty)
C
#include <stdio.h>

#define VALUE 100

int main() {
    printf("VALUE = %d\n", VALUE);

#undef VALUE
    // printf("VALUE = %d\n", VALUE); // 此行会报错,因为 VALUE 已被取消定义

    return 0;
}

3.3 条件编译

C
#include <stdio.h>

#define DEBUG  // 定义调试模式

int main() {
#ifdef DEBUG
    printf("Debug mode is enabled.\n");
#else
    printf("Release mode.\n");
#endif
    return 0;
}

应用场景

C
#if defined(PLATFORM_ARM)
    // ARM 平台代码
#elif defined(PLATFORM_X86)
    // x86 平台代码
#else
    // 默认代码
#endif

4. 指针

4.1 指针基础

C
//定义指针
int* p1;
//给指针分配地址
int c;
c = 5;
p1 = &c;
//获取指针所指向的值与地址
printf("p1 address: 0x%p, p1 value: %d", p1, *p1);

/*输出*/
p1 address: 0x0061FF18, p1 value: 5

经过上述代码流程,我们将指针p1指向变量c的地址,所以当我们改变变量c的值,p1指针指向的值也会随c改变。

C
//改变指针所指向的值
c = 8;
printf("p1 address: 0x%p, p1 value: %d\n", p1, *p1);

/*输出*/
p1 address: 0x0061FF18, p1 value: 5
p1 address: 0x0061FF18, p1 value: 8

可以看到指针指向的地址没有变,一致都是变量c的地址,而变量c的值改变,随之p1指针指向的值也改变。同理,我们可以通过修改*p1实现修改变量c的值,完整代码如下:

C
#include <stdio.h>

int main()
{
   //定义指针
    int* p1;
    //给指针分配地址
    int c;
    c = 5;
    p1 = &c;
    //获取指针所指向的值与地址
    printf("p1 address: 0x%p, p1 value: %d, c value: %d\n", p1, *p1, c);
    //改变指针所指向的值
    c = 8;
    printf("p1 address: 0x%p, p1 value: %d, c value: %d\n", p1, *p1, c);
    //通过指针修改变量的值
    *p1 = 10;
    printf("p1 address: 0x%p, p1 value: %d, c value: %d\n", p1, *p1, c);

   return 0;
}

/*输出*/
p1 address: 0x0061FF18, p1 value: 5, c value: 5
p1 address: 0x0061FF18, p1 value: 8, c value: 8
p1 address: 0x0061FF18, p1 value: 10, c value: 10

4.2 指针与数组

在数组中,我们亦经常会用到取值和取地址。有代码不难看出:&a[i]a + i结果一样,都是取地址,而a[i]则是取值。

C
#include <stdio.h>

int main()
{
    int a[4] = {1, 2, 3, 4};
    for (int i = 0 ; i < 4 ; i++)
    {
        printf("a[%d]: %d\n", i, a[i]);
        printf("a[%d]: %d\n", i, a + i);
        printf("a[%d]: %d\n", i, &a[i]);
    }

    return 0;
}

/*输出*/
a[0]: 1
a[0]: 6422284
a[0]: 6422284
a[1]: 2
a[1]: 6422288
a[1]: 6422288
a[2]: 3
a[2]: 6422292
a[2]: 6422292
a[3]: 4
a[3]: 6422296
a[3]: 6422296

int类型数据为 4 字节,char为 1 字节,而指针可以通过p +/- 1改变指向的地址,而其横跨的地址长度与指针的数据类型有关,比如int类型,指针加 1 (p+1)地址就增加 4 个字节;可以通过这个方法用指针加减方法读取数组数据和地址。

C
#include <stdio.h>

int main()
{
    int a[4] = {1, 2, 3, 4};
    char b[4] = {'a', 'b', 'c', 'd'};
    int *p1;
    char *p2;
    p1 = a + 2;
    p2 = b + 1;
    printf("p1 address1: %p, p1 address2: %p\n", p1+1, p1);
    printf("p2 address2: %p, p2 address2: %p\n", p2+1, p2);

    return 0;
}

/*输出*/
p1 address1: 0061FF14, p1 address2: 0061FF10
p2 address2: 0061FF06, p2 address2: 0061FF05

4.3 指针与函数

① 用指针接受处理函数参数传入的地址,可以实现交换函数外两个变量的值。

C
#include <stdio.h>

void swap(int* a, int* b);

int main()
{
    int a=1, b=9;
    printf("a: %d, b: %d\n", a, b);
    swap(&a, &b);
    printf("a: %d, b: %d\n", a, b);
    return 0;

}

void swap(int* a, int* b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp; 
}

/*输出*/
a: 1, b: 9
a: 9, b: 1

② 将指针作为参数传入函数。

C
#include <stdio.h>

void swap(int* a, int* b);
void my_add(int* p);

int main()
{
    int a=1, b=9;
    printf("a: %d, b: %d\n", a, b);
    swap(&a, &b);
    printf("a: %d, b: %d\n", a, b);
    //此时b已经与a交换,b=1
    int *p;
    p = &b;
    //指针传入函数my_add()
    my_add(p);
    printf("b: %d, *p: %d\n", b, *p);
    return 0;

}

void swap(int* a, int* b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp; 
}

void my_add(int* p)
{
    (*p)++;
}

/*输出*/
a: 1, b: 9
a: 9, b: 1
b: 2, *p: 2

4.4 动态内存分配

C语言标准库函数:malloc()calloc()free()realloc()在程序中可以动态分配内存。

  • malloc()函数用于在 堆区 动态分配一块指定大小的内存,内存内容是未初始化的,因此,分配的内存块中的数据是未知的。 并且,它返回一个void指针,可以将其转换为任何形式的指针。

语法示例:

C
ptr = (float*) malloc(100 * sizeof(float));

浮点数的大小是4字节,所以例子中创建了 400 字节的内存,其中ptr指针指向分配的内存的第一个字节。 如果无法分配内存,则表达式将产生NULL指针。

  • calloc()也是用于在堆上分配内存, 但会将分配的内存初始化为零。

语法示例:

C
ptr = (float*) calloc(25, sizeof(float));
  • free()用于释放calloc()malloc()动态分配的内存空间。

语法示例:

C
free(ptr);

此例子将释放之前由calloc()malloc()动态分配的内存空间,ptr为它们返回的指针。

  • realloc()用于动态分配的内存不足或超过所需,可以使用realloc()函数更改以前分配的内存的大小。

语法示例:

C
ptr = realloc(ptr, x);

此例子ptr被重新分配为新的大小 x 。

示例 1 :

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

int main()
{
    int n, i, *ptr, sum = 0;

    printf("输入元素数量: ");
    scanf("%d", &n);

    ptr = (int*) malloc(n * sizeof(int));

    // 如果无法分配内存
    if(ptr == NULL)                     
    {
        printf("错误! 内存未分配。");
        exit(0);
    }

    printf("输入元素: ");
    for(i = 0; i < n; ++i)
    {
        scanf("%d", ptr + i);
        sum += *(ptr + i);
    }

    printf("Sum = %d", sum);

    //释放内存
    free(ptr);

    return 0;
}

示例 2 :

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

int main()
{
    int *ptr, i, n1, n2;
    printf("输入大小: ");
    scanf("%d", &n1);

    ptr = (int*)malloc(n1 * sizeof(int));

    printf("先前分配的内存地址: ");
    for (i = 0; i < n1; ++i)
        printf("%u\n", ptr + i);

    printf("\n输入新的大小: ");
    scanf("%d", &n2);

    //重新分配内存
    ptr = realloc(ptr, n2 * sizeof(int));

    printf("新分配的内存地址: ");
    for (i = 0; i < n2; ++i)
        printf("%u\n", ptr + i);

    free(ptr);

    return 0;
}

5. 结构体

结构体在嵌入式中非常常见,且我们每天写程序都在用。

5.1 结构体基础

StructName 是结构体的名称,type 是成员的数据类型member1, member2, member3 等是结构体的成员,variable是结构变量。

C
struct StructName {
    type member1;
    type member2;
    type member3;
    // ...
}variable;

结构体简单示例:

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student
{
    /* data */
    char name[20];
    int age;
    float grade;
};


int main()
{
    //定义一个结构体变量
    struct Student student1;
    //成员赋值
    strcpy(student1.name, "C_language");
    student1.age = 18;
    student1.grade = 98.1;
    // 访问结构体成员
    printf("Student Name: %s\n", student1.name);
    printf("Student Age: %d\n", student1.age);
    printf("Student Grade: %.2f\n", student1.grade);

    return 0;
}

/*输出*/
Student Name: C_language
Student Age: 18
Student Grade: 98.10

5.2 结构体指针

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student
{
    /* data */
    char name[20];
    int age;
    float grade;
};


int main()
{
    //定义一个结构体变量
    struct Student student1;
    //成员赋值
    strcpy(student1.name, "C_language");
    student1.age = 18;
    student1.grade = 98.1;
    // 访问结构体成员
    printf("Student Name: %s\n", student1.name);
    printf("Student Age: %d\n", student1.age);
    printf("Student Grade: %.2f\n", student1.grade);
    //定义一个结构体指针
    struct Student *student_ptr;
    student_ptr = &student1;
    printf("Student Name: %s\n", student_ptr->name);
    printf("Student Age: %d\n", student_ptr->age);
    printf("Student Grade: %.2f\n", student_ptr->grade);
    return 0;
}

/*输出*/
Student Name: C_language
Student Age: 18
Student Grade: 98.10
Student Name: C_language
Student Age: 18
Student Grade: 98.10

5.3 结构体做函数参数

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student
{
    /* data */
    char name[20];
    int age;
    float grade;
};

void printf_struct(struct Student student)
{
    printf("Student Name: %s\n", student.name);
    printf("Student Age: %d\n", student.age);
    printf("Student Grade: %.2f\n", student.grade);
}

int main()
{
    //定义一个结构体变量
    struct Student student1;
    //成员赋值
    strcpy(student1.name, "C_language");
    student1.age = 18;
    student1.grade = 98.1;
    printf_struct(student1);
    return 0;
}

/*输出*/
Student Name: C_language
Student Age: 18
Student Grade: 98.10

传递结构体指针

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student
{
    /* data */
    char name[20];
    int age;
    float grade;
};

void printf_struct(struct Student *student_ptr)
{
    printf("Student Name: %s\n", student_ptr->name);
    printf("Student Age: %d\n", student_ptr->age);
    printf("Student Grade: %.2f\n", student_ptr->grade);
}

int main()
{
    //定义一个结构体变量
    struct Student student1;
    //成员赋值
    strcpy(student1.name, "C_language");
    student1.age = 18;
    student1.grade = 98.1;
    printf_struct(&student1);
    return 0;
}

5.4 Typedef使用

很多场合typedef struct Student会直接写出typedef struct都是一个意思。

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义结构体类型,并使用 typedef 为结构体类型起别名
typedef struct Student
{
    char name[20];
    int age;
    float grade;
}Student;  // 这里的 Student 就是结构体类型的别名

int main()
{
    Student student1;
    strcpy(student1.name, "C_language");
    student1.age = 18;
    student1.grade = 98.1;
    // 访问结构体成员
    printf("Student Name: %s\n", student1.name);
    printf("Student Age: %d\n", student1.age);
    printf("Student Grade: %.2f\n", student1.grade);

    return 0;
}

5.5 结构体数组

C
#include <stdio.h>

struct Student {
    char name[50];
    int age;
    float grade;
};

int main() {
    struct Student students[2] = {
        {"Alice", 21, 91.2},
        {"Bob", 22, 85.5}
    };

    // 访问结构体数组的成员
    for (int i = 0; i < 2; i++) {
        printf("Student %d Name: %s\n", i + 1, students[i].name);
        printf("Age: %d\n", students[i].age);
        printf("Grade: %.2f\n\n", students[i].grade);
    }

    return 0;
}

5.6 嵌套结构体

C
#include <stdio.h>

// 定义嵌套的结构体
typedef struct {
    char street[50];
    char city[30];
    char zip[10];
} Address; // 地址结构体

typedef struct {
    char name[50];
    int age;
    Address address; // 嵌套 Address 结构体
} Person; // 人员结构体

int main() {
    // 创建一个 Person 类型的变量并初始化
    Person person1 = {
        "Alice",
        25,
        {
            "123 Main St",
            "New York",
            "10001"
        }
    };

    // 打印嵌套结构体的内容
    printf("Name: %s\n", person1.name);
    printf("Age: %d\n", person1.age);
    printf("Address: %s, %s, %s\n",
           person1.address.street,
           person1.address.city,
           person1.address.zip);

    return 0;
}

/*输出*/
Name: Alice
Age: 25
Address: 123 Main St, New York, 10001

6. 枚举

6.1 枚举基础

默认情况下,枚举类型中值依次为 0,1,2 ... 。

C
enum Weekday {
    MONDAY,     // 默认值为 0
    TUESDAY,    // 默认值为 1
    WEDNESDAY,  // 默认值为 2
    THURSDAY10, // 默认值为 3
    FRIDAY      // 默认值为 4
};

使用typedef

C
#include <stdio.h>

// 定义枚举并指定常量值
typedef enum {
    MONDAY,     // 默认值为 0
    TUESDAY,    // 默认值为 1
    WEDNESDAY,  // 默认值为 2
    THURSDAY10, // 默认值为 3
    FRIDAY      // 默认值为 4
}Weekday ;

int main()
{
    Weekday today;
    today = WEDNESDAY;
    printf("today:%d", today);
    return 0;
}

/*输出*/
today:2

6.2 枚举与结构体的应用

C
// 基于STM32
typedef enum
{
  GPIO_PIN_RESET = 0u,
  GPIO_PIN_SET
} GPIO_PinState;

typedef struct
{
  uint32_t Pin;       /*!< Specifies the GPIO pins to be configured.
                           This parameter can be any value of @ref GPIO_pins_define */

  uint32_t Mode;      /*!< Specifies the operating mode for the selected pins.
                           This parameter can be a value of @ref GPIO_mode_define */

  uint32_t Pull;      /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
                           This parameter can be a value of @ref GPIO_pull_define */

  uint32_t Speed;     /*!< Specifies the speed for the selected pins.
                           This parameter can be a value of @ref GPIO_speed_define */
} GPIO_InitTypeDef;

//函数调用
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
//结构体初始化
GPIO_InitTypeDef GPIO_InitStruct = {0};

7. 链表

7.1 链表基础

其中headNextlast均为指针,蓝色箭头与红色箭头均是指向整个节点,而绿色表示指向节点内部的Next指针,灰色为不指向。每一个节点定义为一个结构体,一个成员为数据,一个为Next指针,下图即是链表结构示意图,也是下面的添加节点、数据的代码流程示意图。

image-20250108230623840

C
#include <stdio.h>
#include <stdlib.h>
#include "node.h"


int main()
{
    //链表 head
    Node * head = NULL;
    int number;
    do
    {
        scanf("Pleases input number: %d", &number);
        if (number != -1)
        {
            /*数据添加至链表*/
            //在堆区开辟一个 Node结构体的内存空间, 
            Node *p = (Node*)malloc(sizeof(Node));
            p->value = number;
            //新的结点 p 的Next指向NULL,而让原来的最后一个结点(last)指向这个新的结点
            p->next = NULL;
            //找最后一个结点
            Node* last = head;
            if (last)
            {
                while (last->next)
                {
                    //一开始,last指向的是一个结点,若结点中的Next指针不为空,则让指针指向结点的Next
                    last = last->next;
                }
                //让最后一个结点(last)的Next指向这个新的结点(p)
                last->next = p;
            }
            else
            {
                head = p;
            }
        }
    } while (number != -1);

    return 0;
}

7.2 链表函数

C
#include <stdio.h>
#include <stdlib.h>
#include "node.h"

/* 自定义一个数据类型,用于处理head被反复调用,但仅传入值不会改变head
 * 与经典的指针swap()函数的原理一样
 */
typedef struct _list
{
    Node* head;
}List;

void linked_list_add(List* pList, int number);

int main()
{
    //链表 head
    // Node * head = NULL;
    List list;
    list.head = NULL;
    int number;
    do
    {
        scanf("Pleases input number: %d", &number);
        if (number != -1)
        {
            linked_list_add(&list, number);
        }
    } while (number != -1);

    return 0;
}

/* 
 * 参数 pList : 是一个List数据类型的结构体指针
 */
void linked_list_add(List* pList, int number)
{
    /*数据添加至链表*/
    //在堆区开辟一个 Node结构体的内存空间, 
    Node *p = (Node*)malloc(sizeof(Node));
    p->value = number;
    //新的结点 p 的Next指向NULL,而让原来的最后一个结点(last)指向这个新的结点
    p->next = NULL;
    //找最后一个结点
    Node* last = pList->head;
    if (last)
    {
        while (last->next)
        {
            //一开始,last指向的是一个结点,若结点中的Next指针不为空,则让指针指向结点的Next
            last = last->next;
        }
        //让最后一个结点(last)的Next指向这个新的结点(p)
        last->next = p;
    }
    else
    {
        pList->head = p;
    }
}

7.3 打印链表

打印链表函数,实质上就是遍历链表,然后打印数据,其中for (p=pList->head; p; p=p->next)为常见方法。

C
#include <stdio.h>
#include <stdlib.h>
#include "node.h"

/* 自定义一个数据类型,用于处理 head被反复调用,但仅传入值不会改变 head
 * 与经典的指针swap()函数的原理一样
 */
typedef struct _list
{
    Node* head;
}List;

void linked_list_add(List* pList, int number);
void linked_list_print(List* pList);

int main()
{
    //链表 head
    // Node * head = NULL;
    List list;
    list.head = NULL;
    int number;
    do
    {
        scanf("%d", &number);
        if (number != -1)
        {
            linked_list_add(&list, number);
        }
    } while (number != -1);
    linked_list_print(&list);
    return 0;
}

/* 
 * 参数 pList : 是一个List数据类型的结构体指针
 */
void linked_list_add(List* pList, int number)
{
    /*数据添加至链表*/
    //在堆区开辟一个 Node结构体的内存空间, 
    Node *p = (Node*)malloc(sizeof(Node));
    p->value = number;
    //新的结点 p 的Next指向NULL,而让原来的最后一个结点(last)指向这个新的结点
    p->next = NULL;
    //找最后一个结点
    Node* last = pList->head;
    if (last)
    {
        while (last->next)
        {
            //一开始,last指向的是一个结点,若结点中的Next指针不为空,则让指针指向结点的Next
            last = last->next;
        }
        //让最后一个结点(last)的Next指向这个新的结点(p)
        last->next = p;
    }
    else
    {
        pList->head = p;
    }
}

/*打印链表*/
void linked_list_print(List* pList)
{
    Node *p;
    for (p=pList->head; p; p=p->next)
    {
        printf("%d\t", p->value);
    }
    printf("\n");
}

7.4 链表的删除

指针p遍历寻找需要删除的结点,指针q在指针p指向下一结点(也就是p->next)前指向p。同时存在一种特殊情况,即删除的结点为第一个结点,此时需要指针q为空,让head指针指向下一结点(也就是p->next)。

Danger

根据解释,指针变化为蓝色至绿色;橙色为特殊情况。

image-20250109213416348

C
#include <stdio.h>
#include <stdlib.h>
#include "node.h"

/* 自定义一个数据类型,用于处理 head被反复调用,但仅传入值不会改变 head
 * 与经典的指针swap()函数的原理一样
 */
typedef struct _list
{
    Node* head;
}List;

void linked_list_add(List* pList, int number);
void linked_list_print(List* pList);

int main()
{
    //链表 head
    // Node * head = NULL;
    List list;
    list.head = NULL;
    int number;
    do
    {
        scanf("%d", &number);
        if (number != -1)
        {
            linked_list_add(&list, number);
        }
    } while (number != -1);
    linked_list_print(&list);
    scanf("%d\n", &number);
    Node* p, q;
    // int isFound = 0;
    for(q=NULL, list.head; p; q=p, p=p->next)
    {
        if(p->value = number)
        {
            if(q){
                q->next = p->next;
            }
            else{
                list.head = p->next;
            }
            free(p);
            // printf("找到了\n");
            // isFound = 1;
            break;
        }
    }
    // if(!isFound) {printf("未找到\n");}
    return 0;
}

/* 
 * 参数 pList : 是一个List数据类型的结构体指针
 */
void linked_list_add(List* pList, int number)
{
    /*数据添加至链表*/
    //在堆区开辟一个 Node结构体的内存空间, 
    Node *p = (Node*)malloc(sizeof(Node));
    p->value = number;
    //新的结点 p 的Next指向NULL,而让原来的最后一个结点(last)指向这个新的结点
    p->next = NULL;
    //找最后一个结点
    Node* last = pList->head;
    if (last)
    {
        while (last->next)
        {
            //一开始,last指向的是一个结点,若结点中的Next指针不为空,则让指针指向结点的Next
            last = last->next;
        }
        //让最后一个结点(last)的Next指向这个新的结点(p)
        last->next = p;
    }
    else
    {
        pList->head = p;
    }
}

/*打印链表*/
void linked_list_print(List* pList)
{
    Node *p;
    for (p=pList->head; p; p=p->next)
    {
        printf("%d\t", p->value);
    }
    printf("\n");
}

7.5 链表的清除

C
#include <stdio.h>
#include <stdlib.h>
#include "node.h"

/* 自定义一个数据类型,用于处理 head被反复调用,但仅传入值不会改变 head
 * 与经典的指针swap()函数的原理一样
 */
typedef struct _list
{
    Node* head;
}List;

void linked_list_add(List* pList, int number);
void linked_list_print(List* pList);

int main()
{
    //链表 head
    // Node * head = NULL;
    List list;
    list.head = NULL;
    int number;
    do
    {
        scanf("%d", &number);
        if (number != -1)
        {
            linked_list_add(&list, number);
        }
    } while (number != -1);
    linked_list_print(&list);
    scanf("%d", &number);
    Node* p;
    Node* q;
    int isFound = 0;
    for(q=NULL, list.head; p; q=p, p=p->next)
    {
        if(p->value = number)
        {
            if(q){
                q->next = p->next;
            }
            else{
                list.head = p->next;
            }
            free(p);
            printf("find it\n");
            isFound = 1;
            break;
        }
    }
    if(!isFound) {printf("not find it\n");}
    //清除链表
    for(p=list.head; p; p=q)
    {
        q = p->next;
        free(p);
    }

    return 0;
}

/* 
 * 参数 pList : 是一个List数据类型的结构体指针
 */
void linked_list_add(List* pList, int number)
{
    /*数据添加至链表*/
    //在堆区开辟一个 Node结构体的内存空间, 
    Node *p = (Node*)malloc(sizeof(Node));
    p->value = number;
    //新的结点 p 的Next指向NULL,而让原来的最后一个结点(last)指向这个新的结点
    p->next = NULL;
    //找最后一个结点
    Node* last = pList->head;
    if (last)
    {
        while (last->next)
        {
            //一开始,last指向的是一个结点,若结点中的Next指针不为空,则让指针指向结点的Next
            last = last->next;
        }
        //让最后一个结点(last)的Next指向这个新的结点(p)
        last->next = p;
    }
    else
    {
        pList->head = p;
    }
}

/*打印链表*/
void linked_list_print(List* pList)
{
    Node *p;
    for (p=pList->head; p; p=p->next)
    {
        printf("%d\t", p->value);
    }
    printf("\n");
}
C
/*最终链输入与输出结果*/
1
2
3
4
5
-1
1       2       3       4       5
2
find it