strncpy的问题以及如何解决它

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

这是本书的问题:

这个程序有一个错误,因为strncpy的设计很差。 去阅读strncpy然后尝试找出当你给出的名称或地址大于512字节时会发生什么。 通过简单地将最后一个字符强制为’\ 0’来解决这个问题,这样无论什么(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'; 

另一种修复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最多输出整个字符数组,但不是更多。

为什么不用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'; 

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

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

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

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

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

主要问题是addr-> name尚未初始化,因此它是一个空指针,它指向no where。

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

并且由于它是一个NULL指针,如果你没有设置它会返回NULL,然后if语句将为true,die函数将停止程序。

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