'strncpy' vs. 'sprintf'

后端 未结 4 1224
萌比男神i
萌比男神i 2021-01-31 06:01

I can see many sprintf\'s used in my applications for copying a string.

I have a character array:

char myarray[10];
const char *str = \"myst         


        
相关标签:
4条回答
  • 2021-01-31 06:02

    I would not use sprintf just to copy a string. It's overkill, and someone who reads that code would certainly stop and wonder why I did that, and if they (or I) are missing something.

    0 讨论(0)
  • 2021-01-31 06:19

    Neither should be used, at all.

    1. sprintf is dangerous, deprecated, and superseded by snprintf. The only way to use the old sprintf safely with string inputs is to either measure their length before calling sprintf, which is ugly and error-prone, or by adding a field precision specifier (e.g. %.8s or %.*s with an extra integer argument for the size limit). This is also ugly and error-prone, especially if more than one %s specifier is involved.

    2. strncpy is also dangerous. It is not a buffer-size-limited version of strcpy. It's a function for copying characters into a fixed-length, null-padded (as opposed to null-terminated) array, where the source may be either a C string or a fixed-length character array at least the size of the destination. Its intended use was for legacy unix directory tables, database entries, etc. that worked with fixed-size text fields and did not want to waste even a single byte on disk or in memory for null termination. It can be misused as a buffer-size-limited strcpy, but doing so is harmful for two reasons. First of all, it fails to null terminate if the whole buffer is used for string data (i.e. if the source string length is at least as long as the dest buffer). You can add the termination back yourself, but this is ugly and error-prone. And second, strncpy always pads the full destination buffer with null bytes when the source string is shorter than the output buffer. This is simply a waste of time.

    So what should you use instead?

    Some people like the BSD strlcpy function. Semantically, it's identical to snprintf(dest, destsize, "%s", source) except that the return value is size_t and it does not impose an artificial INT_MAX limit on string length. However, most popular non-BSD systems lack strlcpy, and it's easy to make dangerous errors writing your own, so if you want to use it, you should obtain a safe, known-working version from a trustworthy source.

    My preference is to simply use snprintf for any nontrivial string construction, and strlen+memcpy for some trivial cases that have been measured to be performance-critical. If you get in a habit of using this idiom correctly, it becomes almost impossible to accidentally write code with string-related vulnerabilities.

    0 讨论(0)
  • 2021-01-31 06:26

    There is one way to use sprintf() (or if being paranoid, snprintf() ) to do a "safe" string copy, that truncates instead of overflowing the field or leaving it un-NUL-terminated.

    That is to use the "*" format character as "string precision" as follows:

    So:

    char dest_buff[32];
    ....
    sprintf(dest_buff, "%.*s", sizeof(dest_buff) - 1, unknown_string);
    

    This places the contents of unknown_string into dest_buff allowing space for the terminating NUL.

    0 讨论(0)
  • 2021-01-31 06:29

    The different versions of printf/scanf are incredibly slow functions, for the following reasons:

    • They use variable argument lists, which makes parameter passing more complex. This is done through various obscure macros and pointers. All the arguments have to be parsed in runtime to determine their types, which adds extra overhead code. (VA lists is also quite a redundant feature of the language, and dangerous as well, as it has farweaker typing than plain parameter passing.)

    • They must handle a lot of complex formatting and all different types supported. This adds plenty of overhead to the function as well. Since all type evaluations are done in runtime, the compiler cannot optimize away parts of the function that are never used. So if you only wanted to print integers with printf(), you will get support for float numbers, complex arithmetic, string handling etc etc linked to your program, as complete waste of space.

    • Functions like strcpy() and particularly memcpy() on the other hand, are heavily optimized by the compiler, often implemented in inline assemble for maximum performance.

    Some measurements I once made on barebone 16-bit low-end microcontrollers are included below.

    As a rule of thumb, you should never use stdio.h in any form of production code. It is to be considered as a debugging/testing library. MISRA-C:2004 bans stdio.h in production code.

    EDIT

    Replaced subjective numbers with facts:

    Measurements of strcpy versus sprintf on target Freescale HCS12, compiler Freescale Codewarrior 5.1. Using C90 implementation of sprintf, C99 would be more ineffective yet. All optimizations enabled. The following code was tested:

      const char str[] = "Hello, world";
      char buf[100];
    
      strcpy(buf, str);
      sprintf(buf, "%s", str);
    

    Execution time, including parameter shuffling on/off call stack:

    strcpy   43 instructions
    sprintf  467 instructions
    

    Program/ROM space allocated:

    strcpy   56 bytes
    sprintf  1488 bytes
    

    RAM/stack space allocated:

    strcpy   0 bytes
    sprintf  15 bytes
    

    Number of internal function calls:

    strcpy   0
    sprintf  9
    

    Function call stack depth:

    strcpy   0 (inlined)
    sprintf  3 
    
    0 讨论(0)
提交回复
热议问题