strncpy的问题以及如何解决它

时间:2014-01-18 21:45:47

标签: c

我正在学习C并通过Learn C The Hard Way阅读(ISBN-10:0-321-88492-2)。我坚持练习17'如何打破它'。

以下是本书的问题:

  

这个程序中有一个错误,因为strncpy很糟糕   设计的。去看看strncpy然后试着找出什么时候会发生什么   您提供的名称或地址大于512个字节。解决这个问题   只需将最后一个字符强制为'\ 0',以便始终设置为no   无论什么(strncpy应该做什么)。

我已经对strncpy做了一些阅读,我明白它是不安全的,因为它不会在字符串的末尾添加一个空字节。但是,我不知道如何将大量字节传递给函数,我不知道如何解决空字节问题。

以下是使用strncpy的函数,MAX_DATA设置为512。

void Database_set(struct Connection *conn, int id, const char *name, const char *email)
{
    struct Address *addr = &conn->db->rows[id];
    if(addr->set) die("Already set, delete it first");

    addr->set = 1;
    // WARNING: bug, read the "How To Break It" and fix this
    char *res = strncpy(addr->name, name, MAX_DATA);
    // demonstrate the strncpy bug
    if(!res) die("Name copy failed");

    res = strncpy(addr->email, email, MAX_DATA);
    if(!res) die("Email copy failed");
}

如何打破它 - 编辑

以下是如何打破strncpy的示例:

void Database_set(struct Connection *conn, int id, const char *name, const char *email)
{
    struct Address *addr = &conn->db->rows[id];
    if(addr->set) die("Already set, delete it first");

    addr->set = 1;
    // WARNING: bug, read the "How To Break It" and fix this

    char name2[] = {
      'a', 's', 't',
      'r', 'i', 'n', 'g'
    };
    char *res = strncpy(addr->name, name2, MAX_DATA);
    // demonstrate the strncpy bug
    if(!res) die("Name copy failed");

    res = strncpy(addr->email, email, MAX_DATA);
    if(!res) die("Email copy failed");
}

要修复,请在字符串末尾添加一个空字节。将name2更改为:

  char name2[] = {
    'a', 's', 't',
    'r', 'i', 'n', 'g', '\0'
  };

或者,在strncpy函数调用上面添加以下行

names2[sizeof(names2)-1] = '\0';

5 个答案:

答案 0 :(得分:4)

修复strncpy 错误的另一种方法是修复printf来电

void Address_print(struct Address *addr)
{
    printf("%d %.*s %.*s\n",
            addr->id, sizeof(addr->name), addr->name, sizeof(addr->email), addr->email);
}

这限制了printf最多输出整个字符数组,但不能更多。

答案 1 :(得分:3)

为什么不用strlcpy替换strncpy? 根据strlcpy手册页:

 EXAMPLES
 The following sets chararray to ``abc\0\0\0'':

       (void)strncpy(chararray, "abc", 6);

 The following sets chararray to ``abcdef'' and does not NUL terminate
 chararray because the length of the source string is greater than or
 equal to the length parameter.  strncpy() only NUL terminates the
 destination string when the length of the source string is less than the
 length parameter.

       (void)strncpy(chararray, "abcdefgh", 6);

 Note that strlcpy(3) is a better choice for this kind of operation.  The
 equivalent using strlcpy(3) is simply:

       (void)strlcpy(buf, input, sizeof(buf));


 The following copies as many characters from input to buf as will fit and
 NUL terminates the result.  Because strncpy() does not guarantee to NUL
 terminate the string itself, it must be done by hand.

       char buf[BUFSIZ];

       (void)strncpy(buf, input, sizeof(buf) - 1);
       buf[sizeof(buf) - 1] = '\0';

答案 2 :(得分:2)

假设str1str2是字符数组,您可以执行以下操作:

strncpy(str1, str2, sizeof(str1) - 1);
str1[sizeof(str1)-1] = '\0';

无论\0多长时间,这始终会将最后一个字符设置为str2。但请注意,如果str2大于str1,则字符串将被截断。

答案 3 :(得分:2)

strncpy的手册页实际上提供了有关如何修复此错误的示例代码:

strncpy(buf, str, n);
if (n > 0)
   buf[n - 1]= '\0';

答案 4 :(得分:0)

主要的问题是addr->名称尚未初始化,因此它是一个空指针,它不指向哪里。

因此,在您首先使用strncpy之前,您必须将内存分配给addr-> name,否则它将无法正常工作。

由于它是一个NULL指针,如果你没有设置它就会返回NULL,那么之后的if语句将为true,而die函数将停止该程序。

您可以查看Database_create函数不会从struct地址初始化两个字符串指针的源代码。