连接名字和姓氏时遇到麻烦

时间:2019-03-19 03:21:23

标签: c

这是我的代码:

#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;
}

试图保护用户输入的名称输入的长度不超过字符数组的容量,但将姓氏的一半复制到名字上

3 个答案:

答案 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

注意:,您无需将1024available都放在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

或用户取消输入的情况:

" "

仔细检查一下,如果还有其他问题,请告诉我。

相关问题