C语言用户层的输出流
✅ 1. 使用 C 标准库的 printf(stdout)
1 2 3 4 5 6
| #include <stdio.h>
int main() { printf("Hello from C stdout (printf)!\n"); return 0; }
|
✅ 2.示例代码:C 标准输入输出流使用
1. 标准 C 库标准输出流函数(跨平台通用)
这些函数是 C 标准库的一部分,在 Linux 和 Windows 上均可使用:
printf / fprintf
1 2 3
| printf("Hello, World!\n"); fprintf(stdout, "Hello, stdout!\n"); fprintf(stderr, "Error: something wrong\n");
|
puts / fputs
1 2
| puts("Auto-adds newline"); fputs("No newline", stdout);
|
putchar / fputc
1 2
| putchar('A'); fputc('B', stderr);
|
2.下面是一个完整的 C 语言示例程序,展示如何使用标准 I/O 流接口(stdin、stdout、stderr)以及常见函数:
fgets、fscanf、getchar(从标准输入读取)
printf、fprintf、puts、putchar(输出到标准输出或标准错误)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <stdio.h> #include <string.h>
int main() { char name[100]; int age;
printf("Enter your name: "); fgets(name, sizeof(name), stdin);
name[strcspn(name, "\n")] = '\0';
fprintf(stdout, "Hello, %s!\n", name);
printf("Enter your age: "); fscanf(stdin, "%d", &age);
puts("You entered:");
printf("Name: "); for (size_t i = 0; i < strlen(name); i++) { putchar(name[i]); } putchar('\n');
if (age < 0 || age > 150) { fprintf(stderr, "Invalid age entered!\n"); } else { printf("Age: %d\n", age); }
printf("Press Enter to exit..."); getchar(); getchar();
return 0; }
|
📌 使用说明
| 函数 |
功能说明 |
fgets() |
从 stdin 读取一行文本(含空格) |
fscanf() |
从 stdin 按格式读取数据 |
getchar() |
读取一个字符 |
printf() |
向 stdout 输出格式化内容 |
fprintf() |
向指定流输出格式化内容,如 stderr |
puts() |
向 stdout 输出一行文本(自动换行) |
putchar() |
输出一个字符 |
✅3.使用文件指针输出到文件或者控制台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdio.h>
int main() { FILE *fp = fopen("log.txt", "w");
if (fp) { fprintf(fp, "Logging some info...\n"); fflush(fp); fclose(fp); }
printf("Please enter a character: "); int c = fgetc(stdin); fputc(c, stdout);
return 0; }
|
✅4.使用文件指针指向标准输出流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <windows.h>
int main() {
FILE* p = stdout; const char* str = "ddd\n"; fputs(str, p);
int num = 11; wchar_t string[100]; swprintf(string, 2, L"%d\n", num);
fprintf(stdout, "hello \n"); fprintf(stderr, "World!\n");
return 0;
}
|
windows程序用户层的输出流
✅ 1. 使用 CreateFile("CONOUT$") 直接打开控制台输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <windows.h> #include <stdio.h>
int main() { HANDLE hConsoleOut = CreateFileA( "CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
if (hConsoleOut == INVALID_HANDLE_VALUE) { printf("无法打开 CONOUT$\n"); return 1; }
const char* msg = "Hello from CreateFile to console output!\n"; DWORD written; WriteFile(hConsoleOut, msg, (DWORD)strlen(msg), &written, NULL);
CloseHandle(hConsoleOut); return 0; }
|
也可以类似地打开 CONIN$,然后用 ReadFile 从控制台读取输入。
一些常见的特殊设备名
| 设备名 |
说明 |
CON |
控制台(等价于 CONIN$ + CONOUT$,但行为不一致) |
CONIN$ |
控制台输入(键盘) |
CONOUT$ |
控制台输出(屏幕) |
NUL |
空设备,相当于 /dev/null |
PRN |
打印机 |
COM1~COM9 |
串口设备 |
LPT1~LPT9 |
并口设备 |
如果重定向了 stdout/stderr,或者是 GUI 应用没有默认控制台窗口时,用 CreateFile("CONOUT$", ...) 也可以获取控制台输出句柄进行直接写入,非常常用。
✅ 2. 使用 GetStdHandle(STD_OUTPUT_HANDLE)
1 2 3 4 5 6 7 8 9 10
| #include <windows.h> #include <stdio.h>
int main() { HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); const char* msg = "Hello from GetStdHandle!\n"; DWORD written; WriteFile(hStdOut, msg, (DWORD)strlen(msg), &written, NULL); return 0; }
|
✅ 4. 使用 freopen 重定向 stdout 到控制台
这个适用于GUI 应用或重定向后恢复标准输出。
1 2 3 4 5 6 7 8 9 10 11
| #include <stdio.h> #include <windows.h>
int main() { AllocConsole();
freopen("CONOUT$", "w", stdout); printf("Hello from freopen to CONOUT$!\n");
return 0; }
|
✅ 5. 使用 WriteConsole
1 2 3 4 5 6 7 8 9 10 11
| #include <windows.h> #include <stdio.h>
int main() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); const wchar_t* msg = L"Hello from WriteConsole!\n"; DWORD written;
WriteConsoleW(hConsole, msg, wcslen(msg), &written, NULL); return 0; }
|
✅ 6.示例:Windows 控制台 I/O 专用函数演示
以下是使用 Windows 控制台 API 的一组完整示例,展示如何使用:
ReadConsole:从控制台读取输入
WriteConsole:写入控制台文本
WriteConsoleOutput:向控制台缓冲区写入字符信息(低级操作)
SetConsoleCursorPosition:设置光标位置
SetConsoleTextAttribute:设置输出文字颜色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <windows.h> #include <stdio.h>
int main() { HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hOutput, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
const wchar_t* msg = L"Hello from WriteConsole (Green Text)\n"; DWORD written; WriteConsoleW(hOutput, msg, wcslen(msg), &written, NULL);
COORD coord = { 20, 10 }; SetConsoleCursorPosition(hOutput, coord);
const wchar_t* msg2 = L"Cursor Moved Here!"; WriteConsoleW(hOutput, msg2, wcslen(msg2), &written, NULL);
wchar_t buffer[128]; DWORD read; WriteConsoleW(hOutput, L"\nEnter some text: ", 19, &written, NULL); ReadConsoleW(hInput, buffer, 128, &read, NULL); buffer[read - 2] = L'\0';
WriteConsoleW(hOutput, L"You typed: ", 11, &written, NULL); WriteConsoleW(hOutput, buffer, wcslen(buffer), &written, NULL); WriteConsoleW(hOutput, L"\n", 1, &written, NULL);
CHAR_INFO ci[4]; for (int i = 0; i < 4; i++) { ci[i].Char.AsciiChar = 'A' + i; ci[i].Attributes = FOREGROUND_RED | FOREGROUND_INTENSITY; }
COORD bufferSize = { 2, 2 }; COORD bufferCoord = { 0, 0 }; SMALL_RECT writeRegion = { 30, 12, 31, 13 };
WriteConsoleOutputA(hOutput, ci, bufferSize, bufferCoord, &writeRegion);
return 0; }
|
Linux程序用户层的输出流
1. 低级 I/O 函数(Unix/Linux 系统调用)
直接使用文件描述符(fd),性能更高但无缓冲:
write
1 2 3
| #include <unistd.h> write(STDOUT_FILENO, "Hello\n", 6); write(STDERR_FILENO, "Error\n", 6);
|
2. 格式化输出扩展
3. 控制台专用操作(Linux 终端控制)
类似 Windows 的控制台 API,Linux 通过终端控制实现高级功能:
4. 示例:Linux 终端控制完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h>
int main() { printf("Standard printf\n"); fprintf(stderr, "Error message\n");
write(STDOUT_FILENO, "Low-level write\n", 16);
dprintf(STDOUT_FILENO, "dprintf: %d\n", 123);
printf("\033[32mGreen Text\033[0m\n"); printf("\033[2;5HCursor at (2,5)\n");
struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); printf("Terminal size: %d rows x %d cols\n", w.ws_row, w.ws_col);
return 0; }
|
对比 Windows 与 Linux 输出函数
| 功能 |
Windows API |
Linux 等效方法 |
| 基础输出 |
WriteConsole |
write / printf |
| 格式化输出 |
wsprintf |
printf / dprintf |
| 控制台颜色 |
SetConsoleTextAttribute |
ANSI 转义序列(\033[31m) |
| 光标位置 |
SetConsoleCursorPosition |
ANSI 转义序列(\033[x;yH) |
| 低级缓冲区操作 |
WriteConsoleOutput |
ioctl + ANSI 控制 |
Linux 更依赖标准 C 库和 POSIX 系统调用,而 Windows 使用专用的控制台 API(如 WriteConsole)。
linux程序的标准输入流
在 Linux 系统中,标准输入流(stdin) 是程序默认的输入来源(通常是键盘输入或管道/重定向内容)。以下是 Linux 中处理标准输入流的常用函数和方法:
1. 标准 C 库函数(跨平台通用)
适用于大多数场景,具有缓冲机制:
① scanf / fscanf
1 2 3 4
| #include <stdio.h> int num; scanf("%d", &num); // 从 stdin 读取整数 fscanf(stdin, "%d", &num); // 等价于 scanf
|
- 缺点:不安全(易缓冲区溢出),推荐使用
fgets + sscanf 组合。
② getchar / fgetc
1 2
| char c = getchar(); // 从 stdin 读取单个字符 char c2 = fgetc(stdin); // 等价于 getchar
|
③ gets (危险!已废弃) / fgets
1 2
| char buf[100]; fgets(buf, sizeof(buf), stdin); // 安全读取一行(包括换行符)
|
④ getline (POSIX 标准)
动态分配内存读取任意长度行:
1 2 3 4
| char *line = NULL; size_t len = 0; ssize_t read = getline(&line, &len, stdin); // 自动扩容缓冲区 free(line); // 需手动释放内存
|
2. 低级 I/O 函数(系统调用)
直接使用文件描述符(fd),无缓冲,适合高性能场景:
① read
1 2 3
| #include <unistd.h> char buf[100]; ssize_t bytes = read(STDIN_FILENO, buf, sizeof(buf)); // 从 stdin 读取
|
STDIN_FILENO 是标准输入的文件描述符(值为 0)。
- 返回实际读取的字节数,可能小于请求的字节数(如遇到 EOF)。
② 非阻塞输入(fcntl)
1 2 3 4
| #include <fcntl.h> fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); char buf[100]; ssize_t bytes = read(STDIN_FILENO, buf, sizeof(buf));
|
3. 终端控制(Linux 特有)
① 禁用行缓冲(termios)
默认情况下,终端会启用行缓冲(需按回车键才提交输入)。以下代码禁用缓冲:
1 2 3 4 5 6 7 8 9 10
| #include <termios.h> struct termios old, new; tcgetattr(STDIN_FILENO, &old); new = old; new.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &new);
char c; read(STDIN_FILENO, &c, 1); tcsetattr(STDIN_FILENO, TCSANOW, &old);
|
② 获取终端输入事件
通过 ioctl 监听终端事件(如窗口大小变化):
1 2 3
| #include <sys/ioctl.h> struct winsize w; ioctl(STDIN_FILENO, TIOCGWINSZ, &w);
|
4. 示例代码
示例 1:安全读取用户输入
1 2 3 4 5 6 7 8
| #include <stdio.h> int main() { char buf[100]; printf("Enter a line: "); fgets(buf, sizeof(buf), stdin); printf("You entered: %s", buf); return 0; }
|
示例 2:非阻塞输入检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <unistd.h> #include <fcntl.h> int main() { fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); char c; while (1) { ssize_t bytes = read(STDIN_FILENO, &c, 1); if (bytes > 0) { printf("Got: %c\n", c); if (c == 'q') break; } usleep(100000); } return 0; }
|
示例 3:逐字符读取(禁用行缓冲)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <termios.h> #include <unistd.h> int main() { struct termios old, new; tcgetattr(STDIN_FILENO, &old); new = old; new.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &new);
printf("Press any key (q to quit): "); char c; while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') { printf("You pressed: %c\n", c); }
tcsetattr(STDIN_FILENO, TCSANOW, &old); return 0; }
|
5. 对比 Windows 与 Linux 输入函数
| 功能 |
Windows API |
Linux 等效方法 |
| 基础输入 |
ReadConsole |
read(STDIN_FILENO, ...) |
| 格式化输入 |
scanf / fscanf |
scanf / fgets + sscanf |
| 逐字符读取 |
_getch |
termios 禁用缓冲 + read |
| 非阻塞输入 |
PeekConsoleInput |
fcntl + O_NONBLOCK |
总结
- 通用场景:优先使用
fgets + sscanf 或 getline。
- 高性能/底层控制:用
read 或 termios 调整终端模式。
- 非阻塞输入:结合
fcntl 和 O_NONBLOCK 标志。
- 终端控制:
ioctl 和 ANSI 转义序列可实现复杂交互(如游戏、CLI 工具)。