- 郝哥卖瓜:
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
int main() {
std::string name1 = "西瓜";
double price1 = 2.0;
int weight1 = 15;
double total1 = price1 * weight1;
std::cout << "买" << name1 << ","
<< std::left << std::setw(8) << std::fixed << std::setprecision(2) << price1 << "元/斤,"
<< std::setw(8) << std::setfill('0') << weight1 << "斤,"
<< std::fixed << std::setprecision(2) << total1 << "元。"
<< std::endl;
const char* name2 = "大象";
double price2 = 267.1;
int weight2 = 5.4 * 2000;
double total2 = price2 * weight2;
printf("买%s,%-8.2f元/斤,%08d斤,%.2f元。\n",
name2, price2, weight2, total2);
return 0;
}
- 简易计算器:
C++版本:
#include <iostream>
#include <limits>
#include <cmath>
#include <iomanip>
int main() {
double num1, num2;
// operator是关键字所以不要用
char op;
std::cout << "简单计算器 (支持 + - * /)" << std::endl;
// 读取第一个数字
while (true) {
std::cout << "请输入第一个数字: ";
if (std::cin >> num1) {
break;
}
std::cout << "错误:请输入有效的数字!" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// 读取运算符
while (true) {
std::cout << "请输入运算符 (+, -, *, /): ";
std::cin >> op;
if (op == '+' || op == '-' || op == '*' || op == '/') {
break;
}
std::cout << "错误:无效的运算符!请重新输入。" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// 读取第二个数字
while (true) {
std::cout << "请输入第二个数字: ";
if (std::cin >> num2) {
if (op == '/' && std::abs(num2) < 1e-10) {
std::cout << "错误:除数不能为零!请重新输入。" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
break;
}
std::cout << "错误:请输入有效的数字!" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// 计算结果
std::cout << "结果: ";
switch(op) {
case '+':
std::cout << std::fixed << std::setprecision(2) << num1 + num2 << std::endl;
break;
case '-':
std::cout << std::fixed << std::setprecision(2) << num1 - num2 << std::endl;
break;
case '*':
std::cout << std::fixed << std::setprecision(2) << num1 * num2 << std::endl;
break;
case '/':
std::cout << std::fixed << std::setprecision(2) << num1 / num2 << std::endl;
break;
}
return 0;
}
这个版本只会在出错的时候清空缓冲区,所以,读者如果一开始就输入 60/55,也能得到结果。交互如下:
简单计算器 (支持 + - * /)
请输入第一个数字: 60/55
请输入运算符 (+, -, *, /): 请输入第二个数字: 结果: 1.09
分析易知,读取第一个数字的时候,std::cin 会一直读取数字一直到空格或者非数字,然后开始读取运算符(一个字符),郑海缓冲区剩下的第一个字符是 /,所以读取也成功。最后读取 55\n,然后丢掉 \n,所以程序正常运行。
C 版本:
#include <stdio.h>
#include <math.h>
int main() {
double num1, num2;
char op;
int result;
printf("简单计算器 (支持 + - * /)\n");
// 读取第一个数字
while (1) {
printf("请输入第一个数字: ");
result = scanf("%lf", &num1);
if (result == 1) {
break;
}
printf("错误:请输入有效的数字!\n");
// 清除输入缓冲区
while (getchar() != '\n');
}
// 读取运算符
while (1) {
printf("请输入运算符 (+, -, *, /): ");
// 清除缓冲区中的换行符
while (getchar() != '\n');
result = scanf("%c", &op);
if (result == 1 && (op == '+' || op == '-' || op == '*' || op == '/')) {
break;
}
printf("错误:无效的运算符!请重新输入。\n");
// 清除输入缓冲区
while (getchar() != '\n');
}
// 读取第二个数字
while (1) {
printf("请输入第二个数字: ");
result = scanf("%lf", &num2);
if (result == 1) {
if (op == '/' && fabs(num2) < 1e-10) {
printf("错误:除数不能为零!请重新输入。\n");
// 清除输入缓冲区
while (getchar() != '\n');
continue;
}
break;
}
printf("错误:请输入有效的数字!\n");
// 清除输入缓冲区
while (getchar() != '\n');
}
// 计算结果
printf("结果: ");
switch(op) {
case '+':
printf("%.2f\n", num1 + num2);
break;
case '-':
printf("%.2f\n", num1 - num2);
break;
case '*':
printf("%.2f\n", num1 * num2);
break;
case '/':
printf("%.2f\n", num1 / num2);
break;
}
return 0;
}
这个版本,因为 scanf 在使用 %c 时会无条件读取下一个字符(包括空格、换行符等),所以需要先用 while (getchar() != '\n') 来清除缓冲区中的换行符,否则空格、换行符会被识别为运算符的输入而必然报错一次。而 std::cin 没有这个问题,因为 std::cin 在读取字符时会自动跳过前导的空白字符。
因此这个版本一次性输入 60/55,只会读取 60。当然,继续一次性输入 /55 是可以正常运行。
-
首先你会收到警告(Windows 系统的 gcc 不会):
test.c: In function ‘main’: test.c:10:12: warning: format not a string literal and no format arguments [-Wformat-security] 10 | printf(str); | ^~~如果你硬要运行,就会惊喜地发现:
%s 段错误 (核心已转储)或者 Windows 下:
%s 纵? ---> 内存中读取的数据当用户输入
"%s"时,printf会把它当作格式化字符串来解析,尝试从栈上读取一个字符串指针并打印,很可能导致程序崩溃或泄露内存数据。真的有黑客通过类似的方式非法读取内存数据。如果需要直接输出,可以使用puts(str);。 -
(跑了遍 DeepSeek,说的比我全面,就它了吧)
1. 银行卡插入阶段
异常情况:
-
插入非银行卡(会员卡、身份证等)
-
插入损坏的银行卡(磁条损坏、芯片损坏)
-
插入已挂失/冻结的银行卡
防范措施:
-
磁条/芯片读取验证机制
-
与银行系统实时验证卡状态
-
物理结构设计防止插入非标准卡片
-
提示 “无法识别卡片,请取出”
2. 密码输入阶段
异常情况:
-
输入非 6 位数字(过长、过短、含字母)
-
3 次密码错误
-
输入超时(60 秒内未完成)
-
连续尝试破解密码
防范措施:
-
输入框只允许数字输入,自动截断为 6 位
-
3 次错误后吞卡并提示联系银行
-
倒计时显示剩余时间
-
异常多次尝试触发安全警报
3. 操作选择阶段
异常情况:
-
选择不存在的操作选项(如按 5、6 等)
-
选择后长时间不操作
-
连续快速切换选项
防范措施:
-
只显示有效选项(1-4),其他输入无效
-
操作选择后启动二级倒计时
-
异常操作频率检测,可能触发安全保护
4. 金额输入阶段
异常情况:
-
输入非数字字符
-
输入金额为 0 或负数
-
超过单笔限额 5000 元
-
取款金额非 100 的整数倍
-
存款金额包含不可识别纸币
-
余额不足(取款、转账时)
防范措施:
-
数字输入验证,自动过滤非数字字符
-
最小值验证(≥100)
-
最大值验证(≤5000)
-
取款金额必须为 100 的倍数校验
-
实时余额检查,提前提示不足
-
存款口纸币识别技术验证
5. 交易确认阶段
异常情况:
-
长时间不确认
-
反复取消重试
-
确认后突然断电/断网
防范措施:
-
确认倒计时(10-15 秒)
-
交易次数限制防止重复操作
-
交易事务机制,确保数据一致性
-
应急电源和网络冗余
6. 系统级异常
异常情况:
-
网络连接中断
-
硬件故障(读卡器、出钞口、存款口)
-
电源故障
-
恶意破坏或盗窃尝试
防范措施:
-
离线交易缓存机制
-
硬件自检和故障报警
-
UPS 备用电源
-
安全摄像头和震动传感器
-
现金箱安全锁机制
作者注:关于 6.,物理防护 的确是 防御的重要的一环。这里我们知道就好,还是将主要注意力放在代码上吧。
-