gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/18/88)
In article <1666@solo10.cs.vu.nl> maart@cs.vu.nl (Litmaath Maarten) writes: >If you want to copy part of a string into a buffer, do you complain you can't >give a count to strcpy()? Or do you say: hey, strcpy() doesn't do what I want, >let's use another function (which happens to be strncpy())? Except strncpy() has its own problems when the limit is reached, just as someone observed that fgets() does! The important idea is that one must think HARD about the implications of the specifications for library functions, in order to use them safely in robust, secure code. There is no easy road to rightness.
poser@csli.Stanford.EDU (Bill Poser) (12/21/89)
Why is it that strncpy(3) does not null terminate the string if the length of the source string is greater than or equal to the length of the target buffer? I cannot think of any circumstances in which this behaviour is desirable, and always end up null terminating it myself. The only case in which a non-null-terminated string might be useful would be one in which a different representation for strings (e.g. descriptors) is used, in which case the use of str(n)cpy would not be appropriate anyhow. It seems to me that the behaviour of strncpy provides: (a) a source of bugs for people who don't bother to arrange null-termination themselves; (b) extra work for those who take care; or, (c) just enough of a pain to be unsafe and use strcpy(3). So I claim that strncpy is mis-designed and should null-terminate. Any comments? Bill
cns@stiatl.UUCP (Chris Straut) (12/21/89)
In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: > Why is it that strncpy(3) does not null terminate >the string if the length of the source string is greater than >or equal to the length of the target buffer? [ other stuff deleted ] I agree that strncpy should null terminate the resulting string. The definition of a string in the C language is a null terminated char array. And the function called (str)ncpy indicates a string operation, which by default should create a null terminated string. To overcome this 'feature' we wrote our own strncpy function, which is benefical to the unsuspecting programmer (or novice), and the result is a null terminated string. -- Christopher Straut | Go Jackets!!!!! gatech!stiatl!cns Sales Technologies, Inc | 3399 Peachtree Rd, NE | Atlanta, GA (404) 841-4000 |
bengsig@oracle.nl (Bjorn Engsig) (12/21/89)
Article <11509@csli.Stanford.EDU> by poser@csli.Stanford.EDU (Bill Poser) says: | | Why is it that strncpy(3) does not null terminate ... strncpy always copies exactly n characters to the destination string by null- padding or truncating. A similar behaviour is also found in a declaration like char thing[][3] = { "a", "ab", "abc", "de" }; where each element of thing is an array of 3 characters (i.e. thing[2] is not null terminated, and puts(thing[2]) would print abcde). A way to do a limited string copy is dst[0] = 0; strncat(dst,src,n); since strncat always null-terminates. -- Bjorn Engsig, Domain: bengsig@oracle.nl, bengsig@oracle.com Path: uunet!{mcsun!orcenl,oracle}!bengsig
tcm@srhqla.SR.COM (Tim Meighan) (12/22/89)
In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: > So I claim that strncpy is mis-designed and should null-terminate. > Any comments? Well, I generally make sure the array I'm copying INTO is larger than what I'm copying. That way the null terminator gets copied in as well and everything is cool. I could make the argument that it's poor programming practice to be copying strings into arrays that aren't large enough to hold them, and that strncpy() is merely making the best of a bad situation. You could probably counter with an excellent example of a real-life instance where the situation arises. I would then counter that if you're doing things that are that sophisticated, it's a good time to write your own function that works just like strncpy(), but that does string termination the way you want it to be done! Personally, I think strncpy() works just fine as is. As I said, I have never had a situation where I needed it to do anything other than what it does. Tim Meighan SilentRadio "The text strings were shifted in memory with care . . . with the hope that NULL TERMINATOR soon would be there!"
sullivan@aqdata.uucp (Michael T. Sullivan) (12/22/89)
From article <11509@csli.Stanford.EDU>, by poser@csli.Stanford.EDU (Bill Poser): > > Why is it that strncpy(3) does not null terminate > the string if the length of the source string is greater than > or equal to the length of the target buffer? I cannot think of It's so you can do things like: strncpy(s1, s2, len)[len] = '\0'; and really freak people out! :-) -- Michael Sullivan uunet!jarthur.uucp!aqdata!sullivan aQdata, Inc. San Dimas, CA
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (12/22/89)
In article <8313@stiatl.UUCP> cns@stiatl.UUCP (Chris Straut) writes: | In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: | > Why is it that strncpy(3) does not null terminate | >the string if the length of the source string is greater than | >or equal to the length of the target buffer? | [ other stuff deleted ] | | [ answer deleted ] I was asked this question just a few days ago mydelf. After some thinking, this appears to be consistent with other behavior, namely fgets() which drops the trailing newline if the buffer is too small. This makes sense if you think of strncpy() as a way to copy to a buffer without overflow. For buffer size N you copy N-1 characters and manually force an end of string in the last byte. If you think of it as a way to copy N characters from a string to a buffer which is always big enough it doesn't make sense. It's a question of intended use. We hacked out a quick version which does always terminate the string rather than add logic to all the places in which it was being used. -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "The world is filled with fools. They blindly follow their so-called 'reason' in the face of the church and common sense. Any fool can see that the world is flat!" - anon
maart@cs.vu.nl (Maarten Litmaath) (12/22/89)
In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes:
\
\ Why is it that strncpy(3) does not null terminate
\the string if the length of the source string is greater than
\or equal to the length of the target buffer? I cannot think of
\any circumstances in which this behaviour is desirable, and
\always end up null terminating it myself. [...]
So you have to write nstrncpy(), a function that always null-terminates the
destination, just like you have to write your own str*c{at,py} functions if
you want them to return something USEFUL, instead of their first arguments,
e.g. the new length of the destination. :-(
--
1755 EST, Dec 14, 1992: Henry Spencer is put on a one-way mission to the moon.|
Maarten Litmaath @ VU Amsterdam: maart@cs.vu.nl, uunet!mcsun!botter!maart
poser@csli.Stanford.EDU (Bill Poser) (12/22/89)
I should clarify that: (a) I know perfectly well HOW strncpy works. My question concerned the rationale for this design; (b) I know that it is not hard to produce null-termination and indeed have my own version of strncpy that does this. But the fact that it isn't hard to produce null termination doesn't explain why the standard version was designed the way it was. There is nothing terribly difficult about anything in the string library. So far I have heard of one good use for strncpy without null termination. I have also been told that it is useful for copying into fixed length char arrays which are not intended to be null-terminated when full. I am aware of such string representations, but those aren't the standard C string representation and so the question arises as to why the standard string library should include a function that doesn't fit the usual representation of strings.
rli@buster.irby.com (Buster Irby) (12/22/89)
cns@stiatl.UUCP (Chris Straut) writes: >I agree that strncpy should null terminate the resulting string. The >definition of a string in the C language is a null terminated char array. >And the function called (str)ncpy indicates a string operation, which by >default should create a null terminated string. To overcome this 'feature' >we wrote our own strncpy function, which is benefical to the unsuspecting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >programmer (or novice), and the result is a null terminated string. CAUTION: There is a lot of existing software in the world that would break if the operation of strncpy or any other standard library function were suddenly changed. You are asking for trouble any time you change the operation of a standard library function such as strncpy, and I would strongly suggest that you not do it. First of all, what happens when you take a piece of software that was not developed inhouse and compile it using your new strncpy function? Or what happens when you hire a senior level C programmer who is used to the way strncpy is documented to work? You are asking for trouble, and believe me, it can cost a lot of time and money to track down bugs which you introduce into the code like this. If you must create your own specialized version of any standard library function, you should call it by your own specialized name, not by its standard name. -- Buster Irby buster!rli
meissner@skeptic.osf.org (Michael Meissner) (12/22/89)
In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: | Why is it that strncpy(3) does not null terminate |the string if the length of the source string is greater than |or equal to the length of the target buffer? Strncpy was created to deal with the pre-Berkeley UNIX file system, whose directory entry took up exactly 16 bytes: 2 bytes for the inode # of the file 14 bytes for the filename If the filename was exactly 14 characters, no null would be used (>14 characters would be truncated). This fact blew away some early attempts at building a readdir function, which is spec'ed to return a null terminated name. I believe, that the kernel would compare all 14 bytes to see if they were equal when doing the namei (name to inode) transformation, which is why strncpy is required to zero out all bytes, if the string is less than n bytes. In addition to limiting names to the wierd 14 character limit, I think that the above scheme also is responsible for breaking large disks into tiny pieces, since each disk piece could not have more than 65K inodes. I suspect that many UNIX systems will shortly be in for the same shock, since 1GB disks are now appearring, and we should soon hit the limit for 32-bit inode numbers. Even if the system can support a multiple GB disk, single files will probably still be limited to 2GB, unless your system supports 64-bit longs (or uses the long long kludge + prototypes all the time). | I cannot think of |any circumstances in which this behaviour is desirable, and |always end up null terminating it myself. The only case in |which a non-null-terminated string might be useful would be |one in which a different representation for strings (e.g. |descriptors) is used, in which case the use of str(n)cpy would |not be appropriate anyhow. It seems to me that the behaviour |of strncpy provides: (a) a source of bugs for people who don't |bother to arrange null-termination themselves; (b) extra work for |those who take care; or, (c) just enough of a pain to be unsafe and |use strcpy(3). | | So I claim that strncpy is mis-designed and should |null-terminate. Any comments? | | Bill
djones@megatest.UUCP (Dave Jones) (12/22/89)
From article <11509@csli.Stanford.EDU>, by poser@csli.Stanford.EDU (Bill Poser): > > ... I claim that strncpy is mis-designed and should > null-terminate. Any comments? > I grepped through a bunch of my code, and it all adds a null everywhere it calls strncpy. So a null-terminating strncpy would seem to be useful. But don't name it strncpy! There just may be code out there that depends on it working the way it's documented. Call it strncpy0 or something.
jonathan.forbes@canremote.uucp (JONATHAN FORBES) (12/22/89)
If the source string is larger than the destination string, and you use strncpy(), then you will be overwriting memory in the destination string! (assuming they both have the same amount of space allocated.) --- * Via ProDoor 3.1R
exspes@gdr.bath.ac.uk (P E Smee) (12/22/89)
In article <11510@csli.Stanford.EDU> poser@csli.stanford.edu (Bill Poser) writes: > >I should clarify that: (a) I know perfectly well HOW strncpy works. My >question concerned the rationale for this design; I suspect it has to do with the original design of the unix directory structure. Filenames were limited to 12 (14? been so long since I used one, I'm not sure of the number) characters, and the directory structure had a char[exactly-that-size] variable to HOLD the filename. So, when putting a filename into or out of a directory structure, you wanted it to be null-terminated if and only if it was shorter than that variable. A maximum-length filename could NOT be null terminated in the describing structure. As to why they decided to do that, sheer byte-saving, I suppose (1 per file in your directory hierarchy). Maybe it made directory entries fit more nicely into whatever their disk blocksize was. -- Paul Smee, Univ of Bristol Comp Centre, Bristol BS8 1TW, Tel +44 272 303132 Smee@bristol.ac.uk :-) (..!uunet!ukc!gdr.bath.ac.uk!exspes if you MUST)
poser@csli.Stanford.EDU (Bill Poser) (12/22/89)
In article <621@buster.irby.com> rli@buster.irby.com (Buster Irby) writes: > >CAUTION: There is a lot of existing software in the world that >would break if the operation of strncpy or any other standard >library function were suddenly changed. I at least was not calling for a redefinition of strncpy in my posting, only asking whether there was a good reason for it to behave as it does. My own null-terminating version is called tstrncpy, and I would guess that other people's have distinct names too.
fredb@llama.rtech.UUCP (Fred Buechler) (12/22/89)
From article <11509@csli.Stanford.EDU>, by poser@csli.Stanford.EDU (Bill Poser): > > ... I claim that strncpy is mis-designed and should > null-terminate. Any comments? > > I have found strncpy to be useful when working in mixed language environments where the other languages don't use null terminated strings, particularly to write shared data structures. Fred.
cns@stiatl.UUCP (Chris Straut) (12/22/89)
In article <621@buster.irby.com> rli@buster.irby.com (Buster Irby) writes: >cns@stiatl.UUCP (Chris Straut) writes: > >>I agree that strncpy should null terminate the resulting string. The >>definition of a string in the C language is a null terminated char array. >>And the function called (str)ncpy indicates a string operation, which by >>default should create a null terminated string. To overcome this 'feature' >>we wrote our own strncpy function, which is benefical to the unsuspecting > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >>programmer (or novice), and the result is a null terminated string. > >CAUTION: There is a lot of existing software in the world that >would break if the operation of strncpy or any other standard >library function were suddenly changed. [ alot of other stuff deleted ] We didn't call the new function strncpy. Sorry if the text above was misleading. We have written a set of library functions to enhance the C run-time library as well as increase its functionality. We currently do development on three different platforms, therefore the need for portable library functions which can be maintained in-house. We currently employ alot of CO-OP programmers, and many are not very experienced in the C language. The use of our libraries assist these CO-OPs in overcoming these holes in the C run-time library naming convention. We are currently putting together a developers guide for in-house use, which will document the characteristics of the C libarary as well as our own C functions which bring to the attention certain "features" of the language run-time library. -- Christopher Straut | Go Jackets!!!!! gatech!stiatl!cns Sales Technologies, Inc | 3399 Peachtree Rd, NE | Atlanta, GA (404) 841-4000 |
sullivan@aqdata.uucp (Michael T. Sullivan) (12/23/89)
From article <11510@csli.Stanford.EDU>, by poser@csli.Stanford.EDU (Bill Poser): > > I have also been told that it is useful for copying into fixed length > char arrays which are not intended to be null-terminated when full. > I am aware of such string representations, but those aren't the standard > C string representation and so the question arises as to why the > standard string library should include a function that doesn't fit the > usual representation of strings. Isn't it nice that there IS a function that handles the non-standard case. I think your question really should be why doesn't the standard library include a function that does a null terminated strncpy. -- Michael Sullivan uunet!jarthur.uucp!aqdata!sullivan aQdata, Inc. San Dimas, CA
jss@jra.ardent.com (12/23/89)
In article <11515@csli.Stanford.EDU> poser@csli.stanford.edu (Bill Poser) writes: > >I at least was not calling for a redefinition of strncpy in my >posting, only asking whether there was a good reason for it to behave >as it does. Not to pick on Bill, because I see questions like this all the time in this newsgroup. But this is not a well posed (groan moan, I couldn't resist :-) question because we don't know what kind of reason will be "good". Do you want Origin: There was a glitch in the XYZ-1 architecture that meant the routine was 10 times faster than the obvious alternative. Historical: When the code was moved from the XYZ-1 to the FOOBAR-990000000 this behavior was unmodified. On the DWIM-9 it was fixed, but when X3J11 decided to standardize there weren't any DWIM-9's around and there were zillions of lines of code derived that dependend on the bug so they standardized on the XYZ-1 behavior. Rationale: It ought to work that way because ... Usually the dominant reason is historical, and frequently there are no good rationales. But is the historical explanation a "good" one? Please, if you ask a question about why some library function behaves the way it does, explain what motivates the question (idle curiosity is fine with me) and what category of answer you're interested in. Jerry Schwarz
mike@relgyro.stanford.edu (Mike Macgirvin) (12/23/89)
In article <1947@crdos1.crd.ge.COM> davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes: >In article <8313@stiatl.UUCP> cns@stiatl.UUCP (Chris Straut) writes: >| In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: >| > Why is it that strncpy(3) does not null terminate >| >the string if the length of the source string is greater than >| >or equal to the length of the target buffer? > I was asked this question just a few days ago mydelf. After some >thinking, this appears to be consistent with other behavior, namely >fgets() which drops the trailing newline if the buffer is too small. ^^^^^^^ > We hacked out a quick version which does always terminate the string >rather than add logic to all the places in which it was being used. Since it was mentioned, has anyone but me ever wondered WHY fgets() should return the newline at all? 'gets()' doesn't. I finally got tired of stripping off newlines, and created 'fgetsn()', which I prefer for reading in text lines from a file.... #include <stdio.h> /* Function which behaves identically to fgets(), except that the trailing newline is not included in the string. It always pissed me off that fgets() and gets() differ in this behaviour, complicating the life of any programmer who may wish to take input from either a file or stdin, and treat them the same. It also makes a mess of strcmp()'s from a file. If you feel the same way, write your congressman or ANSI, or add this function to libc. */ char * fgetsn(string,count,stream) char * string; int count; register FILE * stream; { int ch; register char * pointer = string; if(count <= 0) return(NULL); while (( -- count) && ((ch = getc(stream)) != '\n')) { if(ch == EOF) { if(pointer == string) return(NULL); break; } * pointer ++ = ch; } * pointer = '\0'; return(string); } ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Mike Macgirvin Relativity Gyroscope Experiment (GP-B) + + mike@relgyro.stanford.edu (36.64.0.50) + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
poser@csli.Stanford.EDU (Bill Poser) (12/23/89)
Jerry Schwartz suggests that my question about why strncpy does not null terminate was ill-formulated because I didn't say what sort of answer I was looking for. I disagree. Sometimes only one kind of answer is appropriate. For example, if you're designing a language the only kind of answer you are interested in is rationale, so you can decide if the comparable construct in your own language should work that way. But if you want to know why a feature of a language is the way it is, then the nature of the explanation is open. It may be that there is an explicit rationale or it may be that there is no particular rationale for doing it that way and that the reason is purely historical. But you don't know in advance.
bill@twwells.com (T. William Wells) (12/24/89)
In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes:
: So I claim that strncpy is mis-designed and should
: null-terminate. Any comments?
Who cares? I claim that having an appendix makes a human
mis-designed and that we should not have one. That won't change a
thing.
Strncpy is what it is. If you want a new function, create it.
---
Bill { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com
poser@csli.Stanford.EDU (Bill Poser) (12/24/89)
In response to my query about why strncpy does not null terminate Dr. T. Andrews <cdis-1!tanner@uunet.UU.NET> writes: >Your brain is probably broken. The reason that strncpy() does >not null terminate is so that the supplied buffer is not over-run. >If it were to do as you suggest, then it would be of no value as the >data following the destination might be randomly clobbered. >Alternatively, truncating the source one byte too soon would be >unhelpful: you would lose data for which you had space. (Since direct mail seems to be problematic, I respond here.) One can't help but be overwhelmed by this charming, witty, and insightful response. Only a genius like Dr. Tanner would think of not overflowing the buffer. The "waste" of the terminating null is REQUIRED so long as one conforms to the usual, null-terminated representation of strings in C. All other representations of arbitrary length strings require similar overhead, e.g. a character count in the descriptor. The case in which the terminating null or character count can be dispensed with is that of strings with a known maximum length, as in pre-Berkeley style directory entries, for which I have been told strncpy was designed. In sum, as part of the standard C string implementation, strncpy is deviant. It was actually designed to deal with a different representation of strings, and doesn't quite form a coherent part of the standard string library.
john@chinet.chi.il.us (John Mundt) (12/24/89)
In article <8313@stiatl.UUCP> cns@stiatl.UUCP (Chris Straut) writes: >In article <11509@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: >> Why is it that strncpy(3) does not null terminate >>the string if the length of the source string is greater than >>or equal to the length of the target buffer? > [ other stuff deleted ] > > >I agree that strncpy should null terminate the resulting string. The >definition of a string in the C language is a null terminated char array. >And the function called (str)ncpy indicates a string operation, which by >default should create a null terminated string. To overcome this 'feature' >we wrote our own strncpy function, which is benefical to the unsuspecting >programmer (or novice), and the result is a null terminated string. C has allowed you to shoot yourself in the foot if you want to. And the man page clearly says that the string is not null terminated if it runs to the full length permitted by the integer argument. Good reasons abound. For example, if you wish to replace a *part* of a string with something else, strncpy allows you to do that. Having an terminating \0 stuck in there would truncate the string and wipe out a character. You'd have to do some fancy footwork to get the tail end of the string stuck on the end in such a situation. Not to mention that with the length known, it is easy as pie to null terminate the string if necessary: #defind LENGTH 10 some_func(s1) char *s1; { char buf[500]; buf[LENGTH] = '\0'; (void)strncpy(buf, s1, LENGTH); } -- --------------------- John Mundt Teachers' Aide, Inc. P.O. Box 1666 Highland Park, IL john@admctr.chi.il.us *OR* fred@teacha.chi.il.us (312) 998-5007 (Day voice) || -432-8860 (Answer Mach) && -432-5386 Modem
brucet@cpsc.ucalgary.ca (Bruce Thompson) (12/24/89)
I must be naive, and may be repeating things others have put in, but it seems
to me that if null termination is not needed/wanted, why not use bcopy?
==============================================================================
Bruce Thompson | "I've got this terrible pain in all the
NovAtel Communications Ltd. | diodes down my left side" - Marvin the
| Paranoid Android
The opinions expressed are my own, and do not neccesarily reflect those of
NovAtel Communications Ltd. nor those of The University of Calgary.
thorinn@skinfaxe.diku.dk (Lars Henrik Mathiesen) (12/27/89)
brucet@cpsc.ucalgary.ca (Bruce Thompson) writes: >I must be naive, and may be repeating things others have put in, but it seems >to me that if null termination is not needed/wanted, why not use bcopy? strncpy() seems to be intended for a fixed-length, null-padded string representation. bcopy() can, indeed, be used to copy such a string to another (of the same length), but strncpy is necessary to convert null-terminated strings to null-padded strings. In applications where the null-padded strings are full-word aligned, they have the further advantage that they can be moved and compared (for equality) with full-word operations. (On big-endian machines with unsigned char's, even ordered comparisons can be done that way.) Here's how you convert between the formats: #define LENGTH 14 /* Today's magical number */ char padded[LENGTH]; char terminated[LENGTH + 1]; /* Move terminated to padded */ strncpy(padded, terminated, LENGTH); /* Move padded to terminated */ strncpy(terminated, padded, LENGTH); terminated[LENGTH] = '\0'; Actually the last move could just as well use bcopy(). If you need to guarantee that the move only modifies the destination up to the terminating null, no single library routine seems to fill the ticket. A buffer with a permanent extra null, maybe: /* Move padded to terminated, no extra nulls */ static __move_buffer[LENGTH + 1]; bcopy(padded, __move_buffer, LENGTH); strcpy(terminated, __move_buffer); Given the general lack of support for null-padded strings in the standard library, you will either have to do something like that before accessing one at all (even getting its length), or you will have to write a set of support routines yourself. Come to think of it, strnlen() would be really useful sometimes --- or even, safe_strlen(const char *str, size_t max): Returns the length of the string pointed to by _str_ if _str_ points to valid memory and the string is not longer than _max_, otherwise it returns -1. But this is more a question of safe programming than of string representation. -- Lars Mathiesen, DIKU, U of Copenhagen, Denmark [uunet!]mcvax!diku!thorinn Institute of Datalogy -- we're scientists, not engineers. thorinn@diku.dk
tanner@ki4pv.UUCP (Dr. T. Andrews) (12/27/89)
In <11527@csli.Stanford.EDU>, From: poser@csli.Stanford.EDU (Bill Poser) writes... ) Only a genius like Dr. Tanner would think of not overflowing ) the buffer. At the very least, we may assume that people at Stanford have not considered allocating a buffer of sufficent size for their purposes. ) The "waste" of the terminating null is REQUIRED so long as one ) conforms to the usual, null-terminated representation of strings ) in C. If one had strings which fit neatly into that mold, one would surely use strcpy(), not strncpy(). Except, of course, at Stanford, where strncpy() and on buffers of known size are an important subject of research; we expect Mr. Poser's paper on the same shortly. ) All other representations of arbitrary length strings require ) similar overhead, e.g. a character count in the descriptor. I beg to differ. By defining storage fields of a certain size, and using strncpy() to manipulate them, "strings" of that size may be handled without character counts in the descriptors. That a function strncpy() has been provided for such a cases is nice, but simply a convenience. That they don't teach people about character arrays of known size at Stanford is purely a local teaching decision and therefore not a suitable subject for outside debate. Of course, it may not be all of Stanford. It may just be that Mr. Poser's brain is broken. ...!{bikini.cis.ufl.edu allegra attctc bpa uunet!cdin-1}!ki4pv!tanner
tanner@cdis-1.UUCP (Dr. T. Andrews) (01/02/90)
In <11527@csli.Stanford.EDU>, poser@csli.Stanford.EDU (Bill Poser) writes... ) In response to my query about why strncpy does not null terminate ... Your consideration of strcpy()/strncpy() is more than a little reminiscent of a man who has in hand a claw hammer and a ball peen hammer, and who wishes to know why he can't pull nails with the latter. -- {attctc gatech!uflorida}!ki4pv!cdis-1!tanner {bpa uunet}!cdin-1!cdis-1!tanner
konath@sj.ate.slb.com (Kannan Konath) (01/03/90)
I had asked this question, around two weeks ago. I received a good number of answers by email. Thanks for the replies. I had also promised to post a summary. Well here goes.... The question was: Why does the C compiler not come up with an error when a function, say foo, of zero arguments gets called in the following manner. foo; /* the parentheses are missing */ Answer: I did not know it at that time, but the above statement "foo;" just generates the address of the function foo. Since expressions are allowed to stand all by themselves in C, this passes and rightly so. In fact this statement in particular has a null effect, though in my opinion expressions without any side effect should generate warnings but then I suppose C's philosophy lies behind this lack of warnings even in cases like this. To detect these sort of errors, lint can be used. In particular if you are working on a BSD Unix system you need to use the "-h" flag which points this error out.
john@wsl.UUCP (John Allen on wsl) (01/05/90)
strncpy() copies the termination NULL byte unless it would overrun the specified buffer. THIS IS COOL BEHAVIOUR. Why the hell else have a MAX byte count specification. -- People that don't know want to know from the people that do know and if the poeple that do know don't tell the people that don't know then the people that don't know still won't know. "Don't quote me on any issue whatsoever."
poser@csli.Stanford.EDU (Bill Poser) (01/06/90)
In article <000003Q@cdis-1.UUCP> tanner@cdis-1.UUCP (Dr. T. Andrews) writes: >Your consideration of strcpy()/strncpy() is more than a little >reminiscent of a man who has in hand a claw hammer and a ball peen >hammer, and who wishes to know why he can't pull nails with the >latter. Not really. It's more like the question of why the carpentry shop has a ball peen hammer (used for metal working) and doesn't have a claw hammer. strncpy is a function evidently designed for use with fixed-length character buffers, not the null-terminated strings that are semi-standard in C. That makes one wonder why strncpy is included with functions that are intended for use with null-terminated strings, and why there is no "safe" copying function for the latter.
poser@csli.Stanford.EDU (Bill Poser) (01/06/90)
In response to my claim that: All other representations of arbitrary length strings require similar overhead, e.g. a character count in the descriptor. in article <0000006@ki4pv.UUCP> tanner@ki4pv.UUCP (Dr. T. Andrews) writes: >I beg to differ. By defining storage fields of a certain size, and >using strncpy() to manipulate them, "strings" of that size may be >handled without character counts in the descriptors. This is irrelevant. Fixed size strings are not arbitrary length strings Please read messages before responding to them. He continues: >That they don't teach people about character arrays of known size >at Stanford is purely a local teaching decision and therefore not >a suitable subject for outside debate. There is of course no basis for the inference that I do not know about fixed size strings. Dr. Andrews seems to know this, as he writes: at Stanford, where strncpy() and on [sic] buffers of known size are an important subject of research; we expect Mr. Poser's paper on the same shortly. In any case, I have no idea what is taught on this subject at Stanford, as I am neither a student nor a member of the Computer Science faculty. I first began to use C at Bell Labs.
kuro@vodka.Sun.Com (Teruhiko Kurosaka - Sun Intercon) (01/06/90)
In article <11616@csli.Stanford.EDU> poser@csli.Stanford.EDU (Bill Poser) writes: |are semi-standard in C. That makes one wonder why strncpy is included |with functions that are intended for use with null-terminated strings, |and why there is no "safe" copying function for the latter. I have no opinion about philosophical correctness of the current behavior of strncpy() but always do: strncpy(to, from, n); to[n]=0; This ensures the copied string is always null-terminated, if this is what you mean by "safe". -- ------- T. Kurosaka ("Kuro") --- Sun Microsystems, Intercontinental Operation Internet:kuro@Corp.Sun.Com Voice:+1(415)496-6121/336-5921 Fax:+1(415)858-0284 Mail: Mail Stop A6-18, 1870 Embarcadero Rd., Palo Alto, CA 94303, USA
bill@twwells.com (T. William Wells) (01/07/90)
In article <KURO.90Jan6014431@vodka.Sun.Com> kuro@Corp.Sun.Com writes:
: I have no opinion about philosophical correctness of the current behavior of
: strncpy() but always do:
: strncpy(to, from, n);
: to[n]=0;
: This ensures the copied string is always null-terminated, if this is what you
: mean by "safe".
Maybe you meant "to[n - 1]=0;"?
If the size of to is n, to[n] = 0 is illegal.
---
Bill { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com
kyriazis@ptolemy0.rdrc.rpi.edu (George Kyriazis) (01/08/90)
In article <1990Jan7.145719.15574@twwells.com> bill@twwells.com (T. William Wells) writes: >In article <KURO.90Jan6014431@vodka.Sun.Com> kuro@Corp.Sun.Com writes: >: I have no opinion about philosophical correctness of the current behavior of >: strncpy() but always do: >: strncpy(to, from, n); >: to[n]=0; > >Maybe you meant "to[n - 1]=0;"? > >If the size of to is n, to[n] = 0 is illegal. > I think he means to[n] = 0. strncpy() moves at most n bytes (0 to n-1), therefore byte n is not copied. If you want to keep a string of length n you better have a char array of n+1 characters.. George Kyriazis kyriazis@turing.cs.rpi.edu kyriazis@rdrc.rpi.edu ------------------------------
msb@sq.sq.com (Mark Brader) (01/08/90)
> strncpy is a function evidently designed for use with > fixed-length character buffers, not the null-terminated strings that > are semi-standard in C. That makes one wonder why strncpy is included > with functions that are intended for use with null-terminated strings, > and why there is no "safe" copying function for the latter. The first question has, in effect, already been answered: the behavior of strncpy() was that required for writing entries in UNIX directories. It was natural to put it in the library so that programs could use it for other purposes, and once it became common for them to do so, it had to remain there forever for compatibility. (One example of an "other purpose", by the way, is strncpy(dest, "", n), which puts all zero bits in the destination without needing an ifdef to choose bzero() or memset(). I make no claim that it's fast or readable, but it's certainly legitimate.) As to the second question, there IS a simple way to get the effect of if (strlen (src) > n) { strncpy(dest, src, n); dest[n] = '\0'; } else strcpy(dest, src); (where we don't want to take the time to fill the destination with trailing nulls in the "else" case, otherwise we could just use strncpy). That way is: sprintf (dest, "%.*s", n, src); (or if n is known to be, say, 17, then sprintf (dest, "%.17s", src);). In practice this, too, may of course be too slow. But then, so may strcpy(). -- Mark Brader, Toronto "Mark is probably right about something, utzoo!sq!msb, msb@sq.com but I forget what" -- Rayan Zachariassen This article is in the public domain.
ellisond@phobos.UUCP (Dell Ellison) (01/23/90)
In article <11616@csli.Stanford.EDU>, poser@csli.Stanford.EDU (Bill Poser) writes: > In article <000003Q@cdis-1.UUCP> tanner@cdis-1.UUCP (Dr. T. Andrews) writes: > >Your consideration of strcpy()/strncpy() is more than a little > >reminiscent of a man who has in hand a claw hammer and a ball peen > >hammer, and who wishes to know why he can't pull nails with the > >latter. > > Not really. It's more like the question of why the carpentry shop > has a ball peen hammer (used for metal working) and doesn't have > a claw hammer. strncpy is a function evidently designed for use with > fixed-length character buffers, not the null-terminated strings that > are semi-standard in C. That makes one wonder why strncpy is included > with functions that are intended for use with null-terminated strings, > and why there is no "safe" copying function for the latter. Actually, the reason why they are 'null-terminated strings' is because they are variable in length. If you are using strncpy then you want an exact number of characters. If you want n characters then you don't want to get n+1 characters. Besides, in C, nothing is completely safe. I think we should all take responsability for our own strings. :-)
poser@csli.Stanford.EDU (Bill Poser) (01/23/90)
In article <48314938.f69e@phobos.UUCP> ellisond@phobos.UUCP (Dell Ellison) writes: > >Actually, the reason why they are 'null-terminated strings' is because >they are variable in length. If you are using strncpy then you want an >exact number of characters. I think this reflects a confusion between variability in the size of strings and variability in the size of string containers. (I don't know if this is a standard term, but Bron & Dijkstra use it in a recent SIGPLAN notices.) What strncpy is good for (or would be good for if it null-terminated) is making sure that the copy does not overflow the string container. The container may be (and usually is) used for strings of variable length, and the strings copied may be of variable length. At least in my experience, it is generally not the case that one is trying to truncate the source string to exactly N characters - rather, one is trying to copy as much of the source string as will fit into the target container. Note also that strncpy does not copy "an exact number of characters". It copies up to N characters, stopping at the first null. In other words, it observes the null-termination convention for the source string, and does so for the target string if it isn't too long. That's why it is a bastard function - it is designed to work with the null-termination convention but does not guarantee closure.
daveh@marob.masa.com (Dave Hammond) (01/23/90)
In article <11864@csli.Stanford.EDU> poser@csli.stanford.edu writes: >Note also that strncpy does not copy "an exact number of characters". >It copies up to N characters, stopping at the first null. From the Xenix manual page for string(S): char *strncpy(S1,S2,N) ... strncpy copies exactly N characters, truncating or null-padding S2; the target may not be null-terminated if the length of S2 is N or more. ... -- Dave Hammond daveh@marob.masa.com uunet!masa.com!marob!daveh
davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (01/23/90)
In article <25BC3A32.3F5B@marob.masa.com> daveh@marob.masa.com (Dave Hammond) writes: | From the Xenix manual page for string(S): | | char *strncpy(S1,S2,N) | ... | strncpy copies exactly N characters, truncating or null-padding S2; the | target may not be null-terminated if the length of S2 is N or more. | ... Regardless of what the manual says, it does not seem to work that way. The copy is *at most* N characters, but will stop is a null byte is encountered. You can verify this by placing data after the null byte in the source string and checking that it is not copied. I believe the procedure which functions as described is memcpy(). -- bill davidsen (davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen) "Stupidity, like virtue, is its own reward" -me
peter@ficc.uu.net (Peter da Silva) (01/23/90)
> That's why it is a bastard function - it is designed to work with > the null-termination convention but does not guarantee closure. What it does is provide the most generally useful facility. If it always null-terminated, it couldn't be used for copies into a known fix-sized buffer. And since you know the length, you can always null-terminate the string yourself. -- _--_|\ Peter da Silva. +1 713 274 5180. <peter@ficc.uu.net>. / \ \_.--._/ Xenix Support -- it's not just a job, it's an adventure! v "Have you hugged your wolf today?" `-_-'
brnstnd@stealth.acf.nyu.edu (01/24/90)
In article <ZC91DJ4xds13@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: > > That's why it is a bastard function - it is designed to work with > > the null-termination convention but does not guarantee closure. > What it does is provide the most generally useful facility. No. > If it always > null-terminated, it couldn't be used for copies into a known fix-sized > buffer. Huh? strncpy(s1,s2,n) should work like the current strncpy(s1,s2,n) followed by s1[n - 1] = '\0' if necessary. That's a copy into a known fixed-size buffer, preserving null termination. > And since you know the length, you can always null-terminate > the string yourself. Since I do, in fact, always null-terminate the string myself, I'd prefer that strncpy do the job for me. ---Dan
poser@csli.Stanford.EDU (Bill Poser) (01/24/90)
In article <25BC3A32.3F5B@marob.masa.com> daveh@marob.masa.com (Dave Hammond) writes: >From the Xenix manual page for string(S): > >char *strncpy(S1,S2,N) >... >strncpy copies exactly N characters, truncating or null-padding S2; the >target may not be null-terminated if the length of S2 is N or more. The manual page does indeed say that strncpy(3) copies exactly N characters, but as William Davidsen has pointed out, the manual, at least on some systems, is wrong. Here is a little test program. #include <stdio.h> main() { char src[20]; char tgt[20]; sprintf(src,"%s","abcdefg"); printf("src = %s\n",src); src[3] = '\0'; printf("src = %s\n",src); strncpy(tgt,src,5); printf("tgt = %s\n",tgt); exit(0); } It puts the string "abcdefg" (7 characters plus a null) into src and prints it out. Then it assigns a null to the 4th position, overwriting the "d" and prints it out. Then it strncpy's it to tgt, requesting a 5 character copy, and prints the result. I just compiled and ran this program on: (a) a SUN 4 running SUN-OS; (b) an HP 9000/350 running HP-UX, and (c) an HP 9000/320 running 4.3 Tahoe BSD. On all three the result was that the null byte terminated the copy. Here is the script from the HP 350: Script started on Tue Jan 23 14:58:27 1990 crystals-[1]/user2/poser : foo src = abcdefg src = abc tgt = abc crystals-[2]/user2/poser : script done on Tue Jan 23 14:58:33 1990 If strncpy copied exactly N characters, the third output line should read: tgt = abcde On all three systems the manual says that strncpy copies exactly N characters, and on all three it is wrong.
poser@csli.Stanford.EDU (Bill Poser) (01/24/90)
As a further clarification of what strncpy(3) actually does,
let me point out that although it only COPIES up to the null byte in the
source string, it WRITES exactly N characters into the target string,
padding with nulls if necessary. So the bit about null-padding in the manual
is correct - what is erroneous is the bit about copying. To see this,
try a program like this:
#include <stdio.h>
main()
{
char src[20];
char tgt[20];
sprintf(src,"%s","abcdefg");
sprintf(tgt,"%s","abcdefg");
printf("src = %s\n",src);
printf("tgt = %s\n",tgt);
src[3] = '\0';
printf("src = %s\n",src);
strncpy(tgt,src,5);
printf("tgt = %s\n",tgt);
printf("tgt[4] = %c\n",tgt[4]);
printf("tgt[5+] = %s\n",&(tgt[5]));
exit(0);
}
It initializes both src and tgt to "abcdefg", assigns a null to src[3],
does the strncpy, and then prints the various pieces of tgt. The
result is:
Script started on Tue Jan 23 15:34:49 1990
crystals-[1]/user2/poser
: foo
src = abcdefg
tgt = abcdefg
src = abc
tgt = abc
tgt[4] =
tgt[5+] = fg
crystals-[2]/user2/poser
:
script done on Tue Jan 23 15:34:57 1990
Notice that the fifth character of tgt, originally "e", has been
overwritten by a null, but that the remainder of tgt is unaffected.
So, what strncpy does is to COPY up to the null byte or N characters,
whichever comes first, and then null-pad out to the N character limit.
poser@csli.Stanford.EDU (Bill Poser) (01/24/90)
Just a note to point out that the test of printf-ing the result of a strncpy is not sufficient to show that it stops copying at the null byte (contrary to my previous statement). It is necessary to check what character is in the target string after the null. As my previous message shows, this is in fact a null, showing that copying does stop at the null byte.
john@stat.tamu.edu (John S. Price) (01/24/90)
In article <11881@csli.Stanford.EDU> poser@csli.stanford.edu (Bill Poser) writes: > >The manual page does indeed say that strncpy(3) copies exactly >N characters, but as William Davidsen has pointed out, the manual, >at least on some systems, is wrong. Here is a little test program. > >#include <stdio.h> >main() >{ > char src[20]; > char tgt[20]; > > sprintf(src,"%s","abcdefg"); > printf("src = %s\n",src); > src[3] = '\0'; > printf("src = %s\n",src); > strncpy(tgt,src,5); > printf("tgt = %s\n",tgt); > > exit(0); >} > >It puts the string "abcdefg" (7 characters plus a null) into src >and prints it out. Then it assigns a null to the 4th position, overwriting >the "d" and prints it out. Then it strncpy's it to tgt, requesting a 5 >character copy, and prints the result. I just compiled and ran this program >on: (a) a SUN 4 running SUN-OS; (b) an HP 9000/350 running HP-UX, and >(c) an HP 9000/320 running 4.3 Tahoe BSD. On all three the result was that >the null byte terminated the copy. Here is the script from the HP 350: > >Script started on Tue Jan 23 14:58:27 1990 >crystals-[1]/user2/poser >: foo >src = abcdefg >src = abc >tgt = abc >crystals-[2]/user2/poser >: >script done on Tue Jan 23 14:58:33 1990 > >If strncpy copied exactly N characters, the third output line should >read: tgt = abcde >On all three systems the manual says that strncpy copies exactly N >characters, and on all three it is wrong. Every manual I read says this: "... If the string pointed to by the source has fewer than count characters, nulls are added to the end of dest until count characters have been copied." -- C - The Complete Reference. "... strncpy(s,t,n) copy exactly n characters; null pad if necessary" -- Unix Programming Environment The key to this is the way C, or for that matter, ASCIIZ, string convention works. A series of characters followed by a NULL is considered a string. So, if you have a string foo[] = "This is a test of the Emergency Broadcast System."; and say foo[14] = NULL; You are CHANGING THE STRING LENGTH. It is now 14 characters long, instead of 49. back to the example in question... The only was strncpy can know if the source has fewer than count (in the program example 5) characters is if it's null terminated or not. So, when you say src[3]=0; you, by definition of the ASCIIZ convention, have shortened the string to 3 characters, namely "abc". When strncpy(tgt,src,5) gets a pointer to "abc\000efg" for the source, all it considers to be the STRING is "abc" and that is all it copies to tgt. It pads tgt with NULLs up to N, the number of characters specified to copy. You have to remember that all of these are STRING FUNCTIONS, not block memory move functions. If you wanted to copy the 5 bytes from src to tgt, do memcpy(tgt,src,5); But, printf would still print out "abc" for tgt, because it is NULL terminated. Only difference is that tgt[4]=='e' now, instead of NULL. strncpy() is acting exactly to the manuals specs. When it says is will copy EXACTLY 5 characters from the STRING src to the destination STRING tgt, it copys 5 characters from the string. Since strings are NULL terminated, strncpy has to assume that anything after the NULL is garbage, and not part of the string. It assumes that tgt will have N bytes allocated to it, but doesn't assume that src has at least N bytes. If src is larger than N, then it copys N characters to tgt. It doesn't NULL terminate tgt because it can't assume that tgt is N+1 bytes long. Just to clear things up... -------------------------------------------------------------------------- John Price | It infuriates me to be wrong john@stat.tamu.edu | when I know I'm right.... --------------------------------------------------------------------------
john@stat.tamu.edu (John S. Price) (01/24/90)
In article <11882@csli.Stanford.EDU> poser@csli.stanford.edu (Bill Poser) writes: >...[stuff deleted]... >So, what strncpy does is to COPY up to the null byte or N characters, >whichever comes first, and then null-pad out to the N character limit. This is exactly right. Strncpy copys exactly N characters from source to destination. If N > strlen(source) then strncpy cannot assume that the bytes after the NULL in source are part of the string. The manuals aren't ambiguous if you take into consideration the definition of a string. -------------------------------------------------------------------------- John Price | It infuriates me to be wrong john@stat.tamu.edu | when I know I'm right.... --------------------------------------------------------------------------
peter@ficc.uu.net (Peter da Silva) (01/24/90)
In article <9683@stealth.acf.nyu.edu> brnstnd@stealth.acf.nyu.edu (Dan Bernstein) writes: > > If it always > > null-terminated, it couldn't be used for copies into a known fix-sized > > buffer. > Huh? strncpy(s1,s2,n) should work like the current strncpy(s1,s2,n) > followed by s1[n - 1] = '\0' if necessary. That's a copy into a known > fixed-size buffer, preserving null termination. But what if you want to keep all !n! bytes of information? Then you have to make your buffer 1 byte larger. Which can be a pain in a fixed structure. Let's put it this way. It's easy to implement *your* strncpy in the *real* strncpy, but it's not even possible to do it the other way round. > Since I do, in fact, always null-terminate the string myself, I'd prefer > that strncpy do the job for me. Since I don't, in fact, always null-terminate the string myself, I'm glad it doesn't. Meditate on struct utmp. -- _--_|\ Peter da Silva. +1 713 274 5180. <peter@ficc.uu.net>. / \ \_.--._/ Xenix Support -- it's not just a job, it's an adventure! v "Have you hugged your wolf today?" `-_-'
dave@motto.UUCP (David Brown) (01/24/90)
In article <4128@helios.TAMU.EDU> john@stat.tamu.edu (John S. Price) writes: >Every manual I read says this: >"... If the string pointed to by the source has fewer than count >characters, nulls are added to the end of dest until count characters >have been copied." -- C - The Complete Reference. > >"... strncpy(s,t,n) copy exactly n characters; null pad if necessary" > -- Unix Programming Environment > We found this useful when dealing with the keys of ISAM files under VMS. If the key was a C string, we used strncpy() to copy it rather than strcpy(). This guaranteed that the bytes following the '\0' were all zeroes. This was important because the filesystem compared all bytes of the key. If we had used strcpy(), two keys which were the same string value could be different as far as the filesystem was concerned, because of garbage following the '\0'. ----------------------------------------------------------------------------- | David C. Brown | uunet!mnetor!motto!dave | | Motorola Canada, Ltd. | 416-499-1441 ext 3708 | | Communications Division | "Of course I'm above average! Who isn't?" | -----------------------------------------------------------------------------
rli@buster.irby.com (Buster Irby) (01/25/90)
brnstnd@stealth.acf.nyu.edu writes: >Since I do, in fact, always null-terminate the string myself, I'd prefer >that strncpy do the job for me. You have obviously never worked with any vendor supplied software which used non null-terminated buffers. For example, when you access a string data field under Unify, there is no null termination at the end of the field, because the string fields are fixed length. It is not necessary to terminate fixed length items with any kind of special marker. Special markers, such as the null character, are only useful when dealing with variable length data. Furthermore, in any database, I would consider it very inefficient to store null terminators. -- Buster Irby buster!rli