4
char copy, array[20]

    printf("enter ..."):
    scanf("%s", array);

    if (strlen(array) > 20 ) 
      { 
       strcpy(copy, array....); 

what would I need to do to make it only grab the first 20 character if the input is more then 20 character long

Thao Nguyen
  • 901
  • 7
  • 22
  • 42
  • 1
    What is that supposed to mean? `copy` is not an array at all. `array` is 20 characters long. It `array` simply *cannot* be longer that 20 characters. – AnT stands with Russia Feb 25 '11 at 21:32
  • you overflow with scanf if you enter more than 20bytes. Look at fgets. – Nyx0uf Feb 25 '11 at 21:35
  • @James strncpy doesn't necessarily NUL-terminate the result -- it's almost never what one wants (its original purpose was to copy UNIX directory entries, which fit in 14 bytes and were NUL-terminated if shorter). In any case, it isn't applicable here. – Jim Balter Feb 28 '11 at 09:12

7 Answers7

5
char array[20+1];
scanf("%20s", array);

Problem solved.

aaz
  • 5,136
  • 22
  • 18
  • +1 for solving the problem at source, although I would prefer `fgets`. – zwol Feb 25 '11 at 21:39
  • 1
    @Zach – `fgets` would retain `'\n'`. – aaz Feb 25 '11 at 21:44
  • @Zach @aaz Except for in the case where the array was greater than 20, which means you'd also have to add logic to see whether or not it did retain the '\n' and replace as appropriate. But on the flip side of the coin, with `scanf` you have to specify the size of the buffer twice (you can't pass an integral type to the `scanf` specifier for the width except with platform-specific extensions, so you have to specify it once as an integer when declaring the array and again in a string for the `scanf` specifier [or do something ugly like `snprintf(spec, sizeof(spec), "%d%%s", sizeof(buf));`]) – user470379 Feb 26 '11 at 03:07
  • @user470379 – You could use a preprocessor macro for the buffer size and paste it into the format string, that would be reasonably neat. But, regardless, they are conceptually not interchangeable: `fgets` gives physical lines (and you would typically want to malloc a larger buffer on failure), `scanf("%s")` gives logical names (trimmed, no internal whitespace, probably limited in length by semantics). – aaz Feb 26 '11 at 03:26
3

Your question is not clear, since the code makes little or no sense. Your input cannot be longer than 20 characters since the receiving array is only 20 characters. If the user inputs more, your program will produce undefined behavior. So, the main problem here is not limiting the copy, but rather limiting the input.

However, your question seems to be about limited-length string copying. If that's what you need, then unfortunately there no dedicated function in standard library for that purpose. Many implementation provide the non-standard strlcpy function that does exactly that. So, either check if your implementation provides strlcpy or implement your own strlcpy yourself.

In many cases you might see advices to use strncpy in such cases. While it is possible to beat strncpy into working for this purpose, in reality strncpy is not intended to be used that way. Using strncpy as a limited-length string copying function is always an error. Avoid it.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • If `strncpy` is not intended to be used for this, what do you think it is for? – zwol Feb 25 '11 at 21:37
  • @Zack: You can read about it in Wikipedia or here: http://stackoverflow.com/questions/2884874/when-to-use-strncpy-or-memmove/2884974#2884974 . Basically, `strncpy` is not a zero-terminated string function at all and its name is just a misnomer. It is just a historical blunder that lives on in the standard library. – AnT stands with Russia Feb 25 '11 at 21:39
  • I would appreciate a pointer to an actual primary source for your assertion that `strncpy` was "created to fill 14-character long file name fields in one archaic version of Unix file system." I don't see it in either of the places you point to. – zwol Feb 25 '11 at 21:44
  • Addendum: your assertion is backed up by C99 Rationale §7.21.2.4, but that's not a primary source either, and the C committee has been known to get things horrifically wrong. – zwol Feb 25 '11 at 21:47
  • @Zack: Well, for a programmer of my generation, this is like trying to remember the first fairy-tale I read. Let's just say I heard about it Mr. Ritchie himself in one of his interviews (I do remember that he did talk about it), although there were many other sources. It is just something to memorize: `strncpy` was originally introduced for filling out 14-char file name fields in some old Unix filesystem. – AnT stands with Russia Feb 25 '11 at 21:50
  • @Zack: Here's another blog entry: http://blogs.msdn.com/b/oldnewthing/archive/2005/01/07/348437.aspx – AnT stands with Russia Feb 25 '11 at 21:56
  • `strncpy` serves one purpose: preventing buffer overflow. That's it. It's not good to truncate your input with `strlcpy` and happily continue as if nothing happened. – aaz Feb 25 '11 at 21:58
  • @aaz: Incorrect. `strncpy` is a function that converts zero-terminated strings into fixed-width strings. That's its purpose. The fact that it prevents buffer overflow is just a natural side-effect of fixed-width representation. But it is not "the purpose" of `strncpy` as you seem to believe. I cover it in detail here http://stackoverflow.com/questions/2886931/difference-fixed-width-strings-and-zero-terminated-strings/2886982#2886982 (so that I don't have to explain it all over again). – AnT stands with Russia Feb 25 '11 at 22:00
  • @AndreyT – True. But as the blog linked by @Zack points out, it fails at that conversion by not padding with zeroes. (Fixed width `CHAR(n)` in SQL, which are padded with spaces, come to mind.) So I should have said: one _practical_ purpose. _Edit:_ No, I misread that, it does pad with zeroes. – aaz Feb 25 '11 at 22:04
  • @aaz: Well, I see it is a very misguided practical application of `strncpy`, like driving in woodscrews with a hammer. You see, `strncpy` virtually never works right. If the source string is longer, then `strncpy` fails to zero-terminate. If the source string is shorter, then `strncpy` wastes time by padding the buffer with unnecessary zeros. I don't see why one would use this function. Out of being too lazy to implement their own `strlcpy` maybe. – AnT stands with Russia Feb 25 '11 at 22:09
  • Let's hope they don't implement their own `strlcpy` and get it wrong. It's not the most [elegant bit of code](http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11;content-type=text%2Fx-cvsweb-markup) to begin with. – aaz Feb 25 '11 at 22:30
  • It would make more sense to me if `strlcpy` took the maximum length of the string (i.e. `sizeof buf - 1`). That would be consistent with the return value and its code wouldn't need to special-case `size == 0`. – aaz Feb 25 '11 at 22:57
  • Or you could just defer to `snprintf(x, sizeof(x), "%s", y)`. No added libraries, no extra padding, and no checking to see the string is terminated yourself. – user470379 Feb 26 '11 at 03:00
  • @Zack Anyone who read the UNIX 6 kernel source, as I and nearly all UNIX programmers of my generation did, or was a UNIX or libc developer, as I was, knows the origin of and original purpose of strncpy. UNIX directory entries were 16 bytes long with a 14 byte name field which was zero-padded. strncpy was useful for copying names into such entries; it's nearly never the right choice for anything else. – Jim Balter Feb 28 '11 at 09:19
  • @Jim I confess, I only go back as far as SunOS 4 (for Unix; I was mucking around with various toy micros in the 80s, but that doesn't really count). I guess I'll file this under "baffling design decisions made in the era of V6/V7 that we're now stuck with", next to "close(2) can fail". – zwol Feb 28 '11 at 23:37
  • @Zack fclose can also fail, because it does a fflush before closing. – Jim Balter Mar 01 '11 at 01:23
  • @Jim I'm aware of that, I just think it's an incorrect design decision, and even more so for the system call. Because there's no way to recover from close/fclose failing, and no way even to tell if you need to retry the operation. So the rule should have been that both of those always succeed, and if you care about `fflush` or `fsync` errors, you have to call them yourself first. – zwol Mar 01 '11 at 01:35
  • @Zack Under UNIX V6, a close that returns EIO deallocated the fd -- the operation could not be retried -- it should be treated like any fatal write error. The same was true of fclose -- the FILE was deallocated even for an error. POSIX seems to have broken it by stating that the fd or FILE is in an unspecified state. – Jim Balter Mar 01 '11 at 01:57
1

Use strncpy instead of strcpy. That's all there is to it. (Caution: strncpy does not nul-terminate the destination string if it hits its limit.)

EDIT: I didn't read your program carefully enough. You lose already at the scanf call if user input is longer than 20 characters. You should be calling fgets instead. (Personally I think *scanf should never be used - this is only the tip of the iceberg as far as problems they cause.) Furthermore, copy has room for only one character, not twenty; but I'm going to assume that's a typo on your part.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Why do you not like scanf? – cokedude Mar 28 '14 at 05:07
  • 1
    @cokedude 1) Depending on the format string, it can be just as dangerous as `gets` (as this question demonstrates); 2) numeric overflow triggers undefined behavior, i.e. `scanf("%d", &x)` is allowed to crash if the user types too many digits; 3) writing a parser that's robust in the face of malformed input is easier if you *don't* use scanf. – zwol Mar 28 '14 at 12:51
1

Alternatively, you don't need to use strcpy to read just 20 characters (and you won't have to include strings.h):

char c;
for( i = 0; i < 20; i++ ) {
    c = getchar();
    if (c != '\n') array[i] = c;
    else break;
}
array[i+1] = '\0';

Don't forget to declare array as char array[21] to make sure '\0' will be included.

Fábio Perez
  • 23,850
  • 22
  • 76
  • 100
0

Use strncpy. Make sure to null terminate the destination.

Andrew White
  • 52,720
  • 19
  • 113
  • 137
0
 strncpy (copy, array, 20);

does the trick. Exxcept the string would NOT be null-terminated if it was >20 chars!

http://www.cplusplus.com/reference/clibrary/cstring/strncpy/

DVK
  • 126,886
  • 32
  • 213
  • 327
0

You need to change your scanf() call, not your strcpy() call:

char copy[20], array[20];
printf("enter....");
scanf(%20s",array); // read a maximum of 20 characters
strcpy(copy, array);
Hellion
  • 1,740
  • 28
  • 36