Code Comments
Programming Forum and web based access to our favorite programming groups."Mike Wahler" <mkwahler@mkwahler.net> wrote: > "asm" <arut@post.com> wrote: "typedef" is just a way of naming an arbitrary abstract data type. It has nothing to do with portability per se (except that all C compilers support it). You may be thinking of "#ifdef" which you misheard from some conversation. > Yes, C has 'support' for portablility, in that it > is by definition a portable, platform-independent language. Excuse me, but C is *NOT* a platform-independent language. It specifically states in the ANSI standard that certain operations such as the right shift of signed integers, or the signedness of char are platform specific. By the same token C is not a portable language. Virtually every C compiler exposes important platform specific functionality *by neccessity* and the vast majority of C programs specifically leverage these extensions which makes them non-portable. Compare this with Java, where the only way to make a non-portable Java program is to chew through insane amounts of memory (different platforms will fault at different times), or have a race condition (different timings will cause the race condition to manifest in different ways). > However, one must follow the language rules in order to achieve said > portability. No real world programmers deliberately follow any "rules" that would make a C program completely portable. People may do so inadvertently and usually if their program is completely trivial. Remember that the real purpose of the C language is for making UNIX-style command line utilities. Anything else you try to do with the language will, by itself, usually lead you away from portability (just using floating point on x86, where "double"s can have intermediate calculations of either 64 or 80 bits depending on the compiler *version*), and just ordinary "expected usage" (<isspace(0x80)> is UB, <int digit = (char)cdig - '0';> is not portable) will drive you toward "platform depdendence" with respect to the C language. -- Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Post Follow-up to this messageqed@pobox.com (Paul Hsieh) wrote in message news:<796f488f.0409231810.6904559e@posting.goog le.com>... > "Mike Wahler" <mkwahler@mkwahler.net> wrote: > > "typedef" is just a way of naming an arbitrary abstract data type. It > has nothing to do with portability per se (except that all C compilers > support it). You may be thinking of "#ifdef" which you misheard from > some conversation. Are you being deliberately obtuse? Whether it has anything to do with portability "per se", it's widely used to aid portability. The current C standard even explicitly uses it for that purpose: 7.18.1.1 Exact-width integer types 1 The typedef name intN_t designates a signed integer type with width N. Thus, int8_t denotes a signed integer type with a width of exactly 8 bits. 2 The typedef name uintN_t designates an unsigned integer type with width N. Thus, uint24_t denotes an unsigned integer type with a width of exactly 24 bits. > > > Excuse me, but C is *NOT* a platform-independent language. It > specifically states in the ANSI standard that certain operations such > as the right shift of signed integers, or the signedness of char are > platform specific. By the same token C is not a portable language. > > Virtually every C compiler exposes important platform specific > functionality *by neccessity* and the vast majority of C programs > specifically leverage these extensions which makes them non-portable. > What's your point? C can be used to write portable programs that can run on anything from the largest supercomputers to tiny microcontrollers. It can also be used to write hideously unportable programs that break every time the compiler vendor issues a point release. > Compare this with Java, where the only way to make a non-portable Java > program is to chew through insane amounts of memory (different > platforms will fault at different times), or have a race condition > (different timings will cause the race condition to manifest in > different ways). To me, C is *much* more portable than Java. There are many, many platforms out there that do not have anything even remotely approximating a Java runtime, but have a competent C compiler. > > > No real world programmers deliberately follow any "rules" that would > make a C program completely portable. People may do so inadvertently > and usually if their program is completely trivial. Remember that the > real purpose of the C language is for making UNIX-style command line > utilities. <snip> Now you're just being silly. You follow the portability rules as much as possible, and isolate platform specific code as much as possible. Avoid undefined behavior, and isolate implementation defined behavior. I suppose you'll find some reason that does not qualify as a "rule". Mark F. Haigh mfhaigh@sbcglobal.net
Post Follow-up to this messageIn <lnvfdz2t30.fsf@nuthaus.mib.org> Keith Thompson <kst-u@mib.org> writes: >Dan.Pop@cern.ch (Dan Pop) writes: > >Perhaps, but I think a lot of programmers would automatically >associate the name "myDouble" with C's type "double". That would be a downright foolish association: no point in *unconditionally* renaming a C keyword. >If I wanted to >create typedefs for small and large floating-point types, regardless >of what the C compiler chooses to call them, I might use terms like >"small_float" and "large_float" to avoid confusion. I can still see no confusion with mySingle and myDouble. It is sheer stupidity to assume anything a priori about a user defined identifier. >A typedef whose name is too close to a predefined type name is >probably either redundant (if it's for the same type) or confusing (if >it isn't). For you, maybe. To me, it conveys the message: this type is, in most cases, the same as the C type with a similar name, but it can be something else on unusual implementations. See again my example with myDouble ending up as being defined as long double on an exotic implementation, while being defined as double on most other implementations. >To take another example, "typedef unsigned char byte;" is more >reasonable than "typedef unsigned char uchar;", IMHO. To me, they are both unreasonable, being mere aliases for standard C types. When I read C code, I'm much happier if I don't have to deal with such devices (despite the obvious semantics, such typedef's MUST be checked, to be *sure* that they really are what they appear to be). >(One might >argue that "typedef unsigned char byte;" is redundant, but I might use >such a typedef to distinguish between an unsigned char holding a >character value and an unsigned char holding a fundamental unit of >storage, a distinction that C's type system, for convoluted historical >reasons, doesn't make very well.) The actual distinction is made by using the type char for dealing with character values and the type unsigned char for dealing with bytes. This is why the type char hasn't been made obsolete after the introduction of the types signed char and unsigned char. Dan -- Dan Pop DESY Zeuthen, RZ group Email: Dan.Pop@ifh.de Currently looking for a job in the European Union
Post Follow-up to this messagemfhaigh@sbcglobal.net (Mark F. Haigh) wrote: > qed@pobox.com (Paul Hsieh) wrote: > > Are you being deliberately obtuse? Whether it has anything to do with > portability "per se", it's widely used to aid portability. You mean the typedefs used header files for some C compilers? This is a very marginal claim about creating portability. And more to the point you typically can't use the include files from one compiler in another. When you have platform specific functionality which you want to expose in a portable way, you end up having to leverage #ifdef's in combination with already existing portable interfaces. There's no sense in which you rely in particular on typedef, other than using it to potentially expose a common naming. > [...] The > current C standard even explicitly uses it for that purpose: > > 7.18.1.1 Exact-width integer types > > 1 The typedef name intN_t designates a signed integer type with > width N. Thus, int8_t denotes a signed integer type with a > width of exactly 8 bits. > > 2 The typedef name uintN_t designates an unsigned integer type > with width N. Thus, uint24_t denotes an unsigned integer type > with a width of exactly 24 bits. What the hell has this got to do with portability? First of all its in reference to a C standard that is not widely adopted at all. So in fact use of intN_t will specifically destroy any chance of portability. Secondly, integers in C don't have a predescribed implementation (like 2s complement, for example), so the int16_t on one platform doesn't have to behave the same way as it might on any other platform (except maybe having the same "sizeof"). > > What's your point? C can be used to write portable programs that can > run on anything from the largest supercomputers to tiny > microcontrollers. It can also be used to write hideously unportable > programs that break every time the compiler vendor issues a point > release. The point is that there is no clear dividing line between these two classes of programs in C. And the vast majority of the functionality of your platform will mostly be in these non-portable extensions. > > To me, C is *much* more portable than Java. There are many, many > platforms out there that do not have anything even remotely > approximating a Java runtime, but have a competent C compiler. You are confusing availability with portability. By this reasoning assembly/machine language is even more portable since you have to start there to bootstrap any C compiler. To the best of my knowledge, in fact, there are no two C compilers in existence which are portable with each other (Intel C++ and MSVC are close since they actually share libraries and include files, but each contains different semantics and extensions). By contrast there are no two Java compilers/implementation which are *not* portable (modulo bugs). > <snip> > > Now you're just being silly. You follow the portability rules as much > as possible, and isolate platform specific code as much as possible. > Avoid undefined behavior, and isolate implementation defined behavior. > > I suppose you'll find some reason that does not qualify as a "rule". No, my claim is that such rules are just not followed. Who the hell rigorously test for signedness independence of "char"? C specifically says that char can be either signed or unsigned. And who keeps track of the difference between POSIX and ANSI? And if you are doing heavy floating point, you can't do anything to isolate such platform specific behaviors. -- Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Post Follow-up to this messageChris Torek <nospam@torek.net> wrote: > Paul Hsieh <qed@pobox.com> wrote: > > But good real-world C programmers will often follow many "rules" that > will make their C programs far more portable than if they fail to > follow them. > > > Sensible people do so deliberately. But my point is that they *fail*. The reason is simple -- the code continues to work and is correct for their platform even as they drift to non-portability. So there is no feedback mechanism by which a programmer can realize that what they are doing is non-portabile. Compare this to Java, where there is no choice about portability -- its just is. > > "Complete" portability will indeed only result for a quite limited > subset of "all possible C programs", but not all such programs > should be labeled "trivial", in my opinion. (For instance, yacc/bison > and lex/flex are not "trivial", yet can be written entirely in > portable C.) I said *usually*. Later on in my post I also made mention of UNIX command line utilities (which include yacc/bison.) > > True enough -- but the same problem occurs in other languages as > well. The x86 in particular is especially troublesome, because > "fast" code invariably leaves values inside the FPU, where they > have "too much" precision, even if you use precision-control code > to fiddle with the FPU control word. (In particular, the exponent > is always held in 80-bit format, even when the mantissa is rounded, > so that single and double precision values do not overflow to > infinity correctly.) This not exactly a correct characterization of the x86. The x86 has a 64bit "mode flag" which, in fact, has been turned on in the modern x86 compilers. The problem is with the C *STANDARD* that says x86 compilers could do whatever they want (and did, since in the early 90s the extra precision was seen as an advantage). -- Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Post Follow-up to this messagePaul Hsieh wrote: > "typedef" is just a way of naming an arbitrary abstract data type. If you are *really* concerned about portability, none of the built-in types should appear in the body of your code. Instead, you should substitute your own type definitions for all of the built-in types -- in a header file: typedef int myInteger; typedef float mySingle; typedef double myDouble; typedef size_t myExtent; // etc. #define myIntegerMax INT_MAX // etc. Remember that portability is *not* about writing code that compiles everywhere. It's about writing code that is easy to modify for each target platform. The best strategy is to *sequester* platform dependent code into header files and subprograms that are easy to locate and modify or replace.
Post Follow-up to this message>Chris Torek wrote: In article <news:4154934D.5DB3@mindspring.com> pete <pfiland@mindspring.com> wrote: >I think (int) would be a better cast, if a cast is needed. The cast is indeed not needed at all; I used "char" simply because that was the original example I was quoting. Note that while it *is* OK to assume that the digit-characters '0' through '9' are contiguous and sequential -- i.e., '0' + 1 gives '1', '1' + 1 gives '2', and so on through '8' + 1 == '9' -- it is *not* OK, in terms of C portability, to assume that the same holds for alphabetic characters. In particular, EBCDIC (used on some IBM mainframe machines for instance) has gaps between 'I' and J', and again beween 'R' and 'S': while 'H' + 1 gives 'I', you then have to add 7 or 8 (I forget which) to 'I' to get 'J'. Again, it is not *wrong* to simply "assume ASCII", but it does limit portability. If the tradeoff you gain (whoever "you" may be at the time, and whatever it is you gain) is worth the loss in portability (however large or small you may think it is), go ahead and give up the portability. Just be aware that you are making the assumption "this system uses ASCII", and if possible, document it or test for it. (These days one might even want to "assume Windows character set Q" or "assume UTF-8" or some such, in various applications. Just be aware that not every system has these things. If you *need* them, you need them, and you have to discard portability in that part of the program. If you can isolate the non-portable parts of the code from the portable parts, that will help when it comes time to port the program.) -- In-Real-Life: Chris Torek, Wind River Systems Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603 email: forget about it http://web.torek.net/torek/index.html Reading email is like searching for food in the garbage, thanks to spammers.
Post Follow-up to this messageIn article <news:796f488f.0409231810.6904559e@posting.google.com>
Paul Hsieh <qed@pobox.com> wrote:
>No real world programmers deliberately follow any "rules" that would
>make a C program completely portable.
But good real-world C programmers will often follow many "rules" that
will make their C programs far more portable than if they fail to
follow them.
>People may do so inadvertently
Sensible people do so deliberately.
>and usually if their program is completely trivial.
"Complete" portability will indeed only result for a quite limited
subset of "all possible C programs", but not all such programs
should be labeled "trivial", in my opinion. (For instance, yacc/bison
and lex/flex are not "trivial", yet can be written entirely in
portable C.)
>... Anything else you try to do with the language will, by
>itself, usually lead you away from portability (just using floating
>point on x86, where "double"s can have intermediate calculations of
>either 64 or 80 bits depending on the compiler *version*),
True enough -- but the same problem occurs in other languages as
well. The x86 in particular is especially troublesome, because
"fast" code invariably leaves values inside the FPU, where they
have "too much" precision, even if you use precision-control code
to fiddle with the FPU control word. (In particular, the exponent
is always held in 80-bit format, even when the mantissa is rounded,
so that single and double precision values do not overflow to
infinity correctly.)
>and just ordinary "expected usage" (<isspace(0x80)> is UB, <int digit =
>(char)cdig - '0';> is not portable)
The isspace() function is defined over the domain union{EOF,
all_possible_unsigned_chars}, and UCHAR_MAX must be at least 0xff.
Since 0x80 (128) is between 0 and 255 inclusive, isspace(128) is
well-defined.
Likewise, if "cdig" is a value in '0' through '9', the line:
int digit = (char)cdig - '0';
is guaranteed to set digit to 0 through 9 correspondingly. Moreover,
if you write instead:
unsigned int digit = (unsigned)cdig - '0';
you can then test whether cdig was a valid digit character:
if (digit > 9)
printf("%c was not a valid digit\n", cdig);
also with complete portability (given UCHAR_MAX <= UINT_MAX and
the rules for unsigned arithmetic).
There is nothing inherently wrong with using platform- or
machine-dependent code in C programs, of course. But comp.lang.c
is not the best place to talk about such code; instead, here we
can talk about how to *avoid* such code if and when it is reasonable
to do so (e.g., when dealing with text files, rather than graphical
interfaces).
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Post Follow-up to this message"Paul Hsieh" <qed@pobox.com> wrote > > No real world programmers deliberately follow any "rules" that would > make a C program completely portable. People may do so inadvertently > and usually if their program is completely trivial. > You are confusing "trivial" with "doesn't manipulate hardware". A large number of programs use a GUI, or network, or similar, however not all, particularly if you are writing scientific or similar programs. Often the only "non-portable" feature needed is directory support. Also, a lot of programs have inherently portable components. For instance a spell-checker is most useful when integrated with a wordprocessor, but the routine itself neden't have any platform dependencies.
Post Follow-up to this messageOn 25 Sep 2004 21:24:50 -0700, qed@pobox.com (Paul Hsieh) wrote: > mfhaigh@sbcglobal.net (Mark F. Haigh) wrote: <snip> > > What the hell has this got to do with portability? First of all its > in reference to a C standard that is not widely adopted at all. So in > fact use of intN_t will specifically destroy any chance of > portability. Secondly, integers in C don't have a predescribed > implementation (like 2s complement, for example), so the int16_t on > one platform doesn't have to behave the same way as it might on any > other platform (except maybe having the same "sizeof"). > C integer types in general aren't required to be 2sc, although hardware where they aren't is very rare indeed. But the exact-width types in particular, int<N>_t IF PROVIDED (which is not required) must be 2sc and (uint<N>_t also) have no padding. Signed overflow (and negative rightshift) are still up to the implementation. And they *aren't* required to have to the same sizeof, presumably 2, since a platform with CHAR_BIT 16 and sizeof(int16_t) == 1 is perfectly legal. - David.Thompson1 at worldnet.att.net
Post Follow-up to this messagePowered by vBulletin
Copyright 2000-2006 Jelsoft Enterprises Limited.