Date: Sun, 17 Jun 2018 14:50:35 +1000 (EST) From: Bruce Evans <brde@optusnet.com.au> To: Jilles Tjoelker <jilles@stack.nl> Cc: Bruce Evans <brde@optusnet.com.au>, Eitan Adler <eadler@freebsd.org>, src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: Re: svn commit: r335041 - head/lib/libc/stdlib Message-ID: <20180617134656.I1009@besplex.bde.org> In-Reply-To: <20180613211443.GA57326@stack.nl> References: <201806130852.w5D8qH9a093758@repo.freebsd.org> <20180613194008.W2003@besplex.bde.org> <20180613211443.GA57326@stack.nl>
next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, 13 Jun 2018, Jilles Tjoelker wrote: > On Wed, Jun 13, 2018 at 08:03:13PM +1000, Bruce Evans wrote: >> On Wed, 13 Jun 2018, Eitan Adler wrote: > >>> Log: >>> libc: remove explicit cast NULL in atoi > >>> There isn't any reason to cast NULL so just remove it. Noticed when >>> cleaning up top. > >> There are many reasons to cast NULL for all members of the ato*() family: >> - it is required if no prototype is in scope >> - C99 specifies ato*() in terms of strtol*() and uses the cast to NULL, >> ... >> - POSIX specifies ato*() in terms of strtol*() and uses the cast to NULL, >> ... > > These reasons can be summarized to a single reason: the cast is required > if no prototype is in scope. > > I think it is unwise to call any function without a prototype in scope, > since this runs a risk of undefined behaviour if you get the types > wrong. That is a style matter, so it must not be enforced by specifications or man pages, and specifications and man pages should not have details to advocate it or to say when a prototype is needed for every single function. > For the code in libc, we ensure a prototype is in scope and no cast is > required. For the code in the man page, I doubt we should allow for > programmers that play tricks by declaring system functions manually > using K&R-style declarations or (even worse) call functions without > declaring them at all. Programmers who understand prototypes have no difficulty with not using them. libc isn't even controlled by us. Its API is specified by the standard selected by the user. Even C99 doesn't require prototypes. > Note that NULL may still need a cast when passed to a function with > variable number of parameters. Ideally these types are also checked > using attributes. A cast is almost needed for this NULL too. strtol()'s endptr arg should have type const char * so that strtol() can handle const char * string args without needing to cast away const in the caller. But then strtol() couldn't handle plain char * string args without needing worse casting away of const for the variable pointing to endptr. This variable would have type char * so its address would have type char **. C's type system is too weak for safe conversion of this to const char **, so the prototype is not allowed to do it without a diagnostic and an explicit cast must not be used. This cast is also dangerous, so the problem is reduced by using plain char ** for endptr. Since NULL is either an integer constant with value 0 or such a constant cast to unqualified void *, it can be converted without a diagnostic to either char ** or const char **. This is not obvious, and the explicit cast makes it clearer that conversion to plain char ** is really intended. >> FreeBSD used to do the same here, and should do the same here and >> elsewhere by copying better wording from POSIX whenever possible. > > For some reason, the FreeBSD text does not have the exception about > error handling. This exception permits an implementation like musl's > which calculates using int and hard-codes base 10, even if the compiler > documents a cast from long to int as truncating bits. I don't think we > should take advantage of this, though, since making atoi() faster than > strtol() may encourage people to use atoi(). Encouraging use of atoi() would almost be correct. All it needs to be a good API is defined error handling for unrepresentable values and garbage input. Too bad if the programmer doesn't check for errors. Error checking for strtol() is very rarely complete or correct. Unfortunately, atoi() isn't allowed to handle garbage input correctly. It is only allowed to do the right thing for unrepresentable values. I would clamp atoi() to INT_MAX/MIN and return ERANGE for unrepresentable values instead of blindly truncating long values. This already happens accidentally with 32-bit longs. FreeBSD's man page even documents this by giving the implementation detail for the undefined behaviour. Bruce
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20180617134656.I1009>