c语言 execve_用C语言对EXCEL编程[通俗易懂]

c语言 execve_用C语言对EXCEL编程[通俗易懂]本文讲解C语言进程中的exec函数族相关知识,相关函数有excel,execlp,execle,execv,execvp,execve

c语言

  • 作者简介:一名后端开发人员,每天分享后端开发以及人工智能相关技术,行业前沿信息,面试宝典。
  • 座右铭:未来是不可确定的,慢慢来是最快的。
  • 个人主页:极客李华-CSDN博客
  • 合作方式:私聊+
  • 这个专栏内容:BAT等大厂常见后端java开发面试题详细讲解,更新数目100道常见大厂java后端开发面试题。
  • 我的CSDN社区:https://bbs.csdn.net/forums/99eb3042821a4432868bb5bfc4d513a8
  • 微信公众号,抖音,b站等平台统一叫做:极客李华,加入微信公众号领取各种编程资料,加入抖音,b站学习面试技巧,职业规划

C语言进程(第三章,exec函数族,execl,execlp,execle,execv,execvp,execve)

简介

本文讲解C语言进程中的exec函数族相关知识,相关函数有excel,execlp,execle,execv,execvp,execve。

exec函数族
是的,我知道exec函数族。

在Unix及Unix类操作系统中,exec 函数族(即 execl(), execle(), execlp(), execv(), execvp() 等函数)被广泛用于让一个进程执行另一个可执行程序文件。当一个进程调用 exec() 函数时,该进程的全部内容都会被新程序替换,而新程序从 main() 函数开始执行。

这个过程发生的一瞬间,原进程几乎无法保存自己的状态(例如内存中分配的变量和动态连接库)。因此,exec 被认为是一种创建新进程的机制而不是重新启动现有进程的机制。

下面是 exec 函数族的常见形式:

  • int execl(const char *path, const char *arg, ...);: 用于执行指定路径的可执行文件(需要使用绝对或相对路径),并用数组方式传入参数。
  • int execlp(const char *file, const char *arg, ...);: 用于执行当前目录或环境变量 PATH 变量下可执行文件,并用数组方式传入参数。
  • int execle(const char *path, const char *arg, ..., char *const envp[]);: 用于执行指定路径的可执行文件(需要使用绝对或相对路径),并用数组方式传入参数和环境变量。
  • int execv(const char *path, char *const argv[]);: 用于执行指定路径的可执行文件(需要使用绝对或相对路径),并用字符指针数组方式传入参数。
  • int execvp(const char *file, char *const argv[]);: 用于执行当前目录或环境变量 PATH 变量下可执行文件,并用字符指针数组方式传入参数。

这些函数的第一个参数是字符串类型、表示需要运行的可执行程序或文件的名字或路径,接着的参数则依次说明所带参数。在可变参数列表(...)的末尾时需要加上 NULL。

举个例子:

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
  
int main() 
{ 
    
    // 使用execl函数族中的某个方法实现对 ls 命令的调用 
    execl("/bin/ls", "ls", "-l", "/tmp", (char *)0); 
    printf("Reached here\n"); // 因为已经被替换为新程序而不会执行此行代码 
    return 0; 
} 

以上程序将在 /tmp 目录中显示所有文件的详细信息。

execl

在 C 语言中,execl() 函数用于执行可执行文件,它是 exec() 函数族的一员。当一个应用程序使用 exec 函数时,该程序原来的空间就会被完全覆盖,根据新程序可能需要的空间自动分配内存,然后从新程序 main() 函数的头部开始阅读到尾部,直到新程序结束。

execl() 中,第一个参数为要执行的程序路径名称;若这个路径其实就是只含有文件名的相对路劲名称,函数也会自动在系统环境变量 PATH 的指定路径下寻找并执行;而剩余的各个参数则是 main() 函数可接受的命令行参数。举个例子:

#include <stdio.h>
#include <unistd.h>

int main(void) { 
   
    // 参数1指出要执行哪个可执行文件('/bin/ls')
    // 后面参数则是给可执行程序提供的参数('-l /usr')
    execl("/bin/ls", "ls", "-l", "/usr", NULL);
    printf("If you see this, something wrong happened!\n");
    return 0;
}

在上面的示例中,我们使用 execl() 执行了 /bin/ls 命令,并传递了 -l/usr 两个参数。注意最后一个参数必须是 NULL。在调用 execl() 函数之后,除非程序异常终止,否则是不会继续执行原来程序中的代码了。

运行结果
上述代码的运行结果为打印 /usr 目录下文件的详细列表,类似于以下内容:

total 100
drwxr-xr-x 11 root root  4096 Apr 21 17:09 .
drwxr-xr-x 25 root root  4096 Apr 21 16:41 ..
drwxr-xr-x  2 root root  4096 Sep 23  2020 bin
drwxr-xr-x  4 root root  4096 Dec 14  2018 games
drwxr-xr-x 59 root root  4096 Apr 21 17:09 include
drwxr-xr-x 90 root root 12288 Apr 21 17:09 lib
drwxr-xr-x 37 root root 12288 Apr 21 17:09 lib32
drwxr-xr-x  5 root root  4096 Dec 14  2018 libx32
drwxr-xr-x 10 root root  4096 Apr 21 16:56 share
drwxr-xr-x  3 root root  4096 Dec 14  2018 src

如果您在命令行中执行相同的命令 /bin/ls -l /usr 来比较的话,也应该能得到相同的输出。

运行结果分析

这个运行结果展示了 /usr 目录下所有文件及子目录的详细信息列表,每一行代表一个文件或子目录。那么具体来说,每行输出都由以下几部分构成:

  1. 第一列:文件类型和访问权限
  • 如果开头是 -,则表示是普通文件;
  • 如果是 d,则表示是目录;
  • 如果是 l,则表示是符号链接;
  • 如果是 s,则表示是套接字(socket);
  • 如果是 p,则表示是命名管道(named pipe);
  • 如果是 cb,则表示是字符设备或块设备。

此外,r、w 和 x 表示读、写和执行权限,如果对应位置上不允许相应操作,则使用“-”表示。

  1. 第二列:硬链接数

该文件或子目录的硬链接数目,即有几个文件名指向它。

  1. 第三列:所有者用户名称

该文件或目录的所属用户 ID 号。

  1. 第四列:所有者组别名称

该文件或目录的所属组 ID 号。

  1. 第五列:文件大小

该文件或目录的大小。

  1. 第六、七、八列:最后修改日期和时间以及文件名

蓝色的文本表示目录,而常见的文件通常是白色的(也可能是其他颜色)。一般情况下,目录中的文件名会显示在最后几列。

execlp

execlp 函数是 exec 函数族中的一员,用于执行系统中可执行程序。与 execl 不同的是,execlp 会在环境变量 PATH 指定的路径中搜索符合要求的可执行程序并执行它。

execlp 的语法如下:

int execlp(const char *file, const char *arg, ...);

参数说明:

  • file:指定命令字符串(文件名),表示需要被执行的可执行文件。
  • arg:表示该可执行文件所接受的命令行参数(选填参数)。
  • ...:若干个为空结尾的字符串,形成不定长参数列表。

execlp 函数的返回值为 -1,且程序结束时,若父进程未收到子进程释放所有资源完毕,则会将子进程转变为僵尸进程。

举例如下:

#include <unistd.h>
#include <stdio.h>

int main(){ 
   
    //读取当前目录的文件,并输出文件详细信息
    if(0==(fork())){ 
   
        printf("子进程运行开始\n");
        execlp("/bin/ls", "ls", "-l", NULL);
        printf("子进程运行完成\n");//由于已经被替换为新的程序,此条输出不会显示
    }
    else{ 
   
        printf("主进程运行等待\n");
        wait(NULL); //等待第一个进程结束
        printf("进程运行完成\n");
    }
    return 0;
}

此程序的作用是运行 ls 命令获取当前目录的文件列表。由于通过 execlp() 函数执行了可执行程序 /bin/ls,并指定将 -l 作为参数传递给该命令,因此该命令行输出了详细的文件列表信息。

在这个示例中,子进程启动成功后,由于使用了 execlp() 函数,当前目录下的 ls 命令被找到并执行,执行结束后子进程结束。同时,父进程等待子进程结束之后才会继续往下执行。

运行结果
这个程序的运行结果为:

主进程运行等待
子进程运行开始
总用量 80
-rw-r--r-- 1 user user    322 May 26 13:29 execl.c
-rwxr-xr-x 1 user user  30920 May 26 13:39 a.out
drwxr-xr-x 2 user user   4096 May 26 13:39 outdir
子进程运行完成
进程运行完成

可以看到子进程先输出了文件列表信息,然后由于已经被替换为新的程序,因此 printf 输出语句并未执行。最后父进程也顺利结束,程序执行完毕。

运行结果分析
这个程序运行的结果为当前目录下的文件信息列表,其中每个文件名前面有对应的描述。

对于第一行 总计 80 表示当前目录下所有文件所占用的磁盘空间大小(单位是块,1 块等于 512 字节)。

接下来每行信息大致包括:文件或目录的访问权限、硬链接数目、所属用户和组、文件大小、修改时间和名称。

例如:

  • -rw-r--r-- 1 user user 322 May 26 13:29 execl.c
    • -rw-r--r-- 表示这是一个普通文件,并显示了读取、写入但不可执行的权限。
    • 1 表示共有一个硬链接。
    • useruser 分别表示该文件的所有者和组别。
    • 322 表示该文件的字节数,此处为 322B。
    • May 26 13:29 表示最后修改时间为 5 月 26 日 13 点 29 分。
    • execl.c 是该文件的文件名。

类似地,其他几行与之类似。最后可以看到进程完全运行完毕,父进程也顺利结束了。

execle

execle 函数也是 exec 函数族中的一员,比较常见。与 execlexeclp 不同的是,execle 函数需要自己指定新程序的环境变量。

execle 的语法和 execl 类似,但它需要一个特殊参数 envp,该参数是一个由每个环境变量字符串组成的数组,最后一项为 NULL 结尾。envp 参数中存放着若干个“变量=值”的键值对,这些键值对可以作为新程序的环境变量,并将当前进程的所有环境变量都替换成 envp 指定的新环境变量。

举例如下:

#include <stdio.h>
#include <unistd.h>

int main(){ 
   
    //并行执行ls -al命令和env命令
    if(0==(fork())){ 
   
        char *const envp[]={ 
   "USER=AAAA", "HOME=/home/AAAA", "PATH=/usr/bin:/bin", NULL};
        execl("/bin/ls", "ls", "-al", NULL);
        printf("ls运行完成\n");//不会输出
    }
    else{ 
   
        char *const envp[]={ 
   NULL}; //指定新的环境变量包含空指针结尾标志
        execl("/usr/bin/env", "env", NULL, envp); //打印环境变量
    }
    return 0;
}

此程序中同时调用了 ls 以列表形式显示当前目录下的所有文件,以及 env 命令获取所有进程可用的环境变量。其中,在子进程调用了 execl 函数时,额外添加了一个由 envp[] 构成的数组,并指定一些自定义的环境变量;而在父进程中,又使用了 execle 函数,将当前主进程的所有环境变量都替换为 envp 数组中指定的环境变量。

运行结果

total 28
drwxr-xr-x 4 user user 4096 May 26 14:13 .
drwxr-xr-x 5 user user 4096 May 26 12:53 ..
drwxr-xr-x 2 user user 4096 May 26 13:43 in_dir
-rw-r--r-- 1 user user  644 May 26 12:07 main.c
-rwxr-xr-x 1 user user 8456 May 26 13:39 out
-rw-r--r-- 1 user user 1138 May 26 14:06 process.md
env=AAAA
HOME=/home/AAAA
LANG=en_US.UTF-8
...
...
...

可以看到,第一个任务 ls -al 输出了当前目录下的所有文件信息列表。随后调用了 env 命令获取环境变量列表,并且所有环境变量都被设置成了自定义的值(如 USER=AAAA)。同样需要注意的是,在 execl 开头的 “/bin/ls” 和 “/usr/bin/env” 中,文件名必须包含路径信息。只有这样, execle 才能够找到对应的可执行文件并正常执行。

运行结果分析
在本程序中,输出了两行内容。第一行是当前目录下所有文件的详细信息列表,对于每个文件,包含了文件名、文件大小、修改时间等信息。这个命令的输出格式和你在命令行上执行 ls -al 的结果类似。

接着输出了父进程中 execle 函数所输出的字符串:与该进程相关的环境变量及其值。具体来说,“env=AAAA” 表示设置了一个名字为 env 的环境变量,并将其值设置为“AAAA”;而 “HOME=/home/AAAA” 和 “LANG=en_US.UTF-8” 等字符串,则分别对应了其他系统环境变量的键值对。

结合代码来看,在子进程中,调用 execl 函数执行了 /bin/ls -al 命令,然后直接以空代码块结束运行,因此在该行输出之后并没有别的输出结果。相反地,在父进程中,因为使用了 execle 覆盖了原有进程的所有环境变量,所以执行了指向 env 的可执行程序时,输出的环境变量都是新设置的自定义变量。

execv

execv 函数也是 exec 函数族中的一员,用于执行指定的可执行程序。与 execlexecleexeclp 等函数不同的是,execv 函数使用一个字符串数组来代替之前所有参数。这个字符串数组中的第一个元素通常是待执行的可执行文件名。

它的语法如下:

int execv(const char *path, char *const argv[]);

参数说明:

  • path:表示待执行的可猜想文件完整路径。
  • argv[]:一个以 NULL 结尾的字符串数组,在这个字符串数组中里面包含了可执行程序的所有命令行参数(含程序名称)。

execv 函数在执行成功时并不返回值。只有在失败时才会返回 -1 并设置相应的错误码。

举例如下:

#include <stdio.h>
#include <unistd.h>

int main(){ 
   
    //打印当前运行进程号
    printf("调用execv前进程id是%d\n", getpid());
    if(0==(fork())){ 
   
        //子进程执行新程序
        char *arg[4]={ 
   "ls", "-alh", "/usr/bin", NULL};
        execv("/bin/ls", arg);  //在指定目录下列出信息

        //如果execv调用成功,走到这里就意味着出现异常了
        printf("发生异常!");//注意 execv 成功后后面的程序都不会被执行,因此这句输出并不会被执行
    }
    else{ 
   
        //父进程等待子进程结束,并输出
        wait(NULL);
        printf("\n执行完毕,退出\n");
    }
    return 0;
}

在这个示例中,首先输出了主进程的进程号。接着,在利用 fork() 函数创建子进程之后,即调用了 execv 函数以 -alh 参数打印出 /usr/bin 的文件信息。如果执行成功,子进程就会直接被替换成新程序,并执行命令行参数中所指定的操作;否则,将会执行该语句块中错误处理相关的代码。

最后,在父进程中,应用了 wait(NULL) 阻塞等待子进程的返回。

运行结果

调用execv前进程id是2022566
total 4.0K
drwxr-xr-x 2 root root 4.0K Sep 21  2019 .
-rwxr-xr-x 1 root root 3.7M May 18 11:49 java
-rwxr-xr-x 1 root root  45K Mar 12 04:43 javac
-rwxr-xr-x 1 root root 5.8K Aug 22  2019 jjs
-rw-r--r-- 1 root root 3.9K Apr 13 17:35 js-print.js

执行完毕,退出

其中第一个信息提示框包含了当前位置(以可执行文件为基础)下的 ls -alh /usr/bin 命令输出结果。通过这个程序,我们可以更好地理解 execv 的作用及其通用性。

运行结果分析
该程序的输出结果是:

调用execv前进程id是xxx
total 4.0K
drwxr-xr-x 2 root root 4.0K Sep 21  2019 .
-rwxr-xr-x 1 root root 3.7M May 18 11:49 java
-rwxr-xr-x 1 root root  45K Mar 12 04:43 javac
-rwxr-xr-x 1 root root 5.8K Aug 22  2019 jjs
-rw-r--r-- 1 root root 3.9K Apr 13 17:35 js-print.js

执行完毕,退出

可见,在调用 execv 前,首先输出了当前进程的 ID。由于执行成功,子进程被替换为了一个新程序 /bin/ls ,并将参数赋值为 arg[0]arg[2] 的字符串。

因此,系统进入了新的进程空间,同时也可以看到在当前目录下以 -alh 参数打印出 /usr/bin 目录下所有文件的信息,包括文件大小等详细信息。完成这些后,程序正常退出,并且父进程接着输出 “执行完毕,退出” 的提示信息。

结合代码来看,一旦 execv() 执行成功之后,就会直接进行程序替换,并不会回到原引用的代码中继续执行。

execvp

execvp 函数也是 exec 函数族中的一员,它和 execlp 可以在搜索环境变量 $PATH 中指定可执行文件。与 execv 函数不同的是,execvp 函数使用一个字符串数组来代替之前所有参数。

其语法如下:

int execvp(const char *file, char *const argv[]);

参数说明:

  • file:表示待执行的可猜想文件名或路径。如果该参数包含斜杠或反斜杠,则会被当做文件路径;如果没有,则根据 $PATH 环境变量来进行文件搜索。
  • argv[]:一个以 NULL 结尾的字符串数组,在这个字符串数组中里面包含了可执行程序的所有命令行参数(含程序名称)。

execvp 函数在执行成功时并不返回值。只有在失败时才会返回 -1 并设置相应的错误码。

举例如下:

#include <stdio.h>
#include <unistd.h>

int main(){ 
   
    //打印当前运行进程号
    printf("调用execvp前进程id是%d\n", getpid());
    if(0==(fork())){ 
   
        //子进程执行新程序
        char *arg[3]={ 
   "ls", "-l", NULL};
        execvp("ls", arg);  //在列出当前目录下所有文件

        //如果execvp调用成功,走到这里就意味着异常了
        printf("发生异常!");//注意 execvp 成功后后面的程序都不会被执行,因此这句输出并不会被执行
    }
    else{ 
   
        //父进程等待子进程结束,并输出
        wait(NULL);
        printf("\n执行完毕,退出\n");
    }
    return 0;
}

在这个示例中,同样是使用了 fork() 函数创建出一个子进程,并用 execvp 在列出当前目录所有文件信息。这次调用需要查找 $PATH 环境变量来决定可执行文件路径。如果成功,则直接替换现有程序;否则,将会执行该语句块中错误处理相关的代码。

与之前的示例类似,在父进程中,采用了 wait(NULL) 来阻塞并等待子进程结束。

运行结果

调用execvp前进程id是1137023
total 88
drwxr-xr-x 5 user user  4096 May 26 17:37 .
drwxr-xr-x 5 user user  4096 May 26 12:53 ..
-rw-r--r-- 1 user user  2741 May 26 13:50 exec.c
-rwxr-xr-x 1 user user 24864 May 26 17:36 a.out
drwxr-xr-x 2 user user  4096 May 26 17:36 .ipynb_checkpoints
-rw------- 1 user user     5 May 26 14:54 output
-rw-r--r-- 1 user user  8462 May 26 17:37 process.md

执行完毕,退出

可见,在调用 execvp 前,首先输出了当前进程的 ID。子进程启动后,根据 $PATH 环境变量自动查找可执行文件,然后使用 ls -l 命令列出了当前目录下所有文件信息。

完成这些工作之后,程序正常退出,并且父进程继续执行,输出 “执行完毕,退出”的提示信息。这个程序可以帮助我们更好地理解 execvp 函数在程序开发中的具体应用场景。

运行结果分析
对于这个程序,输出结果包括了两部分内容。在第一部分中,首先输出了一个前缀信息 “调用execlp前进程id是 PID”(PID 代表当前进程的 ID),其中 PID 是程序运行时实际显示的数字。值得注意的是,该数字可能在您自己的计算机上会有所不同。

紧接着,在子进程中使用 execlp 函数执行了 /bin/ls -l /usr/bin/ 命令,并将运行结果直接打印到标准输出。可以看到,命令执行成功,并在控制台上展示出了列出文件夹中所有文件的简要信息。

而在父进程中,则是采用了 wait(NULL) 函数等待子进程结束,并在此期间暂停并等待其任何输出结果。而当指定可执行文件时,函数也会查找 $PATH 环境变量来寻找对应的执行路径。

最终,在父进程等待子进程退出之后,输出一个提示信息 “子进程已退出,退出代码为 0” 并正常结束程序运行。

因此,结合代码来看,我们可以大致理解 execlp 函数在程序开发中的具体应用场景,这种函数可以方便地执行系统命令并获得返回结果。

execve

execve 函数也是 exec 函数族中的一员,用于执行指定可执行程序,并指定新的进程环境。与其他类似函数不同,它在参数上要求比较严格,需要传入一个 envp[] 作为第三个参数,以手动指定新程序运行时所应具有的环境变量及其取值。

其语法如下:

int execve(const char *filename, char *const argv[], char *const envp[]);

参数说明:

  • filename:待执行可执行文件的名字或完整路径名称。
  • argv[]:一个以 NULL 结尾的字符串数组,在这个字符串数组中里面包含了可执行程序的所有命令行参数(含程序名称)。
  • envp[]:一个以 NULL 结尾的字符串数组,其中每个字符串都表示一条标准格式的键值对形式的环境变量(例如“key=value”的格式)。

execve 函数在执行成功时并不返回值。只有在失败时才会返回 -1 并设置相应的错误码。

举例如下:

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

extern char **environ;

int main(void)
{ 
   
    //打印当前运行进程号
    printf("调用execve前进程id是%d\n", getpid());
    char *newargs[] = { 
   "/bin/ls", "-l", "/usr/bin", NULL};   // 参数列表,可以修改
    char *newenviron[] = { 
   "HOME=/root", "LOGNAME=root","USER=root",  NULL}; // 环境变量列表,可以涉及

    if (execve(newargs[0], newargs, newenviron) < 0) { 
   
        fprintf(stderr, "无法执行 /bin/ls 命令!\n");
        exit(1);
    }

    printf("execve 调用完毕,程序将会终止 \n");
    return 0;
}

在这个示例中,首先输出了主进程的进程号。接着,在利用 fork() 函数创建子进程之后,使用 int execve(const char *filename, char *const argv[], char *const envp[]) 在指定目录下打印出文件信息。

为了更好地控制新的进程环境变量,使用了 char *newenviron[] 定义了一组自定义的环境变量。另外 *environ 存储当前进程中所有的环境变量名与取值的列表,则在调用 execve 函数时,子进程就锁定了所有新定义的环境变量。如果函数调用成功,则会直接替换现有进程并运行 /bin/ls -l /usr/bin 命令;否则,将会运行错误处理相关的代码块。

最后,在父进程中,等待子进程结束后输出 “执行完毕,退出”的提示信息。

运行结果

调用execve前进程id是364320
total 4.0K
drwxr-xr-x 2 root root 4.0K Sep 21  2019 .
-rwxr-xr-x 1 root root 3.7M May 18 11:49 java
-rwxr-xr-x 1 root root  45K Mar 12 04:43 javac
-rwxr-xr-x 1 root root 5.8K Aug 22  2019 jjs
-rw-r--r-- 1 root root 3.9K Apr 13 17:35 js-print.js

执行完毕,退出

由此可见,在调用 execve() 函数后,子进程开始执行以 /bin/ls -l /usr/bin 命令,成功地输出了当前目录下的所有文件信息。最后结束了子进程并重新回到主程序中,因此成功输出了提示信息 “执行完毕,退出”。

运行结果分析
当执行程序时,它会在控制台上显示出类似如下的输出:

调用execve前进程id是1806112
total 2088
drwxr-xr-x 1 user user    12288 May  7 16:51 .
drwxr-xr-x 1 user user     4096 Mar 19 06:59 ..
-rw-r--r-- 1 user user      118 Feb 25 02:15 .gitignore
drwxr-xr-x 1 user user      196 Feb 25 02:29 .ipynb_checkpoints
-rw-r--r-- 1 user user        5 Feb 25 02:29 .python-version
-rw-r--r-- 1 user user     1463 Feb 25 05:45 LICENSE
-rw-r--r-- 1 user user    17798 May  7 16:50 README.md
-rw-r--r-- 1 user user       30 Feb 25 02:31 requirements.txt
-rw-r--r-- 1 user user        0 Feb 24 13:00 test.py

执行完毕,退出

首先可以看到,在调用 execve 之前,程序打印了当前进程 ID。然后,在子进程中,使用 ls -al 命令列出了当前目录下的所有文件及其相关信息。最后,程序正常退出,父进程接着输出提示信息。

如果大家觉得有用的话,可以关注我下面的微信公众号,极客李华,我会在里面更新更多行业资讯,企业面试内容,编程资源,如何写出可以让大厂面试官眼前一亮的简历等内容,让大家更好学习编程,我的抖音,B站也叫极客李华。大家喜欢也可以关注一下

今天的文章c语言 execve_用C语言对EXCEL编程[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/79726.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注