这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char firstName[10];
char lastName[10];
char fullName[30];
int length, length2;
printf("Please enter the first name: ");
scanf("%s", firstName);
length = strlen(firstName);
while (length > 10)
{
printf("Error! First name should have < 10 characters.\n");
printf("Please enter the first name again: ");
scanf("%s", firstName);
length = strlen(firstName);
}
printf("Please enter the last name: ");
scanf("%s", lastName);
length2 = strlen(lastName);
while (length2 > 10)
{
printf("Error! Last name should have < 10 characters.\n");
printf("Please enter the last name again: ");
scanf("%s", lastName);
length2 = strlen(lastName);
}
strcpy(fullName, firstName);
strcat(fullName, " ");
strcat(fullName, lastName);
printf("The full name is: %s\n", fullName);
return 0;
}
试图保护用户输入的名称输入的长度不超过字符数组的容量,但将姓氏的一半复制到名字上
答案 0 :(得分:1)
strcpy()
和strcat()
不是长度安全的。一个人应该使用strncpy()
和strncat()
。但是,如果缓冲区不够长,则这两个函数都不会以NULL终止。
针对您的情况的一种简单解决方案是使用始终为NULL终止的snprintf()
:
size_t bytes_needed = snprintf( fullName, sizeof( fullName ), "%s %s", firstName, lastName );
if ( bytes_needed >= sizeof( fullName ) )
{
fprintf( stderr, "<fullName> is too small, needed %lu bytes\n", bytes_needed );
}
答案 1 :(得分:0)
对我来说,逻辑似乎很好,但应该有if条件而不是while循环。
答案 2 :(得分:0)
每当在C中使用固定大小的数组(例如fullName
)并将信息读取到数组中时,都必须戴上会计帽,并确保在数组中写入的字符数不能超过其容纳的字符数(少如果您打算将字符数组用作字符串,则将1
字符用作终止符
如我的第一条评论所述,在使用scanf
系列时,必须使用 field-width 修饰符以确保scanf
读取的内容不超过{{1} }字符以保留 nul-termination 字符的空间。否则,size - 1
会很高兴地在数组的边界外调用 Undefined Behavior (表示未定义行为)(这意味着代码的定义执行已结束,您的代码似乎可以正常工作或 SegFault -或两者之间的任何内容)
提出了任何人都可以提供的最佳建议-请勿尝试使用scanf
读取用户输入的行。而是使用scanf
(或POSIX fgets
)一次读取整行,然后从该行中解析您需要的内容(或者将整行用作字符串,只需修剪{{1 }})。这样,您输入缓冲区中剩余的内容就不会取决于所使用的getline
转换说明符(这会折磨新的C语言程序员……)
接受用户输入时,必须始终防止用户取消输入,方法是输入 Ctrl + d (或在Windows上输入 Ctrl + z )以生成手册{{ 1}}。您可以通过检查所使用的任何输入函数的返回来实现。
如果您想确保用户为您提供了有效的输入(或取消了输入),只需将您的提示放在连续的循环中,并在输入满足所有条件后'\n'
内进行读取。
当在同一个缓冲区(如scanf
中读取多个输入时,它有助于保持一个包含EOF
(或break;
)个字符计数的变量。这样,您无需测试第二个输入的完整缓冲区大小,而是测试缓冲区中可用的其余字符,以查看您的fullName
是否适合(为available
(空格添加一个额外的字符) )之间。
要确保输入内容合适,请读入足够大小的临时缓冲区(*不要在缓冲区大小上跳过!),例如
remaining
上面有lastName
个字符的临时缓冲区-除非用户的猫在键盘上睡觉,否则该缓冲区应该足够了。您将读入" "
char缓冲区,然后测试它是否适合#include <stdio.h>
#include <string.h>
#define MAXN 128u /* if you need a constant, #define one (or more) */
#define MAXC 1024u /* max line buffer size */
int main(void)
{
char buf[MAXC]; /* buffer to hold input */
char fullName[MAXN]; /* 128 chars for first/last will do */
size_t available = MAXN - 1; /* avaliable chars in fullName */
的{{1}}字符。结合起来,您可以执行一个连续的读取循环:
1024
(注意:,您无需将1024
或available
都放在fullName
中。只需使用 for (;;) { /* loop continually until valid input or user cancels */
size_t length; /* string funcitons use size_t */
printf ("Please enter the first name: ");
if (!fgets (buf, MAXC, stdin)) { /* use fgets - validate */
fputs ("(user canceled input)\n", stdout);
return 0;
}
buf[(length = strcspn (buf, "\r\n"))] = 0; /* trim trailing '\n' */
if (length > available) { /* validate length - and save length */
fprintf (stderr, "error: name must have < %zu characters.\n",
available);
}
else { /* name fits - copy */
strcpy (fullName, buf);
available -= length;
break;
}
和添加到firstName
中)
还请注意使用lastName
,它返回排除集fullname
中未包含的字符串中的字符数。因此,它将读取字符串中的字符数,直到遇到第一个行尾为止-然后覆盖以0结尾的行(与buf
相同)以在该点处以nul终止。您可以使用fullName
,但是strcspn
可以为您合并排除集,使您可以在单个命令中nul-terminate。
姓氏的读取与名字几乎相同,除了当您向"\r\n"
添加姓氏时,您需要先附加'\0'
。区别在于:
strlen
完全将其放入,您可以这样做:
strcspn
使用/输出示例
fullName
或用户取消输入的情况:
" "
仔细检查一下,如果还有其他问题,请告诉我。