linux+c语言杂记(二)

news2024/11/23 15:47:31

一、在 Ubuntu 20.04 上安装 GCC

默认的 Ubuntu 软件源包含了一个软件包组,名称为 “build-essential”,它包含了 GNU 编辑器集合,GNU 调试器,和其他编译软件所必需的开发库和工具。

想要安装开发工具软件包,以 拥有 sudo 权限用户身份或者 root 身份运行下面的命令:

sudo apt update
sudo apt install build-essential

这个命令将会安装一系列软件包,包括gcc,g++,和make

你可能还想安装关于如何使用 GNU/Linux开发的手册。

sudo apt-get install manpages-dev

通过运行下面的命令,打印 GCC 版本,来验证 GCC 编译器是否被成功地安装。

gcc --version

在 Ubuntu 20.04 软件源中 GCC 的默认可用版本号为9.3.0:

gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

就这些。GCC 已经在你的 Ubuntu 系统上安装好了,你可以开始使用它了。

二、编译一个 Hello World 实例

使用 GCC 编译一个基本的 C 或者 C++ 程序非常简单。打开你的文本编辑器,并且创建下面的文件:

nano hello.c
#include <stdio.h>
int main()
{
  printf ("Hello World!\n");
  return 0;
}

保存文件,并且将它编译成可执行文件,运行:

gcc hello.c -o hello

这将在你运行命令的同一个目录下创建一个二进制文件,名称为"hello”。

运行这个hell0程序:

./hello

程序应该打印:

Hello World!

三、安装多个 GCC 版本

这一节提供一些指令,关于如何在 Ubuntu 20.04 上安装和使用多个版本的 GCC。更新的 GCC 编译器包含一些新函数以及优化改进。

在写作本文的时候,Ubuntu 源仓库包含几个 GCC 版本,从7.x.x10.x.x。在写作的同时,最新的版本是10.1.0

在下面的例子中,我们将会安装最新的三个版本 GCC 和 G++:

输入下面的命令,安装想要的 GCC 和 G++ :

sudo apt install gcc-8 g++-8 gcc-9 g++-9 gcc-10 g++-10

下面的命令配置每一个版本,并且设置了优先级。默认的版本是拥有最高优先级的那个,在我们的场景中是gcc-10

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-10 --slave /usr/bin/gcov gcov /usr/bin/gcov-10
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 80 --slave /usr/bin/g++ g++ /usr/bin/g++-8 --slave /usr/bin/gcov gcov /usr/bin/gcov-8

以后,如果你想修改默认的版本,使用update-alternatives命令:

sudo update-alternatives --config gcc

输出:

There are 3 choices for the alternative gcc (providing /usr/bin/gcc).
​
  Selection    Path            Priority   Status
------------------------------------------------------------
* 0            /usr/bin/gcc-10   100       auto mode
  1            /usr/bin/gcc-10   100       manual mode
  2            /usr/bin/gcc-8    80        manual mode
  3            /usr/bin/gcc-9    90        manual mode
​
Press <enter> to keep the current choice[*], or type selection number:

你将会被展示一系列已经安装在你的 Ubuntu 系统上的 GCC 版本。输入你想设置为默认的 GCC 版本,并且按回车Enter

这个命令将会创建符号链接到指定版本的 GCC 和 G++。

gdb:

要使用GDB(GNU调试器)调试C程序,你需要按照以下步骤进行操作:

  1. 首先,确保你的系统上已经安装了GDB。GDB是GNU Binutils套件的一部分,你可以通过包管理器安装它。例如,在Ubuntu上,你可以使用以下命令安装GDB:

arduino复制代码
​
sudo apt-get install gdb
  1. 编译你的程序时,使用-g选项启用调试信息。这将告诉编译器在生成的可执行文件中包含额外的调试信息,以便GDB使用。例如,你可以使用以下命令编译你的程序:

复制代码
​
gcc -g -o myprogram myprogram.c
  1. 运行GDB并加载你的程序。你可以使用以下命令启动GDB并加载你的程序:

复制代码
​
gdb myprogram
  1. 在GDB中,你可以使用各种命令来设置断点、运行代码、查看变量值等。以下是一些常用的GDB命令:

    • break(或简写为b):设置断点。例如,break main将在main函数处设置断点。

    • run(或简写为r):开始运行程序,直到遇到断点或程序结束。

    • step(或简写为s):逐行执行代码,进入函数调用。

    • next(或简写为n):逐行执行代码,但不进入函数调用。

    • continue(或简写为c):继续执行程序,直到遇到下一个断点或程序结束。

    • print(或简写为p):打印变量的值。例如,print x将显示变量x的值。

  2. 使用上述命令进行调试。你可以设置断点,使用stepnextcontinue命令执行代码,并使用print命令查看变量的值。

  3. 当你完成调试时,可以使用quit命令退出GDB。

makefile make cmake

编写Makefile并执行make

# Makefile
main : main.c
        gcc main.c -o main

通配符:

# Makefile
main : $(wildcard *.c)
        gcc $(wildcard *.c) -o main

变量:

# Makefile
SRCS := $(wildcard *.c)
​
main : $(SRCS)
        gcc $(SRCS) -o main

gcc 命令时通过 -I 选项指定头文件所在路径

# Makefile
INCS := -I./func
SRCS := $(wildcard *.c)
​
main : $(SRCS)
        gcc $(INCS) $(SRCS) -o main

指针

在C语言中,变量名不是地址。变量名是用来标识内存地址的符号,它表示变量在计算机内存中的位置。当定义一个变量时,系统会为该变量分配一个内存地址,并且可以使用变量名来访问该变量的值。

在C语言中,指针是一种特殊的变量,它存储的是其他变量的内存地址,而不是值本身。通过指针,我们可以间接地访问和修改其指向的内存区域的值。

指针的声明和定义如下:

数据类型 *指针变量名;

其中,数据类型可以是任何有效的C语言数据类型,如int、char、float等。指针变量名是你为指针变量选择的名称。

下面是一个完整的例子,演示了如何声明、定义和使用指针变量:

​
#include <stdio.h>  
  
int main() {  
    int num = 10;  
    int *ptr; // 声明指针变量ptr  
    ptr = &num; // 将num的地址赋值给ptr  
  
    printf("num的值为:%d\n", num);  
    printf("num的地址为:%p\n", &num);  
    printf("ptr指向的值为:%d\n", *ptr);  
    printf("ptr的地址为:%p\n", ptr);  
  
    return 0;  
}

在上面的例子中,我们声明了一个整型变量num并初始化为10。然后声明了一个指向整型的指针变量ptr。通过将&num赋值给ptr,我们将num的地址存储在了ptr中。使用*ptr可以访问ptr所指向的内存区域的值,即num的值。通过&num可以得到num的地址。程序输出了num的值、num的地址、ptr指向的值以及ptr的地址。

数据交互:

可以使用指针来交换两个变量的值,这是一个非常常见的使用指针的例子。以下是一个使用 C 语言实现的示例:

首先,定义一个交换函数,它接收两个整数的指针:

​
#include <stdio.h>  
  
void swap(int *a, int *b) {  
    int temp = *a;  
    *a = *b;  
    *b = temp;  
}

然后,你可以在主函数中这样使用这个函数:

​
​
int main() {  
    int x = 5;  
    int y = 10;  
    printf("Before swap: x = %d, y = %d\n", x, y);  
    swap(&x, &y);  
    printf("After swap: x = %d, y = %d\n", x, y);  
    return 0;  
}

在这个例子中,swap函数通过接收两个指针来交换两个整数的值。当我们调用swap(&x, &y)时,我们传递的是xy的地址,所以函数能够直接影响到这两个变量的值。

ps:

在C语言中,函数的参数传递是值传递。这意味着当你传递一个变量到函数中时,函数会创建一个新的副本,而不是直接引用原始变量。因此,在函数内部对参数的任何修改都不会影响原始变量的值。

下面是一个简单的示例来说明这个概念:

c复制代码
​
#include <stdio.h>  
  
void addOne(int num) {  
    num = num + 1;  
    printf("num inside the function: %d\n", num);  
}  
  
int main() {  
    int num = 0;  
    addOne(num);  
    printf("num in main: %d\n", num);  
    return 0;  
}

在这个例子中,addOne函数接收一个整数参数num,然后对它加一。然而,这种修改不会影响到main函数中的num变量。输出将是:

makefile复制代码
​
num inside the function: 1  
num in main: 0

这表明尽管在函数内部num的值被改变了,但这种改变并没有影响到函数外部的原始变量。这就是因为在C语言中,函数参数是通过值传递的。

如果你希望在函数中修改一个变量的值,并影响到原始变量,你需要使用指针。通过指针,你可以直接引用和修改内存中的原始值,而不是传递一个副本。

数组与指针

在C语言中,数组和指针之间有一个非常紧密的关系。数组的名称可以被看作是一个指向数组第一个元素的指针。同样,一个指向某个特定类型的指针也可以被看作是一个指向该类型的数组。这种关系可以在下面的示例代码中看到:

​
#include <stdio.h>  
  
int main() {  
    // 定义一个包含5个整数的数组  
    int array[5] = {1, 2, 3, 4, 5};  
  
    // 定义一个指向整数的指针  
    int *ptr;  
  
    // 将ptr指向array的第一个元素  
    ptr = array;  
  
    // 使用指针访问数组元素  
    for(int i = 0; i < 5; i++) {  
        printf("array[%d] = %d\n", i, *(ptr + i));  
    }  
  
    // 修改数组中的元素值通过指针  
    *(ptr + 2) = 20;  
  
    // 打印修改后的数组  
    printf("Modified array: ");  
    for(int i = 0; i < 5; i++) {  
        printf("%d ", array[i]);  
    }  
    printf("\n");  
  
    return 0;  
}

在这个例子中,我们首先定义了一个包含5个整数的数组array,然后定义了一个指向整数的指针ptr。我们将ptr指向array的第一个元素,然后使用一个循环通过指针访问数组的每个元素。我们也可以使用指针修改数组中的元素值,如示例中我们将第三个元素值修改为20。

二位数组的指针:

include <stdio.h>
int main(void)
​
{
​
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5, 7} };
int (*pz)[2];
pz = zippo;
​
printf("   pz = %p,    pz + 1 = %p\n",
       pz,         pz + 1);
printf("pz[0] = %p, pz[0] + 1 = %p\n",
       pz[0],      pz[0] + 1);
printf("  *pz = %p,   *pz + 1 = %p\n",
       *pz,        *pz + 1);
printf("pz[0][0] = %d\n", pz[0][0]);
printf("  *pz[0] = %d\n", *pz[0]);
printf("    **pz = %d\n", **pz);
printf("      pz[2][1] = %d\n", pz[2][1]);
printf("*(*(pz+2) + 1) = %d\n", *(*(pz+2) + 1));
​
return 0;
}

/* zippo1.c -- zippo info */

include <stdio.h>
int main(void)
{
int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5, 7} };
​
printf("   zippo = %p,    zippo + 1 = %p\n",
       zippo,         zippo + 1);
printf("zippo[0] = %p, zippo[0] + 1 = %p\n",
       zippo[0],      zippo[0] + 1);
printf("  *zippo = %p,   *zippo + 1 = %p\n",
       *zippo,        *zippo + 1);
printf("zippo[0][0] = %d\n", zippo[0][0]);
printf("  *zippo[0] = %d\n", *zippo[0]);
printf("    **zippo = %d\n", **zippo);
printf("      zippo[2][1] = %d\n", zippo[2][1]);
printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo+2) + 1));
​
return 0;

}

函数与指针

函数指针是指向函数的指针变量。它可以用来存储函数的地址,并在需要时调用该函数。下面是一个简单的例子,演示了如何定义和使用函数指针。

#include <stdio.h>  
  
// 定义一个函数,用于计算两个整数的和  
int add(int x, int y) {  
    return x + y;  
}  
  
int main() {  
    // 定义一个函数指针,指向add函数  
    int (*fp)(int, int) = add;  
  
    // 定义一个整数数组  
    int arr[] = {1, 2, 3, 4, 5};  
  
    // 使用函数指针调用add函数,计算数组元素的和  
    int sum = 0;  
    for (int i = 0; i < 5; i++) {  
        sum = fp(sum, arr[i]);  
    }  
  
    // 输出计算结果  
    printf("Sum of array elements: %d\n", sum);  
  
    return 0;  
}


在这个例子中,我们定义了一个函数add,用于计算两个整数的和。然后,我们定义了一个函数指针fp,指向add函数。在main函数中,我们使用fp来调用add函数,计算一个整数数组的元素之和,并输出结果。

在C语言中,函数指针是指向函数的指针变量。它通常用于回调函数、函数参数传递和函数表等场景。分别给出这三个场景下应用的例子

当然,以下是函数指针在C语言中的三种常见应用场景的例子:

  1. 回调函数(Callback Functions)

在C语言中,函数指针最常见的用途是实现回调函数。以下是一个简单的例子:

c复制代码

#include <stdio.h>  
  
// 定义一个函数指针类型  
typedef void (*callback_t)(int);  
  
// 定义一个函数,这个函数接受一个整数和一个回调函数  
void demo_function(int x, callback_t callback) {  
    printf("The value of x is: %d\n", x);  
    // 调用回调函数  
    callback(x);  
}  
  
// 定义一个回调函数,用于输出一个整数的平方  
void square(int x) {  
    printf("The square of %d is: %d\n", x, x * x);  
}  
  
int main() {  
    // 调用demo_function函数,并传入回调函数square  
    demo_function(5, square);  
    return 0;  
}


  1. 函数参数传递(Function Parameters Passing)

函数指针也可以作为参数传递给其他函数,以实现更灵活的功能。以下是一个例子:

#include <stdio.h>  
  
// 定义一个函数,这个函数接受一个整数和一个函数指针  
void apply_func(int x, void (*func)(int)) {  
    func(x);  
}  
  
// 定义一个函数,用于输出一个整数的平方  
void square(int x) {  
    printf("The square of %d is: %d\n", x, x * x);  
}  
  
int main() {  
    // 调用apply_func函数,并传入函数square作为参数  
    apply_func(5, square);  
    return 0;  
}


  1. 函数表(Function Tables)

函数指针还可以用于实现函数表,以便根据运行时的决策来调用不同的函数。以下是一个例子:

#include <stdio.h>  
  
// 定义一个函数指针类型,用于指向处理函数的指针数组的函数指针类型  
typedef void (*operation_t)(int);  
  
// 定义几个处理函数  
void print_square(int x) { printf("%d\n", x * x); }  
void print_cube(int x) { printf("%d\n", x * x * x); }  
void print_quartic(int x) { printf("%d\n", x * x * x * x); }  
  
// 定义一个包含这三个函数的函数指针数组(即函数表)  
operation_t operations[] = { print_square, print_cube, print_quartic };  
  
int main() {  
    // 通过函数表调用不同的函数  
    for (int i = 0; i < 3; i++) {  
        operations[i](i + 1);  // 分别计算并打印1的平方、立方和四次方,2的平方、立方和四次方,以及3的平方、立方和四次方。  
    }  
    return 0;  
}


在C语言中,函数的可变参数(Variable Arguments)是一种特殊的函数参数类型,它允许函数接受可变数量的参数。这种参数类型被表示为...(三个点),通常作为函数参数列表的最后一个参数。

可变参数的应用场景是在函数需要处理可变数量或类型的参数时,例如函数需要接受任意数量的整数、字符串或其他数据类型,或者需要接受不同数量的参数进行不同的操作。

下面是一个简单的示例,演示了如何使用可变参数实现一个函数,该函数接受任意数量的整数并计算它们的和:

#include <stdio.h>  
#include <stdarg.h>  
  
int sum(int count, ...) {  
    va_list args;  // 定义一个va_list类型的变量,用于存储可变参数的列表  
    int sum = 0;   // 初始化一个sum变量用于计算总和  
  
    va_start(args, count);  // 初始化args变量,将其指向第一个可变参数  
  
    // 遍历可变参数列表,计算它们的总和  
    for (int i = 0; i < count; i++) {  
        int num = va_arg(args, int);  // 依次获取每个整数参数的值  
        sum += num;  
    }  
  
    va_end(args);  // 清理va_list变量  
  
    return sum;  
}  
  
int main() {  
    int a = 1, b = 2, c = 3;  
    printf("Sum: %d\n", sum(3, a, b, c));  // 输出:Sum: 6  
    return 0;  
}


在上面的示例中,sum函数接受一个整数count表示可变参数的数量,然后使用一个va_list类型的变量args来存储可变参数的列表。通过调用va_start宏初始化args变量,然后使用va_arg宏依次获取每个整数参数的值,并计算它们的总和。最后,调用va_end宏清理args变量。

在C语言中,fopen函数用于打开文件,并返回一个文件指针。如果打开文件成功,它会返回一个指向文件的指针,该指针随后可用于进行其他的输入和输出操作。如果打开文件失败,fopen则会返回NULL。

文件

fopen函数的原型如下:

FILE *fopen(const char *path, const char *mode);


  • path:这是一个字符串,表示要打开的文件的路径或文件名。

  • mode
    
    
    

    :这也是一个字符串,表示打开文件的模式。下面是一些常见的模式:

    • r:以只读方式打开文件。文件必须存在。

    • w:以只写方式打开文件。如果文件存在,内容会被清空。如果文件不存在,会尝试创建一个新文件。

    • a:以追加方式打开文件。如果文件存在,写操作将在文件的末尾进行。如果文件不存在,会尝试创建一个新文件。

    • r+:以读/写方式打开文件。文件必须存在。

    • w+:以读/写方式打开文件。如果文件存在,内容会被清空。如果文件不存在,会尝试创建一个新文件。

    • a+:以读/追加方式打开文件。如果文件存在,写操作将在文件的末尾进行。如果文件不存在,会尝试创建一个新文件。

在C语言中,fread()函数用于从文件中读取数据。它是一个非常强大的工具,因为它可以读取任意类型的数据,无论是字符、整数、浮点数,还是自定义的数据结构。

fread()

fread()函数的原型如下:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);



参数说明:

  • ptr:指向用于存储数据的内存块的指针。

  • size:要读取的每个元素的大小,以字节为单位。

  • nmemb:要读取的元素的数量。

  • stream:指向FILE对象的指针,该对象指定了一个输入流。

fread()函数会从stream指向的文件中读取nmemb个元素,每个元素的大小为size字节,并将这些数据存储在由ptr指向的内存块中。函数返回成功读取的元素数量。如果返回值小于nmemb,则可能表示发生了错误或者到达了文件的末尾。

例如,以下代码将从文件中读取一个整数数组:

c复制代码

#include <stdio.h>  
  
int main() {  
    FILE *file;  
    int numbers[10];  
    size_t i, n;  
  
    file = fopen("numbers.txt", "r");  
    if (file == NULL) {  
        printf("Cannot open file\n");  
        return 1;  
    }  
  
    n = fread(numbers, sizeof(int), 10, file);  
    for (i = 0; i < n; i++) {  
        printf("%d ", numbers[i]);  
    }  
    printf("\n");  
  
    fclose(file);  
    return 0;  
}


在这个例子中,我们打开名为"numbers.txt"的文件,并使用fread()函数从文件中读取10个整数。然后,我们遍历这些整数并打印出来。注意,我们使用了sizeof(int)作为fread()的第二个参数,这是因为我们要读取的是整数,所以我们需要知道每个整数在内存中占用的字节数。

数据结构

线性表是一种常见的数据结构,它可以使用数组或者链表来实现。以下是使用数组来实现线性表的示例代码:

c复制代码

#include <stdio.h>  
#include <stdlib.h>  
  
#define MAXSIZE 100 // 线性表的最大长度  
  
typedef struct {  
    int data[MAXSIZE]; // 存储数据元素的数组  
    int length; // 线性表的当前长度  
} SqList;  
  
// 初始化线性表  
void InitList(SqList *L) {  
    L->length = 0;  
}  
  
// 插入元素  
int ListInsert(SqList *L, int i, int e) {  
    if (i < 1 || i > L->length + 1 || L->length >= MAXSIZE) {  
        return 0;  
    }  
    for (int j = L->length; j >= i; j--) {  
        L->data[j] = L->data[j - 1];  
    }  
    L->data[i - 1] = e;  
    L->length++;  
    return 1;  
}  
  
// 删除元素  
int ListDelete(SqList *L, int i) {  
    if (i < 1 || i > L->length) {  
        return 0;  
    }  
    for (int j = i; j < L->length; j++) {  
        L->data[j - 1] = L->data[j];  
    }  
    L->length--;  
    return 1;  
}  
  
// 查找元素  
int GetElem(SqList L, int i) {  
    if (i < 1 || i > L.length) {  
        return -1;  
    }  
    return L.data[i - 1];  
}  
  
// 输出线性表中的元素  
void PrintList(SqList L) {  
    for (int i = 0; i < L.length; i++) {  
        printf("%d ", L.data[i]);  
    }  
    printf("\n");  
}  
  
int main() {  
    SqList L;  
    InitList(&L); // 初始化线性表  
    ListInsert(&L, 1, 10); // 在第1个位置插入元素10  
    ListInsert(&L, 2, 20); // 在第2个位置插入元素20  
    ListInsert(&L, 3, 30); // 在第3个位置插入元素30  
    PrintList(L); // 输出线性表中的元素  
    ListDelete(&L, 2); // 删除第2个位置的元素  
    PrintList(L); // 输出线性表中的元素  
    printf("%d\n", GetElem(L, 2)); // 查找第2个位置的元素并输出  
    return 0;  
}


以上代码实现了线性表的基本操作,包括初始化、插入、删除、查找和输出等。在使用时,可以根据具体的需求来调用这些函数。

**用链式存储实现线性表,并实现了插入、删除、查找和遍历操作。

以下是一个简单的C语言程序,它使用链式存储实现线性表,并实现了插入、删除、查找和遍历操作。

#include <stdio.h>  
#include <stdlib.h>  
  
// 定义线性表结点的结构体  
typedef struct node {  
    int data;  
    struct node *next;  
} Node;  
  
// 创建新结点  
Node* create_node(int data) {  
    Node *new_node = (Node*)malloc(sizeof(Node));  
    new_node->data = data;  
    new_node->next = NULL;  
    return new_node;  
}  
  
// 初始化线性表  
void init_list(Node **head) {  
    *head = NULL;  
}  
  
// 插入元素  
void insert_element(Node **head, int data, int position) {  
    Node *new_node = create_node(data);  
    if (*head == NULL || position == 1) {  
        new_node->next = *head;  
        *head = new_node;  
    } else {  
        Node *current_node = *head;  
        int i;  
        for (i = 1; i < position - 1; i++) {  
            if (current_node == NULL) {  
                printf("线性表长度不足,不能插入元素!\n");  
                return;  
            }  
            current_node = current_node->next;  
        }  
        if (current_node == NULL) {  
            printf("线性表长度不足,不能插入元素!\n");  
            return;  
        }  
        new_node->next = current_node->next;  
        current_node->next = new_node;  
    }  
}  
  
// 删除元素  
void delete_element(Node **head, int position) {  
    if (*head == NULL) {  
        printf("线性表为空,不能删除元素!\n");  
        return;  
    }  
    if (position == 1) {  
        Node *temp = *head;  
        *head = (*head)->next;  
        free(temp);  
    } else {  
        Node *current_node = *head;  
        int i;  
        for (i = 1; i < position - 1; i++) {  
            if (current_node == NULL) {  
                printf("线性表长度不足,不能删除元素!\n");  
                return;  
            }  
            current_node = current_node->next;  
        }  
        if (current_node == NULL || current_node->next == NULL) {  
            printf("线性表长度不足,不能删除元素!\n");  
            return;  
        }  
        Node *temp = current_node->next;  
        current_node->next = current_node->next->next;  
        free(temp);  
    }  
}  
  
// 查找元素  
int find_element(Node *head, int data) {  
    Node *current_node = head;  
    int i = 1;  
    while (current_node != NULL) {  
        if (current_node->data == data) {  
            return i;  
        }  
        current_node = current_node->next;  
        i++;  
    }  
    return -1; // 没有找到元素返回-1  
}  
  
// 遍历线性表  
void traverse_list(Node *head) {  
    Node *current_node = head;  
    while (current_node != NULL) {  
        printf("%d ", current_node->data);  
        current_node = current_node->next;  
    }  
    printf("\n"); // 输出换行符以保持格式整齐  
}`


这段代码是用C语言定义的一个链表节点。链表是一种常见的数据结构,由一系列的节点组成,每个节点包含两部分:数据和指向下一个节点的指针。

具体地,这段代码定义了一个名为'Node'的结构体,用于表示链表的节点。每个'Node'对象都有一个名为'data'的整型字段,用于存储数据,和一个名为'next'的指向下一个'Node'对象的指针。

这里是一个使用这种链表节点的简单例子:

c复制代码

#include <stdio.h>  
#include <stdlib.h>  
  
// 定义链表节点  
typedef struct node {  
    int data;  
    struct node *next;  
} Node;  
  
// 创建新节点  
Node* createNode(int data) {  
    Node* newNode = (Node*)malloc(sizeof(Node));  
    if(newNode == NULL) {  
        printf("Error creating a new node.\n");  
        exit(0);  
    }  
    newNode->data = data;  
    newNode->next = NULL;  
    return newNode;  
}  
  
// 在链表末尾添加新节点  
void appendNode(Node** head, int data) {  
    Node* newNode = createNode(data);  
    if(*head == NULL) {  
        *head = newNode;  
        return;  
    }  
    Node* lastNode = *head;  
    while(lastNode->next != NULL) {  
        lastNode = lastNode->next;  
    }  
    lastNode->next = newNode;  
}  
  
// 打印链表内容  
void printList(Node* head) {  
    while(head != NULL) {  
        printf("%d ", head->data);  
        head = head->next;  
    }  
    printf("\n");  
}  
  
int main() {  
    Node* head = NULL; // 初始化空链表  
    appendNode(&head, 1); // 添加节点1  
    appendNode(&head, 2); // 添加节点2  
    appendNode(&head, 3); // 添加节点3  
    printList(head); // 打印链表:1 2 3   
    return 0;  
}


这个例子中,我们首先定义了一个链表节点类型'Node',然后创建了一个名为'createNode'的函数,用于创建新的链表节点。我们还定义了一个名为'appendNode'的函数,用于在链表的末尾添加新的节点。最后,我们定义了一个名为'printList'的函数,用于打印链表的所有元素。在'main'函数中,我们创建了一个空链表,然后添加了三个节点,并打印了链表的所有元素。

队列:

#include <stdio.h>
#include <stdlib.h>
​
#define MAX_SIZE 100
​
typedef struct {
    int data[MAX_SIZE];
    int front;
    int rear;
} Queue;
​
void initialize(Queue* queue) {
    queue->front = -1;
    queue->rear = -1;
}
​
int isEmpty(Queue* queue) {
    return queue->front == -1;
}
​
int isFull(Queue* queue) {
    return (queue->rear + 1) % MAX_SIZE == queue->front;
}
​
void enqueue(Queue* queue, int item) {
    if (isFull(queue)) {
        printf("Queue is full. Cannot enqueue element.\n");
        return;
    }
    if (isEmpty(queue)) {
        queue->front = 0;
        queue->rear = 0;
    } else {
        queue->rear = (queue->rear + 1) % MAX_SIZE;
    }
    queue->data[queue->rear] = item;
}
​
int dequeue(Queue* queue) {
    if (isEmpty(queue)) {
        printf("Queue is empty. Cannot dequeue element.\n");
        return -1;
    }
    int item = queue->data[queue->front];
    if (queue->front == queue->rear) {
        queue->front = -1;
        queue->rear = -1;
    } else {
        queue->front = (queue->front + 1) % MAX_SIZE;
    }
    return item;
}
​
int getFront(Queue* queue) {
    if (isEmpty(queue)) {
        printf("Queue is empty.\n");
        return -1;
    }
    return queue->data[queue->front];
}
​
int main() {
    // 创建并初始化队列
    Queue queue;
    initialize(&queue);
​
    // 将元素入队列
    enqueue(&queue, 10);
    enqueue(&queue, 20);
    enqueue(&queue, 30);
​
    // 获取并输出队首元素
    printf("Front element: %d\n", getFront(&queue));
​
    // 出队列并输出
    while (!isEmpty(&queue)) {
        int item = dequeue(&queue);
        printf("Dequeued element: %d\n", item);
    }
​
    return 0;
}
​

堆栈

#include <stdio.h>
#include <stdlib.h>
​
#define MAX_SIZE 100
​
typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;
​
void initialize(Stack* stack) {
    stack->top = -1;
}
​
int isEmpty(Stack* stack) {
    return stack->top == -1;
}
​
int isFull(Stack* stack) {
    return stack->top == MAX_SIZE - 1;
}
​
void push(Stack* stack, int item) {
    if (isFull(stack)) {
        printf("Stack is full. Cannot push element.\n");
        return;
    }
    stack->data[++stack->top] = item;
}
​
int pop(Stack* stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty. Cannot pop element.\n");
        return -1;
    }
    return stack->data[stack->top--];
}
​
int getTop(Stack* stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty.\n");
        return -1;
    }
    return stack->data[stack->top];
}
​
int main() {
    // 创建并初始化堆栈
    Stack stack;
    initialize(&stack);
​
    // 将元素压入堆栈
    push(&stack, 10);
    push(&stack, 20);
    push(&stack, 30);
​
    // 获取并输出堆栈顶部元素
    printf("Top element: %d\n", getTop(&stack));
​
    // 弹出堆栈顶部元素并输出
    while (!isEmpty(&stack)) {
        int item = pop(&stack);
        printf("Popped element: %d\n", item);
    }
​
    return 0;
}
​

#include<iostream>
using namespace std;
#define MAXSIZE 100
#include<string>
typedef struct BiTnode{
    string data;
    struct BiTnode *lchild,*rchild;
}BiTnode,*BiTree;
//创建二叉树
void creatbitree(BiTree &T){
​
    char ch;
    cin>>ch;
    if(ch=='#') T=NULL;
    else{
        T=new BiTnode;
        T->data=ch;
        creatbitree(T->lchild);
        creatbitree(T->rchild);
    }
}
//复制树
void Copy(BiTree T,BiTree &newT){
    if(T==NULL){
        newT=NULL;
        return;
    }
    else{
        newT=new BiTnode;
        newT->data=T->data;
        Copy(T->lchild,newT->lchild);
        Copy(T->rchild,newT->rchild);
    }
}
//中序遍历树
void inordertraverse(BiTree T){
    if(T){
        inordertraverse(T->lchild);
        cout<<T->data<<endl;
        inordertraverse(T->rchild);
    }
}
//preortrave
void pretrave(BiTree T){
    if(T){
        cout<<T->data<<" ";
        pretrave(T->lchild);
        pretrave(T->rchild);
    }
}
//latrave
void latrave(BiTree T){
    if(T){
        latrave(T->lchild);
        latrave(T->rchild);
        cout<<T->data<<" ";
    }
}
//深度计算 前序遍历
int Depth(BiTree T){
    int m,n;
    if(T==NULL)return 0;
    else{
        m=Depth(T->lchild);
        n=Depth(T->rchild);
        return (m>n?m:n);
    }
}
//结点计算
int NodeCount(BiTree T){
    if(T==NULL)return 0;
    else{
        return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
    }
}
int main(){
    BiTree T;
    creatbitree(T);
    cout<<"计算深度: "<<Depth(T)<<endl;
    BiTree T1;
    cout<<"复制树T1:"<<endl;
    Copy(T,T1);
    cout<<"前序遍历"<<endl;
    pretrave(T);
    cout<<"中序遍历树"<<endl;
    inordertraverse(T);
    cout<<"后序遍历"<<endl;
    latrave(T);
    cout<<"结点计算:"<<NodeCount(T)<<endl;
    system("pause");
    return 0;    
}
​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1020984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新一代最强开源UI自动化测试神器Playwright(Java版)(对话框处理)

&#x1f3ad;Playwright让网页对话框&#x1f310;&#x1f4ac;处理变得更加快捷&#xff01;网页对话框是在网页上出现的常见弹窗&#xff0c;包括Alert、Confirm和Prompt等。这些对话框通常需要用户输入信息或进行某些选择&#xff0c;但是在自动化测试中处理它们可能会很棘…

双周赛113(枚举、分类讨论 + 二分查找、枚举值域两数之和、换根DP)

文章目录 双周赛113[2855. 使数组成为递增数组的最少右移次数](https://leetcode.cn/problems/minimum-right-shifts-to-sort-the-array/)暴力枚举贪心 O(n) [2856. 删除数对后的最小数组长度](https://leetcode.cn/problems/minimum-array-length-after-pair-removals/)分类讨…

[MAUI]实现动态拖拽排序网格

文章目录 创建页面元素创建可绑定对象创建绑定服务类拖拽&#xff08;Drag&#xff09;拖拽悬停&#xff0c;经过&#xff08;DragOver&#xff09;释放&#xff08;Drop&#xff09; 限流(Throttle)和防抖(Debounce)项目地址 上一章我们使用拖放(drag-drop)手势识别实现了可拖…

TCP/IP协议栈各层涉及到的协议

21/tcp FTP 文件传输协议 22/tcp SSH 安全登录、文件传送(SCP)和端口重定向 23/tcp Telnet 远程连接 80/tcp HTTP 443/tcp HTTPS 计算机各层网络协议 五层&#xff1a;应用层: (典型设备:应用程序&#xff0c;如FTP&#xff0c;SMTP &#xff0c;HTTP) DHCP(Dynamic Host…

Pikachu Burte Force(暴力破解)

一、Burte Force&#xff08;暴力破解&#xff09;概述 ​ “暴力破解”是一攻击具手段&#xff0c;在web攻击中&#xff0c;一般会使用这种手段对应用系统的认证信息进行获取。 其过程就是使用大量的认证信息在认证接口进行尝试登录&#xff0c;直到得到正确的结果。 为了提高…

RFID与人工智能怎么融合,RFID与人工智能融合的应用

随着物联网技术的不断发展&#xff0c;现实世界与数字世界的桥梁已经被打通。物联网通过各种传感器&#xff0c;将现实世界中的光、电、热等信号转化为有价值的数据。这些数据可以通过RFID技术进行自动收集和传输&#xff0c;然后经由人工智能算法进行分析、建模和预测&#xf…

uniapp cli创建 vue3 + typeScript项目 配置eslint prettier husky

1 命令创建项目 npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project2 下载依赖 npm install3 填写appid 4 运行项目并且微信开发工具打开 npm run dev:mp-weixin5 安装 vscode 插件 安装 **Vue Language Features (Volar)** &#xff1a;Vue3 语法提示插件 安装 *…

伦敦银一手是多少?

伦敦银是以国际现货白银价格为跟踪对象的电子合约交易&#xff0c;无论投资者通过什么地方的平台进入市场&#xff0c;执行的都是统一国际的标准&#xff0c;一手标准的合约所代表的就是5000盎司的白银&#xff0c;如果以国内投资者比较熟悉的单位计算&#xff0c;那约相当于15…

http客户端Feign使用

一、RestTemplate方式调用存在的问题 先来看我们以前利用RestTemplate发起远程调用的代码&#xff1a; String url "http://userservice/user/" order.getUserId(); User user restTemplate.getForObject(url, User.class);存在下面的问题&#xff1a; 代码可读…

Mosh Java课程自学(一)

目录 一、前言 二、全局介绍 三、Types 一、前言 首先推荐一下B站上转载的Mosh讲Java课程&#xff0c;当然&#xff0c;建议有一定基础并且英文水平尚可的同学学习&#xff0c;否则你可能会被搞得很累并逐渐失去对编程的兴趣。 Mosh 【JAVA终极教程】中英文字幕 高清完整版…

口袋参谋:如何高效一键下载真实买家秀?

​在淘宝天猫上&#xff0c;即使卖一支笔都有上万个宝贝竞争&#xff0c;所有卖家拼的就是权重带来的曝光度&#xff0c;能展示给买家多少&#xff0c;自己收获多少流量。 如何用自己的优势将流量访客转化为顾客&#xff0c;提升店铺的转化率。而买家秀&#xff0c;就是为此而生…

Java常用类之 String、StringBuffer、StringBuilder

Java常用类 文章目录 一、字符串相关的类1.1、String的 不可变性1.2、String不同实例化方式的对比1.3、String不同拼接操作的对比1.4、String的常用方法1.5、String类与其他结构之间的转换1.5.1、String 与基本数据类型、包装类之间的转换1.5.2、String 与char[]的转换1.5.3、…

ipad可以使用其他品牌的手写笔吗?开学平价电容笔推荐

新学期已经来临&#xff0c;相信不少同学已经开始着手筹备新学期的该准备什么了&#xff0c;毕竟原装的苹果Pencil&#xff0c;功能强大&#xff0c;但价格昂贵&#xff0c;一般人根本买不起。那么&#xff0c;有没有像苹果原装那样的电容笔呢&#xff1f;当然是有的。国产的平…

长安链上线可视化敏捷测试工具v1.0版本

开发者对区块链底层平台进行初步的了解后&#xff0c;一项经常会涉及到的工作是对平台进行测试以考量其性能及稳定性是否符合自身使用需求。长安链推出了可视化UI操作界面的区块链敏捷测试工具v1.0版本&#xff0c;当前版本可对内置合约进行压测并生成网络拓扑图以验证组网方式…

免费开箱即用微鳄售后工单管理系统

编者按&#xff1a;本文介绍基于天翎MyApps低代码平台开发的微鳄售后工单管理系统&#xff0c; 引入低代码平台可以帮助企业快速搭建和部署售后工单管理系统&#xff0c; 以工作流作为支撑&#xff0c;在线完成各环节数据审批&#xff0c;解决售后 工单 服务的全生命周期过程管…

《2023中国氢能源行业分析报告》丨附下载_三叠云

✦ ✦✦ ✦✦ ✦✦ ✦ 1. 国内氢能政策梳理 直接涉及氢能政策&#xff1a;1&#xff09;21年以来&#xff0c;发布国家级10个、省级83个、 市县级252个&#xff1b;2&#xff09;涉及发展规划占比45%、财政支持占比 20%、项目支持占比17%、管理办法占比16%、 氢能安全和标准占…

公私钥非对称加密 生成和验证JSON Web Token (JWT)

前言 这是我在这个网站整理的笔记&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 公私钥非对称加密 生成和验证JSON Web Token 什么是JSON Web Token (JWT)Java程序中生成和验证JWT代码解析 什么是JSON Web Token (JWT) JSON Web Tok…

【C++】泛型算法(二)泛型指针Iterator(迭代器)

迭代器iterator定义 迭代器是一种检查容器内元素并遍历元素的数据类型&#xff1b;迭代器提供一个对容器对象或者string对象的访问方法&#xff0c;并定义了容器范围&#xff1b;迭代器的使用可以提高编程的效率。 其定义应该提供&#xff1a; 迭代对象&#xff08;某个容器&a…

拆贡献算总和(抓住双射)+竞赛图与连通分量相关计数:arc163_d

https://atcoder.jp/contests/arc163/tasks/arc163_d 首先竞赛图有个性质&#xff1a; 然后有了这个性质&#xff0c;我们就可以考虑计数题的经典套路&#xff0c;拆贡献算总和。 考虑假如我们成功划分成两个集合 A , B A,B A,B&#xff0c;其中一个可以为空&#xff08;我们…