[UVa1586] 关于用getchar与scanf_s解决问题

本文介绍了一种使用C++解析化学分子式并计算其摩尔质量的方法。通过两种不同的输入方式(getchar和scanf),实现了对分子式的有效解析,并讨论了不同输入方法对程序的影响。

根据题目思路很明确,就是要分辨出分子式的字母和数字。

当判断为字母时,将字母缓存,计算现在的分子式,将数字缓存清空。

当判断为数字时,将数字缓存*10再加上新获取的数字。

当分子式结束时,判断最后一个字符进行上述的步骤。

下面是代码:

#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{
	const char rev[]= "CHON0123456789";
	const double num[] = { 12.01, 1.008, 16.00, 14.01 };
	int a,b;
	cin >> a;
	while ((b = getchar()) != EOF && b != '\n');//清空缓存区
	while (a--)
	{
		double tempc = 0; //缓存当前字母的分子量
		int tempi=0;//缓存数字
		int c=0;
		double res=0.0;
		while ((c = getchar()) != EOF && c != '\n')
		{
			int i;
			for ( i = 0; c != rev[i]; i++);//获取字符在数组的位置
			//cout << i << endl;
			if (i < 4)//遇到字母时,就要统计一下分子量,并缓存当前字母的分子量并清零数字缓存
			{
				if (tempi) res += tempi*tempc;
				else res += tempc;
				tempc = num[i];
				tempi = 0;
				//cout << res << endl;
			}
			else//遇到数字时,要将数字存入tempi中,再读取字符
			{
				tempi = tempi * 10 + (c - '0');  
				continue;
			}
		}
		if (tempi) res += tempi*tempc;//如果是以数字结尾,就要补上对分子量的计算
		else res += tempc;
		printf("%.3f\n", res);
	}
}



不过很不幸,虽然代码在编缉器(vs2013)里可以完善通过,不过submit之后,却总是wrong answer。

最后很无奈,估计是在线评测系统的缓存区和本地有所不同,只好采用另一种方法了,把getchar 用scanf来代替掉,下面是代码


#include<stdio.h>
#include<iostream>
#include<string.h>

using namespace std;

#define maxn 100
int main()
{
	const char rev[] = "CHON0123456789";
	const double num[] = { 12.01, 1.008, 16.00, 14.01 };
	int a, b;
	cin >> a;
	while (a--)
	{
		double tempc = 0;
		int tempi = 0;
		char c[maxn];
		double res = 0.0;
		scanf("%s", c,80);//原来代码是scanf_s("%s", c,80);在线测评识别不了只得替换
		for (int j = 0; j < strlen(c);j++)
		{
			int i;
			for (i = 0; c[j] != rev[i]; i++);
			if (i < 4)
			{
				if (tempi) res += tempi*tempc;
				else res += tempc;
				tempc = num[i];
				tempi = 0;

			}
			else
			{
				tempi = tempi * 10 + (c[j] - '0');
				continue;
			}
		}
		if (tempi) res += tempi*tempc;
		else res += tempc;
		printf("%.3f\n", res);
	}
}

换成scanf之后,就不用考虑缓存区的问题了,鲁棒性更强了。
<think>首先,用户的问题是关于C语言中scanf_s函数的使用注意事项。我需要参考提供的引用内容,但注意这些引用是关于scanfscanf_s的,所以我应该整合相关信息。 引用摘要: -引用[1]:解释了scanf的基本功能。-引用[2]:对比了scanfscanf_s,提到scanf_s是更安全的版本。 -引用[3]:警告不要使用scanf读取字符串,建议使用其他方法或正则表达式。-引用[4]:讨论了多次调用scanf时的问题,如缓冲区残留字符导致错误。 -引用[5]:提到使用scanf_s时的一些注意事项,如初始化数组。用户明确询问scanf_s的使用注意事项,所以我需要专注于scanf_s。关键点从引用中提取: -scanf_s是scanf的安全版本,用于防止缓冲区溢出(引用[2])。-使用scanf_s时,需要指定缓冲区大小(引用[5])。 -对于字符串输入,scanf_s更安全,但仍有问题,建议其他方法(引用[3])。 -初始化数组为'\0'是好的做法(引用[5])。-多次调用时,注意缓冲区问题(引用[4])。 我的回答需要:-使用中文。-结构清晰,逐步解决问题。-确保真实可靠。-在引用的段落末尾添加引用标识。-生成相关问题。 回答结构:1.**介绍scanf_s函数**:简要解释scanf_s是什么,为什么使用它。2.**主要注意事项**:列出关键的使用注意事项。-指定缓冲区大小。-处理字符串输入。-初始化变量。-多次调用时的缓冲区管理。-错误处理。3.**示例代码**:提供简单示例展示正确用法。4.**生成相关问题**:在最后添加相关问题。确保LaTeX语法正确:-行内数学表达式用$...$,如没有数学内容,这里可能不需要。 -独立公式用$$...$$,但问题中可能不涉及复杂数学。最后,生成相关问题:-基于主题,如scanf_s的安全性、常见错误、替代方法等。完整回答大纲:1.开头:解释scanf_s函数。2.注意事项列表。3.示例代码。4.相关问题。</think>### C语言中scanf_s函数的使用注意事项 在C语言中,`scanf_s`函数是`scanf`的安全版本,主要用于防止缓冲区溢出漏洞。它通过要求指定缓冲区大小来增强安全性,常用于读取用户输入。然而,使用时需谨慎以避免常见错误。以下是关键注意事项,基于标准C11及后续版本(如MSVC实现)的最佳实践。 #### 1. **必须指定缓冲区大小** - `scanf_s`在读取字符串或数组时,必须提供缓冲区的大小参数,否则可能导致未定义行为或运行时错误。 - 例如,读取字符串时:`scanf_s("%s", name, sizeof(name));`,其中`sizeof(name)`确保输入不超过数组边界[^2][^5]。 - 如果省略大小参数,编译器可能报错(在支持`scanf_s`的环境中如MSVC),但并非所有平台都强制检查,因此手动添加是必要的。 #### 2. **初始化变量和数组** - 在使用`scanf_s`前,应将目标变量或数组初始化为默认值(如`'\0'`),以避免残留数据干扰。 - 示例:`char name[50] = {0}; // 初始化为'\0'`[^5]。 - 未初始化可能导致读取错误或安全风险,尤其当输入不匹配格式时。 #### 3. **处理字符串输入的潜在问题** - 尽管`scanf_s`比`scanf`更安全,但仍不建议用于读取字符串(如包含空格的输入),因为它可能截断或忽略部分数据。 - 替代方案:使用`fgets`函数读取整行,再解析。如果必须用`scanf_s`,可结合格式说明符如`%[^\n]`读取直到换行符[^3]。 - 示例:`scanf_s("%[^\n]", input, sizeof(input));`,但需注意换行符残留问题。 #### 4. **管理输入缓冲区** - 多次调用`scanf_s`时,输入缓冲区可能残留换行符或空白字符,导致后续读取失败。 - 解决方法:在读取字符或字符串后,使用`getchar()`或循环清空缓冲区。例如: ```c char ch; scanf_s("%c", &ch, 1); // 读取一个字符 while (getchar() != '\n'); // 清空缓冲区残留[^4] ``` - 忽略此问题可能导致逻辑错误,如意外跳过输入。 #### 5. **错误处理和返回值检查** - `scanf_s`返回成功读取的项数。必须检查返回值以处理无效输入(如类型不匹配)。 - 示例: ```c int num; if (scanf_s("%d", &num) != 1) { printf("输入无效!\n"); while (getchar() != '\n'); // 清空缓冲区 } ``` - 未处理错误可能导致程序崩溃或数据污染[^4]。 #### 6. **平台兼容性** - `scanf_s`是C11标准引入的,但并非所有编译器默认支持(如GCC需启用`-std=c11`)。在跨平台代码中,使用`#ifdef`检查可用性。 - 示例: ```c #if defined(_MSC_VER) || __STDC_VERSION__ >= 201112L scanf_s("%d", &num); #else // 使用替代方法 #endif ``` #### 示例代码:安全读取字符串 ```c #include <stdio.h> #define MAX_LEN 50 int main() { char name[MAX_LEN] = {0}; // 初始化数组 printf("输入姓名: "); if (scanf_s("%49s", name, sizeof(name)) != 1) { // 限制输入长度 printf("读取失败!\n"); } printf("姓名: %s\n", name); return 0; } ``` 此代码确保输入不超过数组大小,并处理错误[^5]。 ### 总结 `scanf_s`通过强制指定缓冲区大小提升了安全性,但需注意初始化、缓冲区管理、错误处理和平台差异。对于关键应用,优先使用`fgets`等更鲁棒的方法。始终测试边界情况(如超长输入)以保证可靠性[^2][^4][^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值