快速上手
本文为《C 与 指针读书笔记》,感兴趣的读者可以去看原书。
上一节我们了解了如何配置 C 语言的开发环境,这一节我们通过一个简单的例子来快速了解 C 语言。
通过一段代码快速了解 C 语言
这个程序从标准输入读取输入行,然后将每一行输入以及行中的某些部分输出到标准输出。第一个输入是一组列号,以负数结束。列号是成对出现的,指定了要打印的输入行的列范围。例如,0 3 10 12 -1 表示只打印列 0 到 3 和列 10 到 12。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_COLS 20 /* max # of columns to process */
#define MAX_INPUT 1000 /* max len of input & output lines */
int read_column_numbers(int columns[], int max);
void rearrange(char *output, char const *input, int n_columns, int const columns[]);
int main(void)
{
int n_columns; /* # of columns to process */
int columns[MAX_COLS]; /* the columns to process */
char input[MAX_INPUT]; /* array for input line */
char output[MAX_INPUT]; /* array for output line */
/*
** 读取输入的列编号
*/
n_columns = read_column_numbers(columns, MAX_COLS);
/*
** 读取、处理并打印剩余的输入行
** gets函数是一个C语言标准库函数,用于从标准输入(通常是键盘)读取一行文本,直到遇到换行符('\n')或文件结束符。
** 它将读取的字符(不包括换行符)存储到其参数所指向的字符数组中,并在最后添加一个字符串结束符'\0'。
** gets函数在读到文件结束符(EOF)时会返回NULL,因此这个循环会一直运行,直到用户输入EOF标志(在Windows中通常是Ctrl+Z,而在Unix/Linux系统中是Ctrl+D)。
*/
while (gets(input) != NULL)
{
printf("Original input : %s\n", input);
rearrange(output, input, n_columns, columns);
printf("Rearranged line: %s\n", output);
}
return EXIT_SUCCESS;
}
/*
** 读取列编号列表,忽略超出最大值的列编号
*/
int read_column_numbers(int columns[], int max)
{
int num = 0;
int ch;
/*
** 获取列编号,遇到EOF或者小于0的数字时停止,循环结束的条件具体包括以下三个
** 1. 输入的列编号数量达到最大值
** 2. scanf(%d", &columns[num])的返回值不为1,即不再读取到整数
** 3. 读取到的整数小于0
*/
while (num < max && scanf("%d", &columns[num]) == 1 && columns[num] >= 0)
num += 1;
/*
** 确保列编号是成对出现的
*/
if (num % 2 != 0)
{
puts("Last column number is not paired.");
exit(EXIT_FAILURE);
}
/*
** 丢弃最后一个数字后面的字符
*/
while ((ch = getchar()) != EOF && ch != '\n')
;
return num;
}
/*
** 通过连接指定列的字符来处理一行输入。然后输出行以NUL结尾。
** 当数组名作为实参时,传给函数的实际上是一个指向数组起始位置的指针,也就是数组在内存中的地址
** 因此当不希望原数据不被改别时,需要添加 const 修饰
*/
void rearrange(char *output, char const *input, int n_columns, int const columns[])
{
int col; /* subscript for columns array */
int output_col; /* output column counter */
int len; /* length of input line */
len = strlen(input);
output_col = 0;
/*
** 处理每一对列编号
*/
for (col = 0; col < n_columns; col += 2)
{
int nchars = columns[col + 1] - columns[col] + 1;
/*
** 如果输入行不够长或者输出数组已满,我们就结束了。
*/
if (columns[col] >= len || output_col == MAX_INPUT - 1)
break;
/*
** 如果输出数组中没有足够的空间,只复制能放下的部分。
*/
if (output_col + nchars > MAX_INPUT - 1)
nchars = MAX_INPUT - output_col - 1;
/*
** 复制相关数据
*/
strncpy(output + output_col, input + columns[col], nchars);
output_col += nchars;
}
/*
** 添加NUL字符
** 在output字符串的output_col位置处插入一个字符串终止符(null terminator),用于标记字符串的结束。在C语言中,字符串是以'\0'(ASCII码为0的字符)结束的字符数组。这个终止符是必须的,因为它告诉字符** 串处理函数(比如printf、strlen等)字符串在哪里结束。
*/
output[output_col] = '\0';
}
通过上面的例子,我们可以快速了解 C 语言的输入,输出,函数,传参,循环等知识点。
所有的 C 程序必须有一个 main 函数,它是程序执行的起点。函数的标量参数通过传值的方式进行传递,而数组名参数则具有传址调用的语义。字符串是一串由 NULL 字节结尾的字符,并且有一组库函数以不同的方式专门用于操纵字符串。printf 函数执行格式化输出,scanf 函数用于格式化输入,getchar 和putchar 分别执行非格式化字符的输入和输出。if 和 while 语句在 C 语言中的用途跟它们在其他语言中的用途差不太多。
编程练习
练习 1
“Hello world!”程序常常是C编程新手所编写的第1个程序。它在标准输出中打印Hello world!,并在后面添加一个换行符。
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
练习 2
编写一个程序,从标准输入读取几行输入。每行输入都要打印到标准输出上,前面要加上行号。在编写这个程序时要试图让程序能够处理的输入行的长度没有限制。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 1024 // 定义缓冲区大小
int main() {
char buffer[BUFFER_SIZE];
int line_number = 1;
printf("请输入文本(CTRL+D 或 CTRL+Z 以结束):\n");
// 读取输入直到EOF
while (fgets(buffer, BUFFER_SIZE, stdin)) {
// 打印行号和输入行
printf("%d: %s", line_number++, buffer);
// 如果最后一个字符不是换行符,说明可能还有更多内容未读取完
if (buffer[strlen(buffer) - 1] != '\n') {
int ch;
// 消耗剩余的字符直到遇到换行符,以完成这一行的读取
while ((ch = getchar()) != '\n' && ch != EOF);
if (ch == '\n') {
printf("\n...这一行超过了缓冲区长度,仅显示部分内容\n");
}
}
}
return 0;
}
练习 3
编写一个程序,从标准输入读取一些字符,并把它们写到标准输出上。它同时应该计算checksum值,并写在字符的后面。
checksum(检验和)用一个singed char类型的变量进行计算,它初始为-1。当每个字符从标准输入读取时,它的值就被加到checksum中。如果checksum变量产出了溢出,那么这些溢出就会被忽略。当所有的字符均被写入后,程序以十进制整数的形式打印出checksum的值,它有可能是负值。注意在checksum后面要添加一个换行符。
#include <stdio.h>
int main() {
signed char checksum = -1; // checksum的初始值
int ch; // 用于存储输入的字符
printf("请输入字符,回车结束输入:\n");
// 使用getchar()从标准输入读取字符直到EOF
while ((ch = getchar()) != EOF && ch != '\n') {
checksum += ch; // 将字符的值加到checksum中
}
checksum += '\n';
// 打印checksum的值
printf("\nChecksum: %d\n", checksum);
return 0;
}
练习 4
编写一个程序,一行行地读取输入行,直至到达文件尾。算出每行输入行的长度,然后把最长的那行打印出来。为了简单起见,你可以假定所有的输入行均不超过1000个字符。
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000 // 假定的最大输入行长度
int main() {
int len; // 当前行长度
int max; // 目前为止发现的最长行的长度
char line[MAX_LINE_LENGTH]; // 当前的输入行
char longest[MAX_LINE_LENGTH]; // 用于保存最长的行
max = 0;
while (fgets(line, MAX_LINE_LENGTH, stdin) != NULL) {
len = strlen(line);
// 如果当前行比之前记录的最长行要长,则更新最长行和最长长度
if (len > max) {
max = len;
strcpy(longest, line);
}
}
if (max > 0) { // 如果有行被读取,则存在最长行
printf("The longest line is:\n%s", longest);
printf("The length of the longest line is: %d\n", max);
}
return 0;
}