1. 顺序结构程序设计基础
在C语言中,顺序结构是最基础也是最常用的程序控制结构。它就像做菜时的步骤清单——从准备食材到装盘上桌,每一步都按照既定的顺序严格执行。对于初学者来说,掌握顺序结构是打开编程大门的第一把钥匙。
顺序结构的核心特征就是"从上到下,逐行执行"。程序从main()函数的第一条语句开始,像流水线一样依次执行每一条指令,直到遇到return语句或函数结束。这种线性的执行方式虽然简单,但构成了所有复杂程序的基础骨架。
一个典型的顺序结构程序包含三个基本部分:
- 数据输入:获取程序运行所需的原始数据
- 数据处理:对输入的数据进行计算或逻辑操作
- 结果输出:将处理后的数据显示或保存
这三个部分就像工厂的生产线:原材料输入→加工处理→成品输出。在实际编程中,我们使用标准输入输出函数来实现数据的输入和输出,而计算部分则通过各种运算符和语句来完成。
2. printf()函数深度解析
2.1 printf()的基本用法
printf()是C语言中最常用的输出函数,它的名字来源于"print formatted"(格式化打印)。这个函数就像是一个智能打印机,能够按照我们指定的格式输出各种类型的数据。
printf()有两种基本格式:
- 原样输出字符串:
printf("Hello World!"); - 格式化输出变量值:
printf("Value is %d", x);
第一种方式简单直接,适合输出固定的提示信息。第二种方式则强大得多,它允许我们在字符串中插入占位符,然后在输出时用变量的值替换这些占位符。
2.2 格式化占位符详解
printf()的强大之处在于它的格式化占位符系统。这些占位符就像模板中的空白处,程序运行时会用实际的值来填充它们。以下是常用的占位符及其含义:
| 占位符 | 数据类型 | 说明 |
|---|---|---|
| %d | int | 十进制有符号整数 |
| %u | unsigned int | 十进制无符号整数 |
| %f | float/double | 浮点数,默认6位小数 |
| %c | char | 单个字符 |
| %s | char数组(字符串) | 字符串 |
| %x/%X | unsigned int | 十六进制无符号整数(小写/大写) |
| %o | unsigned int | 八进制无符号整数 |
| %e/%E | float/double | 科学计数法表示(小写e/大写E) |
| %% | - | 输出百分号%本身 |
在实际使用中,我们还可以在%和字母之间插入一些修饰符来控制输出的格式:
- 数字:指定最小字段宽度,如
%5d表示至少占5个字符宽度 - .数字:指定小数位数,如
%.2f表示保留2位小数 - -:左对齐,默认是右对齐
- +:显示正数的+号
- 0:用0填充空白处
2.3 printf()的高级技巧
2.3.1 控制输出精度
对于浮点数输出,我们经常需要控制小数位数。这时可以使用.n的格式:
c复制double pi = 3.1415926535;
printf("Pi is approximately %.2f\n", pi); // 输出:Pi is approximately 3.14
printf("Pi is approximately %.4f\n", pi); // 输出:Pi is approximately 3.1416
注意:printf()会自动进行四舍五入。
2.3.2 对齐和填充
在制作表格类输出时,对齐和填充非常有用:
c复制printf("|%-10s|%10d|\n", "Apple", 100); // 左对齐,宽度10
printf("|%10s|%-10d|\n", "Orange", 200); // 右对齐,宽度10
printf("|%010d|\n", 123); // 用0填充空白
输出结果:
code复制|Apple | 100|
| Orange|200 |
|0000000123|
2.3.3 输出特殊字符
在字符串中,某些特殊字符需要用转义序列表示:
\n:换行\t:制表符\\:反斜杠\":双引号\':单引号
例如:
c复制printf("This is a \"quote\" and this is a new line:\nSecond line");
3. scanf()函数全面掌握
3.1 scanf()的基本用法
scanf()是printf()的输入版,用于从标准输入(通常是键盘)读取格式化数据。它的基本格式是:
c复制scanf("格式化字符串", 变量地址列表);
与printf()类似,scanf()也使用占位符来指定要读取的数据类型。但有一个关键区别:scanf()需要变量的地址,而不是变量本身。这是通过取地址运算符&实现的。
基本示例:
c复制int age;
printf("Please enter your age: ");
scanf("%d", &age); // 注意&符号
printf("You are %d years old.\n", age);
3.2 scanf()的常见问题与解决方案
3.2.1 缓冲区问题
scanf()的一个常见问题是输入缓冲区中残留的换行符会影响后续的读取。特别是当混合使用%d和%c时:
c复制int num;
char ch;
printf("Enter a number: ");
scanf("%d", &num);
printf("Enter a character: ");
scanf("%c", &ch); // 这里会读取之前输入数字后的回车符
解决方法是在%c前加一个空格:
c复制scanf(" %c", &ch); // 注意%c前的空格
这个空格会跳过所有的空白字符(空格、制表符、换行符等)。
3.2.2 输入验证
scanf()返回成功读取的项目数。我们可以利用这一点进行输入验证:
c复制int num;
printf("Enter an integer: ");
while(scanf("%d", &num) != 1) {
printf("Invalid input. Please enter an integer: ");
while(getchar() != '\n'); // 清空输入缓冲区
}
3.2.3 混合输入时的注意事项
当需要混合输入数字和字符时,要特别注意格式字符串的设计:
c复制int id;
char category;
printf("Enter product ID and category (e.g. 100A): ");
scanf("%d%c", &id, &category); // 正确方式
3.3 scanf()的高级用法
3.3.1 使用扫描集
scanf()支持使用扫描集来限制可接受的输入字符:
c复制char name[50];
printf("Enter your name (letters only): ");
scanf("%49[a-zA-Z ]", name); // 只接受字母和空格,最多49个字符
3.3.2 跳过特定输入
可以在格式字符串中使用*来跳过某些输入:
c复制int day, year;
char month[10];
printf("Enter date (dd-mm-yyyy): ");
scanf("%d-%[^-]-%d", &day, month, &year); // 跳过中间的-
3.3.3 限制输入宽度
为防止缓冲区溢出,应该总是限制字符串输入的最大长度:
c复制char city[20];
printf("Enter your city: ");
scanf("%19s", city); // 最多读取19个字符,留1个给'\0'
4. 字符输入输出的特殊函数
4.1 putchar()函数
putchar()是专门用于输出单个字符的函数,比printf()更简单高效:
c复制putchar('A'); // 输出大写字母A
putchar('\n'); // 输出换行符
putchar()的一个常见用途是逐个字符输出字符串:
c复制char str[] = "Hello";
for(int i=0; str[i]!='\0'; i++) {
putchar(str[i]);
}
4.2 getchar()函数
getchar()用于从标准输入读取单个字符,它不需要任何参数,返回读取的字符:
c复制char ch;
printf("Press any key: ");
ch = getchar(); // 等待用户输入一个字符
printf("You pressed: ");
putchar(ch);
getchar()常用于简单的菜单选择和输入确认:
c复制printf("Continue? (y/n): ");
if(getchar() == 'y') {
// 继续执行
}
4.3 实用技巧:清空输入缓冲区
在输入处理中,经常需要清空输入缓冲区中的剩余内容。可以使用以下方法:
c复制void clear_input_buffer() {
while(getchar() != '\n'); // 读取直到遇到换行符
}
这个函数在混合使用scanf()和getchar()时特别有用。
5. 综合应用实例
5.1 简单计算器程序
c复制#include <stdio.h>
int main() {
double num1, num2;
char op;
printf("Simple Calculator\n");
printf("Enter expression (e.g. 2 + 3): ");
if(scanf("%lf %c %lf", &num1, &op, &num2) != 3) {
printf("Invalid input format!\n");
return 1;
}
switch(op) {
case '+':
printf("Result: %.2f\n", num1 + num2);
break;
case '-':
printf("Result: %.2f\n", num1 - num2);
break;
case '*':
printf("Result: %.2f\n", num1 * num2);
break;
case '/':
if(num2 == 0) {
printf("Error: Division by zero!\n");
} else {
printf("Result: %.2f\n", num1 / num2);
}
break;
default:
printf("Invalid operator!\n");
}
return 0;
}
5.2 学生信息录入系统
c复制#include <stdio.h>
int main() {
char name[50];
int age;
float gpa;
char grade;
printf("Student Information System\n");
printf("-------------------------\n");
printf("Enter student name: ");
scanf("%49[^\n]", name); // 读取一行,直到遇到换行符
printf("Enter age: ");
scanf("%d", &age);
printf("Enter GPA: ");
scanf("%f", &gpa);
printf("Enter letter grade: ");
scanf(" %c", &grade); // 注意空格,跳过之前的换行符
printf("\nStudent Record:\n");
printf("Name: %s\n", name);
printf("Age: %d\n", age);
printf("GPA: %.2f\n", gpa);
printf("Grade: %c\n", grade);
return 0;
}
5.3 格式化表格输出
c复制#include <stdio.h>
int main() {
printf("%-20s%-10s%-10s%-10s\n", "Product", "Price", "Qty", "Total");
printf("--------------------------------------------\n");
printf("%-20s$%-9.2f%-10d$%-9.2f\n", "Apple", 0.99, 5, 0.99*5);
printf("%-20s$%-9.2f%-10d$%-9.2f\n", "Orange", 1.25, 3, 1.25*3);
printf("%-20s$%-9.2f%-10d$%-9.2f\n", "Banana", 0.50, 10, 0.50*10);
printf("--------------------------------------------\n");
printf("%-20s$%-9.2f\n", "Subtotal", 0.99*5 + 1.25*3 + 0.50*10);
return 0;
}
在实际编程中,我发现很多初学者容易混淆printf()和scanf()的格式字符串。记住这个简单的规则:printf()的格式字符串决定如何显示数据,而scanf()的格式字符串决定如何解析输入数据。两者虽然相似,但细节上有重要区别。
