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

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

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);
  • 如果是 c 或 b,则表示是字符设备或块设备。

此外,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 表示共有一个硬链接。
  • user 和 user 分别表示该文件的所有者和组别。
  • 322 表示该文件的字节数,此处为 322B。
  • May 26 13:29 表示最后修改时间为 5 月 26 日 13 点 29 分。
  • execl.c 是该文件的文件名。

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

execle

execle 函数也是 exec 函数族中的一员,比较常见。与 execl 和 execlp 不同的是,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 函数族中的一员,用于执行指定的可执行程序。与 execl、execle 和 execlp 等函数不同的是,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站也叫极客李华。大家喜欢也可以关注一下

相关文章
|
25天前
|
C语言
【C语言基础篇】字符串处理函数(四)strcmp的介绍及模拟实现
【C语言基础篇】字符串处理函数(四)strcmp的介绍及模拟实现
|
1天前
|
存储 缓存 C语言
【C语言】字符函数,字符串函数,内存函数
C语言中的字符串函数和内存函数
9 0
【C语言】字符函数,字符串函数,内存函数
|
8天前
|
C语言
C语言5 字符输出函数和格式输出函数
C语言5 字符输出函数和格式输出函数
13 1
|
9天前
|
算法 编译器 C语言
深入浅出C语言—【函数】下
深入浅出C语言—【函数】下
|
21天前
|
Java C语言 C++
定义C语言的int main()函数
定义C语言的int main()函数
|
25天前
|
C语言
C语言prinf函数
C语言prinf函数
17 4
|
23天前
|
存储 移动开发 C语言
技术心得记录:嵌入式开发中常用到的C语言库函数
技术心得记录:嵌入式开发中常用到的C语言库函数
16 1
|
24天前
|
编译器 程序员 Serverless
函数(C语言)
函数(C语言)
|
24天前
|
机器学习/深度学习 C语言
详细解读C语言math.h中常用函数
详细解读C语言math.h中常用函数
14 1
|
24天前
|
C语言
C语言刷题(函数)
C语言刷题(函数)

相关实验场景

更多