Using strncpy() where destination contains the source

混江龙づ霸主 提交于 2021-02-05 09:19:05

问题


I wrote a function to trim white space characters from a string in C. My concern is the last line in the trim() function below, where the source is contained in the destination. The test cases all turned out fine, along with some other testing. Can copying all or a portion of a string where the source and destination are in the same memory cause weird problems?

Source code:

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

void trim(char *line)
  {
  int  i, len = strlen(line);
  char *ptr, whitespace[] = " \t\n";

  // scan for first char which does not match a char in whitespace string
  for (i=0; i<len; i++)
    if (strchr(whitespace, line[i]) == NULL)
      break;
  ptr = line + i;

  // scan for last char which does not match a char in whitespace string
  for (i=len; i>0; i--)
    if (strchr(whitespace, line[i]) == NULL)
      break;
  line[i] + 1) = '\0';

  // copy result to line (this is the line relevant to the question)
  strncpy(line, ptr, len);
  }

int main(void)
  {
  int i;
  char test[4][64] = {
    "a line with no leading and trailing spaces",
    "  a line with some leading and trailing spaces  ",
    "\ta line with leading and trailing tabs\t",
    "\na line with leading and trailing newlines\n"
    };

  for (i=0; i<4; i++)
    {
    printf("test %d\nno trim: %s\n", i, test[i]);
    trim(test[i]);
    printf("trimmed: %s\n", test[i]);
    }
  return 0;
  }

回答1:


If you read e.g. this strncpy reference you will see

The behavior is undefined if the character arrays overlap.

You need to use memmove instead, which is specified to handle overlapping memory.




回答2:


First, the second loop is erroneous. I'll copy it here to show the exact place where it fails:

// scan for last char which does not match a char in whitespace string
for (i=len; i>0; i--)
  if (strchr(whitespace, *(line + i)) == NULL)
    break;
*(line + i + 1) = '\0';

One of two:

  • or you rewrite the for loop as for(i = len-1; i>=0; i--),
  • or you write inside the loop the reference to *(line + i - 1).

The first time you enter the loop, you get a \0 character (the one at *(line + len) and it's not in the set of "\n \t" you used, so the loop always fails at the beginning, making you write a \0 at position i + 1 (this is undefined behaviour, as you are writing it past the \0 char).

The discouraged use of strncpy on overlapping strings holds also, as has been pointed by other response.

Note

*(line + i - 1) is equivalent to line[i-1], which is more readable and less error prone. And is perfectly compatible with the pointer definition you used in the function header. C defines both expresions as equivalent.

On other thing, I don't know if searching a null terminated string (with strchr(3)) for character '\0' is undefined behaviour or not, but if it finds correctly the string terminator, you'll be lucky and won't get out of the for loop (\0 is present in all strings, somehow) As the manual doesn't say anything in respect to, perhaps somebody can illustrate from the standard.



来源:https://stackoverflow.com/questions/33276068/using-strncpy-where-destination-contains-the-source

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!