C:创建自己的strncpy版本

时间:2016-11-27 21:33:58

标签: c strncpy

我正在制作自己的strncpy版本。我的代码似乎接受输入正常,但程序在输入后终止。如果复制的函数比第一个短,那么strncpy似乎也会用复制函数填充它 - 它的重点是什么?如何在我的代码中实现它?

#include <stdio.h>
#include <stdlib.h>

#define SIZE 50
#define STOP "quit"

char *copywords(char *str1, char *str2, int n);

int main(void) {
    char words[SIZE];
    char newwords[SIZE];
    int num;
    int i = 0;
    int j = 0;

    printf("Type a word, and the # of chars to copy, or type 'quit' to quit: ");

    fgets(words, SIZE, stdin);
    scanf_s("%d", &num);

    if (words == STOP) {
        printf("Good bye!\n");
        return 0; 
    }

    copywords(words, newwords, num);

    printf("The word was");
    puts(words);

    printf("and the copied word is");
    puts(newwords);
}

char *copywords(char *str1, char *str2, int n) {
    int i; 

    for (i = 0; i < n; i++) {
        str2[i] = str1[i];
    }
    return str2;
}

2 个答案:

答案 0 :(得分:1)

简短的回答是:请勿使用strncpy()

您可以在此处阅读原因:https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

strncpy的语义模糊不清,被广泛误解,容易出错。 size参数是目标数组的大小,而不是从源复制的字符数的某些限制。如果源字符串长度为size或更大,则目标将为空终止,如果较短,则目标的其余部分将已填充空字节('\0')。

这些选择的原因是历史性的:strncpy()用于将文件名复制到文件名长度有限的古老文件系统的内存结构中。

缺少空终止容易出错这个函数应该永远不会在生产代码中使用,因为程序员或维护者很容易误解代码实际行为并通过修改它来创建错误。

如果必须将其重新实现为作业,则必须实施完全语义。你只想拥有一个方便的字符串函数来复制带截断的字符串,选择一个不同的名称,可能还有一个不同的参数顺序。

以下是两者的例子:

char *strncpy_reimplemented(char *dest, const char *src, size_t n) {
    size_t i;
    for (i = 0; i < n && src[i] != '\0'; i++) {
        dest[i] = src[i];
    }
    while (i < n) {
        dest[i++] = '\0';
    }
    return dest;
}

char *pstrcpy(char *dest, size_t size, const char *src) {
    size_t i;
    if (size > 0) {
        for (i = 0; i < size - 1 && src[i] != '\0'; i++) {
            dest[i] = src[i];
        }
        dest[i] = '\0';
    }
    return dest;
}

您的copywords功能有问题:

  • 如果源字符串长于size-1
  • ,则不会终止目标
  • 如果源字符串短于size-1
  • ,则取消引用超出其长度的字符串
  • 您不检查用户键入的值是否在源阵列和目标阵列的正确范围内。

您还有其他问题:

  • (words == STOP)不会检查fgets()读取的字符串是否为quit。您必须先从缓冲区中删除尾随换行符,然后使用strcmp()来比较字符串:

    words[strcspn(words, "\n")] = '\0';
    if (!strcmp(words, "quit")) {
        printf("Good bye!\n");
        return 0; 
    }
    

以下是您的代码的更正和简化版本:

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

char *copywords(char *dest, const char *source, size_t n);

int main(void) {
    char words[50];
    char newwords[50];
    int num;

    for (;;) {
        printf("Type a word, or type 'quit' to quit: ");
        if (scanf("%49s", words) != 1) {
            printf("Invalid input!\n");
            return 0; 
        }
        if (!strcmp(words, "quit")) {
            printf("Good bye!\n");
            return 0; 
        }
        printf("Type the # of chars to copy: ");
        if (scanf("%d", &num) != 1) {
            printf("Invalid input!\n");
            return 0; 
        }
        copywords(newwords, words, num);
        printf("The word was %s\n", words);
        printf("and the copied word is %s\n", newwords);
    }
}

char *copywords(char *dest, const char *source, size_t n) {
    size_t i;
    for (i = 0; i < n && source[i] != '\0'; i++) {
        dest[i] = source[i];
    }
    dest[i] = '\0';
    return dest;
}

答案 1 :(得分:0)

问题是你在复制后的刺痛结束时不添加终止字符

for (i = 0; i < n; i++)
{
    str2[i] = str1[i];
}
str2[i] = '\0';

如果你不使用它,为什么要返回str2?

另外我认为你需要通过==运算符进行比较

编辑:

完整代码

#include <stdio.h>
#include <stdlib.h>

#define SIZE 50
#define STOP "quit"

void copywords(char *str1, char *str2, int n);


int main(void)
{
char words[SIZE];
char newwords[SIZE];
int num;
int i = 0;
int j = 0;

printf("Type a word, and the # of chars to copy, or type “quit” to quit: ");
fgets(words, SIZE, stdin);
scanf("%d", &num);
copywords(words, newwords, num);
printf("The word was ");
puts(words);
printf("and the copied word is ");
puts(newwords);
return 0;

}

void copywords(char *str1, char *str2, int n)
{
int i;
for (i = 0; i < n; i++)
{
    str2[i] = str1[i];
}
str2[i] = '\0';
}