我的代码应该扫描用户名和密码,并确保它们不大于最大金额。如果是,它应该重新提示用户输入一些东西。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define MAXNAME 30
int main() {
char username_input[MAXNAME];
char password_input[MAXNAME];
username_input[MAXNAME - 1] = ' ';
password_input[MAXNAME - 1] = ' ';
printf("Please enter your username: ");
while (scanf("%s", username_input) != 1 || strlen(username_input) > MAXNAME) {
printf("Improper input.nPlease enter your username: ");
}
printf("Please enter your password: ");
while (scanf("%s", password_input) != 1 || strlen(password_input) > MAXNAME) {
printf("Improper input.nPlease enter your password: ");
}
return 0;
}
我面临的问题是,每当进入这些while循环中的一个时,我将得到重新提示的罚款,我将能够继续执行代码,但是一旦我击中返回0,我将抛出一个异常。
异常如下:运行时检查失败#2 -变量'username_input'周围的堆栈已损坏。
我也试过使用scanf_s和fgets(),这些似乎都不起作用。
试试这个:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define MAX_LEN 30
void read_ident(char const * descr, char buf[MAX_LEN + 2])
{
do {
printf("Please enter your %s: ", descr);
if (scanf("%31[^n]", buf) == 1 && strlen(buf) <= MAX_LEN) {
break;
}
printf("Improper input.n");
scanf("%*[^n]");
scanf("%*c");
} while (!feof(stdin));
scanf("%*c");
}
int main()
{
char username_input[MAX_LEN + 2], password_input[MAX_LEN + 2];
read_ident("username", username_input);
read_ident("password", password_input);
}
我喜欢scanf
,但你可能会发现fgets
更方便。见参考。
我上面的代码的想法是在行结束之前最多读取31个字符。如果你没有超过30个字符,你就完成了。否则,%*[^n]
跳过行结束前的所有字符(如果有的话:如果行结束紧跟着,这种读取尝试将失败,这就是为什么我们必须有两个连续的scanf
),然后%*c
跳过行结束字符("换行")。
如果你不想或不能硬编码你的缓冲区的长度,你可以这样写:
char format[16];
sprintf("%%%d[^n]", MAX_LEN + 1);
scanf(format, buf);
正如@Retired Ninja所说,你的代码崩溃是因为你没有告诉scanf
它最多应该读取多少个字符,所以它可能会溢出你的缓冲区。读取带有scanf
的字符串通常不应该在没有指定宽度的情况下进行(除非你不关心安全性)。
下面是如何使用fgets
:
void read_ident(char const * descr, char buf[MAX_LEN + 2])
{
do {
printf("Please enter your %s: ", descr);
fgets(buf, MAX_LEN + 2, stdin);
size_t nb_read_chars = strlen(buf);
if (nb_read_chars >= 2 && buf[nb_read_chars - 1] == 'n') {
buf[nb_read_chars - 1] = ' ';
break;
}
printf("Improper input.n");
if (nb_read_chars >= 2) {
while (fgetc(stdin) != 'n');
}
} while (!feof(stdin));
}
我更喜欢scanf
。
scanf_s
不会有帮助,因为如果有错误,它们会触发异常,这不是你想要做的(我认为你很少想要这样做)。
作为一个C程序员是快乐的,直到有一天我们需要处理来自用户的可变长度输入。所有用于"读取用户输入"的libc api都只使用"fix buffer",如scanf
,fgets
…,但我们不知道用户在实际输入之前会输入多长时间,所以我们不知道我们应该为那些scanf
/fgets
做多大的缓冲区。
通常有2个选项。一种是限制用户输入,并丢弃任何超过限制的数据,另一个是"动态分配"我们首先分配一块缓冲区,然后将用户输入以最小单位"追加"到缓冲区,如果要用完缓冲空间,在进一步写入之前先将其扩大。
这个方法有点冗长,我们应该让它"可重用"。下面是一个示例:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#define DB_BLOCK_SIZE (100)
typedef struct tagDynaBuf {
char * buf;
unsigned int capacity;
unsigned int size;
} DynaBuf;
void dynabuf_init(DynaBuf * db){
memset((void*)db, 0, sizeof(*db));
}
void dynabuf_release(DynaBuf * db){
if (!db){
return;
}
if (!db->buf){
return;
}
free(db->buf);
memset((void*)db, 0, sizeof(*db));
}
int dynabuf_append_char(DynaBuf * db, int c){
if (!db){
fprintf(stderr,"%u: 'db' was null.n", __LINE__);
return -1;
}
// 1. Let's make sure we have enough space in out buf.
if (!db->buf){
// malloc for the 1st time
db->buf = malloc(DB_BLOCK_SIZE);
if (!db->buf ){
fprintf(stderr,"%u: failed in 1st malloc.n", __LINE__);
return -2;
}
db->capacity = DB_BLOCK_SIZE;
db->size = 0;
}
if (db->capacity == db->size ){
// Ohoh, we ran out of our buf space, let's make it larger.
char * new_buf = realloc( db->buf, db->capacity + DB_BLOCK_SIZE);
if (!new_buf)
{
fprintf(stderr,"%u: failed in enlarging buf for %u.n", __LINE__, db->capacity + DB_BLOCK_SIZE);
return -3;
}
db->capacity += DB_BLOCK_SIZE;
db->buf = new_buf;
}
// Now we can accecpt the new comer -- 'c' -- with confidence.
db->buf[db->size] = c;
db->size ++;
return 0;
}
int main(int argc, char ** argv){
int ret = 0;
DynaBuf buf;
dynabuf_init(&buf);
printf("Enter text as long as you can (end with ctrl-D), I will try my best to handle them:n");
while (1){
int c = fgetc(stdin);
if ( EOF == c)
{
break;
}
if ( dynabuf_append_char(&buf, c)){
ret = 1;
goto EXIT;
}
}
// make our buf terminated.
if ( dynabuf_append_char(&buf, 0)){
ret = 2;
goto EXIT;
}
printf("nnThank you! I got:n%sn", buf.buf);
EXIT:
dynabuf_release(&buf);
return ret;
}
您甚至可以将其测试为cat {a large text file} | ./aout
。希望能帮到你。