在c中使用strncpy时出现分段错误

问题描述:

此代码应该用作vigenere密码。但是,在运行时,无论您输入什么输入,它都会出现分段错误。我正在为edx在线CS50课程写这篇文章。是否strncpy应该阻止分段错误发生,如果我告诉它复制适当的字符数量?在c中使用strncpy时出现分段错误

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

int main(int argc, string argv[]) { 
    int result; 

    if (argc != 2) { 
     printf("Shame on you!\n"); 
     return 1; 
    } 

    string key = argv[1]; 
    string text = GetString(); 
    string cpy_key = NULL; 

    //strncpy(cpy_key, key, strlen(key)); 
    for (int i = 0, n = strlen(text); i < n; i++) { 
     strcat(cpy_key, key); 
    } 
    cpy_key[strlen(text)] = '\0'; 
    // Main loop starts here 
    for (int i = 0, n = strlen(text); i < n; i++) { 
     result = text[i] + cpy_key[i]; 

     if (isupper(text[i]) && (result > 'Z')) { 
      result = result - 26; 
     } 
     if (islower(text[i]) && (result > 'z')) { 
      result = result - 26; 
     } 
     if (isalpha(text[i])) { 
      printf("%c", result); 
     } else { 
      printf("%c", text[i]); 
     } 
    } 
    printf("\n"); 
    return 0; 
} 
+1

你为什么使用'strncpy()'?使用'strcpy()'或者如果你知道目标缓冲区的长度和源缓冲区的长度,确实也使用'memcpy()',除非字符串在循环内部改变,否则不要在循环中使用'strlen()'。 –

+0

@iharob使用strcpy()仍然会导致分段错误。此外,strlen()仅在循环开始时被调用一次,并且被保存为int n。 – Fluffy

+0

在llp开始的时候这样做,它在我看来就像是'for'的条件。你在Linux上吗?或者mac?如果是这样,我建议[valgrind](http://www.valgrind.org),它应该可以帮助你快速发现问题。 –

你并不需要做的key副本,你可以索引key模长度。

顺便说一句,你应该永远不要strncpy,它不会做你认为它,它的语义是容易出错,这是从来没有为工作的工具。最后一个参数应该是目标数组的大小,但是如果源太大,目标将不会以null结尾,如果大小很小,目标大,这个函数将浪费时间填充整个目标数组将'\0'字节。不使用strncpy将为您节省许多错误。

另外不幸的是,cs50.h定义为stringtypedef char *string;。使用这种类型会导致误导和错误,特别是对于了解C++的人。只需使用char *,它使得您的代码更容易被C程序员阅读。我希望你不需要使用这种类型,隐藏在typedefs后面的指针通常不是一个好主意。

下面是一个简单的版本:

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

int main(int argc, char *argv[]) { 
    int result; 

    if (argc != 2) { 
     printf("Shame on you!\n"); // you might want to be more explicit ;-) 
     return 1; 
    } 

    char *key = argv[1]; 
    int klen = strlen(key); 
    char *text = GetString(); 
    if (!text) { 
     /* end of file or read error */ 
     return 1; 
    } 

    // Main loop starts here 
    for (int i = 0, n = strlen(text); i < n; i++) { 
     result = text[i] + key[i % len]; 

     if (isupper((unsigned char)text[i]) && result > 'Z') { 
      result = result - 26; 
     } 
     if (islower((unsigned char)text[i]) && result > 'z') { 
      result = result - 26; 
     } 
     if (isalpha((unsigned char)text[i])) { 
      printf("%c", result); 
     } else { 
      printf("%c", text[i]); 
     } 
    } 
    printf("\n"); 
    return 0; 
} 

提示:您的V @ genere不正确,你应该使用result = text[i] + key[i % len] - 'A';如果key是全部大写。

+0

这不起作用。它打印一系列不可打印的字符。 – Fluffy

+0

@Fluffy:我修复了崩溃,你应该修正逻辑,我不能猜测你的解密方法应该是什么,虽然我知道* vigenere *,但你似乎做错了。 – chqrlie

+0

啊,谢谢。谢谢您的帮助。 – Fluffy

这是C还是C++?在C语言中,你需要在你自己的字符数组(字符串)分配空间:

char *cpy_key = 0 ; 
... 
size_t key_len = strlen (key) + 1 ; // adding space for terminating NULL 
... 
cpy_key = malloc (key_len) ; // add error management here 
... 
strncpy (cpy_key , key , key_len) ; // add error management here 
... 
free (cpy_key) ; // when you don't need it anymore 
+0

请注意,您正在使用分配'key_len'而不是'key_len + 1'字节的细节。这样创建的字符串不会以null结尾。添加'+ 1'可以让你终止字符串。随着分配时间的更长,你不再需要使用'strncpy()',因为你确保你有足够的空间('strcpy()'可以)。你也可以使用'memmove()'(或甚至是'memcpy()'),它的长度为'key_len + 1'。 –

+0

@JonathanLeffler你(再次)是正确的。现在更新我的答案。谢谢 – mauro

cs50.h头定义typedef char *string;。发生

核心转储,因为你复制到一个空指针:

string cpy_key = NULL; 

//strncpy(cpy_key, key, strlen(key)); 
for (int i = 0, n = strlen(text); i < n; i++) { 
    strcat(cpy_key, key); 

无论是strcat()strncpy(),你需要分配存储空间cpy_key 。在显示的循环中,如果输入的字符串为50个字符,则要将该字符串复制50次,因此您需要分配超过2500个字符才能保证安全。使用strncpy()会正确地完成这项工作 - 只要您分配足够的空间。

请注意strncpy()不是一个很好用的功能。如果您有一个20 KiB缓冲区并将一个10字节的字符串复制到它中,它会在该字符串后面写入20470个空字节。如果你有一个50字节的缓冲区,并且你拷贝了75个字节,它会复制50个字节,并且不会让缓冲区空终止。两者都不明显;缺乏保证的空终止使其变得危险。有更糟糕的接口(strncat()是主要候选人 - 长度参数代表什么?)但不是很多。

你有一些工作要做你的加密算法。

你可以看看Vigenere cipher only works up to until dealing with a space in C — why?看看它可以做什么。

的一个问题是内存即

string cpy_key = NULL;

也就是说,它只是一个没有尺寸名称不分配内存。认识到这一点那么这一定会失败

strcat(cpy_key, key);

在调用你试图连接之类的大小与大小没有一件事一件事。