主要有三种方法可以在 C 语言中调用 Gnuplot:

(图片来源网络,侵删)
- 通过管道 调用 (最常用、最推荐):C 程序创建一个管道,将 Gnuplot 的命令作为字符串写入管道,Gnuplot 在另一端读取这些命令并执行绘图,这是最灵活、最强大的方法。
- 通过
system()函数调用:C 程序通过system()函数直接调用系统的 shell 命令来执行 Gnuplot,这种方法最简单,但交互性差,且不易控制。 - 使用 Gnuplot 的 C API (Gnuplot-iostream):这是一个第三方库,它提供了 C++ 风格的流接口来操作 Gnuplot,虽然是为 C++ 设计的,但也可以在 C 中使用,这是最优雅、功能最全的方法,但需要额外安装库。
通过管道调用 (推荐)
这是最常用且最灵活的方法,它允许你的 C 程序与 Gnuplot 进程进行实时交互。
核心思想
- 使用
popen()函数创建一个管道,并启动一个 Gnuplot 进程。popen()会返回一个文件指针。 - 通过
fprintf()向这个文件指针写入 Gnuplot 命令字符串。 - 使用
pclose()关闭管道,等待 Gnuplot 进程结束。
代码示例
下面的代码会生成一个简单的正弦波图,并将其保存为 PNG 文件。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// 定义数据点的数量
#define NUM_POINTS 100
int main() {
// 1. 使用 popen() 启动 gnuplot 进程,并获取文件指针
// "w" 表示我们以写模式打开管道,向 gnuplot 发送命令
FILE *gnuplot_pipe = popen("gnuplot -persistent", "w");
// 检查管道是否成功创建
if (gnuplot_pipe == NULL) {
fprintf(stderr, "Error: Could not open pipe to gnuplot.\n");
return 1;
}
// 2. 向 gnuplot 发送命令
// 设置输出终端为 png,并指定文件名
fprintf(gnuplot_pipe, "set terminal pngcairo enhanced font 'arial,10' size 800,600\n");
fprintf(gnuplot_pipe, "set output 'sine_wave.png'\n");
// 设置图表标题和坐标轴标签
fprintf(gnuplot_pipe, "set title 'Sine Wave Plot from C'\n");
fprintf(gnuplot_pipe, "set xlabel 'x (radians)'\n");
fprintf(gnuplot_pipe, "set ylabel 'sin(x)'\n");
fprintf(gnuplot_pipe, "set grid\n");
// 开始绘制数据,使用 '-' 表示数据将从标准输入读取
fprintf(gnuplot_pipe, "plot '-' with linespoints title 'sin(x)' lw 2 pt 7\n");
// 3. 生成数据并通过管道发送
for (int i = 0; i < NUM_POINTS; i++) {
double x = 2.0 * M_PI * i / NUM_POINTS;
double y = sin(x);
// 直接将数据点打印到管道中
fprintf(gnuplot_pipe, "%f %f\n", x, y);
}
// 发送一个 "e" (end of data) 标记,告诉 gnuplot 数据结束
fprintf(gnuplot_pipe, "e\n");
// 4. 关闭管道,这会向 gnuplot 发送 EOF 信号并等待其结束
pclose(gnuplot_pipe);
printf("Plot generated and saved as 'sine_wave.png'\n");
return 0;
}
如何编译和运行
-
确保已安装 Gnuplot:
- 在 Linux (Ubuntu/Debian):
sudo apt-get install gnuplot - 在 macOS (使用 Homebrew):
brew install gnuplot - 在 Windows: 从 Gnuplot 官网 下载安装。
- 在 Linux (Ubuntu/Debian):
-
编译 C 代码:
(图片来源网络,侵删)- 如果你的代码在
plot.c文件中,并且使用了math.h库,需要链接数学库。 - 在 Linux/macOS 下,使用以下命令:
gcc plot.c -o plot -lm
-lm是关键,它链接了数学库。
- 如果你的代码在
-
运行程序:
./plot
-
查看结果: 运行后,会在同一目录下生成一个名为
sine_wave.png的图片文件。
关键点解释
popen("gnuplot -persistent", "w"):"gnuplot": 调用系统的 gnuplot 命令。"-persistent": 这是一个重要的选项,它告诉 Gnuplot 在绘制完一个图后不要立即退出窗口,这样你可以在同一个会话中发送多条命令(先画一个图,再改变参数画另一个图),如果不用这个选项,pclose()会立即关闭 Gnuplot。"w": 表示“写模式”,我们的 C 程序是向 Gnuplot 写入命令。
fprintf(gnuplot_pipe, ...): 这里的gnuplot_pipe就像一个普通的文件,我们向它写入 Gnuplot 能识别的命令。plot '-' with linespoints ...: 这是 Gnuplot 的一个强大功能。 表示接下来的数据将从标准输入(也就是我们的管道)中读取。with linespoints表示用线段连接数据点,并在每个点上显示标记。fprintf(gnuplot_pipe, "%f %f\n", x, y);: 在循环中,我们将计算出的数据点逐行发送到管道。fprintf(gnuplot_pipe, "e\n");: 当使用plot '-'时,必须发送一个单独的字母e后跟换行符来标记数据流的结束。pclose(gnuplot_pipe): 关闭管道并回收子进程的资源。必须调用,否则会造成资源泄漏。
通过 system() 函数调用
这种方法最简单,但交互性差,通常只用于一次性生成静态图片。
核心思想
使用 system() 函数执行一个完整的 Gnuplot 命令字符串,这个字符串可以包含一个脚本文件名,或者直接包含绘图命令(如果系统支持)。

(图片来源网络,侵删)
代码示例
#include <stdio.h>
#include <stdlib.h>
int main() {
// 方法A: 直接执行一个包含所有命令的字符串 (不推荐,平台兼容性差)
// system("echo 'set terminal png; set output \"test.png\"; plot sin(x)' | gnuplot");
// 方法B: 推荐方式,先生成一个 gnuplot 脚本文件,然后让 system() 执行它
FILE *script_file = fopen("plot_script.gp", "w");
if (script_file == NULL) {
fprintf(stderr, "Error: Could not create gnuplot script file.\n");
return 1;
}
fprintf(script_file, "set terminal pngcairo enhanced font 'arial,10' size 800,600\n");
fprintf(script_file, "set output 'sine_wave_system.png'\n");
fprintf(script_file, "set title 'Sine Wave from system()'\n");
fprintf(script_file, "plot [-2*pi:2*pi] sin(x) title 'sin(x)' with lines lw 2\n");
fclose(script_file);
// 使用 system() 执行 gnuplot,并传入脚本文件名
printf("Executing gnuplot script...\n");
int status = system("gnuplot plot_script.gp");
if (status == -1) {
perror("Error calling system()");
} else {
printf("Plot generated as 'sine_wave_system.png'\n");
// 可选:删除临时脚本文件
// remove("plot_script.gp");
}
return 0;
}
优缺点
- 优点:
代码非常简单,一行就能搞定。
- 缺点:
- 性能差: 每次调用
system()都会创建一个新的 shell 进程和一个新的 Gnuplot 进程,开销很大。 - 交互性差: 无法动态地、实时地发送数据,数据必须预先准备好(例如在文件中)。
- 平台依赖: 直接在命令行拼接命令的方式在不同操作系统上可能有兼容性问题。
- 性能差: 每次调用
使用 Gnuplot-iostream C++ API (高级)
这是一个更高级、更优雅的 C++ 库,但也可以在 C 项目中使用,它提供了流式接口,语法非常直观。
核心思想
包含库头文件,创建一个 Gnuplot 对象,然后使用 C++ 的流操作符 (<<) 来发送命令和数据。
准备工作
你需要先安装这个库,在 Linux 上通常很方便:
sudo apt-get install libgnuplot-iostream-dev
代码示例 (需要用 C++ 编译器编译)
#include iostream // 注意:这里是 C++ 的 iostream
#include gnuplot-iostream.h // 库头文件
#include vector
#includecmath>
int main() {
// 创建一个 Gnuplot 对象
Gnuplot gp;
// 设置输出终端和文件名
gp << "set terminal pngcairo enhanced font 'arial,10' size 800,600\n";
gp << "set output 'sine_wave_cpp.png'\n";
gp << "set title 'Sine Wave from C++ API'\n";
gp << "set xlabel 'x (radians)'\n";
gp << "set ylabel 'sin(x)'\n";
gp << "set grid\n";
// 准备数据
std::vector<std::pair<double, double>> data;
for (int i = 0; i <= 100; ++i) {
double x = 2.0 * M_PI * i / 100.0;
data.push_back(std::make_pair(x, sin(x)));
}
// 绘制数据
// "plot" 是命令,"-' with linespoints title '...' " 是参数
// gp.send() 会发送数据
gp << "plot '-' with linespoints title 'sin(x)' lw 2 pt 7\n";
gp.send(data); // 将 C++ 容器中的数据发送给 gnuplot
std::cout << "Plot generated as 'sine_wave_cpp.png'" << std::endl;
return 0;
}
优缺点
- 优点:
- 语法优雅: 使用流操作符
<<,代码可读性极高。 - 类型安全: 库会帮你处理数据类型转换。
- 功能强大: 能方便地处理 C++ 标准容器(如
std::vector,std::map等)。
- 语法优雅: 使用流操作符
- 缺点:
- 需要额外库: 必须安装
libgnuplot-iostream。 - C++ 专用: 虽然可以混用,但主要设计用于 C++ 环境。
- 需要额外库: 必须安装
总结与建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 管道调用 | 灵活、强大、交互性好、无需额外库 | 代码比 system() 稍复杂 |
强烈推荐,绝大多数需要从程序生成图形的场景,特别是需要动态交互或实时更新的情况。 |
system() 调用 |
极其简单 | 性能差、无交互性、平台依赖 | 快速生成一次性、静态的、不复杂的图形,或作为简单的脚本工具。 |
| Gnuplot-iostream API | 语法优雅、功能强大、类型安全 | 需要安装额外库、C++ 专用 | 在 C++ 项目中,追求代码可维护性和优雅性的开发者。 |
对于 C 语言开发者,方法一(管道调用)是最佳选择,它在功能、性能和易用性之间取得了最好的平衡,是行业标准做法。
