Kuro5hin.org: technology and culture, from the trenches
create account | help/FAQ | contact | links | search | IRC | site news
[ Everything | Diaries | Technology | Science | Culture | Politics | Media | News | Internet | Op-Ed | Fiction | Meta | MLP ]
We need your support: buy an ad | premium membership

[P]
Are you Ready For C99?

By Carnage4Life in Technology
Fri Feb 23, 2001 at 10:26:49 PM EST
Tags: Software (all tags)
Software

ANSI and the ISO ratified the newest draft of the C standard in 1999 and unleashed the biggest changes to the language to date. New keywords, library functions, and macros are just the tip of the iceberg when it comes to the new improved C. In fact, the language is so different it is no longer compatible with C++. This article attempts to give an overview of the major changes made to the C language and also attempts to discover why no one yet seems to be affected by the new standard.


Interesting New Features

A list of features that should[0] have made it into the C99 standard is available on Thomas Wolf's webpage. Listed below are the changes that most developers would notice or care about at first use.
  1. Increased identifier size limits: specifically 63 significant initial characters in an internal identifier or macro name, 31 significant initial characters in an external identifier, and 4095 characters in a logical source line. These values were 31, 6, and 509, respectively, in C89.

    The small identifier size is the reason so many ANSI functions had terse names (strcpy, strstr, strchr, etc) since the standard only guaranteed that the first six characters would be used to uniquely identify the functions.

  2. C++ style/line comments: The characters '//' can now be used to delineate a line of commented text, just like in C++.

  3. Macros take variable arguments denoted by elipsees: Function-like macros will accept variable arguments denoted by using the ellipsis (...) notation. For replacement, the variable arguments (including the separating commas) are "collected" into one single extra argument that can be referenced as __VA_ARGS__ within the macro's replacement list.

  4. Inline functions: The C language now supports the inline keyword which allows functions to be defined as inline, which is a hint to the compiler that invocations of such functions can be replaced with inline code expansions rather than actual function calls.

  5. Restricted pointers: The C language now supports the restrict keyword which allows pointers to be defined as restricted, which is a hint to the compiler to disallow two restricted pointers from being aliases to the same object which allows for certain optimizations when dealing with the said pointers.

  6. _Bool Macro: There is a _Bool type which is a actually two valued integer type. True is defined as
    #define true (_Bool)1
    while false is defined as
    #define false (_Bool)0
  7. Variable Declarations can appear anywhere in the code block: No longer do variables have to be defined at the top of the code block.

  8. Variable length arrays: These are arrays whose size is determined at runtime.

  9. Variable declarations in for loops: Variables can now be declared and initialized in for loops just like in C++ and Java.

  10. Named initialization of structs: The members of a struct can now be initialized by name such as is done in the code block below
    struct {float x, y, z;} s = { .y = 1.0, .x = 3.5, .z = 12.8};
  11. New long long type: There is a new type called long long which is at least 64 bits and can be both signed or unsigned. The new suffixes "LL" or "ll" (and "ULL" or "ull") are used for constants of the new long long type.

  12. Functions must declare a return value: Function return types no longer defaults to int if the function declares no return type.

  13. Last member of a struct may be an incomplete array type. : This is to support the "struct hack" which works on most existing C compilers already. A code example of the struct hack is shown on Thomas's site.

  14. Addition of _Complex and _Imaginary number types: A boon for programmers doing any sort of advanced math in their programs.

  15. Multiple uses of a type qualifier are ignored after the first occurence: If a type qualifier appears several times (either directly or indirectly through typedefs) in a type specification, it's treated as if it appeared only once. E.g.
    const const int x;
    is the same as
    const int x;
C++ Incompatibilities

The aforementioned features are rather impressive but one soon realizes that this means that C is no longer a subset of C++. There is a list of the major incompatibilities between the current ISO standards for C and C++ on David Tribble's webpage. At this point it is still too early to see if either C or C++ will be harmed by this development but it is clear that there will be some growing pains once C99 adoption becomes widespread.

Bjarne Stroustrup mentioned in a recent interview with LinuxWorld that he would have liked for both languages to be compatible and would favor a technical committee whose express purpose would be integrating both languages, but doubts the possibility of this coming to pass. Stroustrup also contrasted how C's technical commitee decided to implement most of the added functionality via changes to the actual C language against the C++ approach which was by adding additional libraries.

Compiler support

After describing all the exciting new features of C99, one would expect there to be more awareness of the standard by now but there isn't. The primary reason for the lack of awareness of C99 is the fact that compiler support at the current time is practically non-existent. Here is the status of the major compiler vendors for the development platforms that I'm interested in:
  1. Microsoft: A search on Microsoft's site for C99 draws a total blank. It looks like Microsoft does not plan to upgrade the C compiler that ships with Visual C++ to cover the C99 standard.

  2. Borland: A search on Borland's site for C99 also draws a blank. Again it looks like exclusive C++ development has won over keeping their C compiler up to date.

  3. Comeau Computing: The most recent version of their compiler (version 4.2.45.1) which is available for online testing claims to support a large number of features from C99. The compiler has not yet been released but can be tested online by submitting code snippets in an HTML form.

  4. SAS Institute: SAS/C version 7.0 supports the "long long" type, inline functions and a few of the preprocessor directives.

  5. Edison Design Group: The EDG C++ front end supposedly supports both C89 and C99 as well as Microsoft C++ and ANSI/ISO C++.

  6. GNU: GCC has a status page of C99 compliance which shows that they are quite close to becoming fully compliant with the C99 standard.


Where To Get It

If you are interested in being the first kid on your block who knows the finer points of restricted pointers and variable argument macros, you can purchase the C99 standard online from ANSI. Finally no discussion of C99 is complete without a reference to Dennis Ritchie's opinion of the C99 standard.

[0] I didn't buy a copy of the standard, so I cannot guarantee that everything on that site made it into the standard although there is a good chance that everything did.

Sponsors

Voxel dot net
o Managed Hosting
o VoxCAST Content Delivery
o Raw Infrastructure

Login

Poll
Are you looking forward to using C99?
o Yes 34%
o No 16%
o Undecided 21%
o Don't Use C 27%

Votes: 103
Results | Other Polls

Related Links
o Thomas Wolf's webpage
o code example of the struct hack
o list of the major incompatibilities between the current ISO standards for C and C++
o David Tribble's webpage
o recent interview with LinuxWorld
o search on Microsoft's site for C99
o search on Borland's site for C99
o available for online testing
o SAS/C version 7.0
o EDG C++ front end
o status page of C99 compliance
o purchase the C99 standard online
o Dennis Ritchie's opinion of the C99 standard
o Also by Carnage4Life


Display: Sort:
Are you Ready For C99? | 242 comments (238 topical, 4 editorial, 0 hidden)
Some reservations (2.81 / 11) (#4)
by Inoshiro on Fri Feb 23, 2001 at 08:11:26 PM EST

Some of these new 'features' look like concesions for people who can't learn the personal discipline required to create C code effectively.

* Variable Declarations can appear anywhere in the code block: No longer do variables have to be defined at the top of the code block. and * Variable declarations in for loops: Variables can now be declared and initialized in for loops just like in C++ and Java.

Way to encourage bad code. Why not just make us use VB? As for adding long long, why not rename long long to huge? I'd hate to see 128bit processors -- can you say long long long? Or will they add the 'really' keyword? A keyword like 'huge' would've been more effective.



--
[ イノシロ ]
How is bad code encouraged? (4.50 / 4) (#6)
by Carnage4Life on Fri Feb 23, 2001 at 08:30:14 PM EST

* Variable Declarations can appear anywhere in the code block: No longer do variables have to be defined at the top of the code block. and * Variable declarations in for loops: Variables can now be declared and initialized in for loops just like in C++ and Java.

Way to encourage bad code. Why not just make us use VB?


How does allowing variable declarations to be declared in for loops and the like lead to bad code. If anything it leads to better code since there is less chance the developer will make mistakes with regards to the variables type and name when it is declared 20 - 100 lines from where it is used.

In fact, the most annoying thing about C is having to declare the i for a for loop a gazillion lines away from where the for loop was used. That was simply ridiculous.


[ Parent ]
I'm with Carnage here (3.50 / 4) (#7)
by pete on Fri Feb 23, 2001 at 08:37:02 PM EST

It's best to declare variables as close to where they're used as possible, and not reuse them in the same scope. Wait a second Inoshiro -- aren't you a Perl coder?? And you're going to compare this to VB? ;-)

In general this stuff all looks great...kind of makes me wish I was still a C programmer so I would get to use some of it. :|


--pete


[ Parent ]
How dare you! (3.00 / 3) (#10)
by Inoshiro on Fri Feb 23, 2001 at 09:27:18 PM EST

I am so not a perl programmer! Rusty even forbade me from writing any more of my unholy Cperl for Scoop until I got better at it :p

I have 5 years of C experience backing me up here, though. I think I can speak about C.



--
[ イノシロ ]
[ Parent ]
Only 5 years? (4.66 / 3) (#20)
by fluffy grue on Sat Feb 24, 2001 at 12:02:01 AM EST

*yawn*

<flame style="seniority credentials">I learned C in 1991, and used both C and Pascal until 1997 or so when I finally learned (and switched completely to) C++.</flame> I personally define my variables just as they're needed, as there are a lot of cases where not only is it much more readable, but can actually speed code up - if you declare all your object-type variables at the beginning of the code block, then that means that the constructor is called on them, whether you're going to actually use it or not, whereas if you declare an object just as it's needed, the constructor is called only if the object is actually needed.

"But in C, you just explicitly call the 'constructor' equivalent when it's needed anyway!" Yes, and in C you also need to deal with the 'destructor' equivalent on your own, not to mention the 'copy operator' equivalent, all non-fundamental mathematical operations (hey, some of us use our computers for - get this - computation - and being able to treat matrixes and complex numbers and such as fundamentals is very convenient, and has a very minimal speed hit - last time I bothered to benchmark it a few years ago, the speed difference between highly-optimized hand-unrolled vector operations and just letting C++ do operator overloading was under 0.1%), and a hundred other minutae on your own.

I'll take C++'s conveniences over C's rigidity any day. :P

Oh, not to mention that proper use of C++'s STL makes it very easy to always win out in algorithmic complexity. In C, if you want a sparse array, you've got to either do a naive memory-wasting approach, or you've got to code your own btree or linked list-based mechanism, whereas in C++ you can just do a <map> and call it a day (and get O(lg n) time complexity on most operations, to boot), AND if there's any improvements made to the STL, your code immediately sees those improvements as well - it's easy to get a HUGE speedup in STL-using C++ code simply by going from the HP b-tree implementation to the SGI red-black implementation. I'd give numbers, but I can't be bothered to actually hunt down a copy of the HP implementation anymore. :)

There's a point at which rigor becomes pointless. If you really have a problem with stray variables and such, you should really be compiling with -Wall anyway.

FWIW: Last night I came up with a new gross culling algorithm idea. Within 2 hours I had it coded and proven-working in C++ - and this is code which will likely go straight into my 3D engine with a minimum of modification. If I had done it in C, I'd probably still be working out my structures, and integrating it into my engine would probably be a major bitch...
--
"Is not a quine" is not a quine.
I have a master's degree in science!

[ Hug Your Trikuare ]
[ Parent ]

Yes, yes, yes.. (none / 0) (#69)
by Inoshiro on Sat Feb 24, 2001 at 04:23:09 PM EST

I'm sure your nice OT "C++ rocks because .... " dovetails nicely into the discussion of why C is someway, but only on the scoping issue, and then only for a paragraph or two before you go off into ++ land. I'd like to sit down and apply C++ someday, but I have a few years worth of C work backlogged which needs to be done first.

FWIW: I always compile my code with -Wall, and aggresively work to remove all warnings and code problems.



--
[ イノシロ ]
[ Parent ]
Hehe, sorry (none / 0) (#80)
by fluffy grue on Sat Feb 24, 2001 at 09:27:48 PM EST

I was originally just rambling why inline variable declarations are a very nice thing. I guess in C99 the reasoning doesn't hold up, though, since everything has an explicit constructor anyway, so I guess in the context of C99, inline variable declarations aren't such a Good Idea - except that it WOULD make it more convenient for keeping track of where they're constructed and destroyed and such.
--
"Is not a quine" is not a quine.
I have a master's degree in science!

[ Hug Your Trikuare ]
[ Parent ]

Only ten years? (5.00 / 1) (#132)
by ucblockhead on Sun Feb 25, 2001 at 08:30:21 PM EST

<flame style="seniority credentials">I learned C++ in 1985, learned C a year later and have used on or the other during most of my career since. </flame>

You allude to something that makes the issue of in-block declarations important in C++ but perhaps not in C. In C++, every declaration is a potential constructor call, and with it, the potential for a whole hell of a lot of code. Given this, it may be useful to delay its call for as long as possible. If a constructor opens a file, creates a socket and pops up a dialog asking for more information, you will probably want pretty damn fine grained control over where it gets called.

In C, on the other hand, a declaration just causes the stack pointer to change and causes a small bit of memory to be copied. There is no real danger of a performance hit. And, in fact, you want to keep declarations together for performance reasons as it means less stack pointer changes. (Though given the speed of modern CPUs, that is pretty damn anal.)

But anyway, what I'm trying to get at is that just because a language feature is useful in C++ does not necessarily mean that it is good in C.

(I do agree wholeheartedly with your C++ boosting.)
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
My bad... (none / 0) (#42)
by pete on Sat Feb 24, 2001 at 09:42:16 AM EST

...about the Perl comment. :-) Thought you worked on the code here.


--pete


[ Parent ]
Uh-oh (none / 0) (#14)
by DJBongHit on Fri Feb 23, 2001 at 10:37:41 PM EST

Wait a second Inoshiro -- aren't you a Perl coder?? And you're going to compare this to VB? ;-)
Whoops - you shouldn't have said that... Now you have to face his wrath :)

Inoshiro can write some badass C, but when it comes to Perl... I dunno, Ino. Maybe you should just stick to C :)

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
A gazillion lines away? (3.66 / 3) (#8)
by Inoshiro on Fri Feb 23, 2001 at 09:23:45 PM EST

The largest function of any of my programs is aproxamitely 30 to 35 lines. Any more than that per function body means you're doing too much in one place. Heck, the few function bodies which are greater than 25 lines are double and triple checked. Not to mention the fact that you can always resize the x-term to hold more info :p



--
[ イノシロ ]
[ Parent ]
Nah (4.66 / 3) (#11)
by trhurler on Fri Feb 23, 2001 at 09:45:27 PM EST

I can show you things that simply cannot be done in 35 lines of C and cannot profitably be split into multiple functions. Big nasty getopts are one very common example. This is not to say I like such functions, but they do have to exist. Some single tasks are complicated. That's life.

However, you're right - there is aboslutely NO reason to ever declare variables except immediately following an open brace, although I can live with the declaring variables in loop headers. The ability to randomly throw down a variable in the middle of a code block, though, is sheer, unadulterated lameness. There is never any defense for it, and anyone who tries will fall under the might of my invincible Correct Programming Style[tm] :)

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
Hhehehe (3.00 / 2) (#16)
by Inoshiro on Fri Feb 23, 2001 at 11:25:38 PM EST

One of the programs I wrote was an ansi editor for DOS. The event handler for the keyboard was around 200 lines of code. The only way I can think of now to split that off would be to have a structure of pointers to functions which had the keycode as the index to the function.. but I didn't think of that 3-4 years ago :)

getopts and the like are large, nasty things, generally. But they're also nicely encapsulated in one init() function (if you're smart about it). One thing I hate about most of the opensource is the lack of knowledge it shows. DGen/SDL's main is everything -- getopt, set other vars, run loop, cleanup.. in one. It's gross.



--
[ イノシロ ]
[ Parent ]
ah, Inoshiro (3.75 / 4) (#35)
by PresJPolk on Sat Feb 24, 2001 at 04:49:57 AM EST

You know... I cheer you on all the time, especially when you talk about the true greatness of Slackware, but I can't let this slide...

One thing I hate about most of the opensource is the lack of knowledge it shows.

What does open source have to do with anything? I wager you that many closed projects are at least as bad, especially since many run into deadlines, as opposed to hobbyist projects. You just don't get to see how bad the source is. Keep in mind that Sun and Netscape "cleaned up" their source releases.

Example from Joel on Software: One Microsoft programmer, when he had to write a function that would return the height of a line of text (for Word or Excel or something), he chose an algorithm that is simple to a fault:

return 12;

And he left it that way until a bug report was filed. Sure, this one was caught, but how many aren't?



[ Parent ]
Erg.. (4.50 / 2) (#68)
by Inoshiro on Sat Feb 24, 2001 at 04:03:12 PM EST

Obviously I need to clarify the statement. I mean that it shows how many people haven't even taken a basic class, or sat down and thought about serious code design. Of course I don't trust closed-source stuff, considering how nasty just using the program is (I, like I assume most programmers do, get an idea of the algorithms and interactions of a program after using it for a while).

I used to collect source code for things in my pre-Linux days, like WWIV BBS, TheDraw!, etc. The code there was nasty too. Ugh. There is so much bad code out there, so many NIH-syndrome teenagers posting their own ver 0.01 mp3 players on FM with code that makes me want to cry, that it sickens me sometimes.

The problems are that these people all learn from each other's bad code, and then go on to produce more of it. Heck, looking around the Gnome sources looking for examples of the API usage so that I could work out how to use some widgets, I found many examples of obvious bad code usage. Slashapp, for example, doesn't free libghttp requests after usage. It just loses the handle, and thus leaks memory. Or gweather which is so horribly broken on the inside it isn't even barely useable, like Slashapp. How do these people bear to open this to the world at large?

Yeah, I poorly chose words. I should've said "one of the things I don't like about seeing open code is seeing opened bad code which everyone else learns from and (mis)uses."



--
[ イノシロ ]
[ Parent ]
Aahh (none / 0) (#104)
by PresJPolk on Sun Feb 25, 2001 at 08:19:39 AM EST

much better.

Now I can go back to cheering.

[ Parent ]
Post one then. (none / 0) (#84)
by charliex on Sat Feb 24, 2001 at 10:47:50 PM EST

Post or link an example of one then, a function that simply canot be done in less than 35 lines of code or cannot profitably be split into multiple functions.

Although since the use of "profitably" is open to much interpretation here, would you like to clarify that too, as you post the example code.


[ Parent ]
Well, ok... (none / 0) (#136)
by trhurler on Sun Feb 25, 2001 at 09:07:25 PM EST

The code below is over 35 lines long, and it is both reasonably indivisible and obviously incomplete. Filling out the switch to make this function useful would easily double, triple, or perhaps even quadruple the length of the function. I have not done this because I don't have time to write hundreds of lines of code to prove a point that is blatantly obvious. What do I mean by "profitably?" Simple. You cannot profitably split this function up because the attempt will confuse the code and make a maintainer's job harder instead of easier; getopt is space consuming, but done right, it is simple and easy to understand. Obfuscated for the purpose of maintaining a religious stricture about function length, it can be downright hideous.
int
init(int argc, char **argv)
{
int c;
while(c = getopt(argc, argv, "abcdefghijklmnopqrstuvwxyz"))
{
switch(c)
{
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
default:
}
}
}


--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
However (2.00 / 1) (#145)
by charliex on Mon Feb 26, 2001 at 01:17:34 AM EST

getopts are ugly, there are many ways of making that a much better function, I think its one of those pieces of code that people just live with because its commonly available and they don't think of another way to do it.

Make it data driven for one, which would be more general purpose and it wouldn't have a long switch statement, or terse command line argument for programs.

I wasn't trying to get into a religous war, its just that one persons can't is anothers can.



[ Parent ]
Let me understand. (5.00 / 2) (#162)
by trhurler on Mon Feb 26, 2001 at 11:09:41 AM EST

You are seriously suggesting rolling your own options parser, which is a lot more work than just using the existing one, for no better reason than that you have a religious objection to long functions, even though this particular long function is nothing more than a bunch of switch cases and therefore has very regular structure, and is therefore not really a problem for writer, reader, maintainer, or anyone else.

That's the problem with simplistic "rules of programming" that brook no exceptions - they are, almost without exception, wrong on occasion. They might serve well as guidelines for students so they can learn what is accepted practice more quickly, but even in that capacity, they cannot be rigid rules unless you're just out to piss off the students, and in any other capacity, they're nigh on useless; an experienced programmer knows a lot more about how to do things and when to do them than can be summed up in a thousand such rules, much less any number you'd be able to remember.

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
Not so. (none / 0) (#205)
by charliex on Tue Feb 27, 2001 at 06:02:44 AM EST

getopts is not the be all and end all of options parsers, its a common one that people use, just because its common doesn't make it good (or bad). However I wasn't suggesting rewriting getopts, namely the switch that followed.

I have no religious arguments about long functions, I only have qualms with people saying how something cannot be done. I was just looking for a simple debate/discussion.

Lots of compilers don't handle long functions very well, take intels compiler for one, normally an excellent compiler, but it gives up optimizing long functions, so you end up with the last part of the code unoptimized, this goes for size as well as speed. Since a lot of people rely on the compiler to do the optimizing, its a problem. Watcom did the same thing.

I'm not quite sure what you are getting at in the last part of your message, an experienced programmer learns not to be dismissive and say things like "it cannot be done" profitably or otherwise, they will say "I cannot do it", and then either find a way to do it, or someone who can.



[ Parent ]
How I would do that... (none / 0) (#172)
by ucblockhead on Mon Feb 26, 2001 at 11:59:49 AM EST

#define ARRAY_SIZE(X) (sizeof(X)/sizeof(X[0]))

OptFunc* OptArray[] = {OptAFunc, OptBFunc, OptCFunc, ..., OptXFunc, OptYFunc, OptZFunc};

int init(int argc, char **argv)
{
int c;
while(c = getopt(argc, argv, "abcdefghijklmnopqrstuvwxyz")) {
if( c - 'a' < ARRAY_SIZE(OptArray) {
(OptArray[c - 'a'])();
}
}
}

Though I suppose a purist would object to the character arithmetic...

Anyway, to me, this is far more legible, because you don't have to go searching through a massive switch to find the code that deals with a particular option. Each option has its own nice little function. This is especially nice with a modern IDE that can easily take you straight to any function and give you function call graphs and the like.


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

Well, (none / 0) (#175)
by trhurler on Mon Feb 26, 2001 at 12:49:06 PM EST

You find that to be legible because you wrote it. I understand it, but to claim that it is "more legible" to an average programmer than the switch version seems ludicrous, and you cannot count on him having or knowing how best to use a modern IDE. I for one never use such tools, and regard their use as the vilest sort of heresy:) It certainly is more compact, but packing the maximum of semantics into a minimum of syntax is usually what leads to code nobody can understand - hence the brevity of typical obfuscated programming contest entries.

By the way, given that getopt arrays are always characters, the whole array sizing thing is needless obfuscation. You can get multibyte characters in C, but only by deliberately trying; the char type is always eight bits, and sizeof is going to return multiples of eight bits. The code is a good deal simpler without this stuff, and I almost begin to agree that it is simple enough to compete with the switch case version if you do that:)

Oh, and what am I to do if my program has no r option? I suppose I can write an empty function and use that for the missing entry, but this starts to get really gross... bounds check, then call a function, maybe the function is just another bounds check... geh.

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
The point of ARRAY_SIZE (none / 0) (#176)
by ucblockhead on Mon Feb 26, 2001 at 01:10:49 PM EST

Oh, and what am I to do if my program has no r option?

{OptAFunc, ... OptSFunc, NULL, OptTFunc, ..., OptZFunc}

and

if( OptArray[c - 'a'] ) (OptArray[c - 'a'])();

I always use something like ARRAY_SIZE() when iterating through arrays because it helps prevent walking of the end of arrays. It is a good habit to be in. In this case, it would make it trivial to avoid specifying the whole alphabet. For example, if your only options were 'a', 'g' and 'q', you could easily use the code just by adding the above if and changing the data to:

{OptAFunc, NULL, NULL, NULL, NULL,
NULL, OptGFunc, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, OptQFunc}; // Too lazy to format this well

I've found that if you can move crap into data arrays like that, your code becomes much easier to maintain.

If I was working in C++, I'd go one further, and throw all that into a std::map.

Actually, the above is only a subset of what I'd actually write. My data array tends to actually look more like:

struct
{
    char Opt;
    OptFunc* Func;
    char* HelpText;
} Options[] = {
    { 'a', Alphabetize, "\tAlphabetize the list"};
    { 'd', Display, "\tDisplay the list"};
    .
    .

(And then there are some generic routines to call the option, display the help page, etc. that make use of it.)

The reason being that all the specific information ends up concentrated in one place. This is a huge boon to anyone maintaining the code. They don't have to go searching from here to creation to fund everything relavent to the option.


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

No... (none / 0) (#177)
by trhurler on Mon Feb 26, 2001 at 01:14:30 PM EST

If you write the code with all those null pointers and I pass your program an option it doesn't support, it is not going to do what you think it should do:)

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
How So? (none / 0) (#178)
by ucblockhead on Mon Feb 26, 2001 at 02:05:13 PM EST

The code I presented here will simply ignore options that aren't defined. If you want better behavior, you've got two choices:

if( OptArray[c - 'a'] )
    (OptArray[c - 'a'])();
else
     BadOpt();
or

OptArray[] = {OptAFunc, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, OptGFunc, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, BadOpt, OptQFunc};

I personally prefer the latter. Either case, IMHO, is less bug-prone then the switch statement. Switch statements are too prone to things like missing breaks.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

Sorry, (5.00 / 1) (#180)
by trhurler on Mon Feb 26, 2001 at 03:47:48 PM EST

I misread a line in your code. (Actually, read it and somehow thought it hadn't changed from the previous version when it had, but that's not too different.) At any rate, the switch has the advantage that novices understand it, even if they can't safely modify it very well. I'd be hard pressed to get most university graduates to even read your version and tell me what it does, much less produce or modify it, and while I realize that's a sad statement on the condition of universities, it is also a fact that has to be dealt with:( I am lucky enough to work with nothing but talented people right now, but I'm not betting on that remaining the case forever, unfortunately.

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
Less than competent people... (none / 0) (#211)
by ucblockhead on Tue Feb 27, 2001 at 12:12:26 PM EST

Yeah, I know what you mean, but I've come to the conclusion that incompetent people will tend to screw it up no matter what you do and if the novices are competent, it is good to throw them in to good code so that they learn.


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

char is not always 8 bits. (none / 0) (#201)
by charliex on Tue Feb 27, 2001 at 05:37:20 AM EST

an octect is always 8 bits, a char is usually 8 bits, but is not always.


[ Parent ]
Another Example- Emulators (none / 0) (#186)
by logistix on Mon Feb 26, 2001 at 08:15:18 PM EST

down and dirty example for a 68000. Sorry I can't get the formatting right. The only other reasonable way to implement this that I can think of would be an assocative array of opcodes to function pointers, but C programmers aren't too keen on those:

void
executeOpcode(int opcode)
{
switch (opcode)
{
case ABCD:
doABCD();
break;
case ADD:
doADD();
break;
case ADDA:
doADDA();
break;
case ADDI:
doABCD();
break;
case ADDQ:
doADDQ();
break;
case ADDX:
doADDX();
break;
case AND:
doAND();
break;
case ANDI:
doANDI();
break;
case ASL:
doASL();
break;
case ASR:
doASR();
break;
case Bcc:
doBcc();
break;
case BCHG:
doBCHG();
break;
case BCLR:
doBCLR();
break;
case BSET:
doBSET();
break;
case BSR:
doBSR();
break;
case BTST:
doBTST();
break;
case CHK:
doCHK();
break;
case CLR:
doCLR();
break;
case CMP:
doCMP();
break;
case CMPA:
doCMPA();
break;
case CMPI:
doCMPI();
break;
case CMPM:
doCMPM();
break;
case DBcc:
doDBcc();
break;
case DIVS:
doDIVS();
break;
case DIVU:
doDIVU();
break;
case EOR:
doEOR();
break;
case EORI:
doEORI();
break;
case EXG:
doEXG();
break;
case EXT:
doEXT();
break;
case ILLEGAL:
doILLEGAL();
break;
case JMP:
doJMP();
break;
case JSR:
doJSR();
break;
case LEA:
doLEA();
break;
case LINK:
doLINK();
break;
case LSL:
doLSL();
break;
case LSR:
doLSR();
break;
case MOVE:
doMOVE();
break;
case MOVEA:
doMOVEA();
break;
case MOVEM:
doMOVEM();
break;
case MOVEP:
doMOVEP();
break;
case MOVEQ:
doMOVEQ();
break;
case MULS:
doMULS();
break;
case MULU:
doMULU();
break;
case NBCD:
doNBCD();
break;
case NEG:
doNEG();
break;
case NEGX:
doNEGX();
break;
case NOP:
doNOP();
break;
case NOT:
doNOT();
break;
case OR:
doOR();
break;
case ORI:
doORI();
break;
case PEA:
doPEA();
break;
case RESET:
doRESET();
break;
case ROL:
doROL();
break;
case ROR:
doROR();
break;
case ROXL:
doROXL();
break;
case ROXR:
doROXR();
break;
case RTE:
doRTE();
break;
case RTR:
doRTR();
break;
case RTS:
doRTS();
break;
case SBCD:
doSBCD();
break;
case STOP:
doSTOP();
break;
case SUB:
doSUB();
break;
case SUBA:
doSUBA();
break;
case SUBQ:
doSUBQ();
break;
case SUBX:
doSUBX();
break;
case SWAP :
doSWAP();
break;
case TAS:
doTAS();
break;
case TRAP:
doTRAP();
break;
case TRAPV:
doTRAPV();
break;
case TST:
doTST();
break;
case UNLK:
doUNLK();
break;
default:
doInvalidOpcode();
}
}


[ Parent ]
Table driven (none / 0) (#202)
by charliex on Tue Feb 27, 2001 at 05:46:10 AM EST

I'd probably convert it to a table driven routine, instead of an unwieldy case, or I'd try for a perfect hash. It'll also make it easier to maintain and less error prone.

If you're lucky the compiler will turn it into a jump table, if not a huge if block

Not doing it in a switch i'd be fairly sure how the end result looks, since its an emulator speed could be of an issue.

Just so you know I'm not knocking anyones coding style or standards, just looking for some discussion.


[ Parent ]
Table driven... (none / 0) (#210)
by ucblockhead on Tue Feb 27, 2001 at 12:09:26 PM EST

Yes, just imagine the fun if the coder forgets just one "break;" in that code! Compiles fine....
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Variable declarations... (none / 0) (#188)
by Mr Z (The Z is silent) on Mon Feb 26, 2001 at 08:58:06 PM EST

How does allowing variable declarations to be declared in for loops and the like lead to bad code. If anything it leads to better code since there is less chance the developer will make mistakes with regards to the variables type and name when it is declared 20 - 100 lines from where it is used.

Note that C always allowed you to do things like this:

    for (i = 0; i < len; i++)
    {
      short temp; /* Declared in body of for */

      temp = x[i];
      if (temp > 0xFF) temp = 0xFF;
      if (temp < 0x00) temp = 0x00;
      y[i] = temp;
    }

You could always have variable declarations anywhere you opened a curly brace. What I'm pretty sure the C99 spec allows for is such silliness as:

for (int i = 0; i < len; i++) /* ... */

I personally feel *that* is bad style. Particularly if i ends up being defined for the whole rest of the scope that also contains the code outside the for loop.

The following points sum up my position on the topic:

  • Limiting the scope of local variables is good. It prevents errors due to variables having larger scope than the author realized, and it documents to someone reading the code the intended scope of the values in play.
  • Scope should be limited by using explicit operators (in the case of C, the curly brace operators). This lets the programmer know exactly where scope boundaries are.
  • Having a fixed location within a scope for variable declarations is a good mechanism for improving the understandability of the code, as one needn't hunt for a declaration. If it's not at the top of the current block or at the top of any of its parent blocks, it must be a function argument or a global. The C99 approach breaks this.
  • Having variable declarations nearer to uses does limit errors. However, this is overridden by the need to have a well defined place to identify all of the symbols defined in the current scope. The correct answer here I feel is to use sub-blocks to get "very local" variables, or to factor a function into subfunctions.

Note that the ability to declare a local variable at any point is exactly equivalent to putting an opening curly brace just before the definition, and putting a closing curly brace just before the nearest enclosing block. These curly braces explicitly define the scope of that variable. If you consider just how many of these new nested scopes you might end up with, you can start to see just how crazy this can get.

Incidentally, the arguments about moving the stack pointer around are pretty meaningless with today's compilers. Because a return statement can appear anywhere, most compilers move all of their static stack allocation to the top of the function, and the stack release to the bottom. Now, dynamic allocation (eg. alloca and the new C99 variable-length arrays) builds on top of that. It also implies that the compiler must keep a frame pointer (or moral equivalent) so it can correctly release its stack frame. In the absense of dynamic allocation on the stack, the compiler can simply emit code to move the stack pointer by a fixed amount to allocate and release its stack frame. (Hence the -fomit-frame-pointer flag in GCC.)

--Joe



[ Parent ]
Variable declarations (2.50 / 2) (#13)
by DJBongHit on Fri Feb 23, 2001 at 10:35:09 PM EST

Way to encourage bad code. Why not just make us use VB?
Yeah, that's exactly the way I feel about it - one thing that makes me like C better than other languages is that, since it's relatively rigid in form, my code tends to be clean and well-behaved. In perl, where I can declare a variable anywhere, I tend to do so, just because I don't feel like going back to a better place to declare it - I know this is bad coding practice, but when I'm just trying to hack out a quick script I find myself doing it anyway. I'd rather not be allowed to :)

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
huh?? (3.00 / 3) (#22)
by tzanger on Sat Feb 24, 2001 at 12:10:22 AM EST

* Variable Declarations can appear anywhere in the code block: No longer do variables have to be defined at the top of the code block. and * Variable declarations in for loops: Variables can now be declared and initialized in for loops just like in C++ and Java.

Umm... isn't that like
void myfunc(void)
{
int i;

for(i=0; i<6; i++)
{
int j;
j=i*6;
}

int j;

j=77;
}

?? I've seen that done since Microsoft QuickC back in the early 90s...

Way to encourage bad code.

How exactly does it encourage bad code? Personally I don't like coding like that but I don't see how it makes things worse than anything else... I can declare variables anywhere I want, whether they be in for loops or in the middle of code blocks or anywhere... varibale j changes scope but there's ntohing wrong with that!



[ Parent ]
OMFG (1.66 / 3) (#32)
by Inoshiro on Sat Feb 24, 2001 at 03:05:01 AM EST

Having two single character variables with the same name within the same fuction body is very evil. I don't even want to think of the evil scoping of that :p

I prefer just declaring all variables with descriptive names and comments at the head of my fn. Much more maintainable code.



--
[ イノシロ ]
[ Parent ]
Not necessarily (4.66 / 3) (#39)
by Pseudonym on Sat Feb 24, 2001 at 08:46:52 AM EST

Having two single character variables with the same name within the same fuction body is very evil. I don't even want to think of the evil scoping of that :p

If it's done in a disciplined way, it's sometimes not as bad as you think.

We all call our standard integer loop counter variable i, right? And well you should. Convention reuse usually aids readability, and readability is the key thing here. (Quality is important, but you won't find any bugs if your code isn't readable.)

Let's look at the common situation of two loops in the one function body. First, the C99 way:

for (int i=0; i<N1; i++) {
    /* do something with i */
}
/* something in the middle */
for (int i=0; i<N2; i++) {
    /* do something with i */
}

And the C89 way:

int i;
for (i=0; i<N1; i++) {
    /* do something with i */
}
/* something in the middle */
for (i=0; i<N2; i++) {
    /* do something with i */
}

Here's why I think the C99 version is more readable:

  • The variables i as used in the first loop and i as used in the second loop are not related. The C99 version of the code emphasises that by making them different variables, but still manages to keep the convention that a variable called i is more often than not an integer loop counter. Now of course any modern compiler will note this fact via an optimisation known as live range splitting, but this code tells the programmer, and subsequent maintainers, that fact too.
  • The value of the variable i in the "something in the middle" section has no meaning. Well, any of us who know any C know full well what the value means. What I mean is that the value's usefulness is over. In the C99 version this is explicit by because it is an error to use i in the "something in the middle" section.

What it all boils down to is that the new rules about where you can put declarations lets the programmer specify their intentions about where the value in a variable means something more explicitly. And that's good for maintainability.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
This is what I'm talking about! (none / 0) (#62)
by tzanger on Sat Feb 24, 2001 at 12:48:57 PM EST

for (int i=0; i<N1; i++) {

That's what I mean! I swear to god I was able to do this with Microsoft QuickC, Borland Turbo C and Watcom C way back in the EARLY nineties... Not that I like it though... I far prefer declaring 'i' at the top; it's far more readable to me.



[ Parent ]
You could do that, yes (none / 0) (#153)
by aphrael on Mon Feb 26, 2001 at 04:10:20 AM EST

and i would remain in scope until the end of the *enclosing* block rather than going out of scope at the end of the for loop. This changed in, oh, 1994 or so (in c++).

[ Parent ]
Death to variable i (3.00 / 1) (#79)
by Eccles on Sat Feb 24, 2001 at 08:36:25 PM EST

We all call our standard integer loop counter variable i, right?

No, we should give our variables meaningful names. Call it "vertIdx" or "xIdx" or something that actually gives some indication of what the index is. And "i" is especially egregious, given the visual similarity to 1 and I.

We are no longer coding on PDP-10s with 8K of memory, and we're not coding for the Obfuscated C Contest; we can afford meaningful variable names.

[ Parent ]

We might as well (none / 0) (#82)
by kaatunut on Sat Feb 24, 2001 at 10:06:44 PM EST

We might have a tad more than 8k of memory, but we still transfer the code from our brains into the computer pretty much the same way, and the increase in typing speed certainly didn't follow moore's law.

P.S. Being a teen I have not a clue what PDP-10 is, but I'm assuming it used keyboards.

--
there's hole up in the sky from where the angels fall to sire children that grow up too tall, there's hole down in the ground where all the dead men go down purgatory's highways that gun their souls
[ Parent ]

PDP-10s (none / 0) (#102)
by Pseudonym on Sun Feb 25, 2001 at 06:13:46 AM EST

Being a teen I have not a clue what PDP-10 is, but I'm assuming it used keyboards.

Sometimes, if your shop could afford it. The really good ones had a teletype terminal (keyboard + printer arrangement). If you were unlucky, you used a smaller machine which spat out paper tape which you could then feed to your PDP-10. If you were really unlucky and your core got wiped or scrambled, you had to key in the boot loader code (using a panel of switches, no less, setting all the bits manually) so you could then mount the boot tape. Some people knew this code from memory. Someone probably still does.

"A keyboard! How quaint!" -- Lt. Cmdr. Montgomery Scott, Star Trek IV

sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
Wrong. (none / 0) (#133)
by pig bodine on Sun Feb 25, 2001 at 08:43:53 PM EST

Pretty much every PDP-10 had a teletype at the very least. Video terminals were far from uncommon. Remember, these were the machines that time-sharing systems were developed on. There's not much point developing time sharing for paper tape input.

[ Parent ]

I stand corrected (none / 0) (#139)
by Pseudonym on Sun Feb 25, 2001 at 10:33:31 PM EST

Pretty much every PDP-10 had a teletype at the very least.

You're right. I was thinking of PDP-8's.

Now that I think of it, I wrote my honours thesis on an Esprit terminal which dated from around the PDP-10 era.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
PDP-8 (none / 0) (#144)
by pig bodine on Mon Feb 26, 2001 at 12:40:35 AM EST

This machine was sold with an ASR teletype as standard. You would have had to be in a fairly poor shop to be using paper tape for all your input. Plus, paper tape readers and writers were more expensive than teletypes, and the ASR came with its own built-in paper tape reader and punch, although it was drastically limited in speed...110 being the baud rate of the ASR.

[ Parent ]

i is dead. Long live i. (none / 0) (#86)
by Pseudonym on Sun Feb 25, 2001 at 01:10:27 AM EST

No, we should give our variables meaningful names. Call it "vertIdx" or "xIdx" or something that actually gives some indication of what the index is. And "i" is especially egregious, given the visual similarity to 1 and I.

I see what you mean, but in this case, I beg to differ in this particular case. We all know what i means even if we can't articulate it.

We are no longer coding on PDP-10s with 8K of memory, and we're not coding for the Obfuscated C Contest; we can afford meaningful variable names.

But i is meaningful. Mathematicians and programmers alike are all used to it. It's meaningful because it's familiar.

BTW, strangely enough, most of us (and most of our coding standards) still use the same line width (80 columns) that a lot of PDP-10 ttys had, and those of us who don't use Plan 9 pretty much all still use fixed-width fonts for our programming. Maybe the compiler and debugger can handle more than it could, but neither our eyes nor our coding standards have evolved according to Moore's law.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
You can take that too far (none / 0) (#92)
by pig bodine on Sun Feb 25, 2001 at 01:54:40 AM EST

Sometimes there is no reasonably meaningful name for a variable that doesn't require a whole sentence to express. In these cases it's usually much better to use a short name in the code, and document the variable in a comment or elsewhere, if it's important. You can produce some really horrible, unreadable code by over-applying the guideline about meaningful variable names.

[ Parent ]
No kidding! (5.00 / 1) (#189)
by Mr Z (The Z is silent) on Mon Feb 26, 2001 at 09:08:24 PM EST

And then there are those people who think they're applying the rule, and actually they're using names that are as equally meaningless as, or more meaningless than i, j, and k. (And if you're a mathmetician or a computer scientist, actually those variables aren't that meaningless because they're almost always induction variables, by long established convention.)

What sorts of meaningless names am I talking about? Names like (and these were all extracted from the same function, and hold vastly different quantites): numberval, data_word, data_word_val, data_pointer, data_ptr, tempword

Ok, lets dissect some of these... numberval, ok, it's a number, and it has a value. data_word, it's a word of data. data_word_val, it's a word of data that has a value. ... Need I go on?

--Joe



[ Parent ]
I disagree here ... (3.00 / 1) (#152)
by aphrael on Mon Feb 26, 2001 at 04:09:09 AM EST

especially in for loops, it's usually perfectly clear what 'i' is for .... it only gets confusing when you do things like:

for (i=0, j=MAX_VAL; i < j; i++, j--) { // blather };

[ Parent ]

Standards... (none / 0) (#170)
by ucblockhead on Mon Feb 26, 2001 at 11:43:47 AM EST

The reason I believe that 'i' is perfectly fine is that there are certain "standard" names that everyone nows about. 'i' stands for "index" and everyone knows this. Because of this, 'i' is as readable as 'Idx'.

'i' used as an interator that moves through the elements of an array <i>is</i> a meaningful name.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
hmmm. (none / 0) (#224)
by Defect on Tue Feb 27, 2001 at 10:37:42 PM EST

I use i because i have it stand for 'iteration' or 'iterate.' I never thought of 'index.'
defect - jso - joseth || a link
[ Parent ]
wrongo (2.00 / 1) (#96)
by xriso on Sun Feb 25, 2001 at 03:24:28 AM EST

The value of the variable i in the "something in the middle" section has no meaning.

No, that's not true. Have you ever heard of a little think called break? It really helps sometimes to figure out if your loop quit without completion. All you have do do is find out whether i is still satisfying the conditional part. I have used this a few times before[*], and it really simplifies things.

However, in most cases it should be fine to use these in-loop variables.

[*] Actually, I prefer to put the number of loops in i and do while(i--). When you don't need to have i incrementing, it is quite nice. The value of i in the loop goes from (i-1) to 0. Of course, this screws up if i starts out less than 0, but a good programmer can decide whether to trust its value or not.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]

Righto, actually (5.00 / 2) (#99)
by Pseudonym on Sun Feb 25, 2001 at 05:09:35 AM EST

Have you ever heard of a little think called break?

Yes. Did the code example use it? No. That's why I said:

Well, any of us who know any C know full well what the value means. What I mean is that the value's usefulness is over.

Of course if you need the value, you should increase the variable's scope. If you don't, it is usually good programming practice to make the scope as small as possible.

The overwhelming majority of loops over i do not intentionally break early. (They might throw an exception, a signal or longjmp(), but that's not "intentionally" in most circumstances.) Declaring i to be local to the loop makes it an error to try to obtain its value after the loop finishes.

I think of it in the same way that modern compilers use the register keyword. Under normal circumstances, the C/C++ compiler knows better than you do what variables should be placed in registers. However, the keyword is a hint to the compiler that you will never take the address of that variable, and you want the compiler to refuse to compile your code if by some chance you ever do. So what you want to do is to get the compiler to stop you preventing it from assigning the variable to a machine register.

The new restrict keyword is similar. You are telling the compiler that you never intend any part of this variable to be aliased to any part of another formal parameter to any call. So if the compiler ever catches you doing that, you want to be told about it.

These features are for your benefit, to help prevent you from making mistakes. Use them. A bug that the compiler finds for you is one you don't have to hunt for.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
ah... (none / 0) (#116)
by xriso on Sun Feb 25, 2001 at 02:50:47 PM EST

I thought you were talking about all cases of using for. And yes, I can see this feature being a Good Thing in most cases.

BTW: the only way the compiler could catch a restrict error is if you actually passed the same pointer right at the function call. I look at restrict as a speeder-upper, provided that the programmer knows how to use it.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]

Alias analysis (none / 0) (#120)
by Pseudonym on Sun Feb 25, 2001 at 05:05:51 PM EST

BTW: the only way the compiler could catch a restrict error is if you actually passed the same pointer right at the function call.

Modern compilers can do much better than that, actually. Before restrict, they were forced to infer aliasing information. Compiler writers are now quite good at alias analysis.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
Alias analysis (5.00 / 2) (#190)
by Mr Z (The Z is silent) on Mon Feb 26, 2001 at 09:33:56 PM EST

You said: Compiler writers are now quite good at alias analysis.

Yes, and no. The compilers still don't perform arbitrarily complex analysis to work out all pointer patterns, and so it still must guess at times. Also, it can only do whole-program alias analysis when it's presented the whole program at once. If it compiles a function without knowing all of the call sites (the typical case in Makefile-based flow, actually), it must presume at least one of the unseen call sites passes aliasing pointers. It's this latter condition that restrict is the best at solving.

Consider the library function memcpy. The ANSI C standard says that the arguments to memcpy must never overlap. However, (prior to C99) there is no way to actually specify this to the compiler. And since one is writing a library, there is absolutely no way to present all of the call sites ahead of time to the compiler. With the restrict keyword, though, one simply restrict-qualifies the arguments to memcpy in the function prototype (for documentation purposes) and in the function definition, and you're all set.

This makes a really big difference on architectures whose load instructions have a deep pipeline, and which have many registers to facilitate aggressive code scheduling. You really want to move those loads as early as possible to hide their latency. On an advanced superscalar architecture, you can even use this to hide cache misses if you can move the loads early enough. Newer Alpha chips can support up to about 5 outstanding misses while allowing other instructions to hit in the cache. Also, on VLIW machines with exposed pipelines, you really need to schedule the code to hide latency to get decent performance at all.

Pointer alias disambiguation reduces the constraints on this code motion, but C's default aliasing presumption at function-call boundaries makes it hard to actually move the references. The restrict keyword is very useful here. The compiler becomes free to move the references (and in some cases, even eliminate redundant references since it can now prove they're redundant), and code performance goes up accordingly.

--Joe



[ Parent ]
Strong aliasing (none / 0) (#197)
by Pseudonym on Mon Feb 26, 2001 at 11:36:53 PM EST

The compilers still don't perform arbitrarily complex analysis to work out all pointer patterns, and so it still must guess at times. Also, it can only do whole-program alias analysis when it's presented the whole program at once.

More's the pity. Hence my MSc work (which I never got to finish, having run out of money) which was spent retrofitting what I call strong aliasing (analogous, in the obvious way, to strong typing) onto a declarative programming language. The compiler (Mercury, if you're interested) could work out exactly where there is aliasing by eliminating the unknown area, forcing the programmer to declare strong aliasing information for each predicate or function. The rest could be inferred by the compiler. This was important for implementing write-once logic variables and destructive update. (You can only destroy it if someone else isn't using it, right?)

In case anyone thinks that's a bit restrictive, it may help to know that you could overload your predicates by mode, determinism and aliasing (which was really part of the mode), and the compiler would generate separate code for each version.

I believe the strong aliasing system still hasn't been merged with the main tree.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
Strong aliasing (none / 0) (#198)
by Mr Z (The Z is silent) on Tue Feb 27, 2001 at 12:47:15 AM EST

By strong aliasing, I presume you mean that you know precisely all of the variables in a program that reference a given object. That's quite a feat. :-) Of course, the language that you're using probably makes this much simpler.

I unfortunately wouldn't know -- having an EE background rather than a CS background means that I haven't learned much in the way of non-procedural languages. Terms such as "closures" and "continuations" and so on are lost on me, sadly. (Nobody so far has explained what a closure or a continuation is to me to my satisfaction. I noticed the Mercury pages at least refer to closures.)

Given a complete C program, it may be possible to determine exactly the aliasing behavior of the program, but I sincerely doubt it would be feasible in the general case. The willy-nilly nature of pointer mangling in C in-general makes it a rather daunting task.

So what do you think? How does C stack up against Mercury in this regards?

--Joe

[ Parent ]

I disagree. (none / 0) (#87)
by Requiem on Sun Feb 25, 2001 at 01:29:54 AM EST

why is it so awful to be able to declare variables wherever you want to? I understand the problems with throwing in dumb little variables right before you need them, but consider this:

for (int i = 0; i < 10; i++)

this way, the variable i exists only in that loop, rather than throughout the function. it's just a loop control variable, so why should it exist elsewhere? this approach, when used well, can lead to cleaner code.

I agree with you as far as long long goes. that's just dumb.

[ Parent ]
variables in for loops... (none / 0) (#143)
by seebs on Sun Feb 25, 2001 at 11:48:26 PM EST

The real reason for this is that, if you declare a variable in a for loop, *it does not exist outside of the loop*. This is a feature.

It's fascinating that, a bit over a year after the spec got approved, people are starting to show interest. :)


[ Parent ]
Or better still... (none / 0) (#158)
by pallex on Mon Feb 26, 2001 at 05:24:25 AM EST

"As for adding long long, why not rename long long to huge? "

...what is wrong with having int16,int32,int64 etc?
I know C started off saying "an int should contain values in the range x -> y" rather than "an int is n bits" but theres no need to perpetuate that bad design choice now.


[ Parent ]
Damned committees... (3.40 / 10) (#9)
by trhurler on Fri Feb 23, 2001 at 09:26:56 PM EST

C99 has precisely one new feature that matters. That's the restrict keyword. The longer identifiers are nice, but that's not really a new feature. The C++ style comments serve no proper purpose and make the language bigger; this is C, not perl. The varargs macros are evil incarnate; macros cause enough problems without the ability to further complicate them. If your compiler is too stupid to figure out when it should inline functions without being told, you need a new compiler. Bool is like the // comments; it is pointless language bloat. Variable declarations should NOT be allowed anywhere but the top of a code block; if you want them elsewhere, you are wrong. Variable length arrays are nice, but again, they complicate the language, and there's no reason you ever need them given the presence of malloc. Variable declarations in loops and named initialization of structs are more syntax sugar; see my opinion of // comments above. The long long type, while a convenient hack to avoid modifying old code, is not really necessary, and it complicates things. Making long 64 bits doesn't create problems for most old code anyway. Functions must declare a return type - great, but that's not really a new feature so much as it takes away a bad old one. The standard should have specifically DISALLOWED the struct hack, not endorsed it. It is an example of what Dennis Ritchie once aptly termed "unwarranted chumminess with the compiler." The complex and imaginary number support is nothing but bloat; it is easily added in a library, and doing it that way doesn't lose you much of anything - C is a general purpose language, not a newer version of Fortran! The multiple identifier prefix elimination is nice, but it is more of a language bugfix than a feature per se, and it really only improves the compiler's ability to deal with badly written code - the real fix is to not write bad code.

Dennis Ritchie was right - the committee should have worked harder to not add crap to the language. C is beautiful because it is small.

--
'God dammit, your posts make me hard.' --LilDebbie

Re: Damned committees (5.00 / 2) (#187)
by sigwinch on Mon Feb 26, 2001 at 08:19:15 PM EST

The varargs macros are evil incarnate; macros cause enough problems without the ability to further complicate them.

Suppose you want to call syslog(2) in production builds, but syslog(2) *and* printf(3) in debug builds? It's trivial with macro varargs, and highly efficient for the production case. Without macro varargs, you have to call a varargs function and use vsnprintf(3), which entails function call overhead for the production case and is no less ugly than macro varargs. As long as functions have varargs, so should macros.

If your compiler is too stupid to figure out when it should inline functions without being told, you need a new compiler.

That would take a pretty smart compiler. It'd have to know about the machine code size of the function; the frequency with which the function is called; the size, dynamics, and overlap of the instruction and data caches; the performance of the CPU's branch prediction logic; the page and TLB sizes; and so forth. I'll ask Santa Claus for such a compiler, but until then I'll keep using "inline".

Bool is like the // comments; it is pointless language bloat.

A clever compiler can hold several Bools in the same register and use bitwise opcodes. Microcontroller compilers routinely do something like this using bit-addressable memory. Not that such a feature is particularly useful for large machines.

...named initialization of structs are more syntax sugar

Until somebody changes your struct declaration, in which case your initializers and things start breaking in subtle and fascinating ways.

The standard should have specifically DISALLOWED the struct hack, not endorsed it.

It's just a codification of the de facto behavior of most compilers. Without variable-size structures, you have to do extra dynamic memory management. With them, you just have to remember not to overflow the array.

The complex and imaginary number support is nothing but bloat; it is easily added in a library, and doing it that way doesn't lose you much of anything...

Complex numbers are frequently used for signal processing (to represent sine waves). I suspect, but do not know, that quantum physics simulations also make heavy use of complex numbers. Having to call a library function for *every arithmetic operation* is murder, especially on the inner loop of a signal processing algorithm. Function calls instead of operators are also difficult to read: "complex_add(a,complex_mul(b,c)" compared to "a+(b*c)". When you have lots of intricate equations, difficult to read is a Bad Thing. Making complex numbers first-class objects lets you write signal processing code that looks nice and performs fairly well. And it doesn't hurt people who don't use it. What a deal!

--
I don't want the world, I just want your half.
[ Parent ]

Grumpy, grumpy! (5.00 / 1) (#191)
by Mr Z (The Z is silent) on Mon Feb 26, 2001 at 09:45:46 PM EST

Bear in mind, many of the things about which you complain are already in many compilers (C++ style comments, variable argument lists in macros, long long.)

I agree restrict is probably the most important addition, particularly because the processor I program at my day job (the TMS320C6000 VLIW DSP) gets rather impressive benefits from restrict, owing to its deep, exposed pipeline on load instructions.

I do agree with you on variable declarations. That change does make sense in one context though, and one context only, though, and that's the inclusion of variable-length arrays. Despite your dislike of variable-length arrays, they are fairly useful, and actually lend themselves to compiler optimizations and bounds checking that aren't available with malloc(). Also, you are guaranteed that the space is freed when you leave the function, no matter what path you leave the function by. No more messy multitudes of "if (foo != NULL) free(foo)" on the error handler path backing out a series of local allocations if something goes wrong in the middle. Also, allocating on the stack should be much faster than malloc for small allocations, and should have better locality -- something that's desirable for local scratch storage.

As for the _Complex and _Imaginary support -- I have very mixed feelings on these. I pretty much feel that these are perfect examples that should be implemented as classes in C++.

And long long as 64 bits? Part of it was preventing current code from breaking, and part of it was really providing a truly distinct new type. There are still platforms for which int is 16 bits and long is 32 bits. On the platform I use regularly, int is 32 bits and long is 40 bits. A 64-bit integer type (in addition to the others I have already) would be very useful.

--Joe



[ Parent ]
Complex... (4.00 / 1) (#209)
by ucblockhead on Tue Feb 27, 2001 at 12:06:06 PM EST

When I saw that, the only explanation I could come up with is that there must be a lot of math guys on the committee. I'd guess that 99.9% of the people using C have no use for "Complex". I find it odd to add something so clearly restricted to fairly limited domains to a "general purpose" language. I suppose if it were the guys from Id on the committees instead of the math guys, we'd be getting all these matrix math routines.

My biggest objection to "long long" is that it seems like a parsing nightmare. I've always hated the double token types like "unsigned long" and this is even worse. I've long felt that one of the problems with C and C++ is the wild variety of integer and float types. Sometimes I think that we'd be better off if there were just two, "int" and "int<X>" where "X" is the number of bits to use.

Ok, that's a bad idea...
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

inttypes.h (none / 0) (#228)
by Mr Z (The Z is silent) on Wed Feb 28, 2001 at 01:29:18 PM EST

My biggest objection to "long long" is that it seems like a parsing nightmare.

I don't see how it's any more a parsing nightmare than the rest of C's declarations. And given that just about every important C compiler implements it already, I'm not sure what the issue is.

And as for the various int<X> types (where <X> is the desired width), I believe C99 adds a set of standard types along these lines... Reference: inttypes.h (Not sure if it made it into final C99.)

--Joe



[ Parent ]
C++ comments (none / 0) (#200)
by pallex on Tue Feb 27, 2001 at 05:06:56 AM EST

"The C++ style comments serve no proper purpose and make the language bigger; this is C, not perl"

Make it bigger? What are you like - it just makes it easier to comment out single lines, with the second advantage of making it easier to nest commenst (something you can`t do with /* */ blocks).


[ Parent ]
Hmm... a few issues... (5.00 / 1) (#219)
by beergut on Tue Feb 27, 2001 at 02:23:56 PM EST

While restricted pointers might seem nice, I have to wonder how efficably they're likely to be used. For instance, if I have a void*, and a char*, pointing to the head of a buffer, but from two different code modules, how will a restricted pointer help me? One pointer could be private to a module, while the other points into that module using an "extern". The module containing the exported buffer, and the private pointer to that buffer, are compiled with no knowledge of the other pointer that could potentially break data in the buffer. Will C now use guard pointers? If so, why not incorporate refcounting, and obviate malloc()? :)

C++ comments are stupid, anyway. I could, conceivably, use // legitimately in a program, like this:

i = j //* this is kind of tricky */ (a + b + c + d - e);

Granted, this is stupid, but not supporting it breaks old code, and gains you nothing except compatibility with that misfeature of C++. A better choice would be two operators that will never be run together in a mathematical construct, like "/%".

Variadic macros are nice, actually, but the purpose is better served by the "inline" keyword addition. I would have rather seen default arguments to macros than variadic macros, though I've seen places where I could have used the variadic kind, as well (though they don't leap to mind immediately - it usually happens when I'm in the middle of a code binge, and a variadic macro would be a nice, neat way to generalize an interface to some library code).

"inline" is nice, in my not-so-humble opinion. If for no other reason than to have it ignored as syntax, obviating the #define INLINE hack I've seen (and used) in various places. A st00p1d compiler is a fact of life, wherever you go. One feature I'm sure you would have liked to have seen implemented is GCC's idea of "labels as values", which makes direct-threading virtual machines a nice, easy thing to do (rather than a big-switch). That's a limited-use feature, but not language bloat, and can be damned useful. Declaring a function inline in a header file, then being able to use it without symbol collision in multiple places, is _useful_. That it is a "suggestion" rather than a guarantee makes it less useful. "register" as a "suggestion" makes more sense, but the onus should be on the programmer to not force inlining of huge, complex routines.

bool is syntactic sugar, but I've seen places where a guarantee of a result of 0 or 1 from a boolean operation (rather than C's "nonzero is true") would be a boon to efficient implementation. Such an example is something I've hacked on recently, where I wanted to abstract away syscall-level bitwise flag expectations, but not have a conditional for each and every conversion. So, I could have:

int sysflag;
int apiflag;

struct foo {
int api_flag;
int sys_flag[2];
} theflags[] = {
{ MAP_FILE, { 0, 0x0001 } },
{ MAP_ANON, { 0, 0x0002 } },
...
};

sysflag |= theflags[0].sys_flag[((apiflag & theflags[0].api_flag) == 0)];

Which can be rather nicely optimized rather than:

sysflag |= ((apiflag & MAP_FILE) ? (0x0001) : (0));

Which litters the code with conditionals, and is harder to optimize. In this case, the "bool" guarantee of C99 doesn't go far enough. It just so happens that with GCC on OpenBSD on i386, this works - will it work elsewhere? Who knows?

I agree with you completely about variable declarations - they should ONLY and ALWAYS be at the top of a code block. Period.

Variable length arrays are nice for a couple reasons, though, and using malloc() (rather than alloca(), which is not universally available) would be just stupid. Imagine that, inside a function, you have a need for a fixed-size chunk of memory, the size of which you pass into the function. Using malloc() to allocate the memory, and incurring the overhead of searching the heap for a chunk of memory that will work is, shall we say, non-optimal. Why do that when you can simply diddle the stack pointer? You then get a buffer that is quickly allocated, has a decent chance of being in the CPU's cache, and is automatically freed when you go outside the function's scope. Simple, easy, fast, cheap, elegant, efficient, good. C-like, eh?

Variable declarations in loops are syntactic sugar, to be sure, also. But, if you want a temporary variable on the stack, only around for the duration of that block of code, why go to the effort to uglify your code by having another level of nested braces? This:

for (register int i = 0; i < 10; ++i) { ... }

Is much more clear, in my mind, than the alternative:

{
register int i;
for (i = 0; i < 10; ++i) { ... }
}

"long long" is a dumb hack, as "long" should be sufficient to the task. But, then, I'm a proponent of a "byte" type, and a context-dependent "char" type (which would make such things as strcpy() for 2- or 4-byte Unicode character sets more workable). To each his own, but I agree with you here (though possibly from a different angle).

Required return types are good. You are correct in that it is a remedy for a previous misfeature. ANSI did that with prototypes in C89, and it was good.

As for the struct hack, if your compiler is C99-compliant, the struct hack is supported. Thus, you would be working to a standard, rather than to a compiler, and the struct hack is valid C. Argue all you want, but it doesn't matter: the struct hack is a Good Thing [tm].

As I've not used complex and imaginary numbers in my programming yet, I'd have to tentatively agree with you that including them as basic types is probably not warranted. Like Ritchie said, the cross-product of the type system gets more complex with these types included, and for very little benefit. If, due to this addition, a STANDARDIZED imaginary and complex math library results, this could be a great goodness.

Multiple modifier coalescing is ... bogus. Don't write sloppy code.

We agree on some things, but I think ANSI should have tightened up the language a bit more. C is beautiful because it is small, but it could be more beautiful if it was tighter and better-designed.


i don't see any nanorobots or jet engines or laser holography or orbiting death satellites.
i just see some orangutan throwing code-feces at a computer screen.

-- indubitable
[ Parent ]

Microsoft C (3.40 / 5) (#12)
by ucblockhead on Fri Feb 23, 2001 at 09:57:14 PM EST

Microsoft: A search on Microsoft's site for C99 draws a total blank. It looks like Microsoft does not plan to upgrade the C compiler that ships with Visual C++ to cover the C99 standard.

Very likely, especially considering that there are fairly trivial aspects of the C++ standard that they still don't support after five years. Between that, their move away from C++ in general with C#, and the fact that they mostly pretend straight C doesn't exist (though they do compile it), I wouldn't ever expect them to support any of this.


-----------------------
This is k5. We're all tools - duxup

My breakdown of the new features... (3.69 / 13) (#15)
by DJBongHit on Fri Feb 23, 2001 at 10:55:49 PM EST

I may be showing my bias towards old-school C here, but I'll throw in my 2 cents (and a bit of pocket lint).

C++ style/line comments: The characters '//' can now be used to delineate a line of commented text, just like in C++.
I don't think this is necessarily a good thing - I mean, it lets you type in comments a bit quicker, but I think it's at a cost of a bit of readability.

Macros take variable arguments denoted by elipsees: Function-like macros will accept variable arguments denoted by using the ellipsis (...) notation. For replacement, the variable arguments (including the separating commas) are "collected" into one single extra argument that can be referenced as __VA_ARGS__ within the macro's replacement list.
Oooh. Now that's nifty... I always wanted to be able to do that in a macro.

Inline functions: The C language now supports the inline keyword which allows functions to be defined as inline, which is a hint to the compiler that invocations of such functions can be replaced with inline code expansions rather than actual function calls.
Didn't C89 have this already? I know that at least every compiler I've used supported this (although it wasn't mandatory for the compiler to actually listen to you, and still doesn't appear to be).

Restricted pointers: The C language now supports the restrict keyword which allows pointers to be defined as restricted, which is a hint to the compiler to disallow two restricted pointers from being aliases to the same object which allows for certain optimizations when dealing with the said pointers.
By this you mean that 2 restricted pointers can't point at the same memory address, correct? I'm not sure I'm grasping the usefulness of this. How does this allow the compiler to speed things up?

_Bool Macro: There is a _Bool type which is a actually two valued integer type.
God. Useless. And I can see this being a problem for somebody who either doesn't quite know the language or simply hasn't thought out their algorithm. In a situation where you might've done something like this in the past:

if (SomeFunction()) {DoThis();}

Now somebody may be tempted to, for readability or some other reason, do something like this:

if (SomeFunction() == true) {DoThis();}

This appears to be the same on first glance, but while the first one DoesThis() for any non-zero return value from SomeFunction(), the second one only does it when SomeFunction() returns 1. It seems like a way to subtly sneak hard-to-find bugs into code. Someone who's familiar with C's intricacies would probably catch the error, but a less-skilled programmer may not.

Variable Declarations can appear anywhere in the code block: No longer do variables have to be defined at the top of the code block.
Dear God, no. This is just another way to decrease coding standards to the lowest-common-denomiator.

Variable length arrays: These are arrays whose size is determined at runtime.
Good. I always wondered why C didn't allow you to do this and instead made you malloc() it out yourself.

Variable declarations in for loops: Variables can now be declared and initialized in for loops just like in C++ and Java.
Double-God-no.

Named initialization of structs: The members of a struct can now be initialized by name such as is done in the code block below struct {float x, y, z;} s = { .y = 1.0, .x = 3.5, .z = 12.8};
Ahh. Excellent :) Initializing structs in C89 is entirely too much of a pain in the ass unless you do it when variable is declared.

Functions must declare a return value: Function return types no longer defaults to int if the function declares no return type.
Good idea, but I think it's a bit too late to throw a rule like this into the mix - this is gonna break an awful lot of code.

Last member of a struct may be an incomplete array type. : This is to support the "struct hack" which works on most existing C compilers already. A code example of the struct hack is shown on Thomas's site.
Neato. Not sure I'd use it much, but I can definitely see this being useful in a variety of situations.

Addition of _Complex and _Imaginary number types: A boon for programmers doing any sort of advanced math in their programs.
This does ABSOLUTELY NOT belong as a part of the C language - writing a library to support them is simple with the already-existing variable types. One of the reasons I like C is that it doesn't tend to suffer from feature bloat. Let's not start now.

Multiple uses of a type qualifier are ignored after the first occurence: If a type qualifier appears several times (either directly or indirectly through typedefs) in a type specification, it's treated as if it appeared only once.
Are you KIDDING me? C should not try to work around a programmer's badly written code - it's the programmer's job to write proper code to begin with.

~DJBongHit

--
GNU GPL: Free as in herpes.

I mostly agree, but... (3.66 / 3) (#17)
by ucblockhead on Fri Feb 23, 2001 at 11:28:53 PM EST

I'm mostly a C++ coder these days, and since a lot of these changes are from C++, I figure I have some insight. Anyway...

I agree that declaring variables any old place can lead to bad code, however, declaring a variable in a loop context is damn useful. For example:

for(int i=0;i<10;i++) { ... }

This restricts i to just that block, which is conceptually good. It prevents the sort of bad code that uses i outside the context. (Though unfortunately VC++ does not do this right)

Macros for boolean values are true evil, as you say. Microsoft does this, and it leads to horrendously dangerous code. The trouble with "foo == TRUE" is all too real in Windows code.

As far as "inline" goes, it is my understand that with most major C++ compilers, you have to go out of your way to turn it on. Otherwise, the compiler figures out what is to be inline. That is as it should be, in my mind. I have a real problem with code that doesn't involve the algorithm, but is instead a "hint to the compiler". Like the old "register" keyword that seems to have fallen into disuse.


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

A Slight Correction (2.00 / 1) (#25)
by SlydeRule on Sat Feb 24, 2001 at 12:32:56 AM EST

for(int i=0;i<10;i++) { ... }
This restricts i to just that block
Actually, it doesn't, not in C++. The scope of i extends from the for statement to the end of the scope which encompasses the for statement. Thus, you can test the value of i after exiting the loop.

In Java, the scope of i is limited to the for loop. This is one of the subtle differences between C++ and Java.

I wonder which way C99 went with this?

[ Parent ]

Correction to correction (4.50 / 2) (#29)
by thirteen on Sat Feb 24, 2001 at 02:17:47 AM EST

Actually the previos poster is right.

The C++ Programming Language

6.3.3.1 Declarations in For-Statements

A variable can be declared in the initializer part of a for statement. If that initializer is a declaration, the variable (or variables> it introduces is in scope until the end of the for-statement. For Example:

void f(int v[], int max)

{

for (int i = 0; i<max; i++) v[i] = i*i;

}

If the final value of an index needs to be known after the exit from a for-loop, the index variable must be declared outside the for-loop



[ Parent ]
the issue is that VC++ 6 is bad. (3.33 / 3) (#31)
by mpakes on Sat Feb 24, 2001 at 02:58:56 AM EST

The reason (I assume) that the previous poster believed that the scope was beyond the for loop block is because Microsoft Visual C++ 6.0 stupidly allows the scope to reach beyond. It's one of the (many) places where VC fails to meet the C++ spec.

[ Parent ]
Not bad, just slow on the uptake... (3.50 / 2) (#34)
by inpHilltr8r on Sat Feb 24, 2001 at 04:24:22 AM EST

IIRC, and speaking as someone who was using C++ back in '88, this is one of those issues that changed in one of the later revisions to the C++ spec.

I have to write code that runs under both VC++ and gcc's interpretations of the 'standard', and it still catches me occasionally.

[ Parent ]
Slow is an understatement (none / 0) (#49)
by ucblockhead on Sat Feb 24, 2001 at 11:26:00 AM EST

If I recall correctly, this change to the standard was made when VC++ 4.0 was out. It was definitely there when VC++ 5.0 was out. So they've had one, maybe two chances to make this change. Given that this is probably a trivial compiler fix, you've got to question how interested in standards they are.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Why Microsoft isn't C++ compatible (none / 0) (#78)
by Eccles on Sat Feb 24, 2001 at 08:26:59 PM EST

If I recall correctly, this change to the standard was made when VC++ 4.0 was out. It was definitely there when VC++ 5.0 was out. So they've had one, maybe two chances to make this change. Given that this is probably a trivial compiler fix, you've got to question how interested in standards they are.

Probably less interested in standards than they are in not having to rewrite a lot of code that depended on the old structure, probably. It is possible to do horrible (but apparently safe) things like

#define for if (0) ; else

and then it'll follow the standard, so a compiler switch or the like isn't strictly necessary.

I work on a Mac/Windows app, and we periodically run into this problem.

[ Parent ]

I think you mean (none / 0) (#199)
by The Dark on Tue Feb 27, 2001 at 03:13:10 AM EST

Rather than
#define for if (0) ; else
it should be
#define for if (0) ; else for

-- Sig's not here.
[ Parent ]
Not fixed yet (none / 0) (#173)
by Biff Cool on Mon Feb 26, 2001 at 12:14:09 PM EST

IIRC it will be fixed in VC++ 7. Until then as someone pointed out:
#define for if (false) else for

My ass. It's code, with pictures of fish attached. Get over it. --trhurler


[ Parent ]
Note that (none / 0) (#151)
by aphrael on Mon Feb 26, 2001 at 04:05:55 AM EST

when BC++ 5.0 incorporated this change, it also added a compiler switch to *disable* it --- because it would have broken *a lot* of code at the time.

[ Parent ]
Nope (4.00 / 2) (#43)
by DJBongHit on Sat Feb 24, 2001 at 09:43:21 AM EST

Actually, it doesn't, not in C++. The scope of i extends from the for statement to the end of the scope which encompasses the for statement. Thus, you can test the value of i after exiting the loop.
Incorrect - in C++, if you declare a variable inside the for loop like that, it is not visible after the closing brace of the for loop. If you need to test the value of i after exiting the loop, you must declare i before the for loop.

Visual C++ is broken in this regard - it keeps i in scope after the for loop is over. I can't speak for Java, since I don't do much Java coding ever (did a bit a few years ago, but got bored with it real fast), but I'll just assume you're correct, since that's the way it should be anyway.

While we're on the subject of for loops, am I the only person that tends to use 'a' as the loop variable, or 'b' in a loop inside a loop, ...? (well, x and y if I'm doing graphics programming, but that's a different situation.) Why does everybody tend to use 'i'?

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
Implicit Variable Typing (4.00 / 3) (#52)
by Bad Harmony on Sat Feb 24, 2001 at 11:32:13 AM EST

While we're on the subject of for loops, am I the only person that tends to use 'a' as the loop variable, or 'b' in a loop inside a loop, ...? (well, x and y if I'm doing graphics programming, but that's a different situation.) Why does everybody tend to use 'i'?

Back when all real programmers wrote code in FORTRAN, variable names beginning with the letters I through N were implicitly typed as integer. Variable names beginning with any other letter were implicitly typed as real.

54º40' or Fight!
[ Parent ]

also (none / 0) (#95)
by xriso on Sun Feb 25, 2001 at 03:06:05 AM EST

In addition to Bad Harmony's comment: I to N are usually iterators in math, so that's where fortran probably got it from. I used to think i was short for "iterator", which might be true as well.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]
Speaking of FORTRAN.... (none / 0) (#192)
by Mr Z (The Z is silent) on Mon Feb 26, 2001 at 09:53:23 PM EST

...don't forget, GOD is REAL, unless DECLAREd INTEGER.

--Joe :-)



[ Parent ]
Counter Vars (none / 0) (#160)
by priestess on Mon Feb 26, 2001 at 07:48:30 AM EST

While we're on the subject of for loops, am I the only person that tends to use 'a' as the loop variable, or 'b' in a loop inside a loop, ...? (well, x and y if I'm doing graphics programming, but that's a different situation.) Why does everybody tend to use 'i'?


I first did for loops on a Spectrum, in Basic. The Spectrum basic editor did strange things with keywords. Pressing 'n' when the cursor was in the right place brought up the whole word 'next', and pressing 'f' wrote 'for' etc.

Ending the loop tended to be quicker if you just tapped 'n' twice 'next n' so I've always used n as the first counter. Call it 'number' if you want.

After that was 'f', to speed up the 'for f' part at the begining of the loop. I've grown out of that one though.
Pre........

----
My Mobile Phone Comic-books business
Robots!
[ Parent ]
All about inline (4.50 / 2) (#81)
by Eccles on Sat Feb 24, 2001 at 09:52:46 PM EST

As far as "inline" goes, it is my understand that with most major C++ compilers, you have to go out of your way to turn it on.

Not exactly.

Compilers can choose whether to inline a function or create a copy of it. Whther they do so or not depends on the capabilities of the compiler and whether it judges the inlining to be efficient. Typically there are options to force inlining, or to avoid it to allow debugging into an otherwise inlined function.

However, unlike register, it is not merely a hint. An inline function may, via #include, be included in multiple "compilation units" (.C files.) Without the inline keyword, you would thus get multiple versions of the function and a link error. Also, the compiler can't inline a function whose definition isn't part of the current compilation unit. (You can also have different definitions of an inline function in different compilation units with no link error.)

Inline functions can make for a major performance improvement. Generic C++ sort routines are typically *faster* than their C equivalents because the C++ function can use an inlined comparator, unlike qsort() which requires a function pointer be passed to it.

[ Parent ]

More on inline. (5.00 / 2) (#195)
by Mr Z (The Z is silent) on Mon Feb 26, 2001 at 10:19:45 PM EST

However, unlike register, it is not merely a hint. An inline function may, via #include, be included in multiple "compilation units" (.C files.) Without the inline keyword, you would thus get multiple versions of the function and a link error. Also, the compiler can't inline a function whose definition isn't part of the current compilation unit. (You can also have different definitions of an inline function in different compilation units with no link error.)

By the way, you should be very careful here, as the compiler may still insert an "outline" call rather than inlining. As I recall, the only guarantee is that an inline function will not be externally visible if all file-scope declarations of that function are marked inline within that translation unit. A program's behavior is undefined if it relies specifically on calling either the inline or outline version of the function. This substantially limits your freedom in customizing inline functions between translation units, and still requires you to provide outline copies. Reference: The C9X Proposal for Inline.

(Note: Where the I see the "different versions in different translation units" aspect being most important is when library headers provide inline versions of various functions, and the headers are upgraded over time to produce faster code, but the same end effect. Older code compiled against older versions shouldn't need recompilation to be link with newer code compiled against the newer versions as long as the overall functionality is identical. Consider the strXXX() family of functions.)

On a different note... There are compilers that can inline across compilation units. They do this by compiling each unit to an intermediate code form, deferring code generation. (They may also generate object code for the non-inlined functions at the same time.) To inline across the compilation units, the compiler merges the intermediate code from the current unit with the code from the other units and then optimizes.

--Joe



[ Parent ]
heh (3.00 / 1) (#94)
by xriso on Sun Feb 25, 2001 at 03:01:02 AM EST

I do that sort of thing all the time (reusing variables). Outside of that loop, i's value doesn't matter anyway, so you might as well use it for a temp variable, or maybe for another loop.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]
Yiour code (5.00 / 1) (#107)
by ucblockhead on Sun Feb 25, 2001 at 11:32:40 AM EST

I hope you don't plan on it being maintainable in a year...

Seriously.

When you do that, you greatly reduce the readability of the code, for an utterly negligible gain.

The only exception I can see is a second loop that is much like the first, but even that should be avoided. In fact, that's the most important reason for allowing "for(int i=0;....". So that you can use the standard index variable in more than one place in a structured manner.

It is extremely important to remember that a variable is not just an entity for the compiler but for the programmer as well. (And anyone else who reads the code.) If a single variable is used for more than one thing, you will confuse the programmer. Either you, yourself, or whoever has to maintain your code in five years.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
yeah (3.00 / 1) (#121)
by xriso on Sun Feb 25, 2001 at 05:11:44 PM EST

Well, as long as the compiler doesn't do anything stupid to accomplish these local variables, I will probably use them when I remember to. The biggest problem I can see is that the stack pointer has to be modified every time a new variable is created (or destroyed), so that called functions don't smash the stack. This isn't much of a performance hit, but it is still there.

Then again, with a limited stack size, this could be good for functions called outside of the loop, as there isn't a useless variable sitting on the stack.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]

Performance hit... (none / 0) (#123)
by ucblockhead on Sun Feb 25, 2001 at 05:46:47 PM EST

Well, I have to disagree. Back when I programmed on machines with a 64k address space, or on programs with 10k of stack space, I thought it was a bad idea.

You can imagine what I think now that the stack typically grows as needed and where 256 Mb RAM is common.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
heh (none / 0) (#127)
by xriso on Sun Feb 25, 2001 at 07:12:45 PM EST

When I said that stack space was saved, I actually meant to support your argument, although it could support reusing variables depending on the compiler.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]
Why have restrict pointers? (4.66 / 6) (#28)
by pig bodine on Sat Feb 24, 2001 at 02:10:48 AM EST

Quick answer: To enable C compilers to optimize code to a level that makes it similar to Fortran 77 in terms of memory access. C has always had a problem here, and this is why Fortran is still very popular in the scientific community.

Longer answer: Memory access takes much more time than executing instructions. It is preferable for modern compilers to try to optimize code so that the time the processor spends waiting for data from main memory is limited.

In order to do this, the compiler re-orders memory access instructions to take advantage of caching and the use of non-blocking memory access. This cannot be done safely, unless the compiler can be certain that the data it is caching will not be changed before it is used. This assumption is possible in Fortran, but it isn't in C without restrict qualifiers.

Better answer: (plus some critical discussion)

[ Parent ]

Ahh, I see (3.50 / 2) (#41)
by DJBongHit on Sat Feb 24, 2001 at 09:37:12 AM EST

Memory access takes much more time than executing instructions. It is preferable for modern compilers to try to optimize code so that the time the processor spends waiting for data from main memory is limited.

In order to do this, the compiler re-orders memory access instructions to take advantage of caching and the use of non-blocking memory access. This cannot be done safely, unless the compiler can be certain that the data it is caching will not be changed before it is used. This assumption is possible in Fortran, but it isn't in C without restrict qualifiers.
Gotcha. Now what happens if the programmer marks 2 pointers as restricted, but then later in the code they end up pointing to the same location? Will it give a run-time error saying that you can't do that because the pointers are restricted, or will it just open up the possibility for more subtle hard-to-find bugs?

Either way, another way to squeeze more speed out of C is good by me :)

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
"run-time error"?!? (3.50 / 2) (#47)
by mikpos on Sat Feb 24, 2001 at 10:20:48 AM EST

I've never heard "run-time error" uttered within a discussion about C. There is no such thing. Having two pointers, at least one of which restricted, pointing at the same block of memory, invokes undefined behaviour. So don't do it :)

[ Parent ]
Run-time errors (3.50 / 2) (#50)
by DJBongHit on Sat Feb 24, 2001 at 11:26:33 AM EST

I've never heard "run-time error" uttered within a discussion about C. There is no such thing.
Sure there are - they just appear in much lower-level situations than in a language like Perl. Things like segmentation faults and divide-by-zero's are run-time errors. Or, at least, that's what I meant by run-time errors, anyway :)

Having two pointers, at least one of which restricted, pointing at the same block of memory, invokes undefined behaviour. So don't do it :)
Ok, that's what I figured, I was just wondering if the spec provided any safeguards to prevent this to happening. I'm glad it doesn't, because the whole point of restricted pointers is speed anyway, and having the compiler add in checking code would just slow things down.

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
Re: "run-time error"?!? (5.00 / 1) (#71)
by Khalad on Sat Feb 24, 2001 at 05:52:46 PM EST

If you want to get pedantic, though, undefined behavior only invalidates a program's correctness if the offending code is executed.

For example,

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main (void)
{
    int           x = 17,  y = 42;
    restrict int *p = &x, *q;

    srand ((unsigned) time (NULL));

    q = (rand () % 2 ? &x : &y);
    printf ("*p = %d, *q = %d\n", *p, *q);

    return 0;
}
This is a perfectly well-defined, well-behaved program... half of the time (assuming I didn't make any stupid mistakes). The undefined behavior here, if invoked, is most definitely a run-time error. While a compiler is allowed to complain about the program, it must not refuse to translate it; if it really wants to catch the error all it can do is try to trap it at run-time.

You remind me why I still, deep in my bitter crusty broken heart, love K5. —rusty


[ Parent ]
not really (4.00 / 1) (#73)
by mikpos on Sat Feb 24, 2001 at 06:44:35 PM EST

The standard makes no difference between different types of undefined behaviour. In your example above, it would be quite acceptable for a conforming implementation to refuse to compile (or do anything it likes). See 3.18 of C99 for more details.

More specifically, executing of a program is not covered by the standard. Technically, it's not the executing of bad compiled code that causes undefined behaviour, it's the undefined behaviour caused by bad source code which possibly leads to a corrupt binary. The source you posted invokes undefined behaviour, regardless of whether the compiled (assuming the compiler compiles it) code is executed or not.

[ Parent ]

Re: "run-time error"?!? (4.00 / 2) (#72)
by Khalad on Sat Feb 24, 2001 at 06:38:15 PM EST

If you want to get pedantic, though, undefined behavior only invalidates a program's correctness if the offending code is executed.

For example,

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main (void)
{
    int           x = 17,  y = 42;
    restrict int *p = &x, *q;

    srand ((unsigned) time (NULL));

    q = (rand () % 2 ? &x : &y);
    printf ("*p = %d, *q = %d\n", *p, *q);

    return 0;
}
This is a perfectly well-defined, well-behaved program... half of the time (assuming I didn't make any stupid mistakes). The undefined behavior here, if invoked, is most definitely a run-time error. While a compiler is allowed to complain about the program, it must not refuse to translate it; if it really wants to catch the error all it can do is try to trap it at run-time.

You remind me why I still, deep in my bitter crusty broken heart, love K5. —rusty


[ Parent ]
That's funny... (5.00 / 2) (#75)
by Khalad on Sat Feb 24, 2001 at 06:56:54 PM EST

I don't remember hitting the submit button twice. Can I blame the code's undefined behavior? (Anything can happen, you know.)


You remind me why I still, deep in my bitter crusty broken heart, love K5. —rusty


[ Parent ]
Run-time errors and debug mode (4.00 / 1) (#114)
by pin0cchio on Sun Feb 25, 2001 at 12:39:59 PM EST

Now what happens if the programmer marks 2 pointers as restricted, but then later in the code they end up pointing to the same location? Will it give a run-time error saying that you can't do that because the pointers are restricted, or will it just open up the possibility for more subtle hard-to-find bugs?

A decent compiler would give you the best of both worlds through a switch. With debug mode turned on (optimize for Safety), it would check every restrict pointer assignment for aliasing to another pointer in scope. With debug mode off (optimize for Speed), it would assume that you've already done testing under debug mode and drop the checks.


lj65
[ Parent ]
The latter, I think. (none / 0) (#135)
by pig bodine on Sun Feb 25, 2001 at 08:59:25 PM EST

It seems to me that if this did happen, you'd start finding values set incorrectly, or rather stale values being taken from the cache after they should have been changed. I don't think it would do anything like stopping execution in it's tracks, though that might be preferable from a debugging perspective.

[ Parent ]

multiple qualifiers (4.00 / 3) (#30)
by pig bodine on Sat Feb 24, 2001 at 02:25:45 AM EST

It occurs to me that this might be to avoid problems caused by macro expansion. ie. I have a macro "FOO" which might be set to either "const i" or "j". Somewhere in my code I have "const FOO". See the problem? (seems a pretty weird thing to want to do, though)

[ Parent ]
I see, but... (3.50 / 2) (#40)
by DJBongHit on Sat Feb 24, 2001 at 09:33:58 AM EST

It occurs to me that this might be to avoid problems caused by macro expansion. ie. I have a macro "FOO" which might be set to either "const i" or "j". Somewhere in my code I have "const FOO". See the problem?
Yeah, I understand why this might be useful, but it's still done to work around a programmer mistake - and IMHO it shouldn't be doing that.

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
quick clarifications (4.00 / 3) (#46)
by mikpos on Sat Feb 24, 2001 at 10:15:32 AM EST

Inline functions

Didn't C89 have this already? I know that at least every compiler I've used supported this (although it wasn't mandatory for the compiler to actually listen to you, and still doesn't appear to be).

No, it was not in C89. As an addition, the inline functions in C99 are completely incompatible with gcc's (gcc's are back asswards if you ask me).

Variable length arrays

Good. I always wondered why C didn't allow you to do this and instead made you malloc() it out yourself.

The reason (I would guess) is that without alloca(), this is almost impossible to implement well (and difficult to implement otherwise). Not all platforms have alloca().

One of the neater features that he missed was compound initialisers. e.g.:

some_function(&(struct foo){ 3, 4, 5, "yes" });

instead of:

static struct foo ANONYMOUS = { 3, 4, 5, "yes" }; some_function(&ANONYMOUS);

Like pretty well all of C99's additions, it's sugar, but it's still cool ;)

There are other neat ones: variable names, function names (even external symbols!), etc. can have wide characters in their names. Who will use it? No one, but it's cool. The va_copy macro is added (to copy va_list's). The variable __func__ was added (which gcc already has). Generally, just useless cool stuff ;)

[ Parent ]

oops, i'm a retard (none / 0) (#48)
by mikpos on Sat Feb 24, 2001 at 10:53:37 AM EST

"Compound literals" I meant.

[ Parent ]
arrays (2.50 / 2) (#53)
by DJBongHit on Sat Feb 24, 2001 at 11:43:54 AM EST

The reason (I would guess) is that without alloca(), this is almost impossible to implement well (and difficult to implement otherwise). Not all platforms have alloca().
Whoa, hold up here. C doesn't allocate space for an array on the _stack_, does it? Isn't stack space rather limited?

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
Arrays (3.00 / 1) (#57)
by Bad Harmony on Sat Feb 24, 2001 at 12:17:47 PM EST

Whoa, hold up here. C doesn't allocate space for an array on the _stack_, does it? Isn't stack space rather limited?

Most implementations of C allocate all auto class variables, including arrays, on the stack. Some implementations of C use a heap instead of a stack.

54º40' or Fight!
[ Parent ]

Most implementations... (5.00 / 1) (#168)
by ucblockhead on Mon Feb 26, 2001 at 11:38:53 AM EST

"Most Implementations" is an understatement. I've never run into a compiler that didn't, and I've used many versions of the IBM, Borland and Microsoft PC C compilers, the old Aztec C compiler as a cross-compiler, GCC and the old cc that came with AT&T system V and probably a couple of others I've now forgotten. They all put all auto variables on the stack.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Computer Architecture (4.00 / 2) (#193)
by Bad Harmony on Mon Feb 26, 2001 at 10:05:51 PM EST

Some systems (OS/360 and descendants) do not have a stack, using dynamically allocated buffers for saving registers and local variables. Some systems (DSPs and microcontrollers) have stacks that are limited to a small amount of memory or set of scratch registers. A C implementation can have zero, one (the normal case) or two (call/return and parameter) stacks. Most people will never see one of these systems, but they do exist.

54º40' or Fight!
[ Parent ]

Program structure. (3.00 / 1) (#58)
by Carnage4Life on Sat Feb 24, 2001 at 12:19:19 PM EST

Whoa, hold up here. C doesn't allocate space for an array on the _stack_, does it? Isn't stack space rather limited?

Local variables go on the stack, dynamically allocated variables go on the heap and constants go in the constants pool(static/global data area).


[ Parent ]
stack space (3.00 / 1) (#61)
by ucblockhead on Sat Feb 24, 2001 at 12:43:19 PM EST

Under Windows, stack space is essentially unlimited. The stack is grown whenever needed, perhaps even rolling into virtual memory.

(For Win 9X and Win NT. The 16-bit stuff had a limited stack.)

I suspect other modern OSes are much the same.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
why doesn't K5 fill in the Re: subject for me? (2.50 / 2) (#67)
by ethereal on Sat Feb 24, 2001 at 03:22:59 PM EST

Yes, this would be a big problem for C99 use on embedded/realtime platforms. We routinely overflow the stack on our product when someone allocates a local copy of a huge struct, for example. Just 'cause "it works in Windows" doesn't make it a good feature for the language as a whole :)

--

Stand up for your right to not believe: Americans United for Separation of Church and State
[ Parent ]

Meta (3.00 / 1) (#90)
by Inoshiro on Sun Feb 25, 2001 at 01:49:13 AM EST

K5 doesn't fill in the Re: line so we get useful subjects out of people. It's the same amount of work to type in a useful subject vs. copy and paste the silly re: one, so you get better results :-)



--
[ イノシロ ]
[ Parent ]
Meta point accepted (none / 0) (#222)
by ethereal on Tue Feb 27, 2001 at 06:36:46 PM EST

Makes sense to me, I'll just have to get over my laziness :)

--

Stand up for your right to not believe: Americans United for Separation of Church and State
[ Parent ]

alloca does.. (3.00 / 1) (#91)
by Inoshiro on Sun Feb 25, 2001 at 01:52:32 AM EST

alloca is a small alloc-alike which allocates space directly from the stack of the program. It's fairly ecvil. It's also been a problem for Wine developers lately :-)



--
[ イノシロ ]
[ Parent ]
C (3.83 / 6) (#18)
by FigBug on Fri Feb 23, 2001 at 11:48:15 PM EST

> In fact, the language is so different it is no longer compatible with C++.

C never has been a subset C++, so they never have been 'compatible'

Things like sizeof character constants, recursive calls to main(), empty parameter list, casting, and a few other things I forget.

Post in comp.lang.c and say you are programming in C/C++ and you'll see what I mean.

This is technically correct... (5.00 / 3) (#51)
by i on Sat Feb 24, 2001 at 11:27:33 AM EST

but doesn't mean much. For instance, in the K&R2 every single example is valid C code and valid C++ code (they've used a C++ compiler for testing). This is because in practice C is a subset of C++. More precisely, the subset of C which should be used for writing new programs is a proper subset of C++.

C99 changes this, which is unfortunate.



and we have a contradicton according to our assumptions and the factor theorem

[ Parent ]
Artificial counter-example, but still... (2.00 / 1) (#118)
by perle on Sun Feb 25, 2001 at 04:59:28 PM EST

Try compiling the following (add some context) with a C++ compiler.

...
foo = 5 //* a comment */ 3;
...


[ Parent ]
Did you even read his post? (none / 0) (#122)
by Carnage4Life on Sun Feb 25, 2001 at 05:13:14 PM EST

The question isn't whether all of C is compatible with C++ or not (it isn't) but the fact that the places were C differed with C++ was mostly where people were writing brain damaged code that they shouldn't have been using anyway such as recursive calls to main, leaving parameter lists empty instead of using void, putting long comments in the middle of code (your example), not casting void pointers, etc.

Basically with C89/C90, good C code was also C++ code while with C99 good C code is not C++ code.



[ Parent ]
casting void pointers (none / 0) (#215)
by mikpos on Tue Feb 27, 2001 at 01:36:29 PM EST

Casting void pointers is a good thing?! That's where a significant number of bugs in C programs come from, casting away potential warnings. Repeat after me: never cast in C. It is useless and only hides errors.

[ Parent ]
nope (none / 0) (#131)
by Delirium on Sun Feb 25, 2001 at 08:25:52 PM EST

The code you just typed evaulates identically with both C++ and C99. And it's pretty crappy code to begin with. Your point?

[ Parent ]
Another way... (none / 0) (#165)
by ucblockhead on Mon Feb 26, 2001 at 11:31:06 AM EST

To put that another way: It is trivial to create a real-world program that compiles under both C and C++.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Real world example (none / 0) (#167)
by ucblockhead on Mon Feb 26, 2001 at 11:33:35 AM EST

Ok, it is lame to reply to myself, but I just wanted to add that only a few months ago, I "converted" a real world program (in this case, a WinAmp plugin) from C to C++ simply be changing the filename from "foo.c" to "foo.cpp".
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
I wanted operator overloading! Waaaah! (3.33 / 3) (#19)
by 42 on Fri Feb 23, 2001 at 11:56:04 PM EST

Much as I distrust and dislike the whole OOP thing, when I came across operator overloading in C++, I started lusting for it in C. It is so cool to define an arbitrary datatype A, define an operation that makes sense for it (for example +), and then use that operator on two variables of the same type thus :

A x,y,z;

/* Initialize x and y */

z = x + y;

....and there you have it - the power to express your ideas in a few lines elegantly and in a way that closely resembles what you would have written in pseudocode.

And while I'm here whining away, could you please give me some templates too?

Operator Overloading (4.50 / 4) (#26)
by Bad Harmony on Sat Feb 24, 2001 at 01:07:49 AM EST

I'm glad it wasn't included. One of the nice things about C is that you can read the code and have an idea about how much machine code and CPU cycles are required by a statement. You can't do this when people go berserk with operator overloading. I've seen this happen with "clever" C++ code that turns a simple variable/object reference into a trigger for 20 pages of all-singing, all-dancing storage management.

54º40' or Fight!
[ Parent ]

Operator overloading and mathematical axioms (3.00 / 3) (#54)
by evvk on Sat Feb 24, 2001 at 11:54:27 AM EST

> I'm glad it wasn't included.

Metoo! Seriously, though, I think operator overloading is a good when used with certain restrictions. An object overloading addition and multiplication should, for example, satisfy the ring axioms. One defining addition and scalar multiplication should satisfy the field axioms. And one defining just a single operation, the group axioms. Or something like that.


[ Parent ]
My boeuf with C++ I/O (3.00 / 2) (#112)
by pin0cchio on Sun Feb 25, 2001 at 12:25:41 PM EST

One defining addition and scalar multiplication should satisfy the field axioms.

The left-shift operator << looks like a "times 2 to the power of" operator.

  • So why doesn't it work on floating-point numbers, changing only the exponent? (It works this way in FutureBASIC for Macintosh.)
  • So why does C++ I/O use it as a "write" operator? That violates the ring identity "(a << 2) / 4 == a".


lj65
[ Parent ]
Not quite! (4.00 / 1) (#117)
by Cuthalion on Sun Feb 25, 2001 at 03:25:46 PM EST

The left-shift operator << looks like a "times 2 to the power of" operator.

It sure looks that way for positive numbers, doesn't it!

[ Parent ]
If what you meant is "only for positive numbe (none / 0) (#229)
by pin0cchio on Wed Feb 28, 2001 at 06:12:41 PM EST

pin0cchio:
<< looks like a "times 2 to the power of" operator

Cuthalion:
It sure looks that way for positive numbers, doesn't it!

If what you meant by that is "the formula doesn't work for zero or negative integers," that's not quite right either. For simplicity, use 16-bit numbers. Zero is 00000000 00000000; no matter how much you shift it to the left, it's still zero. The binary version of -5 is 11111111 11111011; shift it left 4 places and you get 1111111111 10110000, or -80, which is -5 * 2^4. Try it on other numbers if you don't believe me.


lj65
[ Parent ]
Assessing the performance of code (4.33 / 3) (#60)
by 42 on Sat Feb 24, 2001 at 12:38:10 PM EST

I think what you really mean is that you are famililiar enough with the C language so that it is obvious to you (from years of experience), whether a snippet of code is expensive or not - and that you dont have the experience to do that with code written using operator overloading.

Fair enough. Thats your experience. Bear in mind that there are lots of people out there who can assess code written using operator overloading for their performance, just as easily as you can do it with straighforward C code. And this indicates that even you can be taught to do it. Also keep in mind that since C and C++ are Turing complete, any code written in C++ can be written in equivalent C terms. Its just going to be not as readable as code written using operator overloading.

The "clever" C++ code argument that you use is really a misleading piece of rhetoric. C itself gives you enough rope to hang yourself with by writing "clever" code. The standard argument used by C language advocates is : discipline yourself - dont write clever code for the sake of being clever. I would use the same argument to conteract your statement. If operator overloading evr makes it into C, just because you can write "clever" code is no reason you should. Programming discipline is a good thing - no matter what the language.

[ Parent ]

C vs. C++ (5.00 / 2) (#76)
by Bad Harmony on Sat Feb 24, 2001 at 07:52:00 PM EST

C and C++ are distinct languages and I would like to see them kept that way. If I want objects, operator overloading, inheritance, templates, exceptions and other high level features, I will use C++. If I want an efficient, procedural, systems programming language, I will use C.

54º40' or Fight!
[ Parent ]

Distinct difference (5.00 / 2) (#83)
by ucblockhead on Sat Feb 24, 2001 at 10:16:52 PM EST

No, there is a distinct difference. With C++, there can be lots of hidden code. When you see something like "x+y;" in C, you have a very good indication of how much code is there. The amount of code in '+' never varies from program to program. But in C++, the operator could have been overloaded, so literally any amount of code could underly it.

The same goes for declarations. In C, it is easy to tell how much code is in each declaration. With C++, essentially any amount of code can be hidden in there.

Given equivalently complex code, it is harder to determine the efficiency of the C++ code over the C code because in C, only functions can contain arbitrary code whereas in C++, both operators and declarations can as well.

Whether or not you can do the same thing in both languages is not really the point. We are not talking about what the language can do. We are talking about how easy the languages are to read.

It isn't just a matter of familiarity. I've known C++ as long as I've know C, and C is definitely simpler and easier to read. I happen to prefer coding in C++, but I think it is important to recognize that it does have weaknesses.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Wrong (5.00 / 1) (#142)
by trhurler on Sun Feb 25, 2001 at 11:40:01 PM EST

Bear in mind that there are lots of people out there who can assess code written using operator overloading for their performance, just as easily as you can do it with straighforward C code.
Given certain constraints, yes. The problem is, those constraints don't apply to reality. For instance, not all source code is always available. Not all overloaded operators were chosen by people with functioning frontal lobes. And while you can always choose a good name for a function if you're careful, you can't always get a good fit from the existing set of operators for the operation you're after - but that rarely stops people from trying.

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
While you're at it... (4.00 / 2) (#33)
by cameldrv on Sat Feb 24, 2001 at 04:12:27 AM EST

Why not also allow named operations on structs like a.dotproduct(b). Face it. Abstraction is one of the fundamental tenents of OOP, and it's not one of procedural programming. Once you have gotten this far, the rest of the ideas in OOP follow as a natural consequence.

The big divide between OOP and procedural programming as I see it is one of explicitness and implicitness. In procedural programming, any time you want to make something magical happen, you call a function. The fact that you are doing this is clear to all concerned. In OOP, you get the ability to make logical abstractions. If you think that the + operator makes sense to add matrices, go for it. The only problem is that you have to take responsibility for the wreckage once subtle assumptions are violated when you substitute one seemingly equivalent class for another.

If you want your program to be a detailed list of all the things that happen in your program, use C. If you want your program to be the definition of a structure which is understandable at many levels, and executes by traversing that structure, use C++.

[ Parent ]
Its true that this is the current status... (none / 0) (#56)
by 42 on Sat Feb 24, 2001 at 12:01:46 PM EST

....but really, this is the case only because the C language doesnt support the notation. Abstraction, itself, is very much part of the procedural paradigm. People typically use macros (ugh!) or function pointers to implement abstractions. With operator overloading, the same code that uses macros or function pointers can be re-written so that the code is more readable.

Your complaint seems to be that once this is done, the dichotomy between the style of programming that you have been used to (procedural) and the OOP style will become a bit blurred. That is not a valid argument : things change. The fact that things become different after a change cannot be used as a valid argument against change.

Look at the kludge they put into C99 to support complex numbers. Aaaarrrrggghh! This mess would have been unnecessary if operator overloading had been provided.

[ Parent ]

Operators, functions, and complex numbers (5.00 / 1) (#100)
by cameldrv on Sun Feb 25, 2001 at 05:28:07 AM EST

First, I agree that adding complex numbers to C is a dumb idea. Second, the style of programming I am used to is OOP. The first huge problem with C99 is that they failed to preserve the status of C as a near-subset of C++. This now means that users of C++ will not be able to cut and paste C code into C++ programs for purposes of object wrapping and such.

If you add operator overloading to structs, you have to make a way to define which function gets called in which cases. This sounds simple enough, but what happens when you have pointers to structs, const structs, etc? Then you need a prescidence scheme like C++ has. This can be one of the most confusing aspects of C++ because it is not always clear which functions will be executed when you use operators in certain configurations. In C++, these rules are part of a comprehensive overloading framework which works equivalently on operators and methods.

The argument for C in favor of C++ seems to hinge on complexity. C++ sometimes does things you don't expect because of the way it picks overloaded functions. You are now proposing to add this to C, giving C much greater complexity for no real gain in semantic flexibility. You are adding complexity to the semantics in order to get some syntactic sugar. What I'm saying is that I think that this violates the whole point of using C over C++.

If you want operator overloading, just use your normal C code, define a class (even call it a struct if you must), define some operators to overload, and use a C++ compilier. Let C fit in its nice little package, and let C++ fill its larger one. What you are proposing has the feel of a rube goldberg device like perl rather than a coherent unified language like C or C++.

[ Parent ]
No class (3.00 / 1) (#108)
by ucblockhead on Sun Feb 25, 2001 at 11:44:11 AM EST

You don't need to define a class to do this... You can just overload the operator:

operator int +(int x,int y)
{
    return x+y;
}
This is one reason why I wonder if some of these changes are really necessary. People seem to forget what Stroustrup has said from the beginning: that you can use C++ as a "better C" without OOP.

Most of the changes in C99 can be gotten today just by using C++ and refraining from creating any classes. That's not necessarily a bad thing to do, especially considering that the performance hits you might get in C++ only come with the OOP features.

Too many people think that because C++ allows OOP, it must be OOP. But you can write a completely structured program in C++, with nary a class in sight, use "printf()" for output and even use "malloc()" for heap allocation.

Not that I'm recommending that for most projects, but if the choice were between using C and using C++ without classes, I'd probably choose the latter.
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

Can't overload primitive types in C++ (5.00 / 2) (#113)
by pin0cchio on Sun Feb 25, 2001 at 12:30:10 PM EST

You can just overload the operator: operator int +(int x,int y)

No you can't. I'm not a language lawyer, but I distinctly remember from programming 101 that you cannot overload C++ primitive types' operators. You need a class.


lj65
[ Parent ]
Uh.... (none / 0) (#129)
by ucblockhead on Sun Feb 25, 2001 at 07:45:28 PM EST

Ok, I'm a moron, you are right...

However, you can overload an operator that operates on structs. (Because a C++ struct is really the same as a C++ class that defaults everything to public.)
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Overloading & Templates (3.66 / 3) (#63)
by Puchitao on Sat Feb 24, 2001 at 01:20:47 PM EST

Yah, these would be my picks, too.

I hear that the STL fellow (Stepanov, his name is?) might be working on something like this. He mentioned offhand in some recent article (sorry, can't find a link; mightabeen on lambda.weblogs.org) that he would like to see generic programming in a more C-like language, without all the overhead and strangeness of C++. Then he said something to the effect of "from this, readers can probably guess what my next project is about."

This is something I'd like to see. But hell, while we're whining, what would be great would be a fairly low-level, C-like language with a nice, rich type system and Hindley-Milner type inference. Where you could write a function and, on its own, the compiler would discover that its type was Ord a => a -> a or somthing like that.

No chance C would metamorphose into something like this, but if any of you know one that does, I'd like to see it. And if you're working on one, I'll help ;)

Puchitao
Perhaps we can do *snappy fun* with you everytime! -- Orz
[ Parent ]
A C-like language with a rich type system (none / 0) (#130)
by ravi_n on Sun Feb 25, 2001 at 08:02:30 PM EST

This is something I'd like to see. But hell, while we're whining, what would be great would be a fairly low-level, C-like language with a nice, rich type system and Hindley-Milner type inference. Where you could write a function and, on its own, the compiler would discover that its type was Ord a => a -> a or somthing like that.

It'll never happen. A sound type system (like Hindley-Milner) requires garbage collection. And garbage collection is the poster child of an expensive feature that low-level, C-like languages do not have.

Why does a sound type system require GC? Consider the following scenario:
I have a reference to an A at address XXXX. I free the object, but happen to keep a pointer to it hanging around. Later on, I allocate a B which happens to be stored at XXXX. I use my old pointer to an A. Boom! A type error the compiler cannot detect. The only way to prevent this scenario is not to allow me to free objects. Given a finite-memory machine, I need garbage collection.

[ Parent ]
That's funny! (3.00 / 1) (#137)
by Brandybuck on Sun Feb 25, 2001 at 09:15:38 PM EST

That's really a hoot! I mean you saying you distrustand dislike the whole OOP thing and all that. What you fail to realize is that you just reinvented most of it! OO consists of encapsulation, inheritance and polymorphism. Polymorphism means that you can treat "A" just like a fundamental data type, including adding two together.Encapsulation means that all of the stuff of "A" that makes it act like a fundamental data type should be invisible to the programmer. You code doesn't have inheritance, but it's an easy step to get there. Just imagine "A" is a 2D vector and you need a 3D vector...

[ Parent ]
Polymorphism (5.00 / 1) (#140)
by pig bodine on Sun Feb 25, 2001 at 10:38:10 PM EST

Polymorphism is the ability to treat two datatypes like each other. ie. I define an abstract or partially abstart class called "image", in order to create a standard interface, complete with functions like "display". Now I can create classes which inherit from "image" such as "jpeg" and "png" and contain their own implementations of "display". This can be treated exactly like "thing", so that I can make a list of them, and when I need to display them, I can call "display" without worrying whether it is "jpeg.display" or "png.display".

This has absolutely nothing to do with treating everything like a fundamental type.

[ Parent ]

Typo (none / 0) (#141)
by pig bodine on Sun Feb 25, 2001 at 10:39:06 PM EST

By "thing", I meant "image". Brain spasm.

[ Parent ]

No, no, no (4.00 / 1) (#138)
by trhurler on Sun Feb 25, 2001 at 10:11:29 PM EST

Operator overloading is one of the single worst ideas in the history of bad ideas. It is fun to play with, but it is never necessary, it rarely makes code any easier to read than it could have been otherwise, it never makes code easier to write than it could have been otherwise, and it OFTEN makes code harder to read. C is a simple language with complicated libraries - this is as it should remain. The litmus test for a new feature in C should be this: does this make programmers' lives easier or harder? I don't care how buzzword compliant it is. I don't care if you get wood from it. If it doesn't demonstrably improve the practice of programming in C, then it doesn't belong.

Do you understand, by the way, how much more complex C's type system would have to be in order to support overloading? Conceptually, not much, but in terms of what compilers would have to do and what generated code would look like - it is a world of difference. Currently, typedefs are really nothing more than aliases, and can be implemented as such. Users never create new types in any real sense. This is as it should be - because this is not an OO complexity monster - this is C!

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
A counterpoint (none / 0) (#150)
by aphrael on Mon Feb 26, 2001 at 04:00:01 AM EST

to a case where operator overloading is a *good* thing:

I work all the time with these classes that aggregate COM interface pointers, managing their refcount for me (in effect they are smart pointer wrappers, although they provide other functionality). Operator overloading allows me to say: wrapper->func() rather than wrapper.interface_member->func() .... which, in general, makes the code *much* more readable and clear.

[ Parent ]

Yes (none / 0) (#169)
by trhurler on Mon Feb 26, 2001 at 11:41:22 AM EST

In languages that allow such things, that's great, although that's also practically never how anyone uses overloading; almost every use it gets put to is a bad use. However, none of that matters in this case, because we're talking about C. C doesn't do what you're after anyway, and anyone who wants it to do so should be using C++ anyway.

That's why I found the original comment about overloading so annoying - it is clearly the viewpoint of someone who doesn't know what operator overloading is good for anyway, and yet he's whining because a standards committee didn't include it in the new version of C. I suppose I'm just being mean, but it seems like if you don't have more of a clue about why a feature is a good or bad thing than "what a cool idea!" you might keep your mouth shut...

--
'God dammit, your posts make me hard.' --LilDebbie

[ Parent ]
LCC has operator overloading (none / 0) (#236)
by revbob on Fri Mar 02, 2001 at 09:07:41 PM EST

They do it the C++ way, pretty much, e.g.,

COMPLEX operator+(COMPLEX A, COMPLEX B)
{
... Code for complex number addition goes here
}

You can get it here if you're interested.

I'll let philosophers (dining or otherwise) figure out if it's a good idea. I've debugged code from subcontractors who overloaded operators stupidly (I recall their notation for cross-product overloaded "**"), and I was not amused.

[ Parent ]

New rules..... GCC support? (2.40 / 5) (#21)
by Blarney on Sat Feb 24, 2001 at 12:07:50 AM EST

It may or may not be a good thing to be able to declare variables whenever you want. I've seen a few demented people write code like this in C in order to get the "declare when you want" effect :

int foo()
{
  char * zebra = "640K is all ";
...... page or so of code ....
  /* oh crud, I need another variable */
  {
  char * horse = "all your base";
...... more code, blah, blah, blah,
  /* just add one more bracket here, nobody will ever notice :) */
}}
So this is not really a big change, all it has to do is insert a couple "virtual" curly braces!

One thing that I'm wondering about all these changes is how long GCC will take to support them? While I understand that GCC is not officially standards compliant, in practice it is pretty much closer to standard C/C++ then most commercial compilers - MSVC, MIPS Pro, etc..... It'll be a sad and lonely standard if GCC won't run it.



SORRY! SHOULD'A READ THE ARTICLE! (1.00 / 2) (#24)
by Blarney on Sat Feb 24, 2001 at 12:16:12 AM EST

So GCC is adopting the standards. Sorry for cluttering the board....



[ Parent ]

That code is creepy. <NM> (none / 0) (#149)
by aphrael on Mon Feb 26, 2001 at 03:57:33 AM EST



[ Parent ]
Imaginary & Complex (3.50 / 4) (#27)
by SlydeRule on Sat Feb 24, 2001 at 01:09:44 AM EST

N. Addition of _Complex and _Imaginary number types: A boon for programmers doing any sort of advanced math in their programs.
Since they have a separate _Imaginary type (as opposed to using a float or double), it sounds like they might've gone with the more involved method of dealing with imaginary and complex numbers. See this paper (PDF) by Kahan and Darcy for some details.

As I understand it (and I don't pretend to really understand it), the primary issue revolves around the necessity of distinguishing between -0.0i and +0.0i. From the cited paper:

A streamline goes astray when the complex functions SQRT and LOG are implemented, as is necessary in Fortran and in libraries currently distributed with C/C++ compilers, in a way that disregards the sign of ± 0.0 in IEEE 754 arithmetic and consequently violates identities like SQRT( CONJ( Z ) ) = CONJ( SQRT( Z ) ) and LOG( CONJ( Z ) ) = CONJ( LOG( Z ) ) whenever the COMPLEX variable Z takes negative real values. Such anomalies are unavoidable if Complex Arithmetic operates on pairs (x, y) instead of notional sums x + i·y of real and imaginary variables. The language of pairs is incorrect for Complex Arithmetic; it needs the Imaginary type.
A controversial Complex Arithmetic Extension to the programming language C incorporating that correction, among other things, has been put before ANSI X3J11, custodian of the C language standard, as part of the C9X proposal. It is controversial because it purports to help programmers cope with certain physically important discontinuities by suspending thereat ( and nowhere else ) the logical proposition that " x == y " implies " f(x) == f(y) ". Many a programmer will prefer this anomaly to its alternatives.


The imaginary type (none / 0) (#155)
by cameldrv on Mon Feb 26, 2001 at 04:13:45 AM EST

I don't quite see why this guy thinks that what other complex libraries do is wrong from a mathematical point of view. Clearly +0 = -0. Essentially he has a branch cut problem in some of his codes, and he wants the compilier to make the decision that he would make based upon keeping an extra bit of information. This however is the same as saying that SQRT *VIOLATES IDENTITIES* by not making SQRT(x^2) = x. You don't see anyone trying to add an extra bit onto the FP numbers to keep track of whether a negative value has been squared though. The bottom line is that squaring end exponentiation are not 1-1 functions, so their inverses are not functions. Trying to deny this is pretty pointless.

[ Parent ]
Declarations anywhere (3.42 / 7) (#36)
by arnald on Sat Feb 24, 2001 at 05:37:37 AM EST

I feel I must comment...

Why do so many people feel this is a bad thing? I think it's probably the most important improvement of ALL the new C99 features.

The simplest way to avoid silly mistakes and problems with variables is to make sure they are initialised as soon as they are declared (this is what Stroustrup recommends). But you can't generally do this at the top of the block. So you have a load of declared variables that are unitialised; dangerous!

With unrestricted declarations, you can introduce the variable WHEN you need it, and when it makes SENSE to introduce it.

Moreover, anything that allows restricted-scope declarations has got to be a good thing in large projects.

So why don't more people seem to share my views?!



The only counterpoint (none / 0) (#148)
by aphrael on Mon Feb 26, 2001 at 03:56:34 AM EST

I can think of comes from bad experiences with code maintenance: so where is that variable declared, anyway?

[ Parent ]
Bad experiences with code maintenance... (4.50 / 2) (#164)
by ucblockhead on Mon Feb 26, 2001 at 11:26:10 AM EST

The particular bad experience I've had comes when encountering code like this:

int Foobar(int i)
{
    int x = i;

    if( x == 10 ) {
        int i;
        for(i=0;i<x;i++) {
            DoIt(i);
         }
    }
}

It is easy to get confused about which 'i' is which. Allowing declarations anywhere just makes that worse.

And two points before anyone nitpicks:

  • This is small example. The real functions I've run into that did this were ~200 lines.
  • Don't tell me it is bad code. I know it is bad code. I didn't write it. I'm just the poor sap that had to fix it. That is the most important point about maintainability. It isn't just what you do, but what everyone else does. And you'd be amazed what people will do. I won't even begin to rant about the idiot who declared 'i' as a global variable, used it to pass information into a function and used a local 'i' for loop variables...

  • -----------------------
    This is k5. We're all tools - duxup
    [ Parent ]

Variable Declarations Anywhere (none / 0) (#184)
by pjhunter on Mon Feb 26, 2001 at 05:49:20 PM EST

First let me say that I frequently in C++ declare variables all over the place! However, the reason variables were initially required to be declared at the head of the stack is infact the layout of the stack frame itself. The stack memory allocated for the variables indeed sits at the head of the frame. Executing code, such as initialization, occured afterwards. In most cases, I believe that all variables should be declared and initialized next to the context in which they are used. However, the follow evils should be avoided, which the old C style declaration rules prevented:
  • Same symbol different scope declarations. nothing is worse than the same symbol (the 'int i' is a classic example) declared multiple times in a function, even if it's used in one for-loop after another.
  • Too many stack variables. Not that variables should be reused over and over again (that's confusing), but there is something wrong with your data structures if you've got more than a dozen variables declared in a method. With the variables all declared at the top, you get a sense of how successful you've been in organizing the method.
  • Big methods. If you believe in long implementations (and I don't) in a single method (i.e. more than 25 lines), then you'd better declare your variables at the top of the method. This way, when some poor shmuck is souping his way through your code, trying to figure out where something is declared, he'll know right where to look.

%pjh%
[ Parent ]
I get funny looks... (none / 0) (#235)
by revbob on Fri Mar 02, 2001 at 08:41:04 PM EST

...when I use #defines right before they're used, which seems like just plain good sense to me:

#ifdef O_BINARY
#define HM_OPENMASK (O_WRONLY | O_CREAT | O_BINARY)
#else
#define HM_OPENMASK (O_WRONLY | O_CREAT)
#endif

if (binname) {
binfile = open(binname, HM_OPENMASK, set_filemode);

Yeah, I know it doesn't exactly matter, but it's still a kindness to anybody maintaining the code.

Ada doesn't even let you declare a loop index, which is a good idea (perhaps taken too far). It does relieve you of the annoying extra step of going back and killing off dead variables (which 99 times out of 100 are loop indexes, at least in my code) so you don't ship code that gives compiler warnings.

[ Parent ]

VA_ARG macros (1.25 / 4) (#37)
by pellemell on Sat Feb 24, 2001 at 07:43:42 AM EST

Do we really need variable argument macros?
How about supporting inline keyword for functions instead?

Uh ... (4.00 / 2) (#44)
by kostya on Sat Feb 24, 2001 at 09:51:36 AM EST

Maybe I'm missing a greater context here, but the C99 standard does support inline.

D. Inline functions : The C language now supports the inline keyword which allows functions to be defined as inline, which is a hint to the compiler that invocations of such functions can be replaced with inline code expansions rather than actual function calls.

Perhaps you missed that?



----
Veritas otium parit. --Terence
[ Parent ]
inline functions and macros (4.00 / 2) (#59)
by ucblockhead on Sat Feb 24, 2001 at 12:22:58 PM EST

inline functions can't replace macros because there are many things you can do in macros that can't be done otherwise. For instance, our old favorite typeless function:

#define MAX(X,Y) ((X)>(Y)?(X):(Y))

Macros are the only way to get any sort of generic function in C. template functions can let you do much of this, but that's C++, not C
-----------------------
This is k5. We're all tools - duxup
[ Parent ]

heh, that's funny (none / 0) (#97)
by xriso on Sun Feb 25, 2001 at 03:34:38 AM EST

That's one of the macros that causes problems. What happens is that X and Y are evaluated, and then one of them will be evaluated again. Imagine MAX(4,i++):
If i < 4, then all's fine. It returns 4
If i >= 4, then you get i++ being evaluated twice in the same expression. i will end up being incremented twice, probably not what you were expecting.
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]
VA_ARG for printf-like functions (none / 0) (#74)
by ah on Sat Feb 24, 2001 at 06:54:25 PM EST

As far as I can tell, there is only one legitimate reason for including the VA_ARG macros. It allows you to write code like this:

void debug_internal(int line, char *file, char *func, char *fmt, ...);
#define debug(FMT,...) \
debug_internal(__LINE__, __FILE__, "__func__", FMT, VA_ARGS)

And this is much easier than explicitly writing something like

if (x == 2) printf("x=2 on %d of %s in %s\n", __LINE__, __FILE__, "__func__");

and you can now do tricks like

int x;
...
itrace(x);
which prints out "x = 5 on line 5 of foo.c, function main".

GCC already had support for this, however it was undocumented (and it used something other than the VA_ARGS syntax, I forget what). This is impossible to do in a thread-safe, portable way with C89.

[ Parent ]

On declaring variables anywhere (4.12 / 8) (#38)
by Pseudonym on Sat Feb 24, 2001 at 08:20:31 AM EST

<RANT SERIOUSNESS="half">

I'm shocked at what I'm hearing from those who don't like declaring variables anywhere. Haven't you people ever heard of the principle of latest binding? It should be etched in the skull of every programmer.

AEleen Frisch called it the virtue of "laziness". Kent Beck called it the rule of "you aren't gonna need it". Don Knuth summed one instance of the rule nicely with "premature optimization is the root of all evil". It all boils down to one simple generalisation: Don't type it until you need it. Declaring a variable too far before its first use violates this cornerstone of programming. So there.

</RANT>

On a more serious note, something that nobody seems to have brought up yet is that the argument for declaring variables anywhere is much stronger in C++ than it is in C. The reason is that declaring a variable in C (assuming there's no initialiser part) does nothing more than tell the compiler that space will be needed for a variable of such-and-such-a-type with this name at some point in the enclosing scope. When you declare a C++ variable, on the other hand, you might also call the type's constructor. If all the data that you need for the real initialisation of the variable is not available yet, you would need to re-initialise when it does become available if you were forced to declare it too early. To avoid this cost, C++ lets you declare the variable when all the data needed to construct it is ready.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
Clean code (3.66 / 3) (#45)
by DJBongHit on Sat Feb 24, 2001 at 09:57:58 AM EST

I'm shocked at what I'm hearing from those who don't like declaring variables anywhere. Haven't you people ever heard of the principle of latest binding? It should be etched in the skull of every programmer.

AEleen Frisch called it the virtue of "laziness". Kent Beck called it the rule of "you aren't gonna need it". Don Knuth summed one instance of the rule nicely with "premature optimization is the root of all evil". It all boils down to one simple generalisation: Don't type it until you need it. Declaring a variable too far before its first use violates this cornerstone of programming. So there.
Yeah - so declare variables at the beginning of a block, not in the midst of other code. In C89, you're still not restricted to declaring variables at the beginning of a function - you can declare them at the beginning of any block. If you'd like, you can just throw in a new block to visually separate the block of code and declare new variables. For example, this is perfectly legal code:

int a;
a = 12;

{
int b = a * 2;
printf ("b: %d\n", b);
}


This is valid because it declares b at the beginning of a block, where it's needed. Variable declarations should not be sprinkled through code, they should be at the beginning of the block which uses them. Otherwise code gets messy.

Granted, this is a stupid example, but you get the point. If a section of code needs its own variable declarations, at least put it in its own block - that way the variables go out of scope when they're no longer needed, and it visually helps by letting you see separate areas of code, even if they're not separate functions.

~DJBongHit

--
GNU GPL: Free as in herpes.

[ Parent ]
See below (none / 0) (#101)
by Pseudonym on Sun Feb 25, 2001 at 05:57:19 AM EST

Yeah - so declare variables at the beginning of a block, not in the midst of other code. In C89, you're still not restricted to declaring variables at the beginning of a function - you can declare them at the beginning of any block.

That'd be great if variable lifetimes really did nest, but in practice they rarely do. I expand on my response to this objection below.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
Where to declare variables (4.33 / 3) (#64)
by SlydeRule on Sat Feb 24, 2001 at 01:47:36 PM EST

Good observation on the C++ point. C++ is unusual in that it can, and by default does, allocate objects on the stack rather than the heap. Combined with the ability to specify initial values in the constructor, this leads to a strong motivation to allow mid-procedure declarations.
I'm shocked at what I'm hearing from those who don't like declaring variables anywhere.
And I'm shocked that people don't like goto. After all, I can write a perfectly structured program using goto.

Okay, I'm being facetious. But my point is that it isn't the feature itself that's questionable, it's whether or not it's the right tool for the job.

If you want to declare a variable in mid-procedure, you can always (as many others in this thread have pointed out) create an anonymous block. This is a cleaner approach, since the scoping of the variable is clearly delimited. Otherwise, you have variables whose scopes start at arbitrary points and continue to the end of the enclosing block. This is particularly troublesome in a language like C which still has the goto statement.

However, the wise programmer will, on encountering the desire to declare a variable mid-procedure, stop and look at what's going on. Declaring variables in mid-procedure is a red flag that the procedure has sub-optimal cohesion. The questions to be answered are, "how bad is it?" and "is there a better way?"

The best possible case is sequential cohesion: the first part of the procedure locates or creates the object about to be declared, then the second part of the procedure works with it. Although sequential cohesion is the second-best form, you should at least consider making separate procedures out of one (or both) of the two parts. Typically, the part ahead of the declaration would make a good function.

If the relationship between the pre-declaration and post-declaration parts is anything other than "supplier and user", something's funky and the procedure's cohesion level sucks. You should seriously consider breaking it up to improve the cohesion level.

That said, I recognize that C is often used where performance is paramount, and the overhead of a procedure call may be unacceptable in certain spots. Still, the programmer should at least recognize that cohesion has been compromised in the name of efficiency.

Mid-procedure declarations don't harm procedures; poor cohesion harms procedures.

[ Parent ]

Cleanliness is relative (none / 0) (#89)
by Pseudonym on Sun Feb 25, 2001 at 01:39:10 AM EST

And I'm shocked that people don't like goto. After all, I can write a perfectly structured program using goto.

I note that you're being facetious, but I happen to think you're right. I use gotos, and I do it in a disciplined way. Nobody has ever shown me a better way to implement a state machine in C/C++, in fact. (There's an argument that continuation-passing style is cleaner, but you try writing CPS in C/C++.)

If you want to declare a variable in mid-procedure, you can always (as many others in this thread have pointed out) create an anonymous block. This is a cleaner approach, since the scoping of the variable is clearly delimited.

Maybe. I often find that my variable lifetimes don't nest neatly, because the value in one variable is often needed in the construction of another. For example, consider this C++ fragment:

baztype_t makeABaz(footype_t foo)
{
    do_something_to(foo);
    bartype_t bar(foo);
    do_something_to(bar);
    baztype_t baz(bar);
    return baz;
}

Here's the analogous C89 without anonymous blocks:

baztype_t makeABaz(footype_t foo)
{
    bartype_t bar;
    baztype_t baz;
    do_something_to(&foo);
    bar = constructBar(foo);
    do_something_to(&bar);
    baz = constructBaz(bar);
    return baz;
}

I don't like this because before the call to constructBar, the variable bar is live but not in a usable state, which may incorrectly encourage its use. Ditto for baz. So here's the C89 version with nested blocks:

baztype_t makeABaz(footype_t foo)
{
    do_something_to(&foo);
    {
        bartype_t bar = constructBar(foo);
        do_something_to(&bar);
        {
            baztype_t baz = constructBaz(bar);
            return baz;
        }
    }
}

What do you think? I don't think that separating the functionality into two procedures makes this code any clearer, and using anonymous blocks just adds tabs.


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
Well there is Bison (none / 0) (#103)
by kraant on Sun Feb 25, 2001 at 07:19:50 AM EST

<blockquote type = "cite">I note that you're being facetious, but I happen to think you're right. I use gotos, and I do it in a disciplined way. Nobody has ever shown me a better way to implement a state machine in C/C++, in fact. (There's an argument that continuation-passing style is cleaner, but you try writing CPS in C/C++.)
%man bison
?
--
"kraant, open source guru" -- tumeric
Never In Our Names...
[ Parent ]
Not quite (none / 0) (#105)
by Pseudonym on Sun Feb 25, 2001 at 08:54:23 AM EST

I think you meant "flex". Fine if you're writing a lexical analyser (if a bit slow, at least it's not irrevocably buggy like AT&T lex), not good for any other kind of state machine (e.g. simulating the states of a piece of hardware).


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
[ Parent ]
Bison is not lex (none / 0) (#106)
by Carnage4Life on Sun Feb 25, 2001 at 09:52:56 AM EST

I think you meant "flex". Fine if you're writing a lexical analyser (if a bit slow, at least it's not irrevocably buggy like AT&T lex), not good for any other kind of state machine

Err...I am rather sure that's why he suggested using Bison (based on yacc) and not flex based on (AT&T lex). A state machine is a natural fit for Bison as long as a grammar for the input is known or can be created.


[ Parent ]
My take. (4.42 / 7) (#55)
by i on Sat Feb 24, 2001 at 11:57:34 AM EST

  • Long identifiers -- identifier length should not be limited.
  • C++ style comments -- cool, because block comments are brain damage.
  • Vararg macros -- it's about time.
  • Inline functions -- gosh, another register keyword! When will people learn.
  • Restricted pointers -- probably the single extension that's worth a new round of standartisation.
  • Bool -- good, all those user-defined bools (one per library) must die.
  • Declarations anywhere -- well, there are two schools of thought here, I can't really make up my mind.
  • VLA -- good, alloca must die, but this means sizeof is not a compile-time expression anymore, and this is Not A Good Thing, and a source of some major incompatibilities with C++.
  • Declarations in loops -- way cool.
  • Named initialisation -- way cool.
  • Long long -- this is serious brain damage. C99 already has sized integer types (like int32 and int64). Why on earth anybody would use long long instead of one of those?
  • Functions must declare a return value -- just about time.
  • Struct hack -- ok, everybody uses it anyway.
  • Complex -- seems like another source of incompatibilities with C++.
  • Type qualifiers -- ?? It's Ok with typedefs, but directly? Why?
All in all, it's a pity the committee didn't consider C++ compatibility.

and we have a contradicton according to our assumptions and the factor theorem

Why long long. (4.50 / 2) (#70)
by bafungu on Sat Feb 24, 2001 at 05:09:22 PM EST

You need that in order to say "at least 64 bits", just like "long" means "at least 32 bits".

So what's the point of "at least"? Efficiency. Let's say you want to have at least 32 bits of resolution in some variable. Had you declared it "int32" instead of "long" it normally wouldn't make much difference until you compile the code on a 36-bit machine (like some of the old DEC beasts).

Had you used "long", the compiler would have cheerily used a "naturally-sized" 36-bit register, but had you used "int32" it would have to go through all sorts of contortions to make sure your variable is always 4 bytes long since that's what you insisted on. Hello, fat slow code.

"long long" is just what the doctor ordered when some 72-bit (or whatever) machine comes down the pike.

[ Parent ]

Ah, 36 bits machines. (3.00 / 2) (#93)
by i on Sun Feb 25, 2001 at 03:00:57 AM EST

So why not something along the lines of int_m64 ("m" for "minimum")? You see, one day there will be 128 bit machines, and then 256 bit machines, so will we have "long long long" and "long long long long" in some next incarnation of the Standard?

Many current programs assume that "long" is the longest "naturally represented" integral type, and introduction of "long long" means rewrite. This is unfortunate.



and we have a contradicton according to our assumptions and the factor theorem

[ Parent ]
128-bit numbers won't be integers. (3.50 / 2) (#110)
by pin0cchio on Sun Feb 25, 2001 at 11:58:04 AM EST

So why not something along the lines of int_m64 ("m" for "minimum")? You see, one day there will be 128 bit machines, and then 256 bit machines, so will we have "long long long" and "long long long long" in some next incarnation of the Standard?

I don't see 128-bit numbers treated as single integers very often. 2^128 is approximately 3.4 * 10^38; for comparison, the Milky Way Galaxy is about 10^21 meters across. High bit counts in newer architectures (such as PowerPC G4's claim of being 128-bit) refer to vector support (AltiVec/MMX/3DNow!) or to execution units in a multi-issue superscalar processor (think EPIC).

Many current programs assume that "long" is the longest "naturally represented" integral type, and introduction of "long long" means rewrite.

Then they're not written portably. On some 64-bit compilers, int is 32-bit and both long and long long are 64-bit. Besides, they're written in C89/C90, which is a different language from C99.


lj65
[ Parent ]
Very large integers (none / 0) (#206)
by ucblockhead on Tue Feb 27, 2001 at 11:31:30 AM EST

Well, there are potential uses. A while back, I worked on Point of Sale systems that use a 12 digit number to mark stock. Unfortunately, living in 32-bit land, we were stuck with the choice of either shoving this in a string (bad efficiency) or putting it into a hacked up structure with a long and and short. (Our compiler had no "long long" or "int64".) If we'd had a 64-bit int, it would have been really nice as we could have gotten integer performance with our key. As it was, we used the hacked up structure, which was fairly efficient, but a pain in the ass to code.

One can easily imagine wanting to do the same with a 20 digit number. Such things aren't so wildly unlikely when you consider the situation we had, where different digits had meanings. You might have a couple of check digits at the end, you might have a few digits that represent the style, color and department, and perhaps a few digits that represent the accounting class the thing belongs in. It is very nice having a number that a human being can parse and get meaning out of. Doing that, it is pretty easy to chew up a lot of digits even if most of the digit space ends up unused.


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

One acronym... (none / 0) (#220)
by beergut on Tue Feb 27, 2001 at 03:22:58 PM EST

BCD.

:-)


i don't see any nanorobots or jet engines or laser holography or orbiting death satellites.
i just see some orangutan throwing code-feces at a computer screen.

-- indubitable
[ Parent ]

Re: 128-bit numbers won't be integers (none / 0) (#239)
by Agincourt on Mon Mar 05, 2001 at 02:00:41 PM EST

I don't see 128-bit numbers treated as single integers very often.

Of course not -- there's no compiler support for it.

Ever worked IPv6 addresses? MD5 checksums? UUIDs? These would be *much* easier to deal with using 128 bit integers.

And these stay within the "systems" area -- IMHO, where C belongs. If you're programming general business or scientific stuff, C is the wrong tool for the job. From an applications standpoint, C looks like it is *designed* for causing buffer overflows, dangling pointers, and memory leaks.

"Real Programmers program in C because C is the only language Real Programmers can spell."


-- Nature cannot be fooled. -- Richard Feynman
[ Parent ]

Already there. (none / 0) (#115)
by Scott A. Wood on Sun Feb 25, 2001 at 01:58:11 PM EST

C99 has that. You can use int_least64_t or int_fast64_t for a type that is guaranteed to be at least 64 bits, but may be more. In all likelihood, the only reason long long was put in the spec is because there is a lot of existing code that uses it.

As for using long as the largest integral type, if the code that assumes that only uses char, short, int, and long, then that assumption is still correct, unless it interacts with other code that uses larger types (in which case the code should be updated to use intmax_t rather than long long; this way you won't have to update the code again next time).



[ Parent ]
wrong (5.00 / 1) (#214)
by mikpos on Tue Feb 27, 2001 at 01:27:03 PM EST

There are so many things wrong with this thread, it's hard to know where to jump in.

First off, int64_t is not guaranteed by the standard. The standard says that if an implementation defines a type of exactly 64 bits in size, it will be defined in stdint.h under the name int64_t. If you actually use int64_t in your code, don't expect it to work unless you know intimately all the platforms it will ever run on.

Secondly, long long, int_least64_t and int_fast64_t all have very different semantics. long long must be at least 64 bits in size; it must be at least as big as long. int_least64_t it the smallest integer type at least 64 bits in size (which could not only be smaller than long long, but smaller than long, int and short, as well). int_fast64_t is the faster integer type at least 64 bits in size. In the future, it could possible to find implementations where all three of these types are different (though int_least64_t and int_fast64_t will likely be the same for many years).

[ Parent ]

Hey, chew on this... (2.33 / 3) (#98)
by xriso on Sun Feb 25, 2001 at 03:39:08 AM EST

You know that part where it says that things like "const const" automatically become "const"? Well, doesn't that mean that "long long" turns into "long"?

I sense contradiction...
--
*** Quits: xriso:#kuro5hin (Forever)
[ Parent ]

Serious brain damage? (4.50 / 2) (#111)
by pin0cchio on Sun Feb 25, 2001 at 11:58:31 AM EST

Long identifiers -- identifier length should not be limited.

I agree with the longer limit, but I also realize the reason for a limit in the first place: what if you come up with an identifier that's longer than the longest identifier your architecture's object file format supports?

C++ style comments -- cool, because block comments are brain damage.

Not in all cases. They let you include a 20-line GNU GPL notice (see the bottom of the GPL) without having to add // at the beginning of each line.

Long long -- this is serious brain damage. C99 already has sized integer types (like int32 and int64). Why on earth anybody would use long long instead of one of those?

Partially because you don't want to lose support for recompiling your old code when you upgrade to a C99 compiler, and partially because (as bafungu mentioned) not all machines are pow(2, n)-bit for unsigned int n.

All in all, it's a pity the committee didn't consider C++ compatibility.

They're not obligated to. C is not C++ just like C is not Pascal (which, by the by, is NOT dead).


lj65
[ Parent ]
inline good (none / 0) (#157)
by codemonkey_uk on Mon Feb 26, 2001 at 04:56:38 AM EST

The inline keyword is a boon for encapulation - now you can write:
a = min(b++,c++);
Without worrying about a) evil macro text replacement doubling up the "++", or b) function call over head crippling your performance.

So, as you see, it really does change the semantics of the code, making it more than just a "register" hint...
---
Thad
"The most savage controversies are those about matters as to which there is no good evidence either way." - Bertrand Russell
[ Parent ]

Well, almost... (4.50 / 2) (#179)
by ucblockhead on Mon Feb 26, 2001 at 02:12:39 PM EST

You run into trouble with typing, though. Is that "int min(int,int)" or "float min(float, float)"?
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Well, yes (none / 0) (#204)
by codemonkey_uk on Tue Feb 27, 2001 at 05:56:01 AM EST

Thats why you should use C++ and templates.

But thats a whole other can of worms...
---
Thad
"The most savage controversies are those about matters as to which there is no good evidence either way." - Bertrand Russell
[ Parent ]

Templates... (5.00 / 1) (#207)
by ucblockhead on Tue Feb 27, 2001 at 11:53:07 AM EST

Yeah, if you've got C++, templates may be the way to go, but even that doesn't solve the problem totally, as you've got things like:

double Foo=1.45;
int Bar= 1;

float Result1 = max(Foo, Bar);
float Result2 = max(Bar, Foo);

Here's the question, which template do you use, this:

template <class T> T max(T x, T y)
{
    return x>y?x:y;
}
or this:

template <class T, class S> T max(T x, S y)
{
    return x>y?x:y;
}

If you want the bit of code at the top to compile, you've got to do the latter, but you will get different results in "Result1" and "Result2"!

As far as I can tell, the only way to get consistant results is to punt and define only "double max(double,double);" and rely on promotion, but even there, you've got a potential performance problem and are opening yourself up to bad floating point implementations.

I read an article by Scott Meyers (I think) which I've unfortunately lost that discussed this issue in detail. His conclusion was that there simply is no perfect way to write max!


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

Maybe I'm confused (none / 0) (#212)
by Biff Cool on Tue Feb 27, 2001 at 12:59:58 PM EST

Wouldn't

return x>y?x:y;

promote y to a double in both cases?


My ass. It's code, with pictures of fish attached. Get over it. --trhurler


[ Parent ]
That'd be nice, but... (5.00 / 1) (#216)
by ucblockhead on Tue Feb 27, 2001 at 01:36:32 PM EST

The two examples for reference:

  • template <class T> T max(T x, T y);
  • template <class T, class S> T max(T x, S y);
Yeah, that's what we'd want, but that's not what happens! The template code tries to match the arguments. In the first case, it has to have two "T"'s. But C++ won't promote to match the template, so it can't make a template that matches max(double, int) or max(int, double).

I suspect that they don't allow promotion to match templates because they are afraid of even more confusion.

The second case seems like it might work, but you get screwed up by the return type. The function returns the same type as the first argument, so that means that we get these two functions when we compile:

  • Result1 = double max(double Foo, int Bar);
  • Result2 = int max(int Bar, double Foo); // Doh!

-----------------------
This is k5. We're all tools - duxup
[ Parent ]
Duh (none / 0) (#221)
by Biff Cool on Tue Feb 27, 2001 at 05:30:33 PM EST

return types... I've been using JavaScript for way too long...

My ass. It's code, with pictures of fish attached. Get over it. --trhurler


[ Parent ]
But it's still just a hint ... (3.00 / 1) (#182)
by LegionDaMany on Mon Feb 26, 2001 at 04:49:13 PM EST

The thing I find laughable about inline and register and their ilk is that there's no guarantee that they'll get optimized. There is also no guarantee of the inverse ... that they won't get optimized if the keyword isn't there.

So ... what's the point of having the keyword? If you don't like the way the code is optimized ... talk to the compiler vendor.



Call me Legion for I am Many ...
[ Parent ]
No guarantee, but (3.00 / 1) (#203)
by codemonkey_uk on Tue Feb 27, 2001 at 05:54:18 AM EST

The point is to give the programmer a way of signalling their intent.

Its extra information that the compiler can use, and a good compiler will use it, where it can, without breaking when it cannot.

And as I'm sure you well know, compiler vendors do not change their optermisation algorythms on the whim of one customer!
---
Thad
"The most savage controversies are those about matters as to which there is no good evidence either way." - Bertrand Russell
[ Parent ]

Changing optimisation algorithms. (none / 0) (#208)
by ucblockhead on Tue Feb 27, 2001 at 11:54:59 AM EST

<peeve>No, they don't change it one the whim of one customer, they change it on the whim of one benchmark writer.</peeve>
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
C/C++ User's Journal (3.50 / 2) (#65)
by jeanlucpikachu on Sat Feb 24, 2001 at 02:30:25 PM EST

http://www.cuj.com/ They've ran some interesting articles on C99 already. Worth a read if you don't want to find out about it through trial & error.

--
Peace,
Capt. Jean-Luc Pikachu AIM: jeanlucpikachu
A response to various criticisms (4.50 / 4) (#66)
by damien on Sat Feb 24, 2001 at 02:57:06 PM EST

A number of people are unhappy about various additions to the standard -- C++-style // comments, looser rules on where variables can be declared, long long, etc.

One thing to realize about the standards committee is that it exists, among other things, to standardize common practice. A number of members were very much against long long, but it still made it in. The reason? Virtually every compiler out there supports it already. A lot of code uses it. For better or worse, it's become a part of C in practice; all the committee did was formalize this.

The same thing applies to // comments. Support for this is so widespread now that adding them to the standard merely acknowledges the current state of affairs.

It's not perfect, but it works. New features get tested in the real world; the ones that survive the court of popular opinion get standardized. Compilers comply to the standard faster, since they already supported many of the changes.

The real botches occur when things get added that weren't common practice. Consider trigraphs, for example.

-Damien

Trigraphs (none / 0) (#147)
by aphrael on Mon Feb 26, 2001 at 03:52:01 AM EST

what's your objection to them? They seemed (to me at least) like a reasonable modification to the language to support a problem that doesn't exist where I work but does elsewhere ...

[ Parent ]
trigraphs (none / 0) (#159)
by TransientReflection on Mon Feb 26, 2001 at 06:54:04 AM EST

They should have been switchable with a precompiler directive "#pragma recognize-trigraphs" or something - they can catch people unawares far too easily.


[ Parent ]
Ah ... (none / 0) (#174)
by aphrael on Mon Feb 26, 2001 at 12:31:44 PM EST

my default c++ compiler provides trigraph support via a seperate preprocessor. :)

[ Parent ]
no # char (none / 0) (#213)
by mikpos on Tue Feb 27, 2001 at 01:19:09 PM EST

For platforms without the # char in their character set, #pragma would be a bit tough to type, no? The platforms for which trigraphs are targeted (e.g. EBDIC) usually don't have the # character.

Most ASCII-supporting compilers will probably take gcc's lead and not be conforming by default (you have to -ansi to get trigraph support IIRC).

[ Parent ]

void main (4.00 / 1) (#227)
by Per Abrahamsen on Wed Feb 28, 2001 at 12:26:46 PM EST

But "void main" is still an error ;-)

[ Parent ]
Not so good for embedded developers (4.25 / 4) (#77)
by pw201 on Sat Feb 24, 2001 at 07:56:19 PM EST

I work on hard real time embedded systems, mostly in C (and I'm not speaking for my employer :-). I can't see many people in my application area going for C99 in a big way. We're already using restricted subsets of C90. C99 seems to add a lot of stuff which will just get restricted away again.

Let's see:

  • Identifier size limits: Good idea, fair enough, no problem.
  • Variable argument macros: The preprocessor is the source of enough errors already without adding this.
  • Inline functions: probably a good idea.
  • _Bool: Do the results of comparisons now have this type? What happens when people compare things to this type: are non-zero integer types going to compare equal to "true"? Do comparisons evaluate to a _Bool type? It's possibly a good thing to have a type for flags and so on, but not sure whether it'll end up causing confusion.
  • Declare variables anywhere: may lead to messy code. As people have pointed out, C already allows you do so this by starting a new block.
  • Functions must declare return type: Finally, something unequivocally good.
  • Complex types: What happened to the elevator controller argument? I find the inclusion of complex types bizarre.

ISTM that the good bits of C99 are mostly things which do away with poor coding practices which can already be caught by static analysis tools. I don't feel C99 has any significant advantages over C90 for me, in fact, quite the reverse.

Unused stuff will just get left out of the binary (none / 0) (#243)
by pin0cchio on Fri Jan 25, 2002 at 01:01:49 PM EST

Declare variables anywhere: may lead to messy code. As people have pointed out, C already allows you do so this by starting a new block.

Which leads to a proliferation of {{{ }}} almost as repulsive to some as the ((( ))) of Lisp.

Complex types: What happened to the elevator controller argument? I find the inclusion of complex types bizarre.

The point of language design is to make a language that can do simple things well and complex things well (pardon the pun) without having the baggage for the complex things affect programs that only use the simple things. In the case of an embedded system (anywhere from an elevator controller to a Game Boy Advance), if you don't use any complex types in a program, the related libraries just won't get linked into your executable.


lj65
[ Parent ]
No symmetric treatment of void (3.25 / 4) (#85)
by SIGFPE on Sat Feb 24, 2001 at 11:13:40 PM EST

I think C should treat a void like any other type. For example this should be valid:

void f() {
void a;
return a;
}
I've always thought it weird that void is treated in a special way and it's useful to be able to do this when writing certain types of macro that can take a type as argument. This would add no bloat to the compiler. C++ does it though that's not an argument for anything.
SIGFPE

You /can/ do that. (5.00 / 1) (#88)
by Inoshiro on Sun Feb 25, 2001 at 01:36:20 AM EST

That's what a void pointer is for. Working with some unspecified data type. You just have to be careful to increment the pointer by the proper number of bytes while working with such streams :)

If you want fancy templates, go to C++ and the STL. Macros let you do simple template like stuff. A code generator could certainly let you do templates in C, you'd just need to run it through a preprocessor that then spit out the C code equivalent :)



--
[ イノシロ ]
[ Parent ]
No you can't... (none / 0) (#126)
by ksandstr on Sun Feb 25, 2001 at 06:47:47 PM EST

I think what the previous poster means is that you'd declare a variable as "void", not pointer to void. Sort of like the None value in Python, only with types and in C... I.e. you couldn't write to the variable and reading it would always result in the undefined value.

Granted, that'd be really weird, but symmetry is almost always good in a programming language. It'd be good for the IOCCC, too...



Fin.
[ Parent ]
What the hell (3.00 / 1) (#134)
by Inoshiro on Sun Feb 25, 2001 at 08:45:45 PM EST

Why on Earth would you make a void variable anyways? If you're taking a broken approach at doing templates, use a different preprocessor, or use macros. void is the 0 value. I can give you 0 money all the time if I want, or take 0 money from you. It's not sensical to declare such a thing -- so why would you want to? There's no reason, so I don't see a problem.



--
[ イノシロ ]
[ Parent ]
void is not zero (5.00 / 1) (#146)
by SIGFPE on Mon Feb 26, 2001 at 01:19:07 AM EST

void is the 0 value
Of course it's not. If you return a 0 value from a function the compiler will actually emit instructions to push and pop stuff off the stack. sizeof(void)=0. No memory is used. No instructions are emitted. No registers are used. However currently in C 'void' is a special type treated differently from other types. You can have a 'void' return type, cast to void and a pointer to a void but not a void variable. It's unsymmetric. It's ugly. Maybe it even causes a small amount of bloat in compilers to handle things unsymmetrically. It messes up some macros that some people (not you of course) might like to write. The change makes C more elegant and introduces no new syntax.
There's no reason, so I don't see a problem.
Right. My programming should be limited by your imagination. I'll have to remember that one.

You'd think C programmers were a bunch of old men in need of hormone replacement therapy the way they whine about anything that isn't done their way. :-)
SIGFPE
[ Parent ]

Damned kids! (none / 0) (#154)
by Inoshiro on Mon Feb 26, 2001 at 04:11:40 AM EST

Go study compiler design.. and language design.. void is essential for using new data-types or pointers in a strongly typed lanuage :p

And if you don't want /your/ programming to be (ahem) limited by my imagination (which does sound like an immature whine against agreed standards of syntax), you are at least free to write your own compiler/interpreter..



--
[ イノシロ ]
[ Parent ]
Don't flame if you are going to post wrong info (none / 0) (#156)
by Carnage4Life on Mon Feb 26, 2001 at 04:52:19 AM EST

o study compiler design.. and language design.. void is essential for using new data-types or pointers in a strongly typed lanuage :p

Wrong. First of all, simply because it is done that way in C doesn't make it some rule of language or compiler design. Off the top of my head I can already think of a counter example; Java is strongly typed and does not use void pointers for defining new types, instead the combination of all classes being derived from Object and the Reflection API is enough to handle any new types (even classes that don't exist on the local machine).

Second ly, C is weakly typed because it allows you to cast between incompatible types and even allowed for a while for pointers and integers to be used interchangeably.



[ Parent ]
Heheh (none / 0) (#181)
by Inoshiro on Mon Feb 26, 2001 at 04:02:47 PM EST

My comment was a troll.. :) When SIGPFE said " You'd think C programmers were a bunch of old men in need of hormone replacement therapy the way they whine about anything that isn't done their way. :-)" -- I couldn't help but write a bad "you damned kids" posting.. sorry ;)



--
[ イノシロ ]
[ Parent ]
uhuh (none / 0) (#196)
by lpontiac on Mon Feb 26, 2001 at 11:34:47 PM EST

There's a *lot* of things you can't do with void - void is unique in that it isn't really a 'proper' type. For instance, sizeof(void) is undesigned, so you can't have arrays of type void, or even perform pointer arithmetic on void pointers.

[ Parent ]
C++ with no class (4.50 / 4) (#109)
by ucblockhead on Sun Feb 25, 2001 at 11:48:28 AM EST

It came to me while responding to another post that many of these features can be gotten today simply by using C++ and refraining from using classes, member functions in structures, templates and other OOP features.

That gives you A,B,D,E,F,G,H(I think)I and L. All today.

No one says that just because you compile a ".cpp" file instead of a ".c" one, that you have to use classes, streams, templates or any of that other stuff.


-----------------------
This is k5. We're all tools - duxup

Sort of (none / 0) (#125)
by B'Trey on Sun Feb 25, 2001 at 06:40:25 PM EST

A lot of people have been using C++ as "a better C" since it came out. However, that's no reason not to update the C langauge itself. All programming doesn't occur on PCs. Some platforms, particularly embedded systems, don't have C++ compilers available. And, as the article pointed out, C is not a proper subset of C++. Write a simple "Hello world", using printf, and compile it as a C++ program and as a C program and compare.

[ Parent ]
What am I looking for? (none / 0) (#128)
by ucblockhead on Sun Feb 25, 2001 at 07:29:28 PM EST

I tried that with gcc and the resulting executables differed by only 400 bytes...
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
I might be stupid, but... (none / 0) (#223)
by lb on Tue Feb 27, 2001 at 10:21:17 PM EST

Isn't gcc a c only compiler? Don't you need to use g++ to compile c++ code? I don't believe changing the extension is enough..

-lb

[ Parent ]

GCC is the GNU Compiler Collection (none / 0) (#225)
by Per Abrahamsen on Wed Feb 28, 2001 at 12:25:04 PM EST

and gcc is a shared driver for the C, C++, Fortran77, Chill and Java frontends.

It selects language based on the file extension.

g++ automatically links woth the standard C++ library, gcc doesn't.



[ Parent ]
One more... (none / 0) (#234)
by dmitri on Fri Mar 02, 2001 at 03:19:29 PM EST

There is also the Mercury frontend just recently announced. Very nice development for this cool language.

-- "A society that will trade a little liberty for a little order will deserve neither and lose both." --B. Franklin
[ Parent ]
Ada, Pascal (none / 0) (#238)
by Per Abrahamsen on Mon Mar 05, 2001 at 08:57:31 AM EST

There have also beenAda and Pascal frontends for some time, but these are not part of the GCC distribution yet.


[ Parent ]
Sorry... (none / 0) (#226)
by ucblockhead on Wed Feb 28, 2001 at 12:25:18 PM EST

I meant g++. I tend to refer to them both with "gcc", which is probably a bad habit.

Anyway, the point is that the compiler should, in theory, be able to produce exactly the same executable from code compiled as "c" and "cpp".


-----------------------
This is k5. We're all tools - duxup
[ Parent ]

No it shouldn't. (none / 0) (#237)
by Crutcher on Sun Mar 04, 2001 at 11:43:35 AM EST

You are overlooking the C++ symbol table goofieness.
Crutcher - "Elegant, Documented, On Time. Pick Two"
[ Parent ]
almost right (none / 0) (#183)
by pjhunter on Mon Feb 26, 2001 at 05:34:29 PM EST

Yes you get H and I (not sure which one you were confused about), but you don't get the restrict keyword and you'll need something like this to use it with a c++ compiler:
  • #ifdef __cplusplus
  • #define restrict /* nothing */
  • #endif //__cplusplus

%pjh%
[ Parent ]
Some things I find confusing (3.00 / 2) (#119)
by nymia_g on Sun Feb 25, 2001 at 05:02:26 PM EST

I confused. I think what most posters mean about declaration is actually initialization.

In C, or what I know about C, I can declare storage anywhere in the block. These declarations, if they're local, would then be defined on the local stack. So there, I think I'm explaining myself now. What C99 is probably saying is storage initialization will happen irregardless of location.

Uh, no... (4.00 / 2) (#124)
by B'Trey on Sun Feb 25, 2001 at 06:34:29 PM EST

int MyFuction(

{

char *p = "My string.";

printf("Printing %s", p);

int i; //Error in pre99 c

}

The declaration of i is an error under the old standard because it isn't at the beginning of the block; it's allowed in the new one.

[ Parent ]

Clarification (none / 0) (#166)
by Darth Yoshi on Mon Feb 26, 2001 at 11:31:40 AM EST

To clarify B'Trey's remark, the ability to define declarations anywhere in a block is part of the C++ specification, not pre-C99 C. Basically, you've been writing C code with C++ extensions and not realizing it. :-)

And yes, B'Trey misspelled MyFunction too. ;-)

[ Parent ]
C99 does not change anything, it adds new... (4.00 / 1) (#161)
by C on Mon Feb 26, 2001 at 10:44:32 AM EST

C99 changes this, which is unfortunate.
You are merging two things which should not.

Yes, one can write valid C code which is at the same time valid C++ code. Yes, this is the way to go if you want to be portable. But C99 does not change anything here. It does not remove (i.e. in practice the restrictions of C99 vs. C90 are not a problem¹), nor it does add anything (i.e. you shoult not use any of the new feature, if you want to stay portable). You should stick to the common idiom (which is the subset of C you were referring), which was also acceptable to both C90 and C++98 compilers, and will be accepted by the forecoming C99 compilers. And this will continue this way for a number of years... No change here.

At the same time, C99 brings a lot of new possibilities (inline, longer identifiers, intmax_t, etc.), but this can be seen as an extension of C90. For a part, these extensions are not compatible with C++98 (but one can expect of the next revision of C++ Standard to get some of them; one can also expect the C/C++ compilers vendors to accept them as extensions in C++). At the same time, most compilers do not support them either. And when they'll do, clearly if you are using these new features, then you will be using brand new stuff, exactly what had happened when you wrote your first C++ classes some years ago. And this new stuff brings some advantages, but you cannot ask for backward compatibility at the same time, and everybody knows that.

 

¹: Yes, I know of the heated discussions, for example in comp.std.c, about some C99's new stuff to create problem with well-written code, for example with the assumption of long being the widest integer type available, or size_t to be safely casted to unsigned long, two assumtions that are not valid any more with C99. I consider this to be theological discussions best left to this very forum.

gazillion... (3.00 / 1) (#163)
by C on Mon Feb 26, 2001 at 11:24:44 AM EST

The largest function of any of my programs is aproxamitely 30 to 35 lines.
I tend to have case's to stand in different lines that the code they triggers, and the break; in yet another line. I assume you do the same.

Now, more than once, I needed to handle a switch with more than 10 different "cases"...
In fact, it looks like to me the most interesting use of switch: to stay readable and manageable even with more than 10 case's hence more than 35 lines.

I almost never considered replacing the switch into the equivalent cascade of if + else, and dispatching the if's between various functions, except when dealing with bad code generators in time critical parts of the program.
Also, arrays of function pointers are great when you are at the top level of an editor, but the (mandatory, no inline here) implied cost of a call with the needed pointer(s) to the context is less desired at the very heart of the critical section...
And finally, I consider a 131-line declaration of an array of function pointers to be exactly as long as 131 lines of code in a function... ;-) but I am not a style teacher!

2 restricted ptrs to same loc: don't do that! (4.00 / 1) (#171)
by C on Mon Feb 26, 2001 at 11:56:24 AM EST

Now what happens if the programmer marks 2 pointers as restricted, but then later in the code they end up pointing to the same location?
I would say that if it is able to figure the situation, any half-decent compiler will at least flag the probable wreckage, and much better will refuse to call the function with the restricted parameters.

Now the problem is how to figure the situation. The whole purpose of restrict is just to help the compiler; so we can be sure that there are cases where even the smarter compilers will have problem to really know if two pointers passed as two arguments are in fact aliases or not. And furthermore, to dig into the intrincaties of the graphes of calls, the smart compiler will need quite an amount of time, and perhaps the programmer does not in fact want to allow it to spent its precious time this way, and would like to see the compiler do more productive tasks. This is where restrict is aimed.

As a result, you cannot be sure that the compiler will even flag the dubious use of restrict. Hence, you as a programmer has to make sure that restricted pointers are not aliased. And thou shalt not do that!

What about Objective C? (3.50 / 2) (#185)
by Futeiru on Mon Feb 26, 2001 at 07:58:18 PM EST

Everyone is so busy talking about C++ incompatabilities they forget that Objective C is reemerging with the advent of OS X as a large programming language.

I'm looking at the standard, and I'm curious what ramifications the new standard might have on Objective C (which I code in as often as possible, it's my current favorite language).

The way I see it, this can't affect things in ObjC that much, a few minor tweaks and it's just a speedbump, but I am far from an expert. Does anyone know what kind of ramifications it will have on Objective C?

C and ObjC (4.00 / 1) (#194)
by Delirium on Mon Feb 26, 2001 at 10:12:21 PM EST

I was under the impression that Objective C was already pretty incompatible with standard C, and that was one of the main reasons for C++ gaining popularity over the often-considered-superior Objective C (I haven't programmed in it myself to be able to judge whether it is in fact superior to C++). If so it would seem that changes to the standard C language wouldn't have much of an effect on Objective C. Am I missing something?

[ Parent ]
yes, you're missing something (4.00 / 2) (#217)
by mikpos on Tue Feb 27, 2001 at 01:40:44 PM EST

You were under a dead-wrong impression. One of Objective C's major benefits is that it's a true superset of C, unlike C++. As such, C99 will have no effects on Objective C other than those on the C sub-system itself.

[ Parent ]
why isn't it popular? (none / 0) (#230)
by Delirium on Wed Feb 28, 2001 at 08:34:14 PM EST

Ah, that makes sense then. So why did C++ catch on so much faster than Objective C? Better marketing? Luck?

(and on a side note, could SmallTalk possibly be the OO language I frequently hear described as "better than C++ but didn't catch on since it wasn't C-compatible"?)

[ Parent ]

Possible reason: (none / 0) (#231)
by ucblockhead on Thu Mar 01, 2001 at 04:39:37 PM EST

"Turbo C++ : $99".
-----------------------
This is k5. We're all tools - duxup
[ Parent ]
C++ came first (4.50 / 2) (#232)
by mikpos on Thu Mar 01, 2001 at 07:21:39 PM EST

Bjarne had designed C++ in 1980 and it was selling commercially by 1985. Brad Cox and Stepstone made their Objective C compiler in 1983 and it wasn't until Steve Jobs, in 1985 (or 1986?) founded NeXT that it found significant support out of acadaemia, and even that had nowhere near the support that C++-supporting platforms (such as Unices) had.

Objective C and Smalltalk (which are very closely related, as you can see if you read Brad Cox' second book) are quite often compared to C++, but I think they're very different. Objective C's (and Smalltalk's and Java's) main difference (and (dis)advantage) over C++ (and Simula and Eiffel) is that it's dynamically-bound (i.e. method calls aren't just name-mangled function calls). It seems like a small thing, but it has huge effects on the language if you're perverse enough (e.g. classes are just objects, and can be passed around like any other variable).

Probably C++ caught on because it was popular quick, and it was nothing but C with a pretty face (or ugly face, depending on your perspective): the only thing C++ really did was perform name-mangling. And C++ somewhat targeted itself as a better C than C: miscellaneous crap that should have been in C, but the standards committee didn't like). Objective C, being dynamically-bound, required a bit more work on the run-time side (such as implementing _msg(), which is used to send messages). I'm sure there were a number of people who were in an uproar about how much slower Objective C is (1 method call ~= 1.5 function calls, IIRC), which didn't help its cause. Plus, Objective C has not yet been standardised.

[ Parent ]

C++ versus Objective-C (5.00 / 2) (#233)
by GonzoCoder on Fri Mar 02, 2001 at 02:35:42 PM EST

(Disclaimer: I'm a C++ expert, but only have general knowledge about Objective-C.)

Languages all make trade-offs, and whether one set is better than another often depends on your needs.

C++ has several things Objective-C does not.

General function overloading: you can make function foo(int), foo(MyNewClass), etc. all part of one program, and the compiler determines which is to be called based on the parameter type. So you can define classes like ComplexNumber, and have your own Sqrt() function; or perhaps 2-D and 3-D vectors, each with its own DotProduct() function.

operator overloading: make your own +,-, etc. operators, again for your complex number or vector classes. Also, operator-> and the like can be overloaded to create smart pointers. Part, but not all, of the reason for smart pointers is memory management, which Objective-C achieves via garbage collection.

Speed: inline functions mean that instead of 1 function call or ~1.5 for Objective-C, you often get *0* function calls for a C++ method. Objective-C can achieve generic programming through polymorphism, C++'s tmplate system creates faster code, and works with built-in types.

Uniformity: I believe Objective-C garbage collects its own objects, but nothing you create with malloc, which I think is still needed for arrays and such. In C++, you new and delete built-in types and user-defined ones alike.

Destructors: Objective-C's garbage collection does much of this, but not all, I think (destruction isn't always for memory).

Objective-C has its own advantages, particularly in remote procedure call situations, which is, I believe, why NeXT went for it in a big way.

[ Parent ]

C++ versus Objective-C (none / 0) (#241)
by NeXtAgain on Wed Mar 28, 2001 at 03:00:49 AM EST

(Disclaimer: I've never used Smalltalk)

Hi from someone used to ObjC and C(89) and C++,

currently I'm a bit far off (working OO-oriented in Ansi-C89, well) but just a few cents...

As mikpos said ObjC like Smalltalk has its main features and starts being a hype where others didn't even think of being a possibility. Just think of ObjCs protocol class - being active mostly at compile time (sic!) and doing things Qt tries to simulate the hard way (without compiler support).

Many of the 'features' C++ imported have their draw-backs:

- operator overloading: there are not enough operators to overload. just think of stream-out operator (>>). Do I stream binary or ASCII? What if I need both? What if I need more formats?
Constant problem: what does A + B do? is operator+ overwritten or not?

- templates: increase the code size (by multiplication factors above 10) and hereby decrease cash efficiency. Speed decreases and even inlineing doesn't help. Note: C99 imports inline into ObjC - and since gcc supports ObjC you won't have to wait for long.

- multiple inheritance sounds like a good idea but should be avoided anyway (poor design)

- multiple functions. It's quite easy in ObjC if you differ in Name and/or number of arguments.
[yourObj moveTo: NewPos]; // where newPos is an Object containing X an Y
[yourObj MoveToX: 712 Y: 524];
just solves this problem nicely. Note the format - you also know the parameter you have to pass after the colon. This feature at zero cost helps you a lot when having many parameters (ever programmed Win32?).

- constructor/destructor. It's just the name. ObjC uses new and free. At least in the early versions. Garbage collection looked complicated and I didn't have enough time to track yet.

- uniformity: here you're right. Since ObjC is a mixture of SmallTalk and C you have to decide how you like to solve this special problem. Normally you'll end up with the OO variant.
But try to look at it differntly: even in plain C you may decide to use goto...

When you want to see the strengths of ObjC look ate the distributed objects. If you want to enable this feature in your class just tell so by overwriting one method returning true (yet, I want), and a second generating some unique ID (if I remember correctly). Your Server is ready. Your client just calls (sends a message to) newConnectToServer: <servername> (I'm pretty sure the method is named differntly) instead of new and you gain a proxy. You handle this proxy exaclty the same way you handle an object generated locally. You can even ask it for available methods, parents, instance name, etc. You can even send it unknown methods and the correct handling is done. You need not even KNOW this object is just a proxy!
Just compare this to CORBA, COM and however they are named - you need a different interface, you have to know, it's over some connection.

And this feature was easy to implement. Just think of the implementation of your own server. The call of the serviced method will look something like
[InstanceName @selector(MethodName):...]
where both parameters are plain ASCII strings. No switch/case, no nothing. You're finished, ObjC does the rest.

[ Parent ]
nothing (5.00 / 1) (#218)
by mikpos on Tue Feb 27, 2001 at 01:45:21 PM EST

I'm working on the Objective C standard along with some others on comp.lang.objective-c. Come by and share your concerns. You can see the standard-in-progress at http://members.home.net/mikpos/objc. It's very early on, so it won't be done for some time.

To answer your question, C99 will not affect, in the least, existing Objective C implementations. As far as the ObjC standard is concerned, we've obsolesced (though it's still there for compatibility) BOOL in favour of C's _Bool (and bool in stdbool.h), but that is all. Objective C has always been a true superset of C, so, short of adding the @ to the C character set, and making C keywords using @, there's nothing the C people can add to break Objective C compatibility.

BTW, seriously, drop by comp.lang.objective-c some time. We only have a half dozen people right now giving good suggestions for the standard.

[ Parent ]

The *Real* link for Dennis Ritchie's comments (none / 0) (#240)
by phliar on Mon Mar 05, 2001 at 04:59:51 PM EST

The link given above for Dennis Ritchie's comments on the language is bogus: if you refuse cookies, it just comes up as a blank page.

And it's just a frame provided by those assholes, er I mean fine redistributors of e-commerce news or whatever, around the original article by Linux World.

Go to the Linuxworld article on Dennis Ritchie's comments on the new features in C99.


Faster, faster, until the thrill of...

Shifts (none / 0) (#242)
by NeXtAgain on Wed Mar 28, 2001 at 04:16:15 AM EST

Hope this isn't spam...

I guess there's no compiler around any longer doing it wrongly but my lint still warns me:

Shifts (left and right) of signed values are handled wrongly by some compilers.

Now here are my questions:
Is it defined?
If so how? (correctly I do hope)
And when? With C99? Earlier?

I'm really frustrated with this warning since one of the main advancements of higher languages is to free me of having to think of using either ASR or LSR...

Of course you can come over this warning by using * and / instead but it's so much easier to implement (and read) block floating point using shift operations.

Are you Ready For C99? | 242 comments (238 topical, 4 editorial, 0 hidden)
Display: Sort:

kuro5hin.org

[XML]
All trademarks and copyrights on this page are owned by their respective companies. The Rest © 2000 - Present Kuro5hin.org Inc.
See our legalese page for copyright policies. Please also read our Privacy Policy.
Kuro5hin.org is powered by Free Software, including Apache, Perl, and Linux, The Scoop Engine that runs this site is freely available, under the terms of the GPL.
Need some help? Email help@kuro5hin.org.
My heart's the long stairs.

Powered by Scoop create account | help/FAQ | mission | links | search | IRC | YOU choose the stories!