|
|
When to check the return value of malloc
Hello friends~~
Think about malloc.
Obviously, for tiny allocations like 20 bytes to strcpy a filename,
there's no point putting in a check on the return value of malloc... if
there is so little memory then stack allocations will also be failing and
your program will be dead.
Whereas, if you're allocating a gigabyte for a large array, this might
easily fail, so you should definitely check for a NULL return.
So somewhere in between there must be a point where you stop ignoring the
return value, and start checking it. Where do you draw this line? It must
depend on whether you will deploy to a low memory or high memory
environment... but is there a good rule?
Regards~~
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/15/2010 6:52:59 PM |
|
On 2010-05-15, sandeep <nospam@nospam.com> wrote:
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc... if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
Always check for error conditions. The heap and stack use different areas
of memory; and, the stack memory is likely already be allocated. Therefore, it
is quite possible that you still have stack memory left while you cannot
request any more from the heap.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/15/2010 7:07:01 PM
|
|
sandeep wrote:
> Hello friends~~
>
> Think about malloc.
>
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc...
Obviously, for short trips like half a mile to the supermarket, there's
no point wearing a seatbelt.
> if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
>
> Whereas, if you're allocating a gigabyte for a large array, this might
> easily fail, so you should definitely check for a NULL return.
>
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line?
I never waste my time bothering to check for a null pointer return from
malloc for any allocation under 1 byte. For 1 byte and over, I check it.
> It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
The good rule is: whenever you attempt to acquire an external resource,
don't assume you succeeded - check.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/15/2010 7:13:06 PM
|
|
On 2010-05-15, Tim Harig <usernet@ilthio.net> wrote:
> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
>> there's no point putting in a check on the return value of malloc... if
>> there is so little memory then stack allocations will also be failing and
>> your program will be dead.
>
> Always check for error conditions. The heap and stack use different areas
> of memory; and, the stack memory is likely already be allocated. Therefore, it
> is quite possible that you still have stack memory left while you cannot
> request any more from the heap.
BTW, if you are wondering how you can possibly go on without any more heap
memory, most users prefer a message telling them that you cannot perform an
operation because of insufficient memory then simply having the program
crash with a segfault. It also makes your troubleshooting and debugging
much easier.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/15/2010 7:33:40 PM
|
|
"sandeep" <nospam@nospam.com> wrote in message
news:hsmqib$c10$1@speranza.aioe.org...
> Hello friends~~
>
> Think about malloc.
>
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc... if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
>
> Whereas, if you're allocating a gigabyte for a large array, this might
> easily fail, so you should definitely check for a NULL return.
>
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line? It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
For a proper application, especially to be run by someone else on their own
machine, then you should check allocations of any size (and have the
machinery in place to deal with failures sensibly).
For anything else, where you don't expect a failure, or it is not a big
deal, then use a wrapper function around malloc(). That wrapper will itself
check, and abort in the unlikely event of a memory failure. But it means you
don't have to bother with it in your main code.
It is also possible to just call malloc() and assume it has worked. Your
experience will tell you when you can get away with that. But only do that
with your own programs...
--
Bartc
|
|
0
|
|
|
|
Reply
|
bart
|
5/15/2010 7:47:26 PM
|
|
On 5/15/2010 2:52 PM, sandeep wrote:
> Hello friends~~
>
> Think about malloc.
>
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc...
Obviously, you are ignorant of the Sixth Commandment. The text
of all ten Commandments, along with learned commentary can be found
at <http://www.lysator.liu.se/c/ten-commandments.html>.
> if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
On many systems -- maybe even on most -- memory for auto variables
and memory for malloc() is drawn from different "pools," and one can
run out while the other still has ample space.
> Whereas, if you're allocating a gigabyte for a large array, this might
> easily fail, so you should definitely check for a NULL return.
Yes, you should definitely check for a NULL return.
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line? It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
Yes. You can get away with not checking the value returned by
malloc(N) if N is evenly divisible by all prime numbers (you only
need to test divisors up to sqrt((size_t)-1); any larger primes can
be ignored). For all other values of N, you must check the value
returned by malloc() -- and similarly for calloc() and realloc(),
of course.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/15/2010 7:48:13 PM
|
|
On 2010-05-15, sandeep <nospam@nospam.com> wrote:
> Hello friends~~
>
> Think about malloc.
>
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc... if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
Not necessarily true -- there is no reason to assume that "stack allocations"
and malloc() are using the same pool of memory. Furthermore, it's quite
possible for malloc to fail, not because 20 bytes aren't available, but
because it can't get enough space for the 20 bytes plus overhead.
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line? It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
There is -- ALWAYS check.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/15/2010 8:00:33 PM
|
|
sandeep <nospam@nospam.com> wrote:
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc...
Wrong.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/15/2010 8:25:41 PM
|
|
On Sat, 15 May 2010 18:52:59 +0000 (UTC), sandeep <nospam@nospam.com>
wrote:
>is there a good rule?
Yes. ALWAYS check for error returns.
What you do with an error depends on your application. If it's an
unpublished personal utility an error exit might be sufficient.
Otherwise, more sophisticated error diagnostic messages or recovery
might be needed for larger or published (production) programs.
|
|
0
|
|
|
|
Reply
|
Geoff
|
5/15/2010 8:30:35 PM
|
|
There are implementations where malloc (n) returns a non-null pointer,
but when you try to use the allocated memory, the operating system
takes your program down. Not nice.
On the other hand, say you are using a Mac with 4 GB RAM and 500 GB
free on your hard drive, and you are writing a 64 bit application. And
you allocate and use ever growing amounts of memory in reasonably
small chunks without checking whether malloc returns NULL. At some
point you exceed the available RAM, and virtual memory starts swapping
data. In theory, you could use 500 GB of malloc'ed memory that way. In
practice, once you start swapping with 4 GB of RAM, things will be so
slow, you will never reach the point where malloc fails in the
lifetime of your computer. That is assuming that malloc will return
NULL when the OS runs out of swap space and not fail in some way.
|
|
0
|
|
|
|
Reply
|
christian
|
5/15/2010 8:32:00 PM
|
|
On May 15, 2:52=A0pm, sandeep <nos...@nospam.com> wrote:
> Hello friends~~
>
> Think about malloc.
>
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc... if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
>
> Whereas, if you're allocating a gigabyte for a large array, this might
> easily fail, so you should definitely check for a NULL return.
>
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line? It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
>
> Regards~~
As has been said, in production code, you always check. However this
does not mean you have to code a specific check for each allocation.
The usual is to define a wrapper around malloc() that checks for null
returns and deals with them through a callback mechanism where
presumably resources are freed so the allocation can succeed and/or a
longjmp() that terminates the application gracefully.
One thing that hasn't been mentioned about heap storage is
fragmentation. Even if you are allocating 20 bytes, malloc() can
still fail with (theoretically) 19/20 =3D 95% memory free. The
improbable, the moral is that when heap allocation fails, an app keep
running by compacting the heap. If you aren't checking each
allocation, this doesn't work.
|
|
0
|
|
|
|
Reply
|
Gene
|
5/15/2010 8:34:01 PM
|
|
bart.c writes:
> "sandeep" <nospam@nospam.com> wrote in message
> news:hsmqib$c10$1@speranza.aioe.org...
>> Hello friends~~
>>
>> Think about malloc.
>>
>> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
>> there's no point putting in a check on the return value of malloc... if
>> there is so little memory then stack allocations will also be failing
>> and your program will be dead.
>>
>> Whereas, if you're allocating a gigabyte for a large array, this might
>> easily fail, so you should definitely check for a NULL return.
>>
>> So somewhere in between there must be a point where you stop ignoring
>> the return value, and start checking it. Where do you draw this line?
>> It must depend on whether you will deploy to a low memory or high
>> memory environment... but is there a good rule?
>
> For a proper application, especially to be run by someone else on their
> own machine, then you should check allocations of any size (and have the
> machinery in place to deal with failures sensibly).
>
> For anything else, where you don't expect a failure, or it is not a big
> deal, then use a wrapper function around malloc(). That wrapper will
> itself check, and abort in the unlikely event of a memory failure. But
> it means you don't have to bother with it in your main code.
This is a good idea. I have just made a clever macro to do this - not as
easy as it seems due to void use problems and need for a temporary.
static void* __p;
#define safeMalloc(x) ((__p=malloc(x))?__p:\
(exit(printf("unspecified error")),(void*)0))
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/15/2010 9:02:36 PM
|
|
On 2010-05-15, sandeep <nospam@nospam.com> wrote:
> This is a good idea. I have just made a clever macro to do this - not as
> easy as it seems due to void use problems and need for a temporary.
> static void* __p;
> #define safeMalloc(x) ((__p=malloc(x))?__p:\
> (exit(printf("unspecified error")),(void*)0))
Write a function, not a macro, it'll be easier to make effective use of.
Also.
1. Use fprintf(stderr,...) for error messages.
2. Terminate error messages with newlines.
3. Why the *HELL* would you use "unspecified error" as the error message
when you have ABSOLUTE CERTAINTY of what the error is? Why not:
fprintf(stderr, "Allocation of %ld bytes failed.\n", (unsigned long) x);
The first two are comprehensible mistakes. The third isn't. Under what
POSSIBLE circumstances could you think that "unspecified error" is a better
diagnostic than something that in some way indicates that a memory allocation
failed?
I really don't understand this one. Please try to explain your reasoning,
because I regularly encounter software that fails with messages like these,
and I've always assumed it was something people did out of active malice --
they hate their users and want the users to suffer. If there is any other
possible reason to, given total certainty of what the problem is, refuse
to hint at it, I do not know what it is.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/15/2010 9:05:03 PM
|
|
On 5/15/2010 4:30 PM, Geoff wrote:
> On Sat, 15 May 2010 18:52:59 +0000 (UTC), sandeep<nospam@nospam.com>
> wrote:
>
>> is there a good rule?
>
> Yes. ALWAYS check for error returns.
>
> What you do with an error depends on your application. If it's an
> unpublished personal utility an error exit might be sufficient.
> Otherwise, more sophisticated error diagnostic messages or recovery
> might be needed for larger or published (production) programs.
I often use this pair of functions in toy programs:
void crash(const char *message) {
if (message != NULL)
perror (message);
exit (EXIT_FAILURE);
}
void *getmem(size_t bytes) {
void *new = malloc(bytes);
if (new == NULL && bytes > 0)
crash ("malloc");
return new;
}
Note the limitation to "toy programs." In larger programs, low-
level functions like memory allocators lack information about the
context in which a failure occurs, and so can't make informed
decisions about what should be done. This getmem() is far too
ill-mannered and abrupt for "serious" use, because it can't tell
the difference between a recoverable failure ("Not enough memory;
close some windows and try again"), and an irrecoverable failure
("Not enough memory; shutting down"). Even in the latter case, the
program may want to do a few last-gasp things like saving the user's
work to disk before going away to push up daisies; a getmem() that
just called exit() and ripped the rug from underneath the rest of
the program would be unwelcome indeed.
... and a program that simply didn't check at all but died
with a SIGSEGV or equivalent would be even worse. Even my high-
handed getmem() allows for the possibility of atexit() routines,
while dereferencing NULL does not.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/15/2010 9:12:28 PM
|
|
On 05/16/10 09:02 AM, sandeep wrote:
> bart.c writes:
>>
>> For anything else, where you don't expect a failure, or it is not a big
>> deal, then use a wrapper function around malloc(). That wrapper will
>> itself check, and abort in the unlikely event of a memory failure. But
>> it means you don't have to bother with it in your main code.
>
> This is a good idea. I have just made a clever macro to do this - not as
> easy as it seems due to void use problems and need for a temporary.
>
> static void* __p;
> #define safeMalloc(x) ((__p=malloc(x))?__p:\
> (exit(printf("unspecified error")),(void*)0))
Why mess about with a macro when a function would do?
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
Ian
|
5/15/2010 9:14:56 PM
|
|
Seebs writes:
> Write a function, not a macro, it'll be easier to make effective use of.
??
How?
> Also.
>
> 1. Use fprintf(stderr,...) for error messages. 2. Terminate error
> messages with newlines. 3. Why the *HELL* would you use "unspecified
> error" as the error message when you have ABSOLUTE CERTAINTY of what the
> error is? Why not:
> fprintf(stderr, "Allocation of %ld bytes failed.\n", (unsigned long)
> x);
>
> The first two are comprehensible mistakes. The third isn't. Under what
> POSSIBLE circumstances could you think that "unspecified error" is a
> better diagnostic than something that in some way indicates that a
> memory allocation failed?
>
> I really don't understand this one. Please try to explain your
> reasoning, because I regularly encounter software that fails with
> messages like these, and I've always assumed it was something people did
> out of active malice -- they hate their users and want the users to
> suffer. If there is any other possible reason to, given total certainty
> of what the problem is, refuse to hint at it, I do not know what it is.
Many users will only be confused by technical error messages about memory
allocation etc. It's best not to get into unwanted details - the user
doesn't know about how my program allocates memory, it just needs to know
there was an error that needs a restart. I think in books they call it
leaking abstractions.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/15/2010 9:21:19 PM
|
|
Ian Collins writes:
> On 05/16/10 09:02 AM, sandeep wrote:
>> bart.c writes:
>>>
>>> For anything else, where you don't expect a failure, or it is not a
>>> big deal, then use a wrapper function around malloc(). That wrapper
>>> will itself check, and abort in the unlikely event of a memory
>>> failure. But it means you don't have to bother with it in your main
>>> code.
>>
>> This is a good idea. I have just made a clever macro to do this - not
>> as easy as it seems due to void use problems and need for a temporary.
>>
>> static void* __p;
>> #define safeMalloc(x) ((__p=malloc(x))?__p:\
>> (exit(printf("unspecified error")),(void*)0))
>
> Why mess about with a macro when a function would do?
Obviously for efficiency! malloc may be called many times in the course
of a program.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/15/2010 9:22:07 PM
|
|
sandeep wrote:
> Ian Collins writes:
>> On 05/16/10 09:02 AM, sandeep wrote:
>>> bart.c writes:
>>>> For anything else, where you don't expect a failure, or it is not a
>>>> big deal, then use a wrapper function around malloc(). That wrapper
>>>> will itself check, and abort in the unlikely event of a memory
>>>> failure. But it means you don't have to bother with it in your main
>>>> code.
>>> This is a good idea. I have just made a clever macro to do this - not
>>> as easy as it seems due to void use problems and need for a temporary.
>>>
>>> static void* __p;
>>> #define safeMalloc(x) ((__p=malloc(x))?__p:\
>>> (exit(printf("unspecified error")),(void*)0))
>> Why mess about with a macro when a function would do?
>
> Obviously for efficiency! malloc may be called many times in the course
> of a program.
If you're calling malloc anyway, function call overhead is the least of
your worries. You're trying to micro-optimise. Stop it at once.
"If you must do this damn silly thing, don't do it in this damn silly
way." - Sir Humphrey Appleby
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/15/2010 9:25:41 PM
|
|
On 2010-05-15, sandeep <nospam@nospam.com> wrote:
> Seebs writes:
>> Write a function, not a macro, it'll be easier to make effective use of.
> ??
> How?
This question is too incoherent to answer.
What part of "a function" do you have trouble with? You know how to write
functions, right? You know how to call them, right?
Try adding some verbs. Questions like "how do I declare a function" or "how
do I use a function" might begin to be answerable. An explanation of what
you're having trouble with, specifically, would be even better.
> Many users will only be confused by technical error messages about memory
> allocation etc. It's best not to get into unwanted details - the user
> doesn't know about how my program allocates memory, it just needs to know
> there was an error that needs a restart. I think in books they call it
> leaking abstractions.
Wrong.
Users who are "confused" by an error message can accept that they got "an
error". MANY users, however, know enough to recognize that "out of memory"
is different from "file not found".
Stop trying to outsmart the user. Tell the truth, always. It's fine to
stop short of a register and stack dump, but at least tell people honestly
and accurately what happened.
Where did you get this bullshit? The above paragraph is by far the stupidest
thing I've ever seen you write. It's not just a little wrong; it's not just a
little stupid; it's not just a little callous or unthinking. It's one of
the most thoroughly, insideously, wrong, stupid, and evil things you could
start thinking as a programmer.
Stop. Rethink. THINK AHEAD. For fuck's sake, JUST THINK EVEN A TINY BIT
AT ALL.
1. Users will report the error message to you. You need that error message
to give you the information you need to track down the problem.
2. "Error that needs a restart" is nearly always bullshit. If the program
is running out of memory because you made a mistake causing it to try to
allocate 4GB of memory on a 2GB machine, "restart" will not fix it. Nothing
will fix it until the user finds out what's wrong and submits a bug report
allowing the developer to fix it.
3. "Error that needs a restart" is at best a surrender to inability to fix
bugs. If restarting makes the error "go away", you have a serious bug that
you ought to fix.
4. The chances are very good that many of the prospective users of any
program will, in fact, be able to program at least a little, or will have
basic computer literacy. From what I've seen of your posts in this newsgroup,
I'd guess a large proportion of the users of any software you write will,
in fact, know more about computers than you do.
5. Trying to avoid "confusing" people is power-mad idiocy. Your job here
is not to imagine yourself some kind of arbiter-of-technology, preserving the
poor helpless idiots from the dangers of actual information. Your job is
to make a program which works as well as possible, and that includes CLEAR
statements of what failed.
6. You can never make a message so clear that every concievable user will
understand it. However, a user who won't understand a simple message won't
understand an imprecise or flatly false one, either. There does not exist
a user who will have a clear idea of what went wrong and be able to react
accordingly when confronted with "unspecified error", but who will be utterly
paralyzed like a deer in headlights when confronted with "memory allocation
failed". As a result, even if we restrict our study to the set of users
who simply have no clue what those words mean, you STILL gain no benefit,
at all, from the bad message. But in the real world, you hurt many of your
users by denying them the information that would allow them to address
the issue (say, by closing other applications so that more memory becomes
available).
7. Again, don't lie to people. If you know it's a memory allocation failure,
say so.
Let me put it this way: If I saw that error message in a code sample
submitted by an applicant, that would be the end of the interview process.
I would never hire someone who wrote that. I would not want to take on
the full-time job of correcting badly-written error messages from a developer
who holds users in such contempt.
And make no mistake. By asserting that users will "just be confused", you
are indeed showing contempt for them. You're ignoring the fact that users
are, by and large, humans. If they can read the text "unspecified error",
they are well along the way to being smart enough to understand a lot more
than just that, and they probably are.
Furthermore, "many" is not "all". If writing an informative error message
required a week's effort, I could understand choosing not to. But in this
case, writing an informative message requires NO EFFORT AT ALL. You don't
have to do anything better to provide an informative message than to provide
an uninformative one. Sure, it could take more effort to produce a really
good message, such as "Allocation of 256 bytes failed at list.c, line 623".
But simply writing "memory allocation failed" is not noticably harder than
writing "unspecified error". And yet, for every one of your users who *isn't*
a terrified, panicked, idiot, you've just condemned them to a useless error
message because you *think* they *might* be too stupid or ignorant to own
a computer.
In short, this is a catastrophically bad design approach. Abandon it. Reject
it. Anything you think you know which caused you to adopt this is probably
also utterly wrong, and dangerously so, and until you root all the madness
out and start afresh, your code will be dangerous, untrustworthy, and based
on a bad attitude.
Respect the user. Worst case, the user doesn't get it, but they're no worse
off. You could learn a lot from looking at, say, Blizzard Entertainment.
Their userbase consists largely of marginally-literate teenagers. And yet,
when they encounter errors, they give clear, detailed, error messages which
can be forwarded to their support team to allow the developers to fix
problems.
I have spent an occasional idle afternoon reading their support forums. I
have never, in all that time, seen a user say "help, World of Warcraft
crashed, and it used a word I don't know, is it okay for me to restart
it?"
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/15/2010 9:25:47 PM
|
|
On 2010-05-15, sandeep <nospam@nospam.com> wrote:
> Obviously for efficiency! malloc may be called many times in the course
> of a program.
Please stop trying to outsmart the compiler.
The "cost" of using a function instead of a macro is likely to be so small
that you can't even measure it. If there is even a cost at all, which there
may not be.
Premature optimization is the root of a great deal of evil. Do not try
to optimize something like this until you have PROVEN that it is actually
a significant bottleneck. In general, it won't be. In fact, I do not
think I have ever, in something like twenty years of active use of C, seen
a single program in which such a thing would have been a measurable component
(say, over one hundredth of one percent) of runtime.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/15/2010 9:28:00 PM
|
|
On Sat, 15 May 2010 21:21:19 +0000 (UTC), sandeep <nospam@nospam.com>
wrote:
>Many users will only be confused by technical error messages about memory
>allocation etc.
How many times does "unspecified error" appear in your programs? How
many different causes are there? How is your product maintenance staff
expected to respond to reports of "unspecified error"?
>It's best not to get into unwanted details - the user
>doesn't know about how my program allocates memory, it just needs to know
>there was an error that needs a restart.
How many man-hours are you going to spend debugging "unspecified
error" reports from the field? How many man-hours and support costs
are you going to expend on non-informative error messages from
programs that failed for "unspecified" reasons that can't be traced or
reproduced? Gee, I remember an operating system that used to crash
regularly with an error code message. The screen was called Guru
Meditation. Users hated it.
>I think in books they call it leaking abstractions.
Stop reading those books immediately.
|
|
0
|
|
|
|
Reply
|
Geoff
|
5/15/2010 9:38:09 PM
|
|
On 05/16/10 09:22 AM, sandeep wrote:
> Ian Collins writes:
>> On 05/16/10 09:02 AM, sandeep wrote:
>>> bart.c writes:
>>>>
>>>> For anything else, where you don't expect a failure, or it is not a
>>>> big deal, then use a wrapper function around malloc(). That wrapper
>>>> will itself check, and abort in the unlikely event of a memory
>>>> failure. But it means you don't have to bother with it in your main
>>>> code.
>>>
>>> This is a good idea. I have just made a clever macro to do this - not
>>> as easy as it seems due to void use problems and need for a temporary.
>>>
>>> static void* __p;
>>> #define safeMalloc(x) ((__p=malloc(x))?__p:\
>>> (exit(printf("unspecified error")),(void*)0))
>>
>> Why mess about with a macro when a function would do?
>
> Obviously for efficiency! malloc may be called many times in the course
> of a program.
Obviously any compiler worth using will inline such a trivial function away.
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
Ian
|
5/15/2010 9:44:07 PM
|
|
sandeep <nospam@nospam.com> writes:
[...]
> This is a good idea. I have just made a clever macro to do this - not as
> easy as it seems due to void use problems and need for a temporary.
>
> static void* __p;
> #define safeMalloc(x) ((__p=malloc(x))?__p:\
> (exit(printf("unspecified error")),(void*)0))
Names starting with two underscores, or with an underscore and
a capital letter, are reserved to the implementation for all
purposes. Other names starting with an underscore are reserved in
more limited ways. Basically you should *never* define your own
identifier starting with an underscore (unless you're writing a C
implementation yourself).
Seebs already covered the "unspecified error" message, and I agree
with him. Just say "memory allocation failure\n" (note the trailing
newline!) or something similar. Any naive users who don't understand
what that means will just read it as "unspecified error" anyway,
and users who do understand it just might be able to do something
about it (such as shutting down other programs and trying again,
or running the program with a smaller input file, or ...).
You might also consider adding a parameter to allow the caller to
pass in a string saying what was going on when the allocation failed.
printf() returns the number of characters printed. Passing this
value to exit() makes no sense at all. What is an exit status of
17 supposed to mean?
void *safeMalloc(size_t size)
{
void *const result = malloc(size);
if (result == NULL) {
fputs("Memory allocation failure\n", stderr);
exit(EXIT_FAILURE);
}
else {
return result;
}
}
If you're concerned about performance, just declare it inline.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/15/2010 9:59:35 PM
|
|
Seebs <usenet-nospam@seebs.net> writes:
[...]
> Stop trying to outsmart the user. Tell the truth, always. It's fine to
> stop short of a register and stack dump, but at least tell people honestly
> and accurately what happened.
>
> Where did you get this bullshit? The above paragraph is by far the stupidest
> thing I've ever seen you write. It's not just a little wrong; it's not just a
> little stupid; it's not just a little callous or unthinking. It's one of
> the most thoroughly, insideously, wrong, stupid, and evil things you could
> start thinking as a programmer.
>
> Stop. Rethink. THINK AHEAD. For fuck's sake, JUST THINK EVEN A TINY BIT
> AT ALL.
Um, Seebs, maybe you should try decaf?
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/15/2010 10:03:21 PM
|
|
Geoff <geoff@invalid.invalid> writes:
> On Sat, 15 May 2010 21:21:19 +0000 (UTC), sandeep <nospam@nospam.com>
> wrote:
>
>>Many users will only be confused by technical error messages about memory
>>allocation etc.
[...]
>>I think in books they call it leaking abstractions.
>
> Stop reading those books immediately.
At least until you can understand what they're saying. I rather
doubt that books discussing "leaking abstractions" (a useful concept
and something to avoid) would recommend an "unspecified error"
message over "memory allocation failed".
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/15/2010 10:10:40 PM
|
|
On 2010-05-15, sandeep <nospam@nospam.com> wrote:
> bart.c writes:
>> "sandeep" <nospam@nospam.com> wrote in message
>> news:hsmqib$c10$1@speranza.aioe.org...
>>> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
>>> there's no point putting in a check on the return value of malloc... if
>>> there is so little memory then stack allocations will also be failing
>>> and your program will be dead.
[SNIP]
>> For a proper application, especially to be run by someone else on their
>> own machine, then you should check allocations of any size (and have the
>> machinery in place to deal with failures sensibly).
Note this. As bart.c writes not all insufficient memory errors are causes
for termination. It may mean the the program cannot use one specific
feature do to memory requirements; but, that doesn't mean that it might not
be able to do most anything else, that it might not be able to do so in
the future, or even that you might not be able to revert to a less
optimized but less power hungary algorithm. If there is user data on the
line, the user is going to be seriously pissed if you program closes
without attempting to save any data that it can. In general, most programs
are expected to run as best they can in spite of memory availability.
>> For anything else, where you don't expect a failure, or it is not a big
>> deal, then use a wrapper function around malloc(). That wrapper will
>> itself check, and abort in the unlikely event of a memory failure. But
>> it means you don't have to bother with it in your main code.
> This is a good idea. I have just made a clever macro to do this - not as
> easy as it seems due to void use problems and need for a temporary.
Unless you are in a severvely restricted environment statically allocated
variables will likely be allocated in a different section of memory (the
stack) then will dynamically allocated variables (on the heap). Just
because one area is out of memory doesn't mean that the other is. Therefore,
it is reasonable to assume that create a function (which requires at least
a pointer on the stack) and even use temporary statically allocated
variables if necessary.
Also note that an out like this should be used judiciously. Whether or not
to terminate should be based upon the context of the program and whether
the program can otherwise function; not on how much memory was requested.
Most functions are far better returning error where it is better handled by
the main part of the program which can make better decisions about what
errors are actually terminal.
> static void* __p;
> #define safeMalloc(x) ((__p=malloc(x))?__p:\
> (exit(printf("unspecified error")),(void*)0))
As has been stated by others "unspecified error" is a rather poor error. A
more informative error such as "Insufficient memory to continue with
operation" is much more informative to the user. This isn't leaking
internals as most users *do* realize that programs require memory to
operate. If you are conserned that they are not, you could add a
suggestion such as "You could try closing some windows and try again."
It is also more useful to add debugging versions using the preprocessor
which provide more information while testing and debugging:
#ifndef NDEBUG
printf("Unable to allocate enough memory to copy strings, %lu.", line);
#else
printf("Insuffient memory. Try closing some windows and try again.");
#endif
A function is much better to handle the kind of complexity that is likely
to arise from this operation. Pre-optimization is a bad thing and often
turns out to be unsubstantiated or a performance liability because of
failed assumptions. Never second guess the optimizing abililties of the
compiler. Only optimize *after* you have confirmed that something is
actually a performance liability with hard data to support that claim.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/15/2010 10:25:37 PM
|
|
On 2010-05-15, Keith Thompson <kst-u@mib.org> wrote:
> Um, Seebs, maybe you should try decaf?
Hah! I've been on decaf for ages.
I guess it's just a pet peeve. There is little in this world more infuriating
than the scenario, which I'd guess everyone has been through:
You have some sort of time pressure or deadline. A piece of software fails.
And it gives you NO CLUE AT ALL what went wrong or what might be done to
address it. The error message is absent or uninformative, such that you
can't search on the message and get suggestions or ideas.
I have had programs fail in hundreds of ways, which required anything from
upgrading a kernel to switching from one kind of network to another in order
to address them. I have had programs destroy data.
But the only one that consistently, really, infuriates me, and makes me want
to whack developers upside the head with a lead pipe, is when the failure
has clearly been caught, and intercepted, and replaced with a completely
useless error message. I would rather get a segmentation fault than the
message "unspecified error"; at least I could *debug* that.
Now, to be fair, I'm not exactly at the typical "end user" level... But I
have watched people who have trouble with questions like "and what happens
when you double-click that little picture that looks like a piece of paper?"
interacting with software, and they have the same response. They can accept
that a program's error message will make no sense, but if you show them a
message which is completely legible and clearly designed to hide what went
wrong from them, they get pretty mad. It's insulting.
Sandeep's questions have led me to think that this is a person who would
love to become a good and effective programmer. For all that I think it's
absolutely ridiculous to use a macro instead of a function "for efficiency",
thinking about efficiency, while usually the wrong thing to do, is the
kind of thing that suggests someone who *wants* to become good at this stuff.
Whereupon, it's very important to address the importance of understanding
that, even if your target market is developmentally disabled people, it is
almost ALWAYS a horrific mistake to treat the user like an idiot. There is
no surer path to the uninstaller than wasting the user's time by saying "I
could tell you exactly what went wrong, but I don't think you're smart enough
to do anything about it, so I'll just lie to you."
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/15/2010 10:37:11 PM
|
|
Ian Collins <ian-news@hotmail.com> wrote:
> On 05/16/10 09:22 AM, sandeep wrote:
> > Ian Collins writes:
> >> On 05/16/10 09:02 AM, sandeep wrote:
> >>> bart.c writes:
> >>>>
> >>>> For anything else, where you don't expect a failure, or it is not a
> >>>> big deal, then use a wrapper function around malloc(). That wrapper
> >>>> will itself check, and abort in the unlikely event of a memory
> >>>> failure. But it means you don't have to bother with it in your main
> >>>> code.
> >>>
> >>> This is a good idea. I have just made a clever macro to do this - not
> >>> as easy as it seems due to void use problems and need for a temporary.
> >>>
> >>> static void* __p;
> >>> #define safeMalloc(x) ((__p=malloc(x))?__p:\
> >>> (exit(printf("unspecified error")),(void*)0))
> >>
> >> Why mess about with a macro when a function would do?
> >
> > Obviously for efficiency! malloc may be called many times in the course
> > of a program.
> Obviously any compiler worth using will inline such a trivial function away.
Especially with strong hints like `static inline':
#define malloc(size) xmalloc((size), __func__, __LINE__)
static inline void *xmalloc(size_t size, const char *fn, int ln) {
void *p;
if ((p = (malloc)(size)))
return p;
fprintf(stderr, "unrecoverable system error at %s:%d: %s\n",
fn, ln, strerror(errno));
abort();
}
|
|
0
|
|
|
|
Reply
|
William
|
5/16/2010 12:50:18 AM
|
|
On May 16, 4:22=A0am, sandeep <nos...@nospam.com> wrote:
> Ian Collins writes:
> > Why mess about with a macro when a function would do?
> .
> Obviously for efficiency! malloc may be called many times in the course
> of a program.
Others have given useful correct responses but as someone who
prides himself on writing efficient code, I'd like to answer
this part.
Calling malloc() enough to matter and then worrying about
micro-optimizations in your interface to it is like
putting on your best running-shoes just to ride a slow-moving
elevator.
James Dow Allen
|
|
0
|
|
|
|
Reply
|
James
|
5/16/2010 1:51:29 AM
|
|
"sandeep" <nospam@nospam.com> wrote in message
news:hsn38f$rqg$1@speranza.aioe.org...
> Seebs writes:
>> Write a function, not a macro, it'll be easier to make effective use of.
>
> ??
> How?
>
>> Also.
>>
>> 1. Use fprintf(stderr,...) for error messages. 2. Terminate error
>> messages with newlines. 3. Why the *HELL* would you use "unspecified
>> error" as the error message when you have ABSOLUTE CERTAINTY of what the
>> error is? Why not:
>> fprintf(stderr, "Allocation of %ld bytes failed.\n", (unsigned long)
>> x);
>>
>> The first two are comprehensible mistakes. The third isn't. Under what
>> POSSIBLE circumstances could you think that "unspecified error" is a
>> better diagnostic than something that in some way indicates that a
>> memory allocation failed?
>>
>> I really don't understand this one. Please try to explain your
>> reasoning, because I regularly encounter software that fails with
>> messages like these, and I've always assumed it was something people did
>> out of active malice -- they hate their users and want the users to
>> suffer. If there is any other possible reason to, given total certainty
>> of what the problem is, refuse to hint at it, I do not know what it is.
>
> Many users will only be confused by technical error messages about memory
> allocation etc. It's best not to get into unwanted details - the user
> doesn't know about how my program allocates memory, it just needs to know
> there was an error that needs a restart. I think in books they call it
> leaking abstractions.
You have the message, which folks can the use to find more information in
your help.
If you're runnng a 64bit proram and try to do something requiring 6gb when
the ram and swap total 3gb, you'll run out of memory.
Telling the user what happened, and how to fix it (increasing RAM or swap
file) will let them adjust the system settings and continue.
Otherwise they may well decide it's crap and uninstall it, then tell their
friends that it’s crap, and so on.
Word of mouth spreads pretty quickly in this day of twitter, facebook,
blogs, Usenet, ....
Dennis
|
|
0
|
|
|
|
Reply
|
Dennis
|
5/16/2010 2:12:25 AM
|
|
On 5/15/2010 5:21 PM, sandeep wrote:
>
> Many users will only be confused by technical error messages about memory
> allocation etc. It's best not to get into unwanted details - the user
> doesn't know about how my program allocates memory, it just needs to know
> there was an error that needs a restart. I think in books they call it
> leaking abstractions.
*I* call it "Sandeep imagines he's smarter than everybody who
might ever use his program."
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/16/2010 2:21:02 AM
|
|
"Seebs" <usenet-nospam@seebs.net> wrote in message
news:slrnhuu50r.4tq.usenet-nospam@guild.seebs.net...
> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>> Obviously for efficiency! malloc may be called many times in the course
>> of a program.
>
> Please stop trying to outsmart the compiler.
>
> The "cost" of using a function instead of a macro is likely to be so small
> that you can't even measure it. If there is even a cost at all, which
> there
> may not be.
memset actually showed as a bottleneck in one of programs I had to debug.
It was being called 2,000,000,000 times or so.
Converting from memset to a for loop writing integers helped a bit, but the
real fix was to correct the logic error so that it wasn't called that many
times.
the memset was part of initializing an object, which was being constructed
during processing, but was only needed a fraction of the time.....
Dennis
|
|
0
|
|
|
|
Reply
|
Dennis
|
5/16/2010 2:21:05 AM
|
|
On May 15, 9:52=A0pm, sandeep <nos...@nospam.com> wrote:
>
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line? It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
>
Where it's statistically more likely that the computer will break than
run out of memory, you've got a good case for ignoring malloc().
There are other arguments for ignoring malloc() checks. One is that a
request for 0 bytes can return a null pointer or a non-null pointer to
zero bytes. It's much more likely that a zero request will be
legitimate and be made, and that it won't be obvious at writing time
that requests for zero allocations are legitimate, than it is that a
tiny request will fail. So you are quite likely to trigger a suprious
out of memory mesage, and worse, this won't show up in testing if the
platform always returns a non-null pointer to zero bytes. The correct
test if(request !=3D 0 && ptr =3D=3D 0) can be messy and is often not seen
in production code.
The other issues is that if an allocation request for, say, a few
bytes to hold a file path fails, it's more likely that there is some
bug in the program causing the path size to be set to a garbage
number, than it is that the program ahs actually run out of memory.
Printing out an "out of memory" message is misleading, and might be
expensive. For instance the user might try to run the computer of a
bigger computer, taking him a whole day's work to beg the bigger
computer from the neighbouring department, recompile and reinstall the
program, set it all up ...
|
|
0
|
|
|
|
Reply
|
Malcolm
|
5/16/2010 6:33:28 AM
|
|
Seebs writes:
> In short, this is a catastrophically bad design approach. Abandon it.
> Reject it. Anything you think you know which caused you to adopt this
> is probably also utterly wrong, and dangerously so, and until you root
> all the madness out and start afresh, your code will be dangerous,
> untrustworthy, and based on a bad attitude.
This was quite a long rant!! I think I see your point but you have to
imagine your grannie when Word crashes. I think she will not like to see
a message like
"malloc failed at src\lib\unicode\mapper\table.c:762"
I think that will confuse her!
Anyway I have adopted your suggestion, also used a function instead of a
macro, and built in some extra functionality. Now it will keep some memory
back to use later on if allocations start failing for added robustness.
void* safeMalloc(size_t x)
{
static void* emrgcy=0;
void* x1;
#define ESIZE 0x40000000uLL
if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
#undef ESIZE
printf("WARNING running in unstable mode, program may crash at any "
" time....Close open programs, allow more virtual memory or "
" install extra RAM");
if(!(x1=malloc(x))) {
if(emrgcy) {
free(emrgcy);
x1=malloc(x);
} else {
printf("Severe memory failure, program cannot continue at line "
#define STRGFY(x) #x
STRGFY(__LINE__)
#undef STRGFY
" stack dump follows");
abort();
exit(1);
}
}
return x1;
}
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/16/2010 9:05:35 AM
|
|
On 2010-05-16, sandeep <nospam@nospam.com> wrote:
> Seebs writes:
>> In short, this is a catastrophically bad design approach. Abandon it.
>> Reject it. Anything you think you know which caused you to adopt this
>> is probably also utterly wrong, and dangerously so, and until you root
>> all the madness out and start afresh, your code will be dangerous,
>> untrustworthy, and based on a bad attitude.
> This was quite a long rant!! I think I see your point but you have to
> imagine your grannie when Word crashes. I think she will not like to see
> a message like
> "malloc failed at src\lib\unicode\mapper\table.c:762"
> I think that will confuse her!
Yes, but "unspecified error" will either:
1. Confuse her.
or
2. Infuriate her.
It's *necessarily* worse. It cannot possibly be better. It can only, at
best, be about as bad.
And again: Imagine that such users are a majority. Should you absolutely
cripple 25% of your users, infuriating them and treating them with contempt,
so that 75% of them will be completely unsure what just happened instead
of being completely unsure what just happened?
> Anyway I have adopted your suggestion, also used a function instead of a
> macro, and built in some extra functionality. Now it will keep some memory
> back to use later on if allocations start failing for added robustness.
That technique is... well, dubious at best. It doesn't necessarily work,
and may well make things worse.
> void* safeMalloc(size_t x)
> {
> static void* emrgcy=0;
> void* x1;
> #define ESIZE 0x40000000uLL
> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
> #undef ESIZE
Let me guess. Someone told you to define symbolic names for constants,
right?
This is not how you do it.
1. If you're going to define a constant, define it and leave it defined.
2. Don't use a name starting with a capital E followed by another capital
letter, those are reserved for errno values.
3. If you're only using it once, don't feel like you have to #define it.
4. Don't use "uLL" on a constant that's unambiguously within the size
range of an ordinary signed long. You don't need any qualifier at all,
although in theory a system could exist where that value is too big for
size_t, in which case you'd be allocating 0 bytes.
5. Don't get so clever. Try:
if (!emrgcy) {
emrgcy = malloc(0x40000000);
if (!emrgcy) {
fprintf(stderr, "Uh-oh, failed to allocate spare memory.\n");
}
}
6. Don't allocate a GIGABYTE of memory like that -- all this does is
massively increase the chance of catastrophic failure, as a likely response
from a system which overcommits is to determine that your process allocated
a TON of memory, doesn't use most of it, and is probably the best candidate
for being killed out of hand. A megabyte or two, sure, I guess.
7. Actually, even then, this is just cargo cult stuff. Don't do it, it
won't help.
> printf("WARNING running in unstable mode, program may crash at any "
> " time....Close open programs, allow more virtual memory or "
> " install extra RAM");
This is a really poor message, because this is not an "unstable" mode,
it's the normal state of affairs, where you don't have a spare 1GB allocation.
> if(!(x1=malloc(x))) {
> if(emrgcy) {
> free(emrgcy);
> x1=malloc(x);
> } else {
> printf("Severe memory failure, program cannot continue at line "
> #define STRGFY(x) #x
> STRGFY(__LINE__)
> #undef STRGFY
Again, don't do this. With extremely rare exceptions, you should NEVER
be using #undef on something you just defined.
Also, you're still using plain printf for error messages, which is bad for
the same reasons it was last time.
> " stack dump follows");
So's the missing newline.
> abort();
So's the assumption that abort() gives a "stack dump" -- it may not.
> exit(1);
> }
> }
> return x1;
> }
Finally, you've made a few other mistakes. You're freeing emrgcy, but you
don't set it to NULL, so your check for it is unlikely to be useful. You
don't check malloc() after calling it.
In short, this is full of cargo-cult superstitions. Here's a slightly
more realistic effort:
void *
failsafe_malloc(size_t size) {
static void *failsafe = NULL;
void *ret;
if (!failsafe) {
failsafe = malloc(1024 * 1024);
}
ret = malloc(size);
if (!ret && failsafe) {
free(failsafe);
failsafe = NULL;
ret = malloc(size);
}
if (!ret) {
fprintf(stderr, "failed to allocate %lld bytes of memory.\n",
(long long) size);
#ifndef NDEBUG
abort();
#endif
}
return ret;
}
A few things to note:
1. Picked a size that's much less likely to cause an immediate catastrophic
failure.
2. Don't bother the user with warnings about the supposed "failsafe", since
it's basically a pointless superstition anyway.
3. No trying to show off using an elaborate combination of ?: and assignment
to set something up.
4. Error message is terse, simple, and doesn't clutter the user's world.
A user who knows what it means can use it, a user who doesn't at least gets
a message that Something Went Wrong.
5. abort() is conditional on NDEBUG, for consistency with assert()'s
behavior. (I don't use assert because it yields useless messages.)
6. failsafe is correctly set to NULL when freed, and future calls will
try to reallocate it (which may work if something large has been freed
in the mean time).
7. No #define, use once, #undef hackery, because that's annoying and
generally pointless.
Reading your code, I get the impression you're trying to aim for some kind
of code density, with cool tricks you've seen all thrown in together to
make the code look more impressive. Don't do that. Write the absolute
simplest code you can that clearly expresses what you're doing. You'll
have fewer bugs (if you'd written this more simply, I bet you'd have caught
that you never set emrgcy to NULL after freeing it, but might continue
to test it), and you'll have an easier time fixing things and adding features.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 9:38:42 AM
|
|
Seebs <usenet-nospam@seebs.net> writes:
> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>> Seebs writes:
>>> Write a function, not a macro, it'll be easier to make effective use of.
>
>> ??
>> How?
>
> This question is too incoherent to answer.
Is it "How will it be easier to make effective use of?"
Answer - it won't have multiple-evaluation issues, and it both looks
and behaves like a function.
> What part of "a function" do you have trouble with? You know how to write
> functions, right? You know how to call them, right?
>
> Try adding some verbs. Questions like "how do I declare a function" or "how
> do I use a function" might begin to be answerable. An explanation of what
> you're having trouble with, specifically, would be even better.
>
>> Many users will only be confused by technical error messages about memory
>> allocation etc. It's best not to get into unwanted details - the user
>> doesn't know about how my program allocates memory, it just needs to know
>> there was an error that needs a restart. I think in books they call it
>> leaking abstractions.
>
> Wrong.
>
> Users who are "confused" by an error message can accept that they got "an
> error". MANY users, however, know enough to recognize that "out of memory"
> is different from "file not found".
>
> Stop trying to outsmart the user.
I think this error is called "out-dumbing the user".
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/16/2010 10:55:51 AM
|
|
Phil Carmody writes:
> Seebs <usenet-nospam@seebs.net> writes:
>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>> Seebs writes:
>>>> Write a function, not a macro, it'll be easier to make effective use
>>>> of.
>>
>>> ??
>>> How?
>>
>> This question is too incoherent to answer.
>
> Is it "How will it be easier to make effective use of?"
Yes.
> Answer - it won't have multiple-evaluation issues, and it both looks and
> behaves like a function.
Multiple evaluation is very unlikely in this case. This answer looks
spurious to me.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/16/2010 10:57:53 AM
|
|
Keith Thompson <kst-u@mib.org> writes:
> Geoff <geoff@invalid.invalid> writes:
>> On Sat, 15 May 2010 21:21:19 +0000 (UTC), sandeep <nospam@nospam.com>
>> wrote:
>>
>>>Many users will only be confused by technical error messages about memory
>>>allocation etc.
> [...]
>>>I think in books they call it leaking abstractions.
>>
>> Stop reading those books immediately.
>
> At least until you can understand what they're saying. I rather
> doubt that books discussing "leaking abstractions" (a useful concept
> and something to avoid) would recommend an "unspecified error"
> message over "memory allocation failed".
Leaking *abstractions* sounds a lot better than leaking implementation
details, or leaking specifics.
E.g.:
"Error: out of memory (attempting to clone image buffer)"
may leak a couple of abstrations, but is way better than:
"Error: s_malloc(8192100) returned NULL, called from foo/bar/img.c:6742"
or
"Error: imgbuf_s binary buddy-heap has no free blocks"
IMHO.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/16/2010 11:07:21 AM
|
|
sandeep <nospam@nospam.com> writes:
> Ian Collins writes:
>> On 05/16/10 09:02 AM, sandeep wrote:
>>> static void* __p;
>>> #define safeMalloc(x) ((__p=malloc(x))?__p:\
>>> (exit(printf("unspecified error")),(void*)0))
>>
>> Why mess about with a macro when a function would do?
>
> Obviously for efficiency! malloc may be called many times in the course
> of a program.
Forget everything you've learnt.
Start again.
Do not pick up idiocy like the above next time.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/16/2010 11:10:06 AM
|
|
"Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
> "Seebs" <usenet-nospam@seebs.net> wrote in message
> news:slrnhuu50r.4tq.usenet-nospam@guild.seebs.net...
>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>> Obviously for efficiency! malloc may be called many times in the course
>>> of a program.
>>
>> Please stop trying to outsmart the compiler.
>>
>> The "cost" of using a function instead of a macro is likely to be so small
>> that you can't even measure it. If there is even a cost at all,
>> which there
>> may not be.
>
> memset actually showed as a bottleneck in one of programs I had to debug.
> It was being called 2,000,000,000 times or so.
>
> Converting from memset to a for loop writing integers helped a bit,
> but the real fix was to correct the logic error so that it wasn't
> called that many times.
> the memset was part of initializing an object, which was being
> constructed during processing, but was only needed a fraction of the
> time.....
An object being 'constructed'? Are you sure you were using C?
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/16/2010 11:11:51 AM
|
|
Sorry, Seebs much of this is directed at sandeep not you. You have made
goode points. It is just easier to reply here so as not to duplicate
some things.
On 2010-05-16, Seebs <usenet-nospam@seebs.net> wrote:
> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>> This was quite a long rant!! I think I see your point but you have to
>> imagine your grannie when Word crashes. I think she will not like to see
>> a message like
>> "malloc failed at src\lib\unicode\mapper\table.c:762"
>> I think that will confuse her!
Yes, but "unspecified error" is no less confusing.
The user should receive a message that is useful enough for them to have
a basic idea of what is happening giving them an idea of how to fix it
without a lot of technical reference or inside knowledge of the program.
What they really need to know is "Insufficient Memory." That isn't too
technical for almost any user. Most people, even grannies, know that
programs need memory to run.
Somebody debugging the program would definitely prefer the "malloc failed"
message as it gives them some clue as to where the failure happened within
the program. This could be useful for tracking down other problems runaway
functions, etc.
> And again: Imagine that such users are a majority. Should you absolutely
> cripple 25% of your users, infuriating them and treating them with contempt,
> so that 75% of them will be completely unsure what just happened instead
> of being completely unsure what just happened?
There are ways to satisfy both. The first is to use the preprocessor to
differentiate between test code where detailed debugging information is
included and production code with errors more useful to the user. Another
is to provide a logging system appropriate to your target operating system
where you log messages with additional information while still showing user
oriented messages.
>> Anyway I have adopted your suggestion, also used a function instead of a
>> macro, and built in some extra functionality. Now it will keep some memory
>> back to use later on if allocations start failing for added robustness.
> That technique is... well, dubious at best. It doesn't necessarily work,
> and may well make things worse.
It is a poor choice. If an application truly cannot function then it
should handle the error gracefully and exit. That said, while one feature
of a program may not be able to perform with low memory, often other
functionality is possible. Many programs may not be able to function; but,
may be too critical to exit for what may be a temporary memory issue.
A server process, for instance, may not be able to handle a request
that requires more memory then is available. It should not just exit.
It should send an error to the client, unallocate any data that was
allocated for the clients request, close the connection, and wait for
another client. The next clients request may not be as memory intensive
or another process that was hogging memory may have released it.
In the end, the appropriate action depends on the application.
> 6. Don't allocate a GIGABYTE of memory like that -- all this does is
> massively increase the chance of catastrophic failure, as a likely response
> from a system which overcommits is to determine that your process allocated
> a TON of memory, doesn't use most of it, and is probably the best candidate
> for being killed out of hand. A megabyte or two, sure, I guess.
Unless you know the memory size of the target system, this is a crap shoot
at best.
> 5. abort() is conditional on NDEBUG, for consistency with assert()'s
> behavior. (I don't use assert because it yields useless messages.)
Assert is not designed to give useful messages and it is not designed for
error handling. It is designed to crash the application if it notices
something is wrong. This helps the programmer to know about subtle bugs
that might otherwise go unseen until they produce noticable problems. Bugs
of this nature can be difficult to detect until the software is shipped and
can be difficult to debug as they may cause problems or crash until far
later in the execution then where the actual bug resides. Assert helps
you catch them early before they ship and closer to when they actually
happen.
The execellent book "Writing Solid Code" by Steve Maguire is a must read
for any programmer. It contains great examples how to use assert()
properly and effectively.
> Reading your code, I get the impression you're trying to aim for some kind
> of code density, with cool tricks you've seen all thrown in together to
> make the code look more impressive. Don't do that. Write the absolute
> simplest code you can that clearly expresses what you're doing. You'll
> have fewer bugs (if you'd written this more simply, I bet you'd have caught
> that you never set emrgcy to NULL after freeing it, but might continue
> to test it), and you'll have an easier time fixing things and adding features.
Note that minimizing code density doesn't create a smaller or faster
binary. A concisely written ?: operator on a single line generates the
same code as the equivilant if/else operators spread across multiple lines.
The if/else version is almost always easier to read. "?:"'s are almost
always the sign of an amateur programmer trying to show off their "l337
skilz." Seasoned programmers have learned to avoid them.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/16/2010 11:20:13 AM
|
|
Phil Carmody a �crit :
>
> An object being 'constructed'? Are you sure you were using C?
>
As you (may) know, objects are allocated, constructed (initialized) in C
all the time.
For instance for some hypothetical structure "Foo":
Foo *newFoo(size_t length, double averageUse)
{
Foo *result;
if ((result=calloc(1,sizeof(Foo))) == NULL)
return NULL;
result->averageUse = averageUse;
result->length = length;
result->Stats = DEFAULT_STATS_VAL;
result->count = 0;
return result;
}
|
|
0
|
|
|
|
Reply
|
jacob
|
5/16/2010 11:21:51 AM
|
|
On 15 May, 22:25, Seebs <usenet-nos...@seebs.net> wrote:
> On 2010-05-15, sandeep <nos...@nospam.com> wrote:
> > Seebs writes:
<snip>
> > Many users will only be confused by technical error messages about memo=
ry
> > allocation etc. It's best not to get into unwanted details - the user
> > doesn't know about how my program allocates memory, it just needs to kn=
ow
> > there was an error that needs a restart. I think in books they call it
> > leaking abstractions.
>
> Wrong.
I'm not a fan of putting too much "computer science" into user error
messages or expecting them to know program internals. But a short
succinct description of the cause of the failure is good. I'm a fan of
log files that provide the developer with more specific information.
Note yesterday I encountered someone who got a "not enough memory to
perform operation" error and were surprised because their disk had
plenty of space.
<snip>
> Where did you get this bullshit? =A0The above paragraph is by far the stu=
pidest
> thing I've ever seen you write. =A0It's not just a little wrong; it's not=
just a
> little stupid; it's not just a little callous or unthinking. =A0It's one =
of
> the most thoroughly, insideously, wrong, stupid, and evil things you coul=
d
> start thinking as a programmer.
wow. And you're on decaff?
<snip>
> 2. =A0"Error that needs a restart" is nearly always bullshit. =A0If the p=
rogram
> is running out of memory because you made a mistake causing it to try to
> allocate 4GB of memory on a 2GB machine, "restart" will not fix it. =A0No=
thing
> will fix it until the user finds out what's wrong and submits a bug repor=
t
> allowing the developer to fix it.
the other cause for memory error is that some other program has eaten
the memory
<snip>
> 4. =A0The chances are very good that many of the prospective users of any
> program will, in fact, be able to program at least a little,
what universe do you live in? Are most of the people you know
programmers?
> or will have basic computer literacy.
again quite strange. "basic computer literacy" can be *very* basic
<snip>
> 5. =A0Trying to avoid "confusing" people is power-mad idiocy.
I disagree. Have you seen Airport displays with Windows NT register
dumps?
>=A0Your job here
> is not to imagine yourself some kind of arbiter-of-technology, preserving=
the
> poor helpless idiots from the dangers of actual information. =A0Your job =
is
> to make a program which works as well as possible, and that includes CLEA=
R
> statements of what failed.
well you draw the line at register dumps so I think this is a matter
of where the line is drawn
> 6. =A0You can never make a message so clear that every concievable user w=
ill
> understand it. =A0However, a user who won't understand a simple message w=
on't
> understand an imprecise or flatly false one, either. =A0There does not ex=
ist
> a user who will have a clear idea of what went wrong and be able to react
> accordingly when confronted with "unspecified error", but who will be utt=
erly
> paralyzed like a deer in headlights when confronted with "memory allocati=
on
> failed". =A0As a result, even if we restrict our study to the set of user=
s
> who simply have no clue what those words mean, you STILL gain no benefit,
> at all, from the bad message. =A0But in the real world, you hurt many of =
your
> users by denying them the information that would allow them to address
> the issue (say, by closing other applications so that more memory becomes
> available).
do you have a limit to this? "Database has deadlocked" "link layer
failure" "too many hash collisions"
<snip>
--
"In flipping pig mode again"
error string found in Mac resource fork
|
|
0
|
|
|
|
Reply
|
Nick
|
5/16/2010 11:42:19 AM
|
|
On Sat, 15 May 2010 21:21:19 +0000, sandeep wrote:
> Seebs writes:
>> Write a function, not a macro, it'll be easier to make effective use
>> of.
>>
>> 1. Use fprintf(stderr,...) for error messages. 2. Terminate error
>> messages with newlines. 3. Why the *HELL* would you use "unspecified
>> error" as the error message when you have ABSOLUTE CERTAINTY of what
>> the error is? Why not:
>> fprintf(stderr, "Allocation of %ld bytes failed.\n", (unsigned long)
>> x);
>>
>> The first two are comprehensible mistakes. The third isn't. Under
>> what POSSIBLE circumstances could you think that "unspecified error" is
>> a better diagnostic than something that in some way indicates that a
>> memory allocation failed?
>>
>
> Many users will only be confused by technical error messages about
> memory allocation etc. It's best not to get into unwanted details - the
> user doesn't know about how my program allocates memory, it just needs
> to know there was an error that needs a restart. I think in books they
> call it leaking abstractions.
Suppose your program is a filter, used in a (unix shell: sorry!) commandline like:
$ find . -name \*\.c -print | sort | uniq | yourprogram | lpr
FAILED $
What could the user do to help you solve *your* problem ?
Would he have liked it, if "FAILED" had been written to stdout ?
Will he ever attempt to use your program again ?
HTH,
AvK
|
|
0
|
|
|
|
Reply
|
Moi
|
5/16/2010 11:45:34 AM
|
|
Nick Keighley wrote:
) I'm not a fan of putting too much "computer science" into user error
) messages or expecting them to know program internals. But a short
) succinct description of the cause of the failure is good. I'm a fan of
) log files that provide the developer with more specific information.
)
) Note yesterday I encountered someone who got a "not enough memory to
) perform operation" error and were surprised because their disk had
) plenty of space.
Quite understandable, given that many OSes use disk space as virtual
memory.
) On 15 May, 22:25, Seebs <usenet-nos...@seebs.net> wrote:
)> 4. ?The chances are very good that many of the prospective users of any
)> program will, in fact, be able to program at least a little,
)
) what universe do you live in? Are most of the people you know
) programmers?
No, but 99% of the users are using 1% of the programs.
In other words: most users stick with the big, well-known software,
especially the less computer-literate ones, so any given program is
therefore most likely to be used by a computer-savvy person.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
|
|
0
|
|
|
|
Reply
|
Willem
|
5/16/2010 11:50:38 AM
|
|
On 16 May, 10:38, Seebs <usenet-nos...@seebs.net> wrote:
> On 2010-05-16, sandeep <nos...@nospam.com> wrote:
> > Seebs writes:
<snip>
> > void* safeMalloc(size_t x)
> > {
> > =A0 =A0 static void* emrgcy=3D0;
> > =A0 =A0 void* x1;
> > #define ESIZE 0x40000000uLL
> > =A0 =A0 if(!(emrgcy=3Demrgcy?emrgcy:malloc(ESIZE)))
> > #undef ESIZE
>
> Let me guess. =A0Someone told you to define symbolic names for constants,
> right?
>
> This is not how you do it.
>
> 1. =A0If you're going to define a constant, define it and leave it define=
d.
> 2. =A0Don't use a name starting with a capital E followed by another capi=
tal
> letter, those are reserved for errno values.
> 3. =A0If you're only using it once, don't feel like you have to #define i=
t.
oops! Don't agree. A named constant makes it clear what the constant
is for- it's the good use of abstraction. Then there's multiple use of
the same number (less likely with really big numbers).
sm.state =3D 9;
write_port (9, 9);
for (equip_num =3D 0; equip_num < 9; equip_num++)
reset_equip (equip_num);
If you want to change one of those you have inspect every 9 in the
program
<snip>
|
|
0
|
|
|
|
Reply
|
Nick
|
5/16/2010 11:55:44 AM
|
|
"Phil Carmody" <thefatphil_demunged@yahoo.co.uk> wrote in message
news:87mxw0drt4.fsf@kilospaz.fatphil.org...
> "Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
>> "Seebs" <usenet-nospam@seebs.net> wrote in message
>> news:slrnhuu50r.4tq.usenet-nospam@guild.seebs.net...
>>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>>> Obviously for efficiency! malloc may be called many times in the course
>>>> of a program.
>>>
>>> Please stop trying to outsmart the compiler.
>>>
>>> The "cost" of using a function instead of a macro is likely to be so
>>> small
>>> that you can't even measure it. If there is even a cost at all,
>>> which there
>>> may not be.
>>
>> memset actually showed as a bottleneck in one of programs I had to debug.
>> It was being called 2,000,000,000 times or so.
>>
>> Converting from memset to a for loop writing integers helped a bit,
>> but the real fix was to correct the logic error so that it wasn't
>> called that many times.
>> the memset was part of initializing an object, which was being
>> constructed during processing, but was only needed a fraction of the
>> time.....
>
> An object being 'constructed'? Are you sure you were using C?
I know I was using C++. I doubt C would be any different in this respect.
:-)
Dennis
|
|
0
|
|
|
|
Reply
|
Dennis
|
5/16/2010 12:19:55 PM
|
|
sandeep <nospam@nospam.com> writes:
> Phil Carmody writes:
>> Seebs <usenet-nospam@seebs.net> writes:
>>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>>> Seebs writes:
>>>>> Write a function, not a macro, it'll be easier to make effective use
>>>>> of.
>>>
>>>> ??
>>>> How?
>>>
>>> This question is too incoherent to answer.
>>
>> Is it "How will it be easier to make effective use of?"
>
> Yes.
>
>> Answer - it won't have multiple-evaluation issues, and it both looks and
>> behaves like a function.
>
> Multiple evaluation is very unlikely in this case. This answer looks
> spurious to me.
The operative words are "to me". We've already ascertained that
your perspective is desperately naive. Giving us more evidence
does not strengthen your case at all. The macro you suggested
gains you _nothing_, in particular it doesn't give you the things
that you mindlessly asserted that it does give you in anything
apart from the compilers with the poorest QoI.
Sorry to break this to you, but you are *not* cleverer than your
compiler. Stop pretending you are. And please stop doing it in
public, it's painful to watch.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/16/2010 12:44:06 PM
|
|
On 16 May, 12:45, Moi <r...@invalid.address.org> wrote:
> On Sat, 15 May 2010 21:21:19 +0000, sandeep wrote:
> > Seebs writes:
> >> Write a function, not a macro, it'll be easier to make effective use
> >> of.
>
> >> 1. =A0Use fprintf(stderr,...) for error messages. 2. =A0Terminate erro=
r
> >> messages with newlines. 3. =A0Why the *HELL* would you use "unspecifie=
d
> >> error" as the error message when you have ABSOLUTE CERTAINTY of what
> >> the error is? =A0Why not:
> >> =A0 fprintf(stderr, "Allocation of %ld bytes failed.\n", (unsigned lon=
g)
> >> =A0 x);
>
> >> The first two are comprehensible mistakes. =A0The third isn't. =A0Unde=
r
> >> what POSSIBLE circumstances could you think that "unspecified error" i=
s
> >> a better diagnostic than something that in some way indicates that a
> >> memory allocation failed?
>
> > Many users will only be confused by technical error messages about
> > memory allocation etc. It's best not to get into unwanted details - the
> > user doesn't know about how my program allocates memory, it just needs
> > to know there was an error that needs a restart. I think in books they
> > call it leaking abstractions.
>
> Suppose your program is a filter, used in a (unix shell: sorry!) commandl=
ine like:
>
> $ find . -name \*\.c -print | sort | uniq | yourprogram | lpr
> FAILED $
>
> What could the user do to help you solve *your* problem ?
> Would he have liked it, if "FAILED" had been written to stdout ?
> Will he ever attempt to use your program again ?
I don't understand.
|
|
0
|
|
|
|
Reply
|
Nick
|
5/16/2010 1:42:21 PM
|
|
On 16 May, 12:50, Willem <wil...@turtle.stack.nl> wrote:
> Nick Keighley wrote:
[not all users are programmers]
> ) Note yesterday I encountered someone who got a "not enough memory to
> ) perform operation" error and were surprised because their disk had
> ) plenty of space.
>
> Quite understandable, given that many OSes use disk space as virtual
> memory.
this user wouldn't have known what the term "virtual memory" meant.
Virtual memory doesn't usually eat your entire disk
|
|
0
|
|
|
|
Reply
|
Nick
|
5/16/2010 1:47:47 PM
|
|
On Sun, 16 May 2010, Nick Keighley wrote:
> On 16 May, 12:45, Moi <r...@invalid.address.org> wrote:
>> Suppose your program is a filter, used in a (unix shell: sorry!) commandline like:
>>
>> $ find . -name \*\.c -print | sort | uniq | yourprogram | lpr
>> FAILED $
>>
>> What could the user do to help you solve *your* problem ?
>> Would he have liked it, if "FAILED" had been written to stdout ?
>> Will he ever attempt to use your program again ?
>
> I don't understand.
I don't know what is you don't understand so I'll start with the pipeline.
- Create a list of *.c files present in the directory hierarchy rooted at
the current working directory,
- sort them lexicographically,
- eliminate duplicate lines (duplicate lines were unlikely in this case,
but I digress),
- process the remaining list with "yourprogram",
- pass the output of "yourprogram" to the line printer (ie. queue the
output as a print job -- further processing is possible "within" lpr,
ie. automagically determining whether the data is plain text,
PostScript, PDF and so on, and translating it to the configured printer's
language).
(These processes run in parallel.)
If "yourprogram" writes FAILED to stdout instead of stderr, then FAILED
will show up interleaved with (or after) the other data sent to "lpr".
Supposing "yourprogram" *either* produces data *or* it writes FAILED, and
that lpr knows to ignore an empty job, writing normal data and FAILED to
different output streams works correctly. Writing FAILED to stdout would
print a page with "FAILED (snicker snicker)" on it.
Of course, if "yourprogram" writes data to stdout before it emits FAILED
to stderr, you still end up with an incomplete page (or book).
Cheers,
lacos
|
|
0
|
|
|
|
Reply
|
Ersek
|
5/16/2010 2:32:37 PM
|
|
On Sun, 16 May 2010 16:32:37 +0200, Ersek, Laszlo wrote:
> On Sun, 16 May 2010, Nick Keighley wrote:
>
>> On 16 May, 12:45, Moi <r...@invalid.address.org> wrote:
>
>>> Suppose your program is a filter, used in a (unix shell: sorry!)
>>> commandline like:
>>>
>>> $ find . -name \*\.c -print | sort | uniq | yourprogram | lpr FAILED $
>>>
>> I don't understand.
>
> I don't know what is you don't understand so I'll start with the
> pipeline. - Create a list of *.c files present in the directory
> hierarchy rooted at the current working directory,
> - sort them lexicographically,
> - eliminate duplicate lines (duplicate lines were unlikely in this case,
> but I digress),
> - process the remaining list with "yourprogram", - pass the output of
> "yourprogram" to the line printer (ie. queue the output as a print job
> -- further processing is possible "within" lpr, ie. automagically
>
> If "yourprogram" writes FAILED to stdout instead of stderr, then FAILED
> will show up interleaved with (or after) the other data sent to "lpr".
> Supposing "yourprogram" *either* produces data *or* it writes FAILED,
> and that lpr knows to ignore an empty job, writing normal data and
> FAILED to different output streams works correctly. Writing FAILED to
> stdout would print a page with "FAILED (snicker snicker)" on it.
>
> Of course, if "yourprogram" writes data to stdout before it emits FAILED
> to stderr, you still end up with an incomplete page (or book).
>
Exactly.
My whole point was that, if the program decides to fail
it could _at least_ try to minimize the damage.
Writing nonsense to stdout in some cases only increases the damage.
AvK
|
|
0
|
|
|
|
Reply
|
Moi
|
5/16/2010 2:42:36 PM
|
|
sandeep wrote:
>
> Hello friends~~
>
> Think about malloc.
>
> Obviously, for tiny allocations like 20 bytes to strcpy a filename,
> there's no point putting in a check on the return value of malloc... if
> there is so little memory then stack allocations will also be failing and
> your program will be dead.
>
> Whereas, if you're allocating a gigabyte for a large array, this might
> easily fail, so you should definitely check for a NULL return.
>
> So somewhere in between there must be a point where you stop ignoring the
> return value, and start checking it. Where do you draw this line? It must
> depend on whether you will deploy to a low memory or high memory
> environment... but is there a good rule?
Yes, there is a good rule.
If you have any idea of what it is that you would want
the program to do in the case that malloc returns a null pointer,
then you should write it into the program.
If you don't know what is that you would want
the program to do in the case that malloc returns a null pointer,
then you should think about it until you do know,
and then write it into the program.
--
pete
|
|
0
|
|
|
|
Reply
|
pete
|
5/16/2010 2:50:30 PM
|
|
Nick Keighley wrote:
) On 16 May, 12:50, Willem <wil...@turtle.stack.nl> wrote:
)> Nick Keighley wrote:
)
) [not all users are programmers]
)
)> ) Note yesterday I encountered someone who got a "not enough memory to
)> ) perform operation" error and were surprised because their disk had
)> ) plenty of space.
)>
)> Quite understandable, given that many OSes use disk space as virtual
)> memory.
)
) this user wouldn't have known what the term "virtual memory" meant.
However, he could very well have known that you get out-of-memory errors
more often when your disk is full. Which is true on some OSes. Especially
the ones that are used by people who are not computer-savvy.
) Virtual memory doesn't usually eat your entire disk
There are OSes where it does. Especially older versions of the one that
is most used by non-computer-savvy users.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
|
|
0
|
|
|
|
Reply
|
Willem
|
5/16/2010 2:51:46 PM
|
|
Nick Keighley <nick_keighley_nospam@hotmail.com> writes:
> On 16 May, 10:38, Seebs <usenet-nos...@seebs.net> wrote:
>> On 2010-05-16, sandeep <nos...@nospam.com> wrote:
>> > Seebs writes:
>
> <snip>
>
>> > void* safeMalloc(size_t x)
>> > {
>> > static void* emrgcy=0;
>> > void* x1;
>> > #define ESIZE 0x40000000uLL
>> > if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>> > #undef ESIZE
>>
>> Let me guess. Someone told you to define symbolic names for constants,
>> right?
>>
>> This is not how you do it.
>>
>> 1. If you're going to define a constant, define it and leave it defined.
>> 2. Don't use a name starting with a capital E followed by another capital
>> letter, those are reserved for errno values.
>> 3. If you're only using it once, don't feel like you have to #define it.
>
> oops! Don't agree. A named constant makes it clear what the constant
> is for- it's the good use of abstraction. Then there's multiple use of
> the same number (less likely with really big numbers).
>
> sm.state = 9;
> write_port (9, 9);
> for (equip_num = 0; equip_num < 9; equip_num++)
> reset_equip (equip_num);
>
> If you want to change one of those you have inspect every 9 in the
> program
But in the safeMalloc case, there's no point in using a #define. The
fact that it's immediately followed by #undef implies that the writer
wants it to exist only in a limited scope. So why not just declare it
that way?
...
static void *emrgcy = 0;
void *x1;
const size_t ESIZE = 0x40000000;
if ((!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
...
Of course I wouldn't call it ESIZE, and I wouldn't write that ugly
condition, and I'd use a lot more whitespace, and I wouldn't do it
this way in the first place.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/16/2010 3:22:18 PM
|
|
sandeep <nospam@nospam.com> writes:
> Phil Carmody writes:
>> Seebs <usenet-nospam@seebs.net> writes:
>>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>>> Seebs writes:
>>>>> Write a function, not a macro, it'll be easier to make effective use
>>>>> of.
>>>
>>>> ??
>>>> How?
>>>
>>> This question is too incoherent to answer.
>>
>> Is it "How will it be easier to make effective use of?"
>
> Yes.
>
>> Answer - it won't have multiple-evaluation issues, and it both looks and
>> behaves like a function.
>
> Multiple evaluation is very unlikely in this case. This answer looks
> spurious to me.
The question you should be asking yourself is why you'd want to
write a macro rather than a function, not the other way around.
Use a function unless you have a good specific reason to use a
macro.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/16/2010 3:23:55 PM
|
|
On May 16, 5:05=A0am, sandeep <nos...@nospam.com> wrote:
> Seebs writes:
> > In short, this is a catastrophically bad design approach. =A0Abandon it=
..
> > Reject it. =A0Anything you think you know which caused you to adopt thi=
s
> > is probably also utterly wrong, and dangerously so, and until you root
> > all the madness out and start afresh, your code will be dangerous,
> > untrustworthy, and based on a bad attitude.
>
> This was quite a long rant!! I think I see your point but you have to
> imagine your grannie when Word crashes. I think she will not like to see
> a message like
> "malloc failed at src\lib\unicode\mapper\table.c:762"
> I think that will confuse her!
>
> Anyway I have adopted your suggestion, also used a function instead of a
> macro, and built in some extra functionality. Now it will keep some memor=
y
> back to use later on if allocations start failing for added robustness.
>
> void* safeMalloc(size_t x)
> {
> =A0 =A0 static void* emrgcy=3D0;
> =A0 =A0 void* x1;
> #define ESIZE 0x40000000uLL
> =A0 =A0 if(!(emrgcy=3Demrgcy?emrgcy:malloc(ESIZE)))
> #undef ESIZE
> =A0 =A0 =A0 =A0 printf("WARNING running in unstable mode, program may cra=
sh at any "
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 " time....Close open programs, allow more=
virtual memory or "
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 " install extra RAM");
> =A0 =A0 if(!(x1=3Dmalloc(x))) {
> =A0 =A0 =A0 =A0 if(emrgcy) {
> =A0 =A0 =A0 =A0 =A0 =A0 free(emrgcy);
> =A0 =A0 =A0 =A0 =A0 =A0 x1=3Dmalloc(x);
> =A0 =A0 =A0 =A0 } else {
> =A0 =A0 =A0 =A0 =A0 =A0 printf("Severe memory failure, program cannot con=
tinue at line "
> #define STRGFY(x) #x
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 STRGFY(__LINE__)
> #undef STRGFY
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 " stack dump follows");
> =A0 =A0 =A0 =A0 =A0 =A0 abort();
> =A0 =A0 =A0 =A0 =A0 =A0 exit(1);
> =A0 =A0 =A0 =A0 }
> =A0 =A0 }
> =A0 =A0 return x1;
>
> }
>
>
The shop I worked in (before I retired) had a number of "rules of
thumb" wrt good design and implementation. I agreed with most of those
rules, and utilized them quite regularly.
The above code (and the code it was derived from) violates one of
those "rules" by making an application policy decision within a
support/utility function. In other words, I don't like the invocation
of the exit() function here; a memory allocation utility function
should return allocated memory, or NULL, and leave the handling of a
"no memory available" condition to upper layers of logic.
It is acceptable to have this utility function /log/ the error. It is
not acceptable to have the utility function decide that the
application /cannot recover/ from this error.
--
Lew Pitcher
Master Codewright & JOAT-in-training | Registered Linux User #112576
Me: http://pitcher.digitalfreehold.ca/ | Just Linux: http://justlinux.ca/
---------- Slackware - Because I know what I'm doing.
------
|
|
0
|
|
|
|
Reply
|
Lew
|
5/16/2010 3:47:47 PM
|
|
On 2010-05-16, sandeep <nospam@nospam.com> wrote:
> Phil Carmody writes:
>> Is it "How will it be easier to make effective use of?"
> Yes.
Okay. The obvious answer is that you don't have to think about execution
context or use elaborate hacks with commas and ?:, you can just call it
like a function and it'll work.
>> Answer - it won't have multiple-evaluation issues, and it both looks and
>> behaves like a function.
> Multiple evaluation is very unlikely in this case. This answer looks
> spurious to me.
Again, you're trying way too hard. Don't spend a lot of time trying to
figure out whether a given macro is likely to cause trouble if you evaluate
its arguments too often. Just remember that this is a common risk, and
avoid it.
Anyway, it's not impossible to come up with a conceivable allocation where the
argument to the allocation includes a modifier.
extern int lengths[];
static int length_index = 0;
void
next_block(void) {
return malloc(lengths[length_index++]);
}
But mostly, the issue here is one of developing good habits. If you become
a programmer, you *will* end up having to write something quickly, on not
enough sleep, or while a bit sick, or something. You will make mistakes.
At that point, whether you succeed or fail will depend, in no small part,
on whether you have good *habits*. If the way you do something when you don't
have time or resources to think things through carefully and test everything
out works anyway, because it's conservative and straight-forward, you'll be
okay. If you default to creating macros unless you've already thought of
how multiple-evaluation issues will hurt you, you're going to get bitten,
badly.
Here's the thing. You say "multiple-evaluation is very unlikely". Things
that are "very unlikely" happen pretty often, in the course of a large
programming project. Which is to say, the chances are pretty good that,
in a large program, you'll get hit by it at least once.
This comes back to the problem with your evaluation that "many" users
won't understand an informative error message. You're picking the case you
think is the most common, and building everything around that, accepting
a very high risk of extremely bad outcomes in any case other than the most
common.
You have to remember the scale on which computers operate. They run extremely
fast, and there are a lot of them. No, a one in a million chance of failure
is NOT safe. My filesystem emulator, during the course of a typical build,
executes hundreds of thousands of fairly complex operations. If you turn on
logging, it can be ten million operations or more. Per run. We typically
build for about sixty targets per spin, which is roughly daily, plus we have
over a hundred developers doing several runs a day.
Something that is "vanishingly unlikely" typically happens 2-3 times a day,
at least.
You have the interest in programming to be good at this; put some time
into picking up a good philosophy of programming, and you'll have a great
time.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 5:15:55 PM
|
|
On 2010-05-16, Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:
> Note yesterday I encountered someone who got a "not enough memory to
> perform operation" error and were surprised because their disk had
> plenty of space.
Oh, sure, they were confused.
But! They could report the error to someone less confused, and the less
confused person could at least guess at what went wrong.
> wow. And you're on decaff?
I have had a lot of very negative experiences involving "an unspecified
error occurred" and similar things.
> the other cause for memory error is that some other program has eaten
> the memory
True!
>> 4. �The chances are very good that many of the prospective users of any
>> program will, in fact, be able to program at least a little,
> what universe do you live in? Are most of the people you know
> programmers?
Consider, say, "World of Warcraft". Played by many people who are only
able to play it because someone smarter plugged in a mouse for them.
But out of eleven million users, there are hundreds to thousands of
programmers, at the least, which I consider to be "many" users.
>> 5. �Trying to avoid "confusing" people is power-mad idiocy.
> I disagree. Have you seen Airport displays with Windows NT register
> dumps?
Occasionally. I think they are not noticably worse than an airport
display containing an out-of-date schedule followed by the information
"unspecified error".
> do you have a limit to this? "Database has deadlocked" "link layer
> failure" "too many hash collisions"
My practical rule is usually: If you are targetting non-programmers,
and you don't have the resources to, say, spawn an error-handling application
that'll debug the executable automatically, package up a report, and
send it to the developers automatically, go for a simple textual explanation.
I usually aim for: If one of the non-technical users I know asked me "what
happened", what answers could I give them that I think would leave them
with a feeling that they'd gotten an answer which referred in some way to
an event... And then, if one of the technical users I know asked me "what
happened", what answers could I give them that I think would give them an
accurate (if not necessarily comprehensive) understanding of the failure.
The sets have, thus far, always had an intersection. It's okay to give less
information than someone who's been hired to debug the program would need,
and it's okay to give more information than someone who has only recently
been convinced that this is not actually magic would be able to understand.
The goal is to find something that's not hugely intimidating to reasonably
rational users (you can't do anything about that last five percent), and
not too vague to be useful at all for a more experienced user.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 5:23:31 PM
|
|
On 2010-05-16, Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:
> On 16 May, 10:38, Seebs <usenet-nos...@seebs.net> wrote:
>> 3. �If you're only using it once, don't feel like you have to #define it.
> oops! Don't agree. A named constant makes it clear what the constant
> is for- it's the good use of abstraction. Then there's multiple use of
> the same number (less likely with really big numbers).
> sm.state = 9;
> write_port (9, 9);
> for (equip_num = 0; equip_num < 9; equip_num++)
> reset_equip (equip_num);
> If you want to change one of those you have inspect every 9 in the
> program
True.
And come to think of it, every time I've used a value "only once", it's ended
up being "twice" within a week or two anyway.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 5:25:14 PM
|
|
sandeep <nospam@nospam.com> wrote:
> Seebs writes:
> > In short, this is a catastrophically bad design approach. Abandon it.
> > Reject it. Anything you think you know which caused you to adopt this
> > is probably also utterly wrong, and dangerously so, and until you root
> > all the madness out and start afresh, your code will be dangerous,
> > untrustworthy, and based on a bad attitude.
> This was quite a long rant!! I think I see your point but you have to
> imagine your grannie when Word crashes. I think she will not like to see
> a message like
> "malloc failed at src\lib\unicode\mapper\table.c:762"
> I think that will confuse her!
If you're writing a complex application like Word you would never bail on a
malloc failure, at least not after the application has initialized itself
and reached a steady state. Such applications should have provisioned
resources for showing dialogues notifying users that a particular operation
cannot be completed, and that they're free to attempt to continue or exit.
The application or user may then choose to flush caches and release other
resources.
I think it's a fair presumption that any application bailing from malloc is
a command-line utility, where emiting a message on stderr is the analogue to
popping up a dialogue box in a GUI, and exiting is the only way to signal
failure of the operation.
On the other hand, libraries should never do any such thing. They should
return the error to the calling application directly, and should always
maintain a sane internal state.
|
|
0
|
|
|
|
Reply
|
William
|
5/16/2010 5:30:44 PM
|
|
On 16 May, 15:32, "Ersek, Laszlo" <la...@caesar.elte.hu> wrote:
> On Sun, 16 May 2010, Nick Keighley wrote:
> > On 16 May, 12:45, Moi <r...@invalid.address.org> wrote:
reinsert snipped material
****
>
> > >> 1. Use fprintf(stderr,...) for error messages. 2. Terminate error
> > >> messages with newlines. 3. Why the *HELL* would you use "unspecified
> > >> error" as the error message when you have ABSOLUTE CERTAINTY of what
> > >> the error is? Why not:
> > >> fprintf(stderr, "Allocation of %ld bytes failed.\n", (unsigned long)
> > >> x);
>
> > >> The first two are comprehensible mistakes. The third isn't. Under
> > >> what POSSIBLE circumstances could you think that "unspecified error" is
> > >> a better diagnostic than something that in some way indicates that a
> > >> memory allocation failed?
>
> > > Many users will only be confused by technical error messages about
> > > memory allocation etc. It's best not to get into unwanted details - the
> > > user doesn't know about how my program allocates memory, it just needs
> > > to know there was an error that needs a restart. I think in books they
> > > call it leaking abstractions.
****
> >> Suppose your program is a filter, used in a (unix shell: sorry!) commandline like:
>
> >> $ find . -name \*\.c -print | sort | uniq | yourprogram | lpr
> >> FAILED $
>
> >> What could the user do to help you solve *your* problem ?
> >> Would he have liked it, if "FAILED" had been written to stdout ?
> >> Will he ever attempt to use your program again ?
>
> > I don't understand.
>
> I don't know what is you don't understand so I'll start with the pipeline.
I suppose I was alittle terse. I know what a pipeline is. I couldn't
understand what your post had to do with abstraction leakage. You were
only addressing the "don't write to stdio" bit?
<snip>
> If "yourprogram" writes FAILED to stdout instead of stderr, then FAILED
> will show up interleaved with (or after) the other data sent to "lpr".
> Supposing "yourprogram" *either* produces data *or* it writes FAILED, and
> that lpr knows to ignore an empty job, writing normal data and FAILED to
> different output streams works correctly. Writing FAILED to stdout would
> print a page with "FAILED (snicker snicker)" on it.
>
> Of course, if "yourprogram" writes data to stdout before it emits FAILED
> to stderr, you still end up with an incomplete page (or book).
well I don't write error data to stdout and I write occaisional
filters but this seems a bit nit picky. If it fails I don't get useful
output and where the error message goes isn't all that important. I
suppose "FAILED" on the printer is a bit poor! But then so is "MEMORY
ERROR"
|
|
0
|
|
|
|
Reply
|
Nick
|
5/16/2010 5:56:51 PM
|
|
Seebs writes:
> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>> #define ESIZE 0x40000000uLL
>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>> #undef ESIZE
>
> Let me guess. Someone told you to define symbolic names for constants,
> right?
Yes, of course!
> This is not how you do it.
>
> 1. If you're going to define a constant, define it and leave it
> defined.
This is very bad practise. Localizing the #define is good for the same
reason that using local instead of global variables.
> 2. Don't use a name starting with a capital E followed by
> another capital letter, those are reserved for errno values. 3. If
> you're only using it once, don't feel like you have to #define it.
I would say that the #define is a self-documenting form of code, no?
> 4.
> Don't use "uLL" on a constant that's unambiguously within the size range
> of an ordinary signed long. You don't need any qualifier at all,
> although in theory a system could exist where that value is too big for
> size_t, in which case you'd be allocating 0 bytes.
I think this is wrong. With no "qualifier", the number will be
interpreted as an int and undergo default promotions. Because int may be
16 bits this could overflow.
> 5. Don't get so
> clever. Try:
>
> if (!emrgcy) {
> emrgcy = malloc(0x40000000);
> if (!emrgcy) {
> fprintf(stderr, "Uh-oh, failed to allocate spare
memory.\n");
> }
> }
I think this is the same logic but with longer and more complicated
code...
> 6. Don't allocate a GIGABYTE of memory like that -- all this does is
> massively increase the chance of catastrophic failure, as a likely
> response from a system which overcommits is to determine that your
> process allocated a TON of memory, doesn't use most of it, and is
> probably the best candidate for being killed out of hand. A megabyte or
> two, sure, I guess.
I choose a gigabyte because most allocations will be less than 1 GB... if
you only allow a few MB there could easily be a late allocation for more
that the emergency memory can't satisfy.
> 7. Actually, even then, this is just cargo cult
> stuff.
I don't know what cargo cult stuff is.
>> #define STRGFY(x) #x
>> STRGFY(__LINE__)
>> #undef STRGFY
>
> Again, don't do this. With extremely rare exceptions, you should NEVER
> be using #undef on something you just defined.
By the same argument, with extremely rare exceptions you should NEVER be
using block-scope variables.
> Reading your code, I get the impression you're trying to aim for some
> kind of code density, with cool tricks you've seen all thrown in
> together to make the code look more impressive.
I like using advanced C features, yes. It makes programming fun. I think
all good programmers will be able to understand my code.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/16/2010 8:40:36 PM
|
|
On Sun, 16 May 2010 20:40:36 +0000 (UTC), sandeep <nospam@nospam.com>
wrote:
>I like using advanced C features, yes. It makes programming fun. I think
>all good programmers will be able to understand my code.
Classic misconception.
|
|
0
|
|
|
|
Reply
|
Geoff
|
5/16/2010 8:43:26 PM
|
|
On 2010-05-16, sandeep <nospam@nospam.com> wrote:
> Seebs writes:
>> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>>> #define ESIZE 0x40000000uLL
>>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>>> #undef ESIZE
>> Let me guess. Someone told you to define symbolic names for constants,
>> right?
> Yes, of course!
I partially agree with removing so called "magic numbers" from the code in
favor of more descriptive names where it makes sense to do so.
>> 5. Don't get so
>> clever. Try:
>>
>> if (!emrgcy) {
>> emrgcy = malloc(0x40000000);
>> if (!emrgcy) {
>> fprintf(stderr, "Uh-oh, failed to allocate spare
> memory.\n");
>> }
>> }
>
> I think this is the same logic but with longer and more complicated
> code...
The logic may be the same; however, Seebs version is much more intuitive
then yours. The extra spacing and indentation provides visual clues that
make it easy to pick up what is going on and is consistant with other
properly indented code. I have seen *many* bugs created when using "?:" to
obfuscate the code that could clearly be seen using the normal if/else
syntax.
>> 6. Don't allocate a GIGABYTE of memory like that -- all this does is
>> massively increase the chance of catastrophic failure, as a likely
>> response from a system which overcommits is to determine that your
>> process allocated a TON of memory, doesn't use most of it, and is
>> probably the best candidate for being killed out of hand. A megabyte or
>> two, sure, I guess.
> I choose a gigabyte because most allocations will be less than 1 GB... if
> you only allow a few MB there could easily be a late allocation for more
> that the emergency memory can't satisfy.
I work with many systems that *have* less then a gigabyte of memory. How
can you be sure that nobody will ever try to run your code on such a
system? How large is your memory? What if somebody runs multiple
instances of your program? You have already been told how this can
backfire on operating systems designed to overcommit.
The bottom line is that your "emergency memory" is a very bad idea. There
are much better ways of handling low memory conditions.
>> Reading your code, I get the impression you're trying to aim for some
>> kind of code density, with cool tricks you've seen all thrown in
>> together to make the code look more impressive.
>
> I like using advanced C features, yes. It makes programming fun. I think
> all good programmers will be able to understand my code.
Good code isn't clever. Good code is clear for whoever has to read an
maintain it. You may think that showing of your 1337 skilz is fun. The
guy who has to clean up the bugs you have written because you obfuscated
your code isn't going to have much fun.
The end effect is that you are creating write-once code. Write once
because it is designed to be thrown away and re-written rather then making
any effort to maintain such poorly written code.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/16/2010 9:09:58 PM
|
|
On May 16, 3:21=A0am, "Dennis \(Icarus\)" <nojunkm...@ever.invalid>
wrote:
> memset actually showed as a bottleneck in one of programs I had to debug.
> It was being called 2,000,000,000 times or so.
>
> Converting from memset to a for loop writing integers helped a bit, but t=
he
> real fix was to correct the logic error so that it wasn't called that man=
y
> times.
That's interesting. I found that I got substantial speed improvements
by replacing a loop filling an array with integers with a call to
memset.
|
|
0
|
|
|
|
Reply
|
christian
|
5/16/2010 9:11:38 PM
|
|
On 2010-05-16, sandeep <nospam@nospam.com> wrote:
> Seebs writes:
>> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>>> #define ESIZE 0x40000000uLL
>>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>>> #undef ESIZE
>> Let me guess. Someone told you to define symbolic names for constants,
>> right?
> Yes, of course!
I figured.
>> This is not how you do it.
>>
>> 1. If you're going to define a constant, define it and leave it
>> defined.
> This is very bad practise. Localizing the #define is good for the same
> reason that using local instead of global variables.
Wrong.
The entire POINT of a symbolic constant is to have every usage be the same!
With your system, it is quite easy to imagine:
#define SIZE 1024
v = malloc(size);
#undef SIZE
...
#define SIZE 2048
memcpy(v, src, SIZE);
#undef SIZE
Might I suggest that, since you are clearly at the very beginning newbie
level, you not go around telling people that something is "bad practice"
when they warn you that you're doing something very dangerous?
>> 3. If
>> you're only using it once, don't feel like you have to #define it.
> I would say that the #define is a self-documenting form of code, no?
Not as you used it.
> I think this is wrong. With no "qualifier", the number will be
> interpreted as an int and undergo default promotions. Because int may be
> 16 bits this could overflow.
Again, please consider the *remote* possibility that, with twenty years of
active experience using C, I might have a TINY bit of information.
Constants do not work that way. If a constant is too big to be an int,
it is AUTOMATICALLY made into a larger type, if needed. The constant
in question cannot overflow.
Furthermore, the rules for constants starting with 0x are different.
Furthermore, even if you needed to modify the type, "L" would be sufficient.
>> 5. Don't get so
>> clever. Try:
>>
>> if (!emrgcy) {
>> emrgcy = malloc(0x40000000);
>> if (!emrgcy) {
>> fprintf(stderr, "Uh-oh, failed to allocate spare
> memory.\n");
>> }
>> }
> I think this is the same logic but with longer and more complicated
> code...
No. It is the same logic (or close to it) with longer and SIMPLER code.
Always write something as simply as you can when first writing something.
If you need to do something fancy, do it after you've got the simple
version working.
> I choose a gigabyte because most allocations will be less than 1 GB...
I understood that. However, what you've done is cause many systems to
be unable to allocate that memory at all, and many more to fail
catastrophically because you allocated a gigabyte of memory you didn't
need, when they would have been fine without it.
> if
> you only allow a few MB there could easily be a late allocation for more
> that the emergency memory can't satisfy.
And the emergency memory *does not work* on many systems. At all. I have
used many systems on which your emergency memory would fail completely, or
cause the program to get killed preemptively by the OS. I have used many
on which freeing that "emergency" memory would have NO EFFECT AT ALL on
any allocation of under about 128MB, because the implementation treats
large allocations differently from small allocations.
The entire idea is just plain wrong. You have formed some kind of crazy
theory about "how malloc works", and that theory is incorrect, leading you
to do stuff that makes no sense.
This is like driving a car and making a point of manually triggering the
airbags before you even start the car, so that you'll be safe in the event
of a crash.
>> 7. Actually, even then, this is just cargo cult
>> stuff.
> I don't know what cargo cult stuff is.
During WWII, various forces set up staging areas on islands in the Pacific,
some of which were inhabited. Some primitive cultures on the more isolated
islands were unable to comprehend why suddenly there were planes and food
and stuff. They didn't know how planes worked, or where the food came from.
When they were hungry, they did their best to build things that looked sort
of like airstrips, because that would make more "cargo" come.
What you are doing is like this. You don't understand malloc, you've seen
something sort of like this somewhere, and you're imitating it without
understanding what it was, how it worked, or when it would (or wouldn't)
be useful.
Don't do that.
>>> #define STRGFY(x) #x
>>> STRGFY(__LINE__)
>> #undef STRGFY
>> Again, don't do this. With extremely rare exceptions, you should NEVER
>> be using #undef on something you just defined.
> By the same argument, with extremely rare exceptions you should NEVER be
> using block-scope variables.
No, not the same argument at all.
The preprocessor isn't scoped, and isn't supposed to be scoped. If you
are #defining something, it should be because you want to make sure that
any possible reference to it will get the same value.
>> Reading your code, I get the impression you're trying to aim for some
>> kind of code density, with cool tricks you've seen all thrown in
>> together to make the code look more impressive.
> I like using advanced C features, yes. It makes programming fun. I think
> all good programmers will be able to understand my code.
A couple of concerns.
1. Don't assume everyone will be a good programmer. You should write with
the intent that very inexperienced programmers will be able to understand
your code if at all possible. The fact is, someone will have to maintain it.
2. I understand it just fine, and it's bad, because you're trying to be
"clever".
I am a bit sympathetic to this, because I certainly did a bunch of crazy stuff
like this when I was first learning to program... But the best advice I ever
got was: "DON'T".
Having looked at that old code, and in a couple of cases tried to get it to
run on newer compilers, I am fully persuaded. Simple, clear, code is better.
The problem with using advanced features is that you have to know how to
use them well. Very good race drivers sometimes use the hand brake in a car
to control the car's behavior in unusual ways. This allows them to do
things that most of us could never do with a car. However, the solution
is not for me to, every time I pull up to a stop sign, use the hand brake
instead of the regular brakes. That would damage my car very severely,
very quickly.
If your skill at first aid extends about to applying band-aids, don't start
trying to do brain surgery.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 9:12:43 PM
|
|
On Sun, 16 May 2010 14:11:38 -0700 (PDT), "christian.bau"
<christian.bau@cbau.wanadoo.co.uk> wrote:
>On May 16, 3:21�am, "Dennis \(Icarus\)" <nojunkm...@ever.invalid>
>wrote:
>
>> memset actually showed as a bottleneck in one of programs I had to debug.
>> It was being called 2,000,000,000 times or so.
>>
>> Converting from memset to a for loop writing integers helped a bit, but the
>> real fix was to correct the logic error so that it wasn't called that many
>> times.
>
>That's interesting. I found that I got substantial speed improvements
>by replacing a loop filling an array with integers with a call to
>memset.
I don't think the problem was the choice of initialization method, the
problem was the initialization was called repeatedly, apparently for
no legitimate reason.
The analogy might be calling memset 2x10^9 times to initialize 1 byte
each time versus calling memset once to initialize 2x10^9 bytes. The
overhead makes all the difference.
|
|
0
|
|
|
|
Reply
|
Geoff
|
5/16/2010 9:18:36 PM
|
|
Lew Pitcher <lpitcher@teksavvy.com> writes:
[...]
> It is acceptable to have this utility function /log/ the error. It is
> not acceptable to have the utility function decide that the
> application /cannot recover/ from this error.
Another way to look at it is that the application decided, by calling
this particular utility function, that it could not recover from
a memory allocation failure. If it could, it should have called
some malloc() or other function that would permit recovery.
If allocating memory and aborting the program on failure is a
common operation, combining the two into a single utility function
makes sense.
(Figuring out how to recover from allocation failures makes even
more sense, but that can be non-trivial.)
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/16/2010 9:20:04 PM
|
|
On 5/16/2010 4:40 PM, sandeep wrote:
> Seebs writes:
>> On 2010-05-16, sandeep<nospam@nospam.com> wrote:
>>> #define ESIZE 0x40000000uLL
>>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>>> #undef ESIZE
>>
>> Let me guess. Someone told you to define symbolic names for constants,
>> right?
>
> Yes, of course!
>
>> This is not how you do it.
>>
>> 1. If you're going to define a constant, define it and leave it
>> defined.
>
> This is very bad practise. Localizing the #define is good for the same
> reason that using local instead of global variables.
There are few similarities between variables and macros, even
macros that are "manifest constants," so the criteria for what is
good or bad are dissimilar.
But, okay: Let's take your "localization" dictum as Truth, and
see where it leads us:
#define SIZE 0x40000000uLL
void *ptr = malloc(SIZE);
#undef SIZE
if (ptr == NULL) ...
... forty lines ...
#define SIZE 0x40000000uLL
char *buf = malloc(SIZE);
#undef SIZE
if (buf == NULL) ...
... one hundred lines ...
#define SIZE 0x40000000uLL
void *tmp = realloc(bigbuf, SIZE);
#undef SIZE
if (tmp == NULL) ...; else bigbuf = tmp;
... still more lines ...
Each definition and use of SIZE is now localized to its minimal scope.
But one day you decide that a gigabyte is the wrong amount, and want
to change to forty megabytes instead. Seebs makes a one-line change;
you're faced with three (or perhaps more) and the possibility of having
missed a few. Who's in better shape?
>> 4.
>> Don't use "uLL" on a constant that's unambiguously within the size range
>> of an ordinary signed long. You don't need any qualifier at all,
>> although in theory a system could exist where that value is too big for
>> size_t, in which case you'd be allocating 0 bytes.
>
> I think this is wrong. With no "qualifier", the number will be
> interpreted as an int and undergo default promotions. Because int may be
> 16 bits this could overflow.
You should re-read your C textbook or other reference, because
you are wrong about the treatment of literal constants in source code.
>> 7. Actually, even then, this is just cargo cult
>> stuff.
>
> I don't know what cargo cult stuff is.
<http://en.wikipedia.org/wiki/Cargo_cult_programming>
>>> #define STRGFY(x) #x
>>> STRGFY(__LINE__)
>>> #undef STRGFY
>>
>> Again, don't do this. With extremely rare exceptions, you should NEVER
>> be using #undef on something you just defined.
>
> By the same argument, with extremely rare exceptions you should NEVER be
> using block-scope variables.
Again, the dissimilarities outweigh the similarities, and the
claim that the same argument applies has little weight.
>> Reading your code, I get the impression you're trying to aim for some
>> kind of code density, with cool tricks you've seen all thrown in
>> together to make the code look more impressive.
>
> I like using advanced C features, yes. It makes programming fun. I think
> all good programmers will be able to understand my code.
In the first place, you're not using "advanced" features, just
unnecessarily convolutions of normal features. (I don't think C
even *has* any "advanced" features -- there are seldom-used areas,
particularly in corners of the library -- but "rare" and "advanced"
are not synonyms.)
In the second place, you might do well to consider the words of
Brian Kernighan (the "K" of "K&R," in case you don't recognize the
name): "Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it." Yes, he was
probably being a bit facetious, but I think he has a point worth
pondering -- especially by someone who's already shown that he's
writing beyond the limits of his own cleverness.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/16/2010 9:21:14 PM
|
|
On Sun, 16 May 2010 14:20:04 -0700, Keith Thompson <kst-u@mib.org>
wrote:
>(Figuring out how to recover from allocation failures makes even
>more sense, but that can be non-trivial.)
Which is another way of saying if a supposedly safe malloc could be
written it would never need an error return.
A safe malloc function could return a pointer to and a size of the
actually allocated space and let the program decide whether to
continue with the smaller allocation or emit an error.
|
|
0
|
|
|
|
Reply
|
Geoff
|
5/16/2010 9:37:33 PM
|
|
sandeep <nospam@nospam.com> writes:
> Seebs writes:
>> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>>> #define ESIZE 0x40000000uLL
>>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>>> #undef ESIZE
>>
>> Let me guess. Someone told you to define symbolic names for constants,
>> right?
>
> Yes, of course!
>
>> This is not how you do it.
>>
>> 1. If you're going to define a constant, define it and leave it
>> defined.
>
> This is very bad practise. Localizing the #define is good for the same
> reason that using local instead of global variables.
So use a local variable.
I don't think I've ever seen C code in which a macro is #define'd, then
used, then immediately #undef'ed. I can see the argument in favor of
doing it, but in practice most macros are global anyway.
But again, there's no good reason to use a macro rather than a
constant object declaration:
const size_t esize = 0x40000000;
If you want it scoped locally, use a feature that lets the compiler
handle it for you.
Even your #define/#undef pair doesn't do the same thing as a local
declaration; it clobbers any previous definition.
[...]
>> 4.
>> Don't use "uLL" on a constant that's unambiguously within the size range
>> of an ordinary signed long. You don't need any qualifier at all,
>> although in theory a system could exist where that value is too big for
>> size_t, in which case you'd be allocating 0 bytes.
>
> I think this is wrong. With no "qualifier", the number will be
> interpreted as an int and undergo default promotions. Because int may be
> 16 bits this could overflow.
Nope. An unqualified integer constant, unless it exceeds UINTMAX_MAX (I
think that's the right name) is always of some type into which its value
will fit.
[...]
>> 7. Actually, even then, this is just cargo cult
>> stuff.
>
> I don't know what cargo cult stuff is.
Google it.
Basically, "cargo cult programming" means programming by rote
without understanding what you're doing. (The roots of the term
are fascinating, but not really relevant.)
[...]
>> Reading your code, I get the impression you're trying to aim for some
>> kind of code density, with cool tricks you've seen all thrown in
>> together to make the code look more impressive.
>
> I like using advanced C features, yes. It makes programming fun. I think
> all good programmers will be able to understand my code.
A lot of good programmers have been reading your code. Not being able
to understand it isn't the problem.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/16/2010 9:38:07 PM
|
|
On 2010-05-16, Keith Thompson <kst-u@mib.org> wrote:
> I don't think I've ever seen C code in which a macro is #define'd, then
> used, then immediately #undef'ed. I can see the argument in favor of
> doing it, but in practice most macros are global anyway.
I've done it very rarely, in cases where I used a macro, say, ten or fifteen
times, because it was initialization code of some sort.
Something like:
#define FOO(x) { x, #x }
struct { int value; char *name; } lookup[] = {
FOO(VAL_MIN),
FOO(VAL_ONE),
FOO(VAL_TWO),
{ 0, 0 }
}
#undef FOO
> Basically, "cargo cult programming" means programming by rote
> without understanding what you're doing. (The roots of the term
> are fascinating, but not really relevant.)
I agree that they're not very relevant, but the image is so evocative I
like to explain it. :)
> A lot of good programmers have been reading your code. Not being able
> to understand it isn't the problem.
In a way, though, it is.
There are a couple of questions you must ask about any piece of code you
wish to truly understand:
1. What does it do?
2. What was it intended to do?
3. Why does it need to do that?
4. Why is it doing that in this particular way?
The problem here is not that people can't figure out the answer to question
#1 -- surely, most of us can read this code and see what it's doing. The
problem is that if you write something in an unusual way, it leads the reader
to wonder why you did it that way rather than in a more straightforward
way.
Consider:
void
copystring(char *to, char *from) {
void *t = (void *) to;
void *f = (void *) from;
union { int zero; char c; } u;
u.zero = 0L;
u.c = 0;
do {
memcpy(t, f, 1);
t = (void *) ((char *) t) + 1;
f = (void *) ((char *) f) + 1;
} while (memcmp(f, &u.c, 1));
return;
free(&u);
}
An experienced programmer can probably say that this (unless I screwed it
up, no promises) copies the contents of "from" to "to" up to but not
including the first NUL byte encountered in "from".
But that doesn't lead to *understanding* the code. Why do we use t and f
instead of to and from? Why are we using memcmp to see whether the next
byte is 0x0? Why is u a union? Why is u.zero initialized before u.c, and
why was it initialized at all? Why does the code contain a free of a
non-allocated address, but put this after a return statement so it can't
be executed?
All of these questions suggest that one of two things is at issue:
1. The person who wrote this code had a very primitive, at best,
understanding of pointers in C. The code is probably unreliable.
2. There is something extremely unusual going on which you need to
know about.
In fact, it's really:
3. It is a contrived example.
Sandeep's code reads like a contrived example of some sort -- it acts as
though something special is going on, when nothing can be found to justify
the choices made, and that is a big red flag usually.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 9:46:26 PM
|
|
Geoff <geoff@invalid.invalid> writes:
> On Sun, 16 May 2010 14:20:04 -0700, Keith Thompson <kst-u@mib.org>
> wrote:
>>(Figuring out how to recover from allocation failures makes even
>>more sense, but that can be non-trivial.)
>
> Which is another way of saying if a supposedly safe malloc could be
> written it would never need an error return.
>
> A safe malloc function could return a pointer to and a size of the
> actually allocated space and let the program decide whether to
> continue with the smaller allocation or emit an error.
No, that's not what I was saying at all. It didn't even occur to
me that the response to a failure to allocate a certain amount
of memory might be to allocate some smaller amount of memory.
I'm sure that approach makes sense in some cases (for example,
if you're allocating an in-memory file buffer, a smaller buffer is
better than none at all). And I can imagine a highly specialized
allocation function that allocates as much as it can up to what
was requested -- but I wouldn't call such a function a "safe malloc".
For most allocations, if you can't get what you asked for, that's
a failure, and you either need to fall back to some other approach
or prepare to shut down. If I'm trying to allocate a tree node,
half a node does me no good.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/16/2010 9:48:22 PM
|
|
Seebs <usenet-nospam@seebs.net> writes:
<snip>
[About pre-allocating memory for a low-memory emergency.]
> The entire idea is just plain wrong. You have formed some kind of crazy
> theory about "how malloc works", and that theory is incorrect, leading you
> to do stuff that makes no sense.
This "crazy theory" might have been formed, in part, by reading the
standard. A straight-forward interpretation of 7.20.3.2 paragraph 2:
"The free function causes the space pointed to by ptr to be
deallocated, that is, made available for further allocation."
suggests that what the OP was doing would work.
You and I may be world-weary enough to know that such wording is never
straight-forward, and implementations are not always conforming, but
perhaps the OP took this (or some paraphrase of it in a textbook) at
face value.
> This is like driving a car and making a point of manually triggering the
> airbags before you even start the car, so that you'll be safe in the event
> of a crash.
Or it's like reading in the car manual "if you trigger the airbags early
they will keep you safe in future crashes" and believing it.
<snip>
--
Ben.
|
|
0
|
|
|
|
Reply
|
Ben
|
5/16/2010 10:45:47 PM
|
|
On 2010-05-16, Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
> Seebs <usenet-nospam@seebs.net> writes:
><snip>
> [About pre-allocating memory for a low-memory emergency.]
>> The entire idea is just plain wrong. You have formed some kind of crazy
>> theory about "how malloc works", and that theory is incorrect, leading you
>> to do stuff that makes no sense.
> This "crazy theory" might have been formed, in part, by reading the
> standard. A straight-forward interpretation of 7.20.3.2 paragraph 2:
> "The free function causes the space pointed to by ptr to be
> deallocated, that is, made available for further allocation."
> suggests that what the OP was doing would work.
Ahh, I see. Yes, that would imply that, except of course that "available"
doesn't mean "usable". But I do agree, it might seem that way at first.
>> This is like driving a car and making a point of manually triggering the
>> airbags before you even start the car, so that you'll be safe in the event
>> of a crash.
> Or it's like reading in the car manual "if you trigger the airbags early
> they will keep you safe in future crashes" and believing it.
I think possibly reading "The air bags ensure safety in a crash", and
concluding that you should trigger them to be sure you're safe.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/16/2010 10:46:50 PM
|
|
"Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
> "Phil Carmody" <thefatphil_demunged@yahoo.co.uk> wrote in message
> news:87mxw0drt4.fsf@kilospaz.fatphil.org...
>> "Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
>>> "Seebs" <usenet-nospam@seebs.net> wrote in message
>>> news:slrnhuu50r.4tq.usenet-nospam@guild.seebs.net...
>>>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>>>> Obviously for efficiency! malloc may be called many times in the course
>>>>> of a program.
>>>>
>>>> Please stop trying to outsmart the compiler.
>>>>
>>>> The "cost" of using a function instead of a macro is likely to be
>>>> so small
>>>> that you can't even measure it. If there is even a cost at all,
>>>> which there
>>>> may not be.
>>>
>>> memset actually showed as a bottleneck in one of programs I had to debug.
>>> It was being called 2,000,000,000 times or so.
>>>
>>> Converting from memset to a for loop writing integers helped a bit,
>>> but the real fix was to correct the logic error so that it wasn't
>>> called that many times.
>>> the memset was part of initializing an object, which was being
>>> constructed during processing, but was only needed a fraction of the
>>> time.....
>>
>> An object being 'constructed'? Are you sure you were using C?
>
> I know I was using C++. I doubt C would be any different in this respect.
> :-)
All that stuff that's just done for you in C++ so you don't have to
worry about may be the stuff that you don't want to happen. Whilst
it's certainly possible, I think you'd have to go a little further
out of your way to get such behaviour in C, you would have to
deliberately design it in more.
However, thanks for sharing your C++ woes on comp.lang.c.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 5:47:23 AM
|
|
sandeep <nospam@nospam.com> writes:
>> 4.
>> Don't use "uLL" on a constant that's unambiguously within the size range
>> of an ordinary signed long. You don't need any qualifier at all,
>> although in theory a system could exist where that value is too big for
>> size_t, in which case you'd be allocating 0 bytes.
>
> I think this is wrong. With no "qualifier", the number will be
> interpreted as an int and undergo default promotions. Because int may be
> 16 bits this could overflow.
On the other hand, you could unlearn all the nonsense you've learnt
and actually learn C properly this time. Next time pay more attention
to:
[#5] The type of an integer constant is the first of the
corresponding list in which its value can be represented.
>> 5. Don't get so
>> clever. Try:
>>
>> if (!emrgcy) {
>> emrgcy = malloc(0x40000000);
>> if (!emrgcy) {
>> fprintf(stderr, "Uh-oh, failed to allocate spare
> memory.\n");
>> }
>> }
>
> I think this is the same logic but with longer and more complicated
> code...
Nope. It's significantly easier to read code. Count the number of
references to the emrgcy variable, for example. You had 6. Seebs
has 4. 2 of yours are demonstrably completely unnecessary, and
are just warts in the code.
>> 7. Actually, even then, this is just cargo cult
>> stuff.
>
> I don't know what cargo cult stuff is.
It's stuff from somewhere external that is adopted without proper
understanding of what it is, and what it does, and therefore it is
almost always misused.
> I like using advanced C features, yes. It makes programming fun. I think
> all good programmers will be able to understand my code.
You are the classic example of "a little knowledge is a dangerous thing".
I pity the maintenance programmer who ever has to maintain your
unreadable illogical code. Which I guess is self-pity - I have
seen plenty of such code written by just-out-of-university or
student workers that was as bad, and been forced to fix it (at
the end of a pointy paycheque, of course, I'd not do that for
fun).
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 6:14:14 AM
|
|
Tim Harig <usernet@ilthio.net> writes:
> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>> Seebs writes:
>>> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>>>> #define ESIZE 0x40000000uLL
>>>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>>>> #undef ESIZE
>>> Let me guess. Someone told you to define symbolic names for constants,
>>> right?
>> Yes, of course!
>
> I partially agree with removing so called "magic numbers" from the code in
> favor of more descriptive names where it makes sense to do so.
>
>>> 5. Don't get so
>>> clever. Try:
>>>
>>> if (!emrgcy) {
>>> emrgcy = malloc(0x40000000);
>>> if (!emrgcy) {
>>> fprintf(stderr, "Uh-oh, failed to allocate spare
>> memory.\n");
>>> }
>>> }
>>
>> I think this is the same logic but with longer and more complicated
>> code...
>
> The logic may be the same; however, Seebs version is much more intuitive
> then yours. The extra spacing and indentation provides visual clues that
> make it easy to pick up what is going on and is consistant with other
> properly indented code. I have seen *many* bugs created when using "?:" to
> obfuscate the code that could clearly be seen using the normal if/else
> syntax.
The main problem was that he wanted a "?:=" version of gcc's "?:"
operator. If he'd have actually wanted "? :", it wouldn't have been
so bad to use "? :" (I like dense code, I don't like redundant code).
And as an aside, I think ?: should be standardised.
(And please overlook the syntactical sloppiness, I didn't want to have
to explain ?:)
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 6:19:01 AM
|
|
Seebs wrote:
<snip>
> The problem with using advanced features is that you have to know how to
> use them well. Very good race drivers sometimes use the hand brake in a car
> to control the car's behavior in unusual ways. This allows them to do
> things that most of us could never do with a car. However, the solution
> is not for me to, every time I pull up to a stop sign, use the hand brake
> instead of the regular brakes. That would damage my car very severely,
> very quickly.
In the UK, at a STOP sign (black text in red triangle on white
background), you are *required* to use your hand-brake, since you must
bring the vehicle to a complete and safe stop. (But of course you don't
use it /instead/ of the regular brakes - you use the footbrake to stop
the car, and then the handbrake to keep it stopped.)
> If your skill at first aid extends about to applying band-aids, don't start
> trying to do brain surgery.
Perhaps sandeep will be persuaded by this argument: if you treat your
users like idiots, smart people will soon stop using your program.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 6:30:18 AM
|
|
Phil Carmody wrote:
<snip>
>
> An object being 'constructed'? Are you sure you were using C?
I construct objects very often in C. What's the problem?
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 6:38:52 AM
|
|
In article <UYKdnZBMGKz-f23WnZ2dnUVZ8iOdnZ2d@bt.com>,
Richard Heathfield <rjh@see.sig.invalid> wrote:
> Seebs wrote:
>
> <snip>
>
> > The problem with using advanced features is that you have to know how to
> > use them well. Very good race drivers sometimes use the hand brake in a car
> > to control the car's behavior in unusual ways. This allows them to do
> > things that most of us could never do with a car. However, the solution
> > is not for me to, every time I pull up to a stop sign, use the hand brake
> > instead of the regular brakes. That would damage my car very severely,
> > very quickly.
>
> In the UK, at a STOP sign (black text in red triangle on white
> background), you are *required* to use your hand-brake, since you must
> bring the vehicle to a complete and safe stop. (But of course you don't
> use it /instead/ of the regular brakes - you use the footbrake to stop
> the car, and then the handbrake to keep it stopped.)
The difference with the US is that in the UK we only use Stop signs
where there is a junction with some danger to it (such as a steep slope
to the junction, or hedges). So they are quite rare, since with most
junctions a Give Way is quite sufficient.
The US has almost no Give Way signs and lots of Stop signs where they
are not needed. Like, you're going along a road and there is a Stop sign
serving no purpose whatever (not at a junction, not by a school, even).
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/17/2010 9:26:06 AM
|
|
"Tim Harig" <usernet@ilthio.net> ha scritto nel messaggio > Note that minimizing
code density doesn't create a smaller or faster
> binary. A concisely written ?: operator on a single line generates the
> same code as the equivilant if/else operators spread across multiple lines.
> The if/else version is almost always easier to read. "?:"'s are almost
> always the sign of an amateur programmer trying to show off their "l337
> skilz." Seasoned programmers have learned to avoid them.
programming in about reduce text size
it is to say to the computer many little precise thing all togheter
if "?:" reduce the size of the prog at last of one char: it is ok
|
|
0
|
|
|
|
Reply
|
io_x
|
5/17/2010 10:12:40 AM
|
|
On Mon, 17 May 2010 12:16:58 +0200, "io_x" <a@b.c.invalid> wrote:
>
>"Tim Harig" <usernet@ilthio.net> ha scritto nel messaggio > Note that minimizing
>code density doesn't create a smaller or faster
>> binary. A concisely written ?: operator on a single line generates the
>> same code as the equivilant if/else operators spread across multiple lines.
>> The if/else version is almost always easier to read. "?:"'s are almost
>> always the sign of an amateur programmer trying to show off their "l337
>> skilz." Seasoned programmers have learned to avoid them.
>
>programming in about reduce text size
>it is to say to the computer many little precise thing all togheter
Programming is about correctness of the solution to a problem, not
about terseness. To obtain correctness and ease of maintenance the
programmer should write it as though someone unfamiliar with the
solution will be maintaining the code. The compiler will optimize the
code. Yes, there are occasions when the solution has bottlenecks, but
these are conceptual problems with the implementation of the solution,
not the size of the code.
>
>if "?:" reduce the size of the prog at last of one char: it is ok
>
Simply false. As Tim stated above, if/else generates essentially the
same machine code as ?: yet the latter is harder to read and maintain.
|
|
0
|
|
|
|
Reply
|
Geoff
|
5/17/2010 11:19:49 AM
|
|
Keith Thompson <kst-u@mib.org> wrote:
> Lew Pitcher <lpitcher@teksavvy.com> writes:
> [...]
> > It is acceptable to have this utility function /log/ the error. It is
> > not acceptable to have the utility function decide that the
> > application /cannot recover/ from this error.
>
> Another way to look at it is that the application decided, by calling
> this particular utility function, that it could not recover from
> a memory allocation failure. If it could, it should have called
> some malloc() or other function that would permit recovery.
In theory, yes. In practice, when I've seen such functions used, either
the code is throwaway or command line utility code, or the programmer
has _assumed_, not decided, that that he could not recover from malloc()
failures. In the latter case, the programmer is almost always wrong. (In
the first ones, the decision is not so much "could not" as "considered
it too much bother", with, for that size program, is more ofter
justifiable.)
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/17/2010 12:47:45 PM
|
|
Tim Streater wrote:
> In article <UYKdnZBMGKz-f23WnZ2dnUVZ8iOdnZ2d@bt.com>,
> Richard Heathfield <rjh@see.sig.invalid> wrote:
>
>> Seebs wrote:
>>
>> <snip>
>>
>>> The problem with using advanced features is that you have to know how to
>>> use them well. Very good race drivers sometimes use the hand brake in a car
>>> to control the car's behavior in unusual ways. This allows them to do
>>> things that most of us could never do with a car. However, the solution
>>> is not for me to, every time I pull up to a stop sign, use the hand brake
>>> instead of the regular brakes. That would damage my car very severely,
>>> very quickly.
>> In the UK, at a STOP sign (black text in red triangle on white
>> background), you are *required* to use your hand-brake, since you must
>> bring the vehicle to a complete and safe stop. (But of course you don't
>> use it /instead/ of the regular brakes - you use the footbrake to stop
>> the car, and then the handbrake to keep it stopped.)
>
> The difference with the US is that in the UK we only use Stop signs
> where there is a junction with some danger to it (such as a steep slope
> to the junction, or hedges). So they are quite rare, since with most
> junctions a Give Way is quite sufficient.
Actually, they are /so/ rare in the UK that I misreported what they look
like! (They are actually octagonal, red with a thin white border, and
white text.) I don't recall ever coming across one that was there for
spurious reasons.
> The US has almost no Give Way signs and lots of Stop signs where they
> are not needed. Like, you're going along a road and there is a Stop sign
> serving no purpose whatever (not at a junction, not by a school, even).
Well, that's your problem, not mine. :-)
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 4:23:52 PM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
[...]
> The main problem was that he wanted a "?:=" version of gcc's "?:"
> operator. If he'd have actually wanted "? :", it wouldn't have been
> so bad to use "? :" (I like dense code, I don't like redundant code).
>
> And as an aside, I think ?: should be standardised.
>
> (And please overlook the syntactical sloppiness, I didn't want to have
> to explain ?:)
Since you're talking about a gcc-specific extension (and since C already
has a ?: operator), perhaps explaining it would have been a good idea.
gcc allows the middle operand of the conditional operator to be
omitted. ``x ? : y'' is equivalent to ``x ? x : y'' except that x
is evaluated only once. It yields the value of x if x is non-zero,
otherwise it yields the value of y.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 4:24:53 PM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> Phil Carmody wrote:
> <snip>
>>
>> An object being 'constructed'? Are you sure you were using C?
>
> I construct objects very often in C. What's the problem?
Who said there was a problem?
The word "constructed" is often (but by no means always) C++-specific
jargon. And it turned out the previous poster was using C++.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 4:26:04 PM
|
|
io_x wrote:
> "Tim Harig" <usernet@ilthio.net> ha scritto nel messaggio > Note that minimizing
> code density doesn't create a smaller or faster
>> binary. A concisely written ?: operator on a single line generates the
>> same code as the equivilant if/else operators spread across multiple lines.
>> The if/else version is almost always easier to read. "?:"'s are almost
>> always the sign of an amateur programmer trying to show off their "l337
>> skilz." Seasoned programmers have learned to avoid them.
>
> programming in about reduce text size
That's an interesting perspective, but not one that would keep you
employed for very long. Other than as an interesting intellectual
exercise (or, perhaps, as an IOCCC entry), a program that is optimised
for source code length is of no use whatsoever. Programs should be
readable, and readability *requires* redundancy.
> it is to say to the computer many little precise thing all togheter
>
> if "?:" reduce the size of the prog at last of one char: it is ok
Not if it is at the expense (and I do mean *expense*) of a reduction in
readability.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 4:27:59 PM
|
|
On 2010-05-17, Tim Streater <timstreater@waitrose.com> wrote:
> The difference with the US is that in the UK we only use Stop signs
> where there is a junction with some danger to it (such as a steep slope
> to the junction, or hedges). So they are quite rare, since with most
> junctions a Give Way is quite sufficient.
Ahh! That makes sense. Ours are used any time you're expected to come
to a stop, but there's no expectation of using the hand brake -- just
slowing down enough that you're clearly not-in-motion.
> The US has almost no Give Way signs and lots of Stop signs where they
> are not needed. Like, you're going along a road and there is a Stop sign
> serving no purpose whatever (not at a junction, not by a school, even).
I have never, ever, seen such a thing. I have only seen Stop signs at
intersections of some sort.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 4:28:22 PM
|
|
Keith Thompson wrote:
> Richard Heathfield <rjh@see.sig.invalid> writes:
>> Phil Carmody wrote:
>> <snip>
>>> An object being 'constructed'? Are you sure you were using C?
>> I construct objects very often in C. What's the problem?
>
> Who said there was a problem?
Nobody explicitly said there was a problem, but I inferred, from what
Phil Carmody said, that he thought there was a problem.
>
> The word "constructed" is often (but by no means always) C++-specific
> jargon.
<shrug> Precisely so. The fact that the word has a specific meaning in
C++, however, is surely irrelevant in a C newsgroup?
> And it turned out the previous poster was using C++.
Well, that's his problem. :-)
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 4:34:38 PM
|
|
In article <slrnhv2s7g.ee7.usenet-nospam@guild.seebs.net>,
Seebs <usenet-nospam@seebs.net> wrote:
> On 2010-05-17, Tim Streater <timstreater@waitrose.com> wrote:
> > The difference with the US is that in the UK we only use Stop signs
> > where there is a junction with some danger to it (such as a steep slope
> > to the junction, or hedges). So they are quite rare, since with most
> > junctions a Give Way is quite sufficient.
>
> Ahh! That makes sense. Ours are used any time you're expected to come
> to a stop, but there's no expectation of using the hand brake -- just
> slowing down enough that you're clearly not-in-motion.
>
> > The US has almost no Give Way signs and lots of Stop signs where they
> > are not needed. Like, you're going along a road and there is a Stop sign
> > serving no purpose whatever (not at a junction, not by a school, even).
>
> I have never, ever, seen such a thing. I have only seen Stop signs at
> intersections of some sort.
Well, this *was* California.
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/17/2010 4:57:07 PM
|
|
In article <R-adnZyNE_Ef8GzWnZ2dnUVZ7sudnZ2d@bt.com>,
Richard Heathfield <rjh@see.sig.invalid> wrote:
> Tim Streater wrote:
> Actually, they are /so/ rare in the UK that I misreported what they look
> like! (They are actually octagonal, red with a thin white border, and
> white text.) I don't recall ever coming across one that was there for
> spurious reasons.
>
> > The US has almost no Give Way signs and lots of Stop signs where they
> > are not needed. Like, you're going along a road and there is a Stop sign
> > serving no purpose whatever (not at a junction, not by a school, even).
>
> Well, that's your problem, not mine. :-)
Well, it was when I lived there. Now, of course, it's not.
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/17/2010 4:57:55 PM
|
|
On 2010-05-17, Tim Streater <timstreater@waitrose.com> wrote:
> In article <slrnhv2s7g.ee7.usenet-nospam@guild.seebs.net>,
> Seebs <usenet-nospam@seebs.net> wrote:
>> I have never, ever, seen such a thing. I have only seen Stop signs at
>> intersections of some sort.
> Well, this *was* California.
Ahh.
Well, then we know what it was there for; safety! Kinetic energy is a
property of matter known to the State of California to increase the risk
of certain kinds of injury*.
-s
[*] In theory, the requirement is that any object which has kinetic energy
shall bear a sticker stating this, and no object which does not have kinetic
energy shall bear such a sticker, as it wouldn't do to alarm people unduly.
Most manufacturers have fine print somewhere in their product kinetic energy
declarations stating that the product shall constitute its own rest frame,
which is why you never see the stickers.
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 5:27:35 PM
|
|
On 2010-05-17, sandeep <nospam@nospam.com> wrote:
> Keith Thompson writes:
>> For most allocations, if you can't get what you asked for, that's a
>> failure, and you either need to fall back to some other approach or
>> prepare to shut down. If I'm trying to allocate a tree node, half a
>> node does me no good.
> I think you are not seeing the whole picture here. You should think of
> alternative situations too.
> Maybe you want the allocation to perform a fast sort routine. If you
> can't get enough memory, you can just fall back to a slower out-of-core
> sorting routine.
Oh, certainly.
But it's still more useful for the larger allocation to Just Fail, so you
can ask for a smaller one.
The point is, in many cases, you *can't* use a smaller allocation, so
automatically giving you a smaller allocation you may not be able to use
is pointless. Better to just succeed or fail, and let the caller decide
whether to try to allocate something else instead.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 7:05:35 PM
|
|
Keith Thompson writes:
> For most allocations, if you can't get what you asked for, that's a
> failure, and you either need to fall back to some other approach or
> prepare to shut down. If I'm trying to allocate a tree node, half a
> node does me no good.
I think you are not seeing the whole picture here. You should think of
alternative situations too.
Maybe you want the allocation to perform a fast sort routine. If you
can't get enough memory, you can just fall back to a slower out-of-core
sorting routine.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/17/2010 7:07:18 PM
|
|
On 2010-05-17, sandeep <nospam@nospam.com> wrote:
> Keith Thompson writes:
>> But again, there's no good reason to use a macro rather than a constant
>> object declaration:
>> const size_t esize = 0x40000000;
> Constant variables in C are not really constants. You couldn't define an
> array of size esize for example.
You could, in C99.
But it doesn't matter, because in the context in question (the size of a
block of memory), no constant was needed.
> Preprocessor defines are needed - but
> making them globally visible is not needed and is harmful when they are
> only used once.
Again, your proposed scheme is likely (over time, certain) to result in
multiple places where a value is "used only once" with conflicting values.
Fundamentally...
If it's used only once, and it doesn't need to be the same elsewhere, *it's
not a constant*. The point of a manifest constant is to be a thing which
is in some way always the right value.
You have never demonstrated an actual example of the alleged harm; millions
upon millions of lines of code have been written using values which are
defined before any other code is used, and remain defined throughout the
rest of the program, and which do not cause problems. You have not shown
a single case in which this could cause a problem.
More importantly, if it *did* cause a problem, that would be a GOOD thing!
The only way it could cause a problem would be if you had inconsistent
beliefs about what an alleged constant should be. If that happens, you WANT
it to cause a visible, obvious, failure, at the earliest possible
opportunity, so you can understand the error and fix it.
It really comes across as though you're just trying to be contrary with
anything anyone says about C.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 7:09:05 PM
|
|
Keith Thompson writes:
> But again, there's no good reason to use a macro rather than a constant
> object declaration:
>
> const size_t esize = 0x40000000;
Constant variables in C are not really constants. You couldn't define an
array of size esize for example. Preprocessor defines are needed - but
making them globally visible is not needed and is harmful when they are
only used once.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/17/2010 7:09:16 PM
|
|
sandeep <nospam@nospam.com> writes:
> Keith Thompson writes:
>> But again, there's no good reason to use a macro rather than a constant
>> object declaration:
>>
>> const size_t esize = 0x40000000;
>
> Constant variables in C are not really constants.
Before posting something like that, please consider the possibility
that I'm already perfectly well aware of it.
The real point is that "const" doesn't mean "constant"; it means
read-only.
> You couldn't define an
> array of size esize for example.
In this case, you probably couldn't because it's too big. But C99
does allow you to declare arrays with non-constant lengths (VLAs).
But yes, that's a valid concern if your code might be compiled with
a compiler that doesn't support VLAs.
In any case, you didn't use esize as an array length. Declaring it
as a const size_t object would have worked just fine for your code.
> Preprocessor defines are needed - but
> making them globally visible is not needed and is harmful when they are
> only used once.
Yes, preprocessor defines are sometimes needed. Enumeration
constants are often better, but they work only when you know the
values are within the range of type int (an unfortunate limitation
in the language). But this one *wasn't needed*, and your code
would have been clearer if you had defined a const object rather
than a macro.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 7:23:58 PM
|
|
sandeep <nospam@nospam.com> writes:
> Keith Thompson writes:
>> For most allocations, if you can't get what you asked for, that's a
>> failure, and you either need to fall back to some other approach or
>> prepare to shut down. If I'm trying to allocate a tree node, half a
>> node does me no good.
>
> I think you are not seeing the whole picture here. You should think of
> alternative situations too.
>
> Maybe you want the allocation to perform a fast sort routine. If you
> can't get enough memory, you can just fall back to a slower out-of-core
> sorting routine.
I think you didn't bother to read the rest of my article, in which I
specifically acknowledged the same point you just made.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 7:25:42 PM
|
|
On 5/17/2010 3:07 PM, sandeep wrote:
> Keith Thompson writes:
>> For most allocations, if you can't get what you asked for, that's a
>> failure, and you either need to fall back to some other approach or
>> prepare to shut down. If I'm trying to allocate a tree node, half a
>> node does me no good.
>
> I think you are not seeing the whole picture here. You should think of
> alternative situations too.
>
> Maybe you want the allocation to perform a fast sort routine. If you
> can't get enough memory, you can just fall back to a slower out-of-core
> sorting routine.
What part of "fall back to some other approach" are you
having trouble understanding?
Besides, this whole thread started with your suggestion
that there's no reason to check the value of malloc() for "small"
requests. In the scenario you describe, how could you choose
between algorithms without making the check? I know of one way,
but it's pretty silly:
Type *ptr = malloc(SMALL_SIZE);
free(ptr);
do_slow_sort();
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/17/2010 8:01:25 PM
|
|
In article <hss7ar$1ri$1@news.eternal-september.org>, esosman@ieee-dot-
org.invalid says...
>
> On 5/17/2010 3:07 PM, sandeep wrote:
> > Keith Thompson writes:
> >> For most allocations, if you can't get what you asked for, that's a
> >> failure, and you either need to fall back to some other approach or
> >> prepare to shut down. If I'm trying to allocate a tree node, half a
> >> node does me no good.
> >
> > I think you are not seeing the whole picture here. You should think of
> > alternative situations too.
> >
> > Maybe you want the allocation to perform a fast sort routine. If you
> > can't get enough memory, you can just fall back to a slower out-of-core
> > sorting routine.
>
> What part of "fall back to some other approach" are you
> having trouble understanding?
>
> Besides, this whole thread started with your suggestion
> that there's no reason to check the value of malloc() for "small"
> requests. In the scenario you describe, how could you choose
> between algorithms without making the check? I know of one way,
> but it's pretty silly:
>
> Type *ptr = malloc(SMALL_SIZE);
> free(ptr);
> do_slow_sort();
If a component is to be considered reliable, then the return status of
malloc() must be checked.
Any other assertion is ludicrous.
There are a few functions in the C library where we normally don't need
to analyze the return. An example would be puts(). If puts() fails,
exactly what are we going to do about it?
But for things like fopen(), fwrite(), malloc(), etc. that rely upon
resource availability, user rights, etc. it is sloppy, lazy, and stupid
not to examine the return code.
|
|
0
|
|
|
|
Reply
|
Dann
|
5/17/2010 8:12:22 PM
|
|
Eric Sosman writes:
> Besides, this whole thread started with your suggestion
> that there's no reason to check the value of malloc() for "small"
> requests. In the scenario you describe, how could you choose between
> algorithms without making the check? I know of one way, but it's pretty
> silly:
>
> Type *ptr = malloc(SMALL_SIZE);
> free(ptr);
> do_slow_sort();
I'm sorry you want to make this silly instead of a serious discussion.
But anyway you are wrong: if malloc returns a NUL pointer then free may
blow up if it tries to dereference its argument.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/17/2010 8:27:41 PM
|
|
In article <hss8rt$8ma$1@speranza.aioe.org>, nospam@nospam.com says...
>
> Eric Sosman writes:
> > Besides, this whole thread started with your suggestion
> > that there's no reason to check the value of malloc() for "small"
> > requests. In the scenario you describe, how could you choose between
> > algorithms without making the check? I know of one way, but it's pretty
> > silly:
> >
> > Type *ptr = malloc(SMALL_SIZE);
> > free(ptr);
> > do_slow_sort();
>
> I'm sorry you want to make this silly instead of a serious discussion.
>
> But anyway you are wrong: if malloc returns a NUL pointer then free may
> blow up if it tries to dereference its argument.
Supposing that you mean a NULL pointer, I suggest you read the ANSI/ISO
C standard. Consider the second sentence of #2 below:
7.20.3.2 The free function
Synopsis
1 #include <stdlib.h>
void free(void *ptr);
Description
2 The free function causes the space pointed to by ptr to be
deallocated, that is, made available for further allocation. If ptr is a
null pointer, no action occurs. Otherwise, if the argument does not
match a pointer earlier returned by the calloc, malloc, or realloc
function, or if the space has been deallocated by a call to free or
realloc, the behavior is undefined.
Returns
3 The free function returns no value.
|
|
0
|
|
|
|
Reply
|
Dann
|
5/17/2010 8:35:21 PM
|
|
In <hss8rt$8ma$1@speranza.aioe.org>, sandeep wrote:
> Eric Sosman writes:
>> Besides, this whole thread started with your suggestion
>> that there's no reason to check the value of malloc() for "small"
>> requests. In the scenario you describe, how could you choose
>> between
>> algorithms without making the check? I know of one way, but it's
>> pretty silly:
>>
>> Type *ptr = malloc(SMALL_SIZE);
>> free(ptr);
>> do_slow_sort();
>
> I'm sorry you want to make this silly instead of a serious
> discussion.
He's tried having a serious discussion with you, but it was a bit of a
one-way street.
> But anyway you are wrong: if malloc returns a NUL pointer then free
> may blow up if it tries to dereference its argument.
Rubbish. free(NULL) is well-defined. See 7.20.3.2(2).
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 8:47:53 PM
|
|
On 2010-05-17, sandeep <nospam@nospam.com> wrote:
> I'm sorry you want to make this silly instead of a serious discussion.
I think he's pointing out that you already made it silly.
> But anyway you are wrong: if malloc returns a NUL pointer then free may
> blow up if it tries to dereference its argument.
Incorrect. free(NULL) may not blow up. It's required to work by the
standard, and has been for about twenty years.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 8:50:01 PM
|
|
On 2010-05-17, sandeep <nospam@nospam.com> wrote:
> What I think this overlooks is:
It overlooks nothing.
THAT IS THE SPECIFICATION OF THE LANGUAGE.
> malloc may return a pointer to an area of
> memory with some "bookkkeeping info" at the beginning, eg size of block,
> pointer to next buddy block. Free will then need to read this info, which
> will involve dereferencing the pointer. If the pointer is NULL... bang!
No, free won't need to read that info, because the first thing it would do
is check to see whether the pointer is a null pointer, and if it is, return
without doing anything.
It doesn't matter how easy or hard it is for the implementor to comply.
They are *REQUIRED* to correctly do nothing without crashing if a null
pointer is passed to free(). As it happens, this is very easy.
You have about three posts left to convince me that you're not a troll who's
just trying to stir stuff up by posting hilariously wrong stuff before I plonk
you and stop trying to help you. I've rarely seen someone so militantly
unwilling to learn.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 8:52:17 PM
|
|
Dann Corbit writes:
> 7.20.3.2 The free function
> Synopsis
> 1 #include <stdlib.h>
> void free(void *ptr);
> Description
> 2 The free function causes the space pointed to by ptr to be
> deallocated, that is, made available for further allocation. If ptr is a
> null pointer, no action occurs. Otherwise, if the argument does not
> match a pointer earlier returned by the calloc, malloc, or realloc
> function, or if the space has been deallocated by a call to free or
> realloc, the behavior is undefined.
What I think this overlooks is: malloc may return a pointer to an area of
memory with some "bookkkeeping info" at the beginning, eg size of block,
pointer to next buddy block. Free will then need to read this info, which
will involve dereferencing the pointer. If the pointer is NULL... bang!
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/17/2010 8:53:55 PM
|
|
In article <hssad3$b5e$1@speranza.aioe.org>, nospam@nospam.com says...
>
> Dann Corbit writes:
> > 7.20.3.2 The free function
> > Synopsis
> > 1 #include <stdlib.h>
> > void free(void *ptr);
> > Description
> > 2 The free function causes the space pointed to by ptr to be
> > deallocated, that is, made available for further allocation. If ptr is a
> > null pointer, no action occurs. Otherwise, if the argument does not
> > match a pointer earlier returned by the calloc, malloc, or realloc
> > function, or if the space has been deallocated by a call to free or
> > realloc, the behavior is undefined.
>
> What I think this overlooks is: malloc may return a pointer to an area of
> memory with some "bookkkeeping info" at the beginning, eg size of block,
> pointer to next buddy block. Free will then need to read this info, which
> will involve dereferencing the pointer. If the pointer is NULL... bang!
I think you have an interesting imagination. However, when it comes to
programming, it's a good idea to use specifications instead of your
imagination.
IOW, you are dead wrong. If an implementation goes 'bang' because you
freed a NULL pointer, then the implementation is broken and you have the
right to complain to your compiler vendor.
If you are paranoid about broken compilers, you can always test the
return value of malloc() and if it is NULL, don't bother freeing it,
since free() in this instance is simply a no-op.
|
|
0
|
|
|
|
Reply
|
Dann
|
5/17/2010 9:00:49 PM
|
|
In article <hssad3$b5e$1@speranza.aioe.org>,
sandeep <nospam@nospam.com> wrote:
> Dann Corbit writes:
> > 7.20.3.2 The free function
> > Synopsis
> > 1 #include <stdlib.h>
> > void free(void *ptr);
> > Description
> > 2 The free function causes the space pointed to by ptr to be
> > deallocated, that is, made available for further allocation. If ptr is a
> > null pointer, no action occurs. Otherwise, if the argument does not
> > match a pointer earlier returned by the calloc, malloc, or realloc
> > function, or if the space has been deallocated by a call to free or
> > realloc, the behavior is undefined.
>
> What I think this overlooks is: malloc may return a pointer to an area of
> memory with some "bookkkeeping info" at the beginning, eg size of block,
> pointer to next buddy block. Free will then need to read this info, which
> will involve dereferencing the pointer. If the pointer is NULL... bang!
No, if the pointer is NULL then, no action, like it says. I would have
expected malloc to do its memory management well away from any block
that it returns a pointer to.
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/17/2010 9:01:25 PM
|
|
Keith Thompson <kst-u@mib.org> writes:
> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> [...]
>> The main problem was that he wanted a "?:=" version of gcc's "?:"
>> operator. If he'd have actually wanted "? :", it wouldn't have been
>> so bad to use "? :" (I like dense code, I don't like redundant code).
>>
>> And as an aside, I think ?: should be standardised.
>>
>> (And please overlook the syntactical sloppiness, I didn't want to have
>> to explain ?:)
>
> Since you're talking about a gcc-specific extension (and since C already
> has a ?: operator), perhaps explaining it would have been a good idea.
Nope, my time's too valuable to me currently. Anyone interested enough
in gcc specifics had all the clues available to them in order to stick
them into a search engine.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 9:11:09 PM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> Phil Carmody wrote:
> <snip>
>>
>> An object being 'constructed'? Are you sure you were using C?
>
> I construct objects very often in C. What's the problem?
I don't remember using the word 'problem'. Your use of the
definite article implies that there ought to be a single
unambiguous instance of same. Sounds like a PDtCP moment.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 9:16:31 PM
|
|
On 5/17/2010 4:27 PM, sandeep wrote:
> Eric Sosman writes:
>> Besides, this whole thread started with your suggestion
>> that there's no reason to check the value of malloc() for "small"
>> requests. In the scenario you describe, how could you choose between
>> algorithms without making the check? I know of one way, but it's pretty
>> silly:
>>
>> Type *ptr = malloc(SMALL_SIZE);
>> free(ptr);
>> do_slow_sort();
>
> I'm sorry you want to make this silly instead of a serious discussion.
It's been silly from the get-go. You starte the thread by
proposing to omit checking malloc() values; that's silly to begin
with. Then you suggested using malloc() in hopes of getting enough
memory for a fast algorithm, but falling back on a slow algorithm
if malloc() failed. Put the two ideas together, and the second
highlights the silliness of the first -- I didn't add the silliness,
I just shone a brighter light on silliness already present.
> But anyway you are wrong: if malloc returns a NUL pointer then free may
> blow up if it tries to dereference its argument.
Do you know *any* C? Open your textbook or reference (if you
own one, which I'm beginning to doubt) and read what it tells you
about the behavior of free(NULL). Go on, do it now -- before you
post still more nonsense.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/17/2010 9:34:54 PM
|
|
sandeep <nospam@nospam.com> writes:
> Eric Sosman writes:
>> Besides, this whole thread started with your suggestion
>> that there's no reason to check the value of malloc() for "small"
>> requests. In the scenario you describe, how could you choose between
>> algorithms without making the check? I know of one way, but it's pretty
>> silly:
>>
>> Type *ptr = malloc(SMALL_SIZE);
>> free(ptr);
>> do_slow_sort();
>
> I'm sorry you want to make this silly instead of a serious discussion.
>
> But anyway you are wrong: if malloc returns a NUL pointer then free may
> blow up if it tries to dereference its argument.
Woo woo - we have been trolled.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 10:04:56 PM
|
|
sandeep <nospam@nospam.com> writes:
From: sandeep <nospam@nospam.com>
Organization: Aioe.org NNTP Server
Frig, it was there in black and white all along.
What was the final score?
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 10:06:38 PM
|
|
On 2010-05-17, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Frig, it was there in black and white all along.
It seems likely.
> What was the final score?
Hard to say. I do concede that, between this and the various other very
densely packed mistakes (using NUL for "null pointer", etcetera), arguing
with everyone, and most recently, saying that the C standard overlooks
something about real implementations, this has GOT to be a joke. A very
nicely done one, though.
IHBT. IHL. IWHAND.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 10:10:35 PM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> Keith Thompson <kst-u@mib.org> writes:
>> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>> [...]
>>> The main problem was that he wanted a "?:=" version of gcc's "?:"
>>> operator. If he'd have actually wanted "? :", it wouldn't have been
>>> so bad to use "? :" (I like dense code, I don't like redundant code).
>>>
>>> And as an aside, I think ?: should be standardised.
>>>
>>> (And please overlook the syntactical sloppiness, I didn't want to have
>>> to explain ?:)
>>
>> Since you're talking about a gcc-specific extension (and since C already
>> has a ?: operator), perhaps explaining it would have been a good idea.
>
> Nope, my time's too valuable to me currently. Anyone interested enough
> in gcc specifics had all the clues available to them in order to stick
> them into a search engine.
I happened to have read about about gcc's extension of the
conditional operator previously, so I knew what you were talking
about. Someone who didn't know about it would assume that ?:
referred to the operator defined by the C standard (and would
wonder what you meant by ``gcc's "?:"'' and why you think it should
be standardized).
When some of your valuable time becomes available, try sticking "?:"
into a search engine and see what happens.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 10:11:55 PM
|
|
On 2010-05-17, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> sandeep <nospam@nospam.com> writes:
>> From: sandeep <nospam@nospam.com>
>> Organization: Aioe.org NNTP Server
> Frig, it was there in black and white all along.
HEY!!!
|
|
0
|
|
|
|
Reply
|
Tim
|
5/17/2010 10:13:03 PM
|
|
On 2010-05-17, Keith Thompson <kst-u@mib.org> wrote:
> I happened to have read about about gcc's extension of the
> conditional operator previously, so I knew what you were talking
> about. Someone who didn't know about it would assume that ?:
> referred to the operator defined by the C standard (and would
> wonder what you meant by ``gcc's "?:"'' and why you think it should
> be standardized).
I even KNOW about it, and didn't realize that was the intended reference.
Speaking of GNU C: Nested functions, worst idea ever. I have never seen
them used in a remotely sane way. I did, however, just encounter a provably
insane nested function which Made No Sense At All. Also it caused crashes
on the ARM compiler. But mostly it was just insane.
It is the Flying Dutchman of C language extensions, a solution doomed to
wander the libraries and source trees of the world until the end of the time,
seeking a problem.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 10:14:59 PM
|
|
On 2010-05-17, Tim Harig <usernet@ilthio.net> wrote:
> On 2010-05-17, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>> sandeep <nospam@nospam.com> writes:
>>> From: sandeep <nospam@nospam.com>
>>> Organization: Aioe.org NNTP Server
>> Frig, it was there in black and white all along.
> HEY!!!
The occasional counter-examples don't do much to undermine the general
principle. aioe exists to allow people to harass with impunity.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 10:15:34 PM
|
|
Keith Thompson <kst-u@mib.org> writes:
> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>> Keith Thompson <kst-u@mib.org> writes:
>>> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>>> [...]
>>>> The main problem was that he wanted a "?:=" version of gcc's "?:"
>>>> operator. If he'd have actually wanted "? :", it wouldn't have been
>>>> so bad to use "? :" (I like dense code, I don't like redundant code).
>>>>
>>>> And as an aside, I think ?: should be standardised.
>>>>
>>>> (And please overlook the syntactical sloppiness, I didn't want to have
>>>> to explain ?:)
>>>
>>> Since you're talking about a gcc-specific extension (and since C already
>>> has a ?: operator), perhaps explaining it would have been a good idea.
>>
>> Nope, my time's too valuable to me currently. Anyone interested enough
>> in gcc specifics had all the clues available to them in order to stick
>> them into a search engine.
>
> I happened to have read about about gcc's extension of the
> conditional operator previously, so I knew what you were talking
> about. Someone who didn't know about it would assume that ?:
> referred to the operator defined by the C standard (and would
> wonder what you meant by ``gcc's "?:"'' and why you think it should
> be standardized).
>
> When some of your valuable time becomes available, try sticking "?:"
> into a search engine and see what happens.
Why would I stick punctuation into a text-based search engine?
Do you think I'm the kind of idiot who would waste time doing
that? If I was looking for something clearly identified as a
gcc c language extension, I'd probably search for something like
"gcc c language extension", for example. Maybe the first hit
would even contain the string '?:', if I was lucky.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/17/2010 10:23:07 PM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> sandeep <nospam@nospam.com> writes:
> Frig, it was there in black and white all along.
>
> What was the final score?
Observation: Some trolls post through aioe.org.
Conclusion: All aioe.org users are trolls?
Nonsense. I'm a former aioe.org user myself (I later switched to
eternal-september.org because it seemed, at the time, to be more
stable).
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 10:29:13 PM
|
|
Seebs <usenet-nospam@seebs.net> writes:
[...]
> Speaking of GNU C: Nested functions, worst idea ever. I have never seen
> them used in a remotely sane way. I did, however, just encounter a provably
> insane nested function which Made No Sense At All. Also it caused crashes
> on the ARM compiler. But mostly it was just insane.
>
> It is the Flying Dutchman of C language extensions, a solution doomed to
> wander the libraries and source trees of the world until the end of the time,
> seeking a problem.
Interesting. I've certainly made extensive use of nested functions
(and/or procedures) in Pascal and Ada. Perhaps there's something
about C (other than the obvious fact that the standard doesn't
support them) that makes nested functions less useful?
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 10:31:59 PM
|
|
On 2010-05-17, Seebs <usenet-nospam@seebs.net> wrote:
> On 2010-05-17, Tim Harig <usernet@ilthio.net> wrote:
>> On 2010-05-17, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>> sandeep <nospam@nospam.com> writes:
>>>> From: sandeep <nospam@nospam.com>
>>>> Organization: Aioe.org NNTP Server
>>> Frig, it was there in black and white all along.
>> HEY!!!
> The occasional counter-examples don't do much to undermine the general
> principle. aioe exists to allow people to harass with impunity.
Since ISPs have starting dropping usenet support, I have had little choice.
I cannot justify the costs a paid service in addition to my other
telecommunications costs.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/17/2010 10:32:29 PM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> Keith Thompson <kst-u@mib.org> writes:
>> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>>> Keith Thompson <kst-u@mib.org> writes:
>>>> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>>>> [...]
>>>>> The main problem was that he wanted a "?:=" version of gcc's "?:"
>>>>> operator. If he'd have actually wanted "? :", it wouldn't have been
>>>>> so bad to use "? :" (I like dense code, I don't like redundant code).
>>>>>
>>>>> And as an aside, I think ?: should be standardised.
>>>>>
>>>>> (And please overlook the syntactical sloppiness, I didn't want to have
>>>>> to explain ?:)
>>>>
>>>> Since you're talking about a gcc-specific extension (and since C already
>>>> has a ?: operator), perhaps explaining it would have been a good idea.
>>>
>>> Nope, my time's too valuable to me currently. Anyone interested enough
>>> in gcc specifics had all the clues available to them in order to stick
>>> them into a search engine.
>>
>> I happened to have read about about gcc's extension of the
>> conditional operator previously, so I knew what you were talking
>> about. Someone who didn't know about it would assume that ?:
>> referred to the operator defined by the C standard (and would
>> wonder what you meant by ``gcc's "?:"'' and why you think it should
>> be standardized).
>>
>> When some of your valuable time becomes available, try sticking "?:"
>> into a search engine and see what happens.
>
> Why would I stick punctuation into a text-based search engine?
> Do you think I'm the kind of idiot who would waste time doing
> that? If I was looking for something clearly identified as a
> gcc c language extension, I'd probably search for something like
> "gcc c language extension", for example. Maybe the first hit
> would even contain the string '?:', if I was lucky.
The point is that it wasn't at all clear from your previous article
what you were talking about. I barely figured it out myself,
and Seebs (who is no idiot) didn't understand it at all until I
explained it.
As you know, ?: is a standard C language feature. The phrase ``gcc's
"?:"'' is not, in my opinion, sufficient to make it clear that you're
talking about an extension. Plenty of people are unclear on the
distinction between features defined by the language and features
supported by a particular compiler; I don't think you're one of them,
but it's unreasonable to expect everyone to be aware of that.
Please spend just a moment considering the possibility that I might
have a point.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 10:39:41 PM
|
|
Seebs <usenet-nospam@seebs.net> writes:
> On 2010-05-17, Tim Harig <usernet@ilthio.net> wrote:
>> On 2010-05-17, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>> sandeep <nospam@nospam.com> writes:
>>>> From: sandeep <nospam@nospam.com>
>>>> Organization: Aioe.org NNTP Server
>>> Frig, it was there in black and white all along.
>
>> HEY!!!
>
> The occasional counter-examples don't do much to undermine the general
> principle. aioe exists to allow people to harass with impunity.
You are making an unwarranted assumption about the purpose behind
aioe.org, and an equally unwarranted assumption that aioe.org users
are necessarily aware of that purpose.
I used aioe.org myself for some time, simply because it was a
prominent free NNTP server. A quick look at aioe.org's web site
seems to imply that they have reasonable terms of service and
complaint policies. They might not be very good at handling
complaints, but I don't recall seeing any direct evidence of
that myself.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 10:44:28 PM
|
|
Seebs wrote:
<snip>
>
> You have about three posts left to convince me that you're not a troll who's
> just trying to stir stuff up by posting hilariously wrong stuff before I plonk
> you and stop trying to help you. I've rarely seen someone so militantly
> unwilling to learn.
You must be new here. :-)
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 11:11:41 PM
|
|
Phil Carmody wrote:
> Richard Heathfield <rjh@see.sig.invalid> writes:
>> Phil Carmody wrote:
>> <snip>
>>> An object being 'constructed'? Are you sure you were using C?
>> I construct objects very often in C. What's the problem?
>
> I don't remember using the word 'problem'.
Are you being deliberately obtuse, or is this genuinely a maze of twisty
little misunderstandings, all different?
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/17/2010 11:13:11 PM
|
|
"Geoff" <geoff@invalid.invalid> wrote in message
news:u292v5d98m538lgd49a971kv2m66is406g@4ax.com...
> On Mon, 17 May 2010 12:16:58 +0200, "io_x" <a@b.c.invalid> wrote:
>
>>
>>"Tim Harig" <usernet@ilthio.net> ha scritto nel messaggio > Note that
>>minimizing
>>code density doesn't create a smaller or faster
>>> binary. A concisely written ?: operator on a single line generates the
>>> same code as the equivilant if/else operators spread across multiple
>>> lines.
>>> The if/else version is almost always easier to read. "?:"'s are almost
>>> always the sign of an amateur programmer trying to show off their "l337
>>> skilz." Seasoned programmers have learned to avoid them.
>>
>>programming in about reduce text size
>>it is to say to the computer many little precise thing all togheter
>
> Programming is about correctness of the solution to a problem, not
> about terseness. To obtain correctness and ease of maintenance the
> programmer should write it as though someone unfamiliar with the
> solution will be maintaining the code. The compiler will optimize the
> code. Yes, there are occasions when the solution has bottlenecks, but
> these are conceptual problems with the implementation of the solution,
> not the size of the code.
>
>>
>>if "?:" reduce the size of the prog at last of one char: it is ok
>>
>
> Simply false. As Tim stated above, if/else generates essentially the
> same machine code as ?: yet the latter is harder to read and maintain.
Does it?
x = a?b:c would need to be written like this:
if (a)
x=b;
else
x=c;
Notice the x is written twice. This may or may not generate the same code,
but when x is complex it's more work to write.
And when ?: is properly embedded in an expression:
f( p+(a?b:c)+q*(d?e:g))
then I'd really like to see your version using if/else.
--
Bartc
|
|
0
|
|
|
|
Reply
|
bart
|
5/17/2010 11:15:23 PM
|
|
On 2010-05-17, Keith Thompson <kst-u@mib.org> wrote:
> Interesting. I've certainly made extensive use of nested functions
> (and/or procedures) in Pascal and Ada. Perhaps there's something
> about C (other than the obvious fact that the standard doesn't
> support them) that makes nested functions less useful?
I don't know. The usage in question was as follows:
There's a function in glibc called rpmatch(), which basically tells
you whether a provided string looks like "yes" or "no". To do this,
it has a nested function called try().
/* Determine whether string value is affirmation or negative response
according to current locale's data.
This file is part of the GNU C Library.
Copyright (C) 1996, 1997, 2000, 2003 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <langinfo.h>
#include <stdlib.h>
#include <regex.h>
int
rpmatch (response)
const char *response;
{
/* Match against one of the response patterns, compiling the pattern
first if necessary. */
auto int try (const int tag, const int match, const int nomatch,
const char **lastp, regex_t *re);
int try (const int tag, const int match, const int nomatch,
const char **lastp, regex_t *re)
{
const char *pattern = nl_langinfo (tag);
if (pattern != *lastp)
{
/* The pattern has changed. */
if (*lastp)
{
/* Free the old compiled pattern. */
__regfree (re);
*lastp = NULL;
}
/* Compile the pattern and cache it for future runs. */
if (__regcomp (re, pattern, REG_EXTENDED) != 0)
return -1;
*lastp = pattern;
}
/* Try the pattern. */
return __regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch;
}
/* We cache the response patterns and compiled regexps here. */
static const char *yesexpr, *noexpr;
static regex_t yesre, nore;
return (try (YESEXPR, 1, 0, &yesexpr, &yesre) ?:
try (NOEXPR, 0, -1, &noexpr, &nore));
}
A bit of analysis:
It does make some sense to create a function for the "create a new
regex for this pattern, if we don't have one yet already". (The reason
for the elaborate dance to figure out whether the pattern is the same,
I think, is in case the locale changes after you've previously called this.)
It's a nested function because it wants to check the value of "response",
and apparently, while it's awesome to have a function that takes five
parameter, including one for what it should return on a match and one
for what it should return on a non-match, it is ridiculous to have a function
that takes a sixth parameter.
Had I been writing this, it would likely have been:
static int
check_response(const int tag,
const char **lastp,
const regex_t *re,
const char **response) {
/* return 1 on a match */
}
and been invoked as:
if (check_response(YESEXPR, &yesexpr, &yesre, response)) {
return 1;
} else if (check_response(NOEXPR, &noexpr, &nore, response)) {
return 0;
} else {
return -1;
}
It is amusing to me to note that the GNU C "?:" was used here. (And to
good effect, even!)
Basically, though, this is a great example of the same kind of insane
pseudo-cleverness that sandeep's example code has been such a rich source
of. The nested function is not particularly necessary. The elaborate
match/nomatch returns create an undue complication, to put it mildly.
I'm sure, somewhere, there must be legitimate uses for this extension, but
I've never seen it for anything but stuff like the above, which exists only
to be a weakest link in some piece of software with some set of compiler
options, because it's so rarely used.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 11:25:31 PM
|
|
On 2010-05-17, Keith Thompson <kst-u@mib.org> wrote:
> You are making an unwarranted assumption about the purpose behind
> aioe.org, and an equally unwarranted assumption that aioe.org users
> are necessarily aware of that purpose.
I'm making an observation about the net effect it has.
> I used aioe.org myself for some time, simply because it was a
> prominent free NNTP server. A quick look at aioe.org's web site
> seems to imply that they have reasonable terms of service and
> complaint policies.
Their complaint policy, for any kind of harassment, is that they will do
NOTHING without an order signed by an Italian judge.
So if I were harassing you through aioe.org, your options would be:
1. Put up with it.
You can't, in practice, get an Italian judge to care about internet
harassment occurring between two people neither of whom is in Italy.
> They might not be very good at handling
> complaints, but I don't recall seeing any direct evidence of
> that myself.
They are the news server of choice for stalkers these days, because they
provide anonymizing (no injection IP or anything) and will not cooperate
with any kind of investigation that isn't in Italy.
They're not too bad about spam, which is nice, but they are a magnet for
stalkers, and don't seem likely to change this in the forseeable future.
And you're right, individual users may well be exceptions to that. It
could even be that a majority of their users are harmless, but the ones
who aren't are pretty bad, and the aioe policy pages look to me to
be expressing derision and contempt for the victims of harassment through
their servers.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 11:29:06 PM
|
|
On 2010-05-17, Richard Heathfield <rjh@see.sig.invalid> wrote:
> Seebs wrote:
><snip>
>> You have about three posts left to convince me that you're not a troll who's
>> just trying to stir stuff up by posting hilariously wrong stuff before I plonk
>> you and stop trying to help you. I've rarely seen someone so militantly
>> unwilling to learn.
> You must be new here. :-)
I said *unwilling*, not *unable*. :)
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/17/2010 11:29:29 PM
|
|
sandeep <nospam@nospam.com> wrote:
> Keith Thompson writes:
> > For most allocations, if you can't get what you asked for, that's a
> > failure, and you either need to fall back to some other approach or
> > prepare to shut down. If I'm trying to allocate a tree node, half a
> > node does me no good.
> I think you are not seeing the whole picture here. You should think of
> alternative situations too.
>
> Maybe you want the allocation to perform a fast sort routine. If you
> can't get enough memory, you can just fall back to a slower out-of-core
> sorting routine.
That's a poor example. It's unreasonable as a practical matter to write two
sort routines just to recover gracefully from a malloc failure, not to
mention the possibility of opening oneself up to a computational complexity
attack. The scenarios where writing two routines is sensible should be
extremely limited.
In this particular case, the better approach involves an ounce of
prevention. I never use data structure implementations which require a
separately allocated node object, primarily because it greatly simplifies
memory management. Making the node a member of a structure is simple enough.
Algorithms which require a dynamically sized array or stack is a different
story, but all things being equal (or nearly so), I tend to prefer
algorithms where that isn't necessary. For example, I always choose to use a
red-black tree over an AVL tree.
|
|
0
|
|
|
|
Reply
|
William
|
5/17/2010 11:38:13 PM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> Phil Carmody wrote:
>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>> Phil Carmody wrote:
>>> <snip>
>>>> An object being 'constructed'? Are you sure you were using C?
>>> I construct objects very often in C. What's the problem?
>>
>> I don't remember using the word 'problem'.
>
> Are you being deliberately obtuse, or is this genuinely a maze of twisty
> little misunderstandings, all different?
I suspect the latter.
Richard, you introduced the word "problem" into the conversation.
What exactly did you mean? Consider that Phil was not the only
person who was bewildered by your question.
In my opinion, Phil simply asked a reasonable question to which
the answer was not entirely obvisou.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/17/2010 11:43:04 PM
|
|
On 2010-05-18, Keith Thompson <kst-u@mib.org> wrote:
> Seebs <usenet-nospam@seebs.net> writes:
>> On 2010-05-17, Keith Thompson <kst-u@mib.org> wrote:
>>> You are making an unwarranted assumption about the purpose behind
>>> aioe.org, and an equally unwarranted assumption that aioe.org users
>>> are necessarily aware of that purpose.
>> I'm making an observation about the net effect it has.
> It was phrased as an assumption about their purpose: "aioe exists to
> allow people to harass with impunity".
Ahh.
Sorry, badly phrased. That's meant in the same way that "teenage girls
exist to keep Sanrio in business".
> Are you referring to the "Log Data Retention" section on
><http://aioe.org/19.html>? That's the only thing I can see that
> refers to Italian court orders, and it refers only to revealing
> their logs. Harassment should be covered by the last bullet in
> "Content-Related Rules" on <http://aioe.org/03.html>, "Respect
> netiquette.".
Okay. So, imagine that someone is harassing you to a sufficient extent
that courts would normally become involved.
Unless you're Italian, you're screwed. You can never get them to disclose
the posting IP address, without which there's nothing you can do but
speculate as to who might be doing it.
>> So if I were harassing you through aioe.org, your options would be:
>> 1. Put up with it.
> 2. Follow the complaint procedure documented on aioe.org's web page,
> which says nothing about court orders.
But also says nothing about any likelihood that they'll do anything.
> I'm not debating whether they're a haven for harassers, just whether
> there's any real indication of it on their web pages.
NNTP-posting-host is not optional, IMHO. Hiding it, and refusing to
disclose it without a court order from a judge is an attractive nuisance
and an invitation to stalkers and harassers.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/18/2010 12:05:53 AM
|
|
Seebs <usenet-nospam@seebs.net> writes:
> On 2010-05-17, Keith Thompson <kst-u@mib.org> wrote:
>> You are making an unwarranted assumption about the purpose behind
>> aioe.org, and an equally unwarranted assumption that aioe.org users
>> are necessarily aware of that purpose.
>
> I'm making an observation about the net effect it has.
It was phrased as an assumption about their purpose: "aioe exists to
allow people to harass with impunity".
>> I used aioe.org myself for some time, simply because it was a
>> prominent free NNTP server. A quick look at aioe.org's web site
>> seems to imply that they have reasonable terms of service and
>> complaint policies.
>
> Their complaint policy, for any kind of harassment, is that they will do
> NOTHING without an order signed by an Italian judge.
Are you referring to the "Log Data Retention" section on
<http://aioe.org/19.html>? That's the only thing I can see that
refers to Italian court orders, and it refers only to revealing
their logs. Harassment should be covered by the last bullet in
"Content-Related Rules" on <http://aioe.org/03.html>, "Respect
netiquette.".
> So if I were harassing you through aioe.org, your options would be:
> 1. Put up with it.
2. Follow the complaint procedure documented on aioe.org's web page,
which says nothing about court orders.
Again, I can't confirm from my own experience that that actually pay
attention, but I see no statement of policy that says they won't.
I'm not debating whether they're a haven for harassers, just whether
there's any real indication of it on their web pages.
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 12:07:19 AM
|
|
William Ahern <william@wilbur.25thandClement.com> writes:
> sandeep <nospam@nospam.com> wrote:
>> Keith Thompson writes:
>> > For most allocations, if you can't get what you asked for, that's a
>> > failure, and you either need to fall back to some other approach or
>> > prepare to shut down. If I'm trying to allocate a tree node, half a
>> > node does me no good.
>
>> I think you are not seeing the whole picture here. You should think of
>> alternative situations too.
>>
>> Maybe you want the allocation to perform a fast sort routine. If you
>> can't get enough memory, you can just fall back to a slower out-of-core
>> sorting routine.
>
> That's a poor example. It's unreasonable as a practical matter to write two
> sort routines just to recover gracefully from a malloc failure, not to
> mention the possibility of opening oneself up to a computational complexity
> attack. The scenarios where writing two routines is sensible should be
> extremely limited.
>
> In this particular case, the better approach involves an ounce of
> prevention. I never use data structure implementations which require a
> separately allocated node object, primarily because it greatly simplifies
> memory management. Making the node a member of a structure is simple enough.
>
> Algorithms which require a dynamically sized array or stack is a different
> story, but all things being equal (or nearly so), I tend to prefer
> algorithms where that isn't necessary. For example, I always choose to use a
> red-black tree over an AVL tree.
Suppose you want to sort a potentially large disk file. Whatever
algorithm you use, it's likely to involve loading chucks of the file
into memory. The more you can load, the faster the sort is likely to
be. In that particular case, it's perfectly reasonable to ask for a
large buffer and settle for a smaller one.
But that's a relatively rare special case, and it doesn't justify
making your default memory allocator allocate a small buffer when
you ask for a large one.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 12:13:18 AM
|
|
Seebs <usenet-nospam@seebs.net> writes:
> On 2010-05-18, Keith Thompson <kst-u@mib.org> wrote:
[...]
>> Are you referring to the "Log Data Retention" section on
>><http://aioe.org/19.html>? That's the only thing I can see that
>> refers to Italian court orders, and it refers only to revealing
>> their logs. Harassment should be covered by the last bullet in
>> "Content-Related Rules" on <http://aioe.org/03.html>, "Respect
>> netiquette.".
>
> Okay. So, imagine that someone is harassing you to a sufficient extent
> that courts would normally become involved.
>
> Unless you're Italian, you're screwed. You can never get them to disclose
> the posting IP address, without which there's nothing you can do but
> speculate as to who might be doing it.
If they follow the policies stated on their own web pages, they should
at least shut down the harassing user's access to aioe.org. I concede
that that's a big "if".
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 12:55:03 AM
|
|
"Phil Carmody" <thefatphil_demunged@yahoo.co.uk> wrote in message
news:87eihbdqqc.fsf@kilospaz.fatphil.org...
> "Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
>> "Phil Carmody" <thefatphil_demunged@yahoo.co.uk> wrote in message
>> news:87mxw0drt4.fsf@kilospaz.fatphil.org...
>>> "Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
>>>> "Seebs" <usenet-nospam@seebs.net> wrote in message
>>>> news:slrnhuu50r.4tq.usenet-nospam@guild.seebs.net...
>>>>> On 2010-05-15, sandeep <nospam@nospam.com> wrote:
>>>>>> Obviously for efficiency! malloc may be called many times in the
>>>>>> course
>>>>>> of a program.
>>>>>
>>>>> Please stop trying to outsmart the compiler.
>>>>>
>>>>> The "cost" of using a function instead of a macro is likely to be
>>>>> so small
>>>>> that you can't even measure it. If there is even a cost at all,
>>>>> which there
>>>>> may not be.
>>>>
>>>> memset actually showed as a bottleneck in one of programs I had to
>>>> debug.
>>>> It was being called 2,000,000,000 times or so.
>>>>
>>>> Converting from memset to a for loop writing integers helped a bit,
>>>> but the real fix was to correct the logic error so that it wasn't
>>>> called that many times.
>>>> the memset was part of initializing an object, which was being
>>>> constructed during processing, but was only needed a fraction of the
>>>> time.....
>>>
>>> An object being 'constructed'? Are you sure you were using C?
>>
>> I know I was using C++. I doubt C would be any different in this respect.
>> :-)
>
> All that stuff that's just done for you in C++ so you don't have to
> worry about may be the stuff that you don't want to happen. Whilst
> it's certainly possible, I think you'd have to go a little further
> out of your way to get such behaviour in C, you would have to
> deliberately design it in more.
Not really - just declare a struct, then call a function to initialize it.
Doesn't seem to be that far out of the way.
>
> However, thanks for sharing your C++ woes on comp.lang.c.
The behavior described could happen in any language. Still, you're welcome.
Dennis
|
|
0
|
|
|
|
Reply
|
Dennis
|
5/18/2010 2:04:45 AM
|
|
On 17 May, 23:14, Seebs <usenet-nos...@seebs.net> wrote:
> On 2010-05-17, Keith Thompson <ks...@mib.org> wrote:
>
> > I happened to have read about about gcc's extension of the
> > conditional operator previously, so I knew what you were talking
> > about. =A0Someone who didn't know about it would assume that ?:
> > referred to the operator defined by the C standard (and would
> > wonder what you meant by ``gcc's "?:"'' and why you think it should
> > be standardized).
>
> I even KNOW about it, and didn't realize that was the intended reference.
>
> Speaking of GNU C: =A0Nested functions, worst idea ever. =A0I have never =
seen
> them used in a remotely sane way. =A0I did, however, just encounter a pro=
vably
> insane nested function which Made No Sense At All. =A0Also it caused cras=
hes
> on the ARM compiler. =A0But mostly it was just insane.
>
> It is the Flying Dutchman of C language extensions, a solution doomed to
> wander the libraries and source trees of the world until the end of the t=
ime,
> seeking a problem.
plenty of other languages implement nested function definitions. Why
are functions so special that everything else can be nested but them?
Its another locality of reference thingy
|
|
0
|
|
|
|
Reply
|
Nick
|
5/18/2010 8:01:16 AM
|
|
Keith Thompson wrote:
> Richard Heathfield <rjh@see.sig.invalid> writes:
>> Phil Carmody wrote:
>>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>>> Phil Carmody wrote:
>>>> <snip>
>>>>> An object being 'constructed'? Are you sure you were using C?
>>>> I construct objects very often in C. What's the problem?
>>> I don't remember using the word 'problem'.
>> Are you being deliberately obtuse, or is this genuinely a maze of twisty
>> little misunderstandings, all different?
>
> I suspect the latter.
>
> Richard, you introduced the word "problem" into the conversation.
> What exactly did you mean? Consider that Phil was not the only
> person who was bewildered by your question.
>
> In my opinion, Phil simply asked a reasonable question to which
> the answer was not entirely obvisou.
What I meant by "what's the problem?" is, broadly speaking: the OP spoke
of constructing an object. Phil Carmody replied, "An object being
'constructed'? Are you sure you were using C?", with his quote-marks
suggesting strongly that the object of his question was the word
'constructed', the validity of which in a C context he seemed to me to
be questioning. In short, I was questioning his questioning. I see
nothing wrong, odd, or unusual about the concept of "object
construction" in C. I do it frequently. The fact that C++ users also use
object construction is neither here nor there. They use ints, too, but I
don't see anyone saying "You declared it as int? Are you sure you're
using C?" (As it turns out, the OP /was/ using C++, but that's by the
by, for the same reason that it would be in the case of 'int', or
precedence, or maximum munch, or binary search.)
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/18/2010 8:22:05 AM
|
|
Keith Thompson <kst-u@mib.org> writes:
> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>> sandeep <nospam@nospam.com> writes:
>> Frig, it was there in black and white all along.
>>
>> What was the final score?
>
> Observation: Some trolls post through aioe.org.
>
> Conclusion: All aioe.org users are trolls?
>
> Nonsense.
Indeed - and entirely from your pen!
It's /evidence/ that is as clear as day, which, in combination with the
fact that his content is becoming more indistinguishable from a troll's,
is quite convincing; I never claimed it was /proof/. I may even be wrong.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/18/2010 8:55:16 AM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> Phil Carmody wrote:
>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>> Phil Carmody wrote:
>>> <snip>
>>>> An object being 'constructed'? Are you sure you were using C?
>>> I construct objects very often in C. What's the problem?
>>
>> I don't remember using the word 'problem'.
>
> Are you being deliberately obtuse, or is this genuinely a maze of
> twisty little misunderstandings, all different?
One might say my understanding has been almost preternatural.
I correctly worked out what language he was using, a language
to which this newsgroup is not dedicated, despite the fact
that he never explicitly stated it.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/18/2010 9:07:24 AM
|
|
"bart.c" <bartc@freeuk.com> writes:
> And when ?: is properly embedded in an expression:
>
> f( p+(a?b:c)+q*(d?e:g))
>
> then I'd really like to see your version using if/else.
Why? If the thing that makes the line unreadable is the
fact that it's *embedded in an expression*, then the solution
is to simply *not embed it in an expression*.
int base_foo = a?b:c; /* or ``a ? b : c'' if you like airy code */
int scaled_foo = d?e:g;
f(p + base_foo + q*scaled_foo);
There's nothing intrinsically less readable about the ternary ? :
operator than multiple assignments in an if/else.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/18/2010 9:12:10 AM
|
|
Keith Thompson <kst-u@mib.org> writes:
> William Ahern <william@wilbur.25thandClement.com> writes:
>> sandeep <nospam@nospam.com> wrote:
>>> Keith Thompson writes:
>>> > For most allocations, if you can't get what you asked for, that's a
>>> > failure, and you either need to fall back to some other approach or
>>> > prepare to shut down. If I'm trying to allocate a tree node, half a
>>> > node does me no good.
>>
>>> I think you are not seeing the whole picture here. You should think of
>>> alternative situations too.
>>>
>>> Maybe you want the allocation to perform a fast sort routine. If you
>>> can't get enough memory, you can just fall back to a slower out-of-core
>>> sorting routine.
>>
>> That's a poor example. It's unreasonable as a practical matter to write two
>> sort routines just to recover gracefully from a malloc failure, not to
>> mention the possibility of opening oneself up to a computational complexity
>> attack. The scenarios where writing two routines is sensible should be
>> extremely limited.
>>
>> In this particular case, the better approach involves an ounce of
>> prevention. I never use data structure implementations which require a
>> separately allocated node object, primarily because it greatly simplifies
>> memory management. Making the node a member of a structure is simple enough.
>>
>> Algorithms which require a dynamically sized array or stack is a different
>> story, but all things being equal (or nearly so), I tend to prefer
>> algorithms where that isn't necessary. For example, I always choose to use a
>> red-black tree over an AVL tree.
>
> Suppose you want to sort a potentially large disk file. Whatever
> algorithm you use, it's likely to involve loading chucks of the file
> into memory. The more you can load, the faster the sort is likely to
> be. In that particular case, it's perfectly reasonable to ask for a
> large buffer and settle for a smaller one.
>
> But that's a relatively rare special case,
It was very common in my wing of the company, I don't know about
yours. You'll find many instances of ever-shrinking DMA (thus
physically contiguous) buffer requests until one suceeds in the
various subsystem module's .probe. Ditto the various comms pipelines
like USB, WLAN, and Bluetooth even if there's no physical contiguity
requirement. It's just an instance of "graceful degradation of service",
and may apply to everything from kernels to high-level GUI apps.
> and it doesn't justify
> making your default memory allocator allocate a small buffer when
> you ask for a large one.
Absolutely. Pretending you know what all callers want is arrogant.
Even 'falling back onto a smaller allocation' is sufficiently lacking
in detail that it might not be right even when it's right. If you want
4MB, but have a fallback that only requires 65KB, then the allocation
returning 3MB, assuming that's how much was actually available, as
earlier proposed, would be dumb.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/18/2010 9:25:48 AM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> Keith Thompson <kst-u@mib.org> writes:
>> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>>> sandeep <nospam@nospam.com> writes:
>>> Frig, it was there in black and white all along.
>>>
>>> What was the final score?
>>
>> Observation: Some trolls post through aioe.org.
>>
>> Conclusion: All aioe.org users are trolls?
>>
>> Nonsense.
>
> Indeed - and entirely from your pen!
>
> It's /evidence/ that is as clear as day, which, in combination with the
> fact that his content is becoming more indistinguishable from a troll's,
> is quite convincing; I never claimed it was /proof/. I may even be wrong.
I suggest that the fact that sandeep posts from aioe.org provides
essentially zero evidence, one way or the other, regarding whether
or not he's a troll. The evidence is entirely in the content of
his posts, which is as it should be.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 3:21:02 PM
|
|
On 2010-05-18, Keith Thompson <kst-u@mib.org> wrote:
> I suggest that the fact that sandeep posts from aioe.org provides
> essentially zero evidence, one way or the other, regarding whether
> or not he's a troll. The evidence is entirely in the content of
> his posts, which is as it should be.
I think it's noticably over zero. Not a ton over zero, but some. It'd
show up in a good Bayesian filter. :)
The obvious evidence is that the moment people pointed out that he was
trolling he stopped.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/18/2010 3:29:13 PM
|
|
Seebs writes:
> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
>> What I think this overlooks is:
>
> It overlooks nothing.
>
> THAT IS THE SPECIFICATION OF THE LANGUAGE.
>
>> malloc may return a pointer to an area of memory with some
>> "bookkkeeping info" at the beginning, eg size of block, pointer to next
>> buddy block. Free will then need to read this info, which will involve
>> dereferencing the pointer. If the pointer is NULL... bang!
>
> No, free won't need to read that info, because the first thing it would
> do is check to see whether the pointer is a null pointer, and if it is,
> return without doing anything.
>
> It doesn't matter how easy or hard it is for the implementor to comply.
> They are *REQUIRED* to correctly do nothing without crashing if a null
> pointer is passed to free(). As it happens, this is very easy.
You may get away with it, but passing a NULL pointer to free is a mark of
careless code, it should never happen.
Making free check if it was passed a bad pointer is completely against
the spirit of C. There are no checks for division by 0, no checks for
array bounds - the programmer is expected and trusted to know what he is
doing. It should be the same with passing a valid pointer to free and no
good programmer should pass in a NULL pointer whether or not he will get
away with it. Forcing free to test for NULL is pointless and inefficient
if the programmer is competent.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/18/2010 5:11:21 PM
|
|
sandeep wrote:
> Seebs writes:
>> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
>>> What I think this overlooks is:
>> It overlooks nothing.
>>
>> THAT IS THE SPECIFICATION OF THE LANGUAGE.
>>
>>> malloc may return a pointer to an area of memory with some
>>> "bookkkeeping info" at the beginning, eg size of block, pointer to next
>>> buddy block. Free will then need to read this info, which will involve
>>> dereferencing the pointer. If the pointer is NULL... bang!
>> No, free won't need to read that info, because the first thing it would
>> do is check to see whether the pointer is a null pointer, and if it is,
>> return without doing anything.
>>
>> It doesn't matter how easy or hard it is for the implementor to comply.
>> They are *REQUIRED* to correctly do nothing without crashing if a null
>> pointer is passed to free(). As it happens, this is very easy.
>
> You may get away with it,
No, the language standard *requires* that you can get away with it.
There's no "may" about it.
> but passing a NULL pointer to free is a mark of
> careless code, it should never happen.
There, you have half a point. IIRC it's the first point you've made that
might reasonably be defended.
<snip>
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/18/2010 6:05:57 PM
|
|
Richard Heathfield ha scritto:
> sandeep wrote:
>> Seebs writes:
>>> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
>>>> What I think this overlooks is:
>>> It overlooks nothing.
>>>
>>> THAT IS THE SPECIFICATION OF THE LANGUAGE.
>>>
>>>> malloc may return a pointer to an area of memory with some
>>>> "bookkkeeping info" at the beginning, eg size of block, pointer to next
>>>> buddy block. Free will then need to read this info, which will involve
>>>> dereferencing the pointer. If the pointer is NULL... bang!
>>> No, free won't need to read that info, because the first thing it would
>>> do is check to see whether the pointer is a null pointer, and if it is,
>>> return without doing anything.
>>>
>>> It doesn't matter how easy or hard it is for the implementor to comply.
>>> They are *REQUIRED* to correctly do nothing without crashing if a null
>>> pointer is passed to free(). As it happens, this is very easy.
>>
>> You may get away with it,
>
> No, the language standard *requires* that you can get away with it.
> There's no "may" about it.
excuse me: what's the difference between "can" and "may" in english? i
thought they were synonyms...
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/18/2010 6:10:35 PM
|
|
sandeep <nospam@nospam.com> writes:
[...]
> You may get away with it, but passing a NULL pointer to free is a mark of
> careless code, it should never happen.
>
> Making free check if it was passed a bad pointer is completely against
> the spirit of C. There are no checks for division by 0, no checks for
> array bounds - the programmer is expected and trusted to know what he is
> doing. It should be the same with passing a valid pointer to free and no
> good programmer should pass in a NULL pointer whether or not he will get
> away with it. Forcing free to test for NULL is pointless and inefficient
> if the programmer is competent.
You're wrong, but I no longer believe that there's any point in
trying to explain it to you. No matter what we tell you, however
well founded it is in the C Standard and in years of professional
experience, your recent behavior indicates that you'll just
arrogantly assert that you're right and everyone else is wrong.
If you'll change your attitude, I'd be glad to discuss it with
you further.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 6:13:19 PM
|
|
superpollo wrote:
> Richard Heathfield ha scritto:
>> sandeep wrote:
>>> Seebs writes:
>>>> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
>>>>> What I think this overlooks is:
>>>> It overlooks nothing.
>>>>
>>>> THAT IS THE SPECIFICATION OF THE LANGUAGE.
>>>>
>>>>> malloc may return a pointer to an area of memory with some
>>>>> "bookkkeeping info" at the beginning, eg size of block, pointer to
>>>>> next
>>>>> buddy block. Free will then need to read this info, which will involve
>>>>> dereferencing the pointer. If the pointer is NULL... bang!
>>>> No, free won't need to read that info, because the first thing it would
>>>> do is check to see whether the pointer is a null pointer, and if it is,
>>>> return without doing anything.
>>>>
>>>> It doesn't matter how easy or hard it is for the implementor to comply.
>>>> They are *REQUIRED* to correctly do nothing without crashing if a null
>>>> pointer is passed to free(). As it happens, this is very easy.
>>>
>>> You may get away with it,
>>
>> No, the language standard *requires* that you can get away with it.
>> There's no "may" about it.
>
> excuse me: what's the difference between "can" and "may" in english? i
> thought they were synonyms...
The meaning of the word "may" depends heavily on context. It may mean
"might", or it may mean "has/have permission to" or "is/are allowed to".
For example, "it may rain tomorrow" doesn't mean "it *can* rain
tomorrow", but merely acknowledges the possibility of rain. But "you may
climb the tree" gives permission to climb the tree (making no claim
either way about whether you are *able* to climb the tree).
"You may get away with it" strongly suggests "might" rather than "can",
but the language standard *requires* that the implementation behaves
properly on free(NULL). In this case, "behaves properly" means "doesn't
do anything to affect the notional state of the abstract machine" - i.e.
free(NULL) is basically a nop.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/18/2010 6:25:59 PM
|
|
In article <4bf2d81c$0$18993$4fafbaef@reader5.news.tin.it>,
superpollo <utente@esempio.net> wrote:
> Richard Heathfield ha scritto:
> > sandeep wrote:
> >> Seebs writes:
> >>> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
> >>>> What I think this overlooks is:
> >>> It overlooks nothing.
> >>>
> >>> THAT IS THE SPECIFICATION OF THE LANGUAGE.
> >>>
> >>>> malloc may return a pointer to an area of memory with some
> >>>> "bookkkeeping info" at the beginning, eg size of block, pointer to next
> >>>> buddy block. Free will then need to read this info, which will involve
> >>>> dereferencing the pointer. If the pointer is NULL... bang!
> >>> No, free won't need to read that info, because the first thing it would
> >>> do is check to see whether the pointer is a null pointer, and if it is,
> >>> return without doing anything.
> >>>
> >>> It doesn't matter how easy or hard it is for the implementor to comply.
> >>> They are *REQUIRED* to correctly do nothing without crashing if a null
> >>> pointer is passed to free(). As it happens, this is very easy.
> >>
> >> You may get away with it,
> >
> > No, the language standard *requires* that you can get away with it.
> > There's no "may" about it.
>
> excuse me: what's the difference between "can" and "may" in english? i
> thought they were synonyms...
In the context where they were used here, "may" implies doubt about
getting away with it, whereas "can" implies certainty.
More generally the exact meaning will also depend on the emphasis placed
on the words when spoken.
Here's a slightly different context. If you and I are in a room
together, and I say:
"May I leave this room?"
then I'm asking permission to do so. If I say:
"Can I leave this room?"
then strictly speaking I'm asking whether it's *possible* (i.e.,
physically) for me to leave (and you may say "No" because all the doors
are locked).
Slang or everyday usage might result in these two in practice being used
interchangeably.
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/18/2010 6:27:18 PM
|
|
On 5/18/2010 1:11 PM, sandeep wrote:
> Seebs writes:
>> [...]
>> It doesn't matter how easy or hard it is for the implementor to comply.
>> They are *REQUIRED* to correctly do nothing without crashing if a null
>> pointer is passed to free(). As it happens, this is very easy.
>
> You may get away with it, but passing a NULL pointer to free is a mark of
> careless code, it should never happen.
The ability to free(NULL) is convenient, not usually in that
exact guise but in a realloc(NULL,...) setting:
char *buff = NULL;
size_t buffsiz = 0;
...
while (something) {
...
if (len > buffsiz) {
char *temp = realloc(buff, buffsiz = len * 2);
if (temp == NULL) ...
buff = temp;
}
...
}
Without the ability to realloc(NULL,...), the buffer-growing code
would need to make a special case of the initial allocation, or
else the initial allocation would need to occur before the loop
(and the error-handling would need to be duplicated). Given that
realloc(NULL,...) is so handy, free(NULL) is pretty much a given --
"for free," as it were.
> Making free check if it was passed a bad pointer is completely against
> the spirit of C.
The Committee that wrote the C Standard twenty-plus years ago
paid a good deal of attention to the "spirit of C," and went so far
as to write down just what they thought the S.O.C. meant (see the
Rationale). Since they wrote the "free(NULL) is a no-op" requirement,
they plainly disagreed with your notions of the S.O.C.
> There are no checks for division by 0, no checks for
> array bounds - the programmer is expected and trusted to know what he is
> doing.
The C Standard does not forbid either of these types of checks.
Indeed, division by zero *is* checked on *every* C implementation I've
used in the last three and a half decades.
As for "trust the programmer," yes: That's part of the S.O.C.
and the Rationale says so in exactly those words. (I consider it
unfortunate that the C1x Committee appears to be backing away from
this ideal -- but given the existence of programmers who think it a
good idea not to check for NULLs from malloc(), perhaps "trust the
programmer" is indeed no longer viable.)
> It should be the same with passing a valid pointer to free and no
> good programmer should pass in a NULL pointer whether or not he will get
> away with it.
It's not a matter of "getting away with" anything at all. NULL
*is* a valid argument to free() and a valid first argument to realloc(),
and there's no reason for a programmer to avoid passing valid arguments.
What would you think of
int y = (x >= 0 ? x : abs(x));
.... to avoid a no-op of another kind?
> Forcing free to test for NULL is pointless and inefficient
> if the programmer is competent.
"Pointless" is a matter of opinion, and we can disagree about
matters of opinion. But "inefficient" has a more quantitative basis;
I'd be interested to see the measurements you've made of the penalty
free() incurs by testing whether its argument is NULL. (Suggestion:
Get hold of an open-source memory management library, maybe glibc,
and build versions with and without the NULL test in free(). Run a
suite of actual programs with both library versions, and measure the
speed difference. Discuss.)
Finally, there's your "if the programmer is competent" line.
I put it to you that a programmer who fails to check the value of
malloc(), realloc(), or calloc() against NULL is not competent,
not competent enough to write C at any rate.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/18/2010 6:28:33 PM
|
|
On 2010-05-18, sandeep <nospam@nospam.com> wrote:
> You may get away with it, but passing a NULL pointer to free is a mark of
> careless code, it should never happen.
Why not? It's a value returned by malloc(). :)
> Making free check if it was passed a bad pointer is completely against
> the spirit of C. There are no checks for division by 0, no checks for
> array bounds - the programmer is expected and trusted to know what he is
> doing. It should be the same with passing a valid pointer to free and no
> good programmer should pass in a NULL pointer whether or not he will get
> away with it. Forcing free to test for NULL is pointless and inefficient
> if the programmer is competent.
Do some maintenance programming for a few years and get back to us on that,
'k?
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/18/2010 6:38:53 PM
|
|
On 2010-05-18, superpollo <utente@esempio.net> wrote:
> Richard Heathfield ha scritto:
>>> You may get away with it,
>> No, the language standard *requires* that you can get away with it.
>> There's no "may" about it.
> excuse me: what's the difference between "can" and "may" in english? i
> thought they were synonyms...
No, they both have several different meanings.
The relevant ones are: "may" => "it is possible", "can" => "it is certain".
I *may* be able to fix this bug today, I *can* have a look at it right after
lunch. I *may* get away with code that tries to dereference a null pointer,
I *can* get away with code that takes the address of a valid object, converts
it to a pointer to unsigned char, and reads the first byte of that object
through that pointer.
The other distinction they sometimes have, in a different context, is that
"can" denotes capability, while "may" denotes permission. I *can* drive
well over the speed limit. I *may* drive slightly under the speed limit.
So "may" denotes either permission (in some contexts) or possibility which
is uncertain (in others). "can" denotes definite capability.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/18/2010 6:41:47 PM
|
|
Richard Heathfield ha scritto:
> superpollo wrote:
>> Richard Heathfield ha scritto:
>>> sandeep wrote:
>>>> Seebs writes:
>>>>> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
>>>>>> What I think this overlooks is:
>>>>> It overlooks nothing.
>>>>>
>>>>> THAT IS THE SPECIFICATION OF THE LANGUAGE.
>>>>>
>>>>>> malloc may return a pointer to an area of memory with some
>>>>>> "bookkkeeping info" at the beginning, eg size of block, pointer to
>>>>>> next
>>>>>> buddy block. Free will then need to read this info, which will
>>>>>> involve
>>>>>> dereferencing the pointer. If the pointer is NULL... bang!
>>>>> No, free won't need to read that info, because the first thing it
>>>>> would
>>>>> do is check to see whether the pointer is a null pointer, and if it
>>>>> is,
>>>>> return without doing anything.
>>>>>
>>>>> It doesn't matter how easy or hard it is for the implementor to
>>>>> comply.
>>>>> They are *REQUIRED* to correctly do nothing without crashing if a null
>>>>> pointer is passed to free(). As it happens, this is very easy.
>>>>
>>>> You may get away with it,
>>>
>>> No, the language standard *requires* that you can get away with it.
>>> There's no "may" about it.
>>
>> excuse me: what's the difference between "can" and "may" in english? i
>> thought they were synonyms...
>
> The meaning of the word "may" depends heavily on context. It may mean
> "might", or it may mean "has/have permission to" or "is/are allowed to".
>
> For example, "it may rain tomorrow" doesn't mean "it *can* rain
> tomorrow", but merely acknowledges the possibility of rain. But "you may
> climb the tree" gives permission to climb the tree (making no claim
> either way about whether you are *able* to climb the tree).
>
> "You may get away with it" strongly suggests "might" rather than "can",
> but the language standard *requires* that the implementation behaves
> properly on free(NULL). In this case, "behaves properly" means "doesn't
> do anything to affect the notional state of the abstract machine" - i.e.
> free(NULL) is basically a nop.
>
as opposed to -- say -- dereferencing NULL?
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/18/2010 6:56:46 PM
|
|
Tim Streater ha scritto:
> In article <4bf2d81c$0$18993$4fafbaef@reader5.news.tin.it>,
> superpollo <utente@esempio.net> wrote:
>
>> Richard Heathfield ha scritto:
>>> sandeep wrote:
>>>> Seebs writes:
>>>>> On 2010-05-17, sandeep <nospam@nospam.com> wrote:
>>>>>> What I think this overlooks is:
>>>>> It overlooks nothing.
>>>>>
>>>>> THAT IS THE SPECIFICATION OF THE LANGUAGE.
>>>>>
>>>>>> malloc may return a pointer to an area of memory with some
>>>>>> "bookkkeeping info" at the beginning, eg size of block, pointer to next
>>>>>> buddy block. Free will then need to read this info, which will involve
>>>>>> dereferencing the pointer. If the pointer is NULL... bang!
>>>>> No, free won't need to read that info, because the first thing it would
>>>>> do is check to see whether the pointer is a null pointer, and if it is,
>>>>> return without doing anything.
>>>>>
>>>>> It doesn't matter how easy or hard it is for the implementor to comply.
>>>>> They are *REQUIRED* to correctly do nothing without crashing if a null
>>>>> pointer is passed to free(). As it happens, this is very easy.
>>>> You may get away with it,
>>> No, the language standard *requires* that you can get away with it.
>>> There's no "may" about it.
>> excuse me: what's the difference between "can" and "may" in english? i
>> thought they were synonyms...
>
> In the context where they were used here, "may" implies doubt about
> getting away with it, whereas "can" implies certainty.
>
> More generally the exact meaning will also depend on the emphasis placed
> on the words when spoken.
>
> Here's a slightly different context. If you and I are in a room
> together, and I say:
>
> "May I leave this room?"
>
> then I'm asking permission to do so. If I say:
>
> "Can I leave this room?"
>
> then strictly speaking I'm asking whether it's *possible* (i.e.,
> physically) for me to leave (and you may say "No" because all the doors
> are locked).
>
> Slang or everyday usage might result in these two in practice being used
> interchangeably.
thanks.
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/18/2010 6:57:50 PM
|
|
On 2010-05-18, superpollo <utente@esempio.net> wrote:
> Richard Heathfield ha scritto:
>> "You may get away with it" strongly suggests "might" rather than "can",
>> but the language standard *requires* that the implementation behaves
>> properly on free(NULL). In this case, "behaves properly" means "doesn't
>> do anything to affect the notional state of the abstract machine" - i.e.
>> free(NULL) is basically a nop.
> as opposed to -- say -- dereferencing NULL?
Right. It is explicitly defined that free(NULL) *MUST NOT* do anything.
In particular, it must not introduce undefined behavior. (On a system where
dereferencing a null pointer is safe, of course, the library is welcome to
do so...)
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/18/2010 6:58:20 PM
|
|
Seebs ha scritto:
> On 2010-05-18, superpollo <utente@esempio.net> wrote:
>> Richard Heathfield ha scritto:
>>>> You may get away with it,
>
>>> No, the language standard *requires* that you can get away with it.
>>> There's no "may" about it.
>
>> excuse me: what's the difference between "can" and "may" in english? i
>> thought they were synonyms...
>
> No, they both have several different meanings.
>
> The relevant ones are: "may" => "it is possible", "can" => "it is certain".
> I *may* be able to fix this bug today, I *can* have a look at it right after
> lunch. I *may* get away with code that tries to dereference a null pointer,
> I *can* get away with code that takes the address of a valid object, converts
> it to a pointer to unsigned char, and reads the first byte of that object
> through that pointer.
>
> The other distinction they sometimes have, in a different context, is that
> "can" denotes capability, while "may" denotes permission. I *can* drive
> well over the speed limit. I *may* drive slightly under the speed limit.
>
> So "may" denotes either permission (in some contexts) or possibility which
> is uncertain (in others). "can" denotes definite capability.
>
> -s
very clear, thanks.
best regards.
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/18/2010 6:59:00 PM
|
|
On 2010-05-18, Keith Thompson <kst-u@mib.org> wrote:
> The word "may" can also denote something hypothetical, which I suspect
> is how sandeep meant it:
> "Well, two plus two may be equal to four, but I don't have to like it!"
Ah-hah!
I had interpreted it as a possibility-may, but it also makes sense as a
hypothetical-may.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/18/2010 6:59:29 PM
|
|
On 5/18/2010 2:28 PM, Eric Sosman wrote:
> [...]
> I put it to you that a programmer who fails to check the value of
> malloc(), realloc(), or calloc() against NULL is not competent,
> not competent enough to write C at any rate.
"Upon further review," as they say in American football, I'll
back down just a little bit on this. I've seen an instance where
an unchecked realloc() was all right, because (1) the new size was
strictly smaller than the old and (2) the code was part of "the
implementation," and could therefore rely on implementation-specific
knowledge like "A shrinking realloc() always succeeds" (the C library
does not guarantee this, but an implementor privy to the details of
a particular implementation might know it anyhow).
HOWEVER, the reason I was looking at the code was that a crash
bug had been reported. Not far from the un-checked realloc() I
found (paraphrased)
/* Free the strings and the NULL-terminated array of
* pointers to them. Each string was obtained from malloc()
* and friends, as was the array of pointers.
*/
void discard(char **array) {
while (*array!= NULL)
free (*array++);
free (array);
}
So even though the un-checked realloc() *might* have been valid,
it turns out that the programmer was a blunderer all the same.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/18/2010 7:03:15 PM
|
|
Seebs <usenet-nospam@seebs.net> writes:
> On 2010-05-18, superpollo <utente@esempio.net> wrote:
>> Richard Heathfield ha scritto:
>>>> You may get away with it,
>
>>> No, the language standard *requires* that you can get away with it.
>>> There's no "may" about it.
>
>> excuse me: what's the difference between "can" and "may" in english? i
>> thought they were synonyms...
>
> No, they both have several different meanings.
>
> The relevant ones are: "may" => "it is possible", "can" => "it is certain".
> I *may* be able to fix this bug today, I *can* have a look at it right after
> lunch. I *may* get away with code that tries to dereference a null pointer,
> I *can* get away with code that takes the address of a valid object, converts
> it to a pointer to unsigned char, and reads the first byte of that object
> through that pointer.
>
> The other distinction they sometimes have, in a different context, is that
> "can" denotes capability, while "may" denotes permission. I *can* drive
> well over the speed limit. I *may* drive slightly under the speed limit.
>
> So "may" denotes either permission (in some contexts) or possibility which
> is uncertain (in others). "can" denotes definite capability.
The word "may" can also denote something hypothetical, which I suspect
is how sandeep meant it:
"Well, two plus two may be equal to four, but I don't have to like it!"
There's no implication there of any real possibility that 2+2!=4.
English is not always entirely consistent.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 7:04:35 PM
|
|
superpollo <utente@esempio.net> writes:
> Richard Heathfield ha scritto:
[...]
>> "You may get away with it" strongly suggests "might" rather than "can",
>> but the language standard *requires* that the implementation behaves
>> properly on free(NULL). In this case, "behaves properly" means "doesn't
>> do anything to affect the notional state of the abstract machine" - i.e.
>> free(NULL) is basically a nop.
>
> as opposed to -- say -- dereferencing NULL?
Yes. free(NULL) is required to do nothing. Deferencing a null pointer
invokes undefined behavior.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 7:10:29 PM
|
|
Seebs <usenet-nospam@seebs.net> wrote:
>
> Speaking of GNU C: Nested functions, worst idea ever.
But an old one. Back in the pre-ANSI days, Pennello and DeRemer's
MetaWare C compiler (which was written in Pascal[!]) supported nested
functions.
--
Larry Jones
Archaeologists have the most mind-numbing job on the planet. -- Calvin
|
|
0
|
|
|
|
Reply
|
lawrence
|
5/18/2010 7:28:59 PM
|
|
Eric Sosman writes:
> The ability to free(NULL) is convenient, not usually in that
> exact guise but in a realloc(NULL,...) setting:
I think you have a serious misunderstanding here. realloc does not always
call free, for example if it is passed a smaller size to reallocate. It
is nice to be able to pass NULL to realloc to get extra functionality,
with free there is no new functionality from passing NULL.
> Given that
> realloc(NULL,...) is so handy, free(NULL) is pretty much a given -- "for
> free," as it were.
I don't see why at all.
> The C Standard does not forbid either of these types of checks.
> Indeed, division by zero *is* checked on *every* C implementation I've
> used in the last three and a half decades.
You may not have used many implementations then. Maybe the division is
getting optimized out - try volatile. For example on gcc
main()
{
volatile a=0;
a=1/a;
}
Running this on Linux produces,
Floating point exception
> It's not a matter of "getting away with" anything at all. NULL
> *is* a valid argument to free() and a valid first argument to realloc(),
> and there's no reason for a programmer to avoid passing valid arguments.
> What would you think of
>
> int y = (x >= 0 ? x : abs(x));
>
> ... to avoid a no-op of another kind?
What would you think of passing a NULL pointer to strlen or fclose? Even
if you can get away with it in the case of free, it is sloppy programming.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/18/2010 8:11:58 PM
|
|
sandeep <nospam@nospam.com> writes:
> Eric Sosman writes:
[...]
>> The C Standard does not forbid either of these types of checks.
>> Indeed, division by zero *is* checked on *every* C implementation I've
>> used in the last three and a half decades.
>
> You may not have used many implementations then. Maybe the division is
> getting optimized out - try volatile. For example on gcc
>
> main()
> {
> volatile a=0;
> a=1/a;
> }
>
> Running this on Linux produces,
> Floating point exception
Right. The floating point exception is the result of the check.
What did you expect?
[...]
> What would you think of passing a NULL pointer to strlen or fclose?
Undefined behavior, unlike free(NULL).
> Even
> if you can get away with it in the case of free, it is sloppy programming.
Suppose we have a pointer object that we know we're not going to
be using anymore. Depending on what's happened previously, either
it's null or it points to an allocated object. For example, say
it's in an interactive program, where a certain command requires
the creation and use of a buffer. If the user doesn't invoke that
command, the buffer is never allocated, but if the user invokes
the command multiple times, the same buffer is re-used. Now we're
leaving the mode in which that command is available, and we want
to deallocate the buffer if it was allocated. Assume that that's
the only cleanup that's necessary.
Is it ok with you if I write this?
free(buf);
or must I write this?
if (buf != NULL) {
free(buf);
}
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 8:26:33 PM
|
|
Seebs wrote:
> Speaking of GNU C: Nested functions, worst idea ever.
Done right, used right nested functions can be very useful.
I have done a lot of programming in Pascal and controlling
function scope in a application offers some real advantages.
I have probably started a flame war on this. C has variable
scoping sorted out but does not have similar capabilities of
function scoping. At a WG-14 meeting I once brought up
nested functions. Most the arguments against were
conventional implementations related.
To be clear it will never happen as part of the standard
but I like the ability to declare a function with a function
to process some small local function that gets used multiple
times.
Regards,
Walter..
--
Walter Banks
Byte Craft Limited
http://www.bytecraft.com
|
|
0
|
|
|
|
Reply
|
Walter
|
5/18/2010 8:44:14 PM
|
|
Seebs <usenet-nospam@seebs.net> writes:
> Speaking of GNU C: Nested functions, worst idea ever. I have never seen
> them used in a remotely sane way. I did, however, just encounter a provably
> insane nested function which Made No Sense At All. Also it caused crashes
> on the ARM compiler. But mostly it was just insane.
The handy property of nested functions is that they can use the
automatic variables of the function in which they are nested.
This can be much more convenient than packaging them up inside a
struct or passing them as potentially numerous arguments to a
function.
Obviously nested functions can be misused to obfuscate. But
carefully used they can clarify code.
But there's not much point in talking about them in a C context,
since they can't be used in portable code anyhow.
--
"It wouldn't be a new C standard if it didn't give a
new meaning to the word `static'."
--Peter Seebach on C99
|
|
0
|
|
|
|
Reply
|
Ben
|
5/18/2010 9:25:41 PM
|
|
On Mon, 17 May 2010 07:30:18 +0100, Richard Heathfield wrote:
> In the UK, at a STOP sign (black text in red triangle on white
> background), you are *required* to use your hand-brake, since you must
> bring the vehicle to a complete and safe stop. (But of course you don't
> use it /instead/ of the regular brakes - you use the footbrake to stop
> the car, and then the handbrake to keep it stopped.)
Are you sure about this? I don't recall reading anything
about the hand-brake and Stop signs in the Highway Code,
but then, my copy is several years old and currently
mislocated. Fortunately, it is available on the web these
days:
<http://www.direct.gov.uk/en/TravelAndTransport/Highwaycode/DG_070332>
I can't find anything about hand-brakes in relation to
diagram 601.1 in the TSRGD either, but I have only performed
the most cursorest of readings so I might not even be looking
at the right document.
<http://www.opsi.gov.uk/si/si2002/20023113.htm#16>
<http://www.opsi.gov.uk/si/si2002/02311331.gif>
[Unusual shades of triangular has been mentioned elsewhere.]
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/18/2010 9:52:02 PM
|
|
On 5/18/2010 4:11 PM, sandeep wrote:
> Eric Sosman writes:
>> The ability to free(NULL) is convenient, not usually in that
>> exact guise but in a realloc(NULL,...) setting:
>
> I think you have a serious misunderstanding here. realloc does not always
> call free, for example if it is passed a smaller size to reallocate. It
> is nice to be able to pass NULL to realloc to get extra functionality,
> with free there is no new functionality from passing NULL.
realloc() need not actually call free(), of course. However,
it must occasionally do the same thing free() does: De-allocate a
formerly-allocated region and make it available for re-allocation.
I've shown a scenario in which it is convenient for realloc() to
operate with a NULL first argument (that is, "de-allocate" the
non-existent old region). Since it's doing "the same thing" as
free(), things are more consistent if we can say "When realloc()
cannot readjust the old region in place, it allocates a new region,
copies the relevant data, and treats the old region `as if by' free()."
Also, some realloc() implementations will *always* move the region,
even when shrinking it or when there happens to be a "gap" right after
it. And then they'll fill the old region with 0xDEADBEEF or some such.
Can you guess why a realloc() implementor would choose to do this?
>> Given that
>> realloc(NULL,...) is so handy, free(NULL) is pretty much a given -- "for
>> free," as it were.
>
> I don't see why at all.
>
>> The C Standard does not forbid either of these types of checks.
>> Indeed, division by zero *is* checked on *every* C implementation I've
>> used in the last three and a half decades.
>
> You may not have used many implementations then. Maybe the division is
> getting optimized out - try volatile. For example on gcc
>
> main()
> {
> volatile a=0;
> a=1/a;
> }
>
> Running this on Linux produces,
> Floating point exception
"He sees, but he does not observe." -- S. Holmes
Ponder this, Sandeep: How did the computer know it should emit
a "Floating point exception" message? What prompted it do do so?
Is this just something computers do every now and again, when the
whim strikes them?[*] Funny coincidence that the whim happened to
strike just at the moment when a zero division was attempted,
don't you think?
Or, hey, wait: Maybe a check was made? Naaah, too obvious ...
[*] Sometimes it seems that way, doesn't it? But somehow there
always turns out to be some other excuse for their whimsical behavior.
Computers are liberally endowed with what my grandfather called "the
innate animosity of inanimate objects," but you'll never get them to
actually admit how much they enjoy messing with our minds.
> What would you think of passing a NULL pointer to strlen or fclose? Even
> if you can get away with it in the case of free, it is sloppy programming.
What would you think of passing a null pointer to fflush()?
Or to system()? Or to tmpnam(), or as the second argument to
setlocale(), or as the first argument to strtok(), or as the
first argument to freopen(), or as an operand of ==, or ...?
All of these functions have well-defined behaviors when the
argument/operand is a null pointer. So does free().
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/18/2010 9:57:04 PM
|
|
On 5/18/2010 2:05 PM, Richard Heathfield wrote:
> sandeep wrote:
>> Seebs writes:
[... Passing NUL to free() ...]
>>> It doesn't matter how easy or hard it is for the implementor to comply.
>>> They are *REQUIRED* to correctly do nothing without crashing if a null
>>> pointer is passed to free(). As it happens, this is very easy.
>>
>> You may get away with it,
>
> No, the language standard *requires* that you can get away with it.
> There's no "may" about it.
>
>> but passing a NULL pointer to free is a mark of careless code, it
>> should never happen.
>
> There, you have half a point. IIRC it's the first point you've made that
> might reasonably be defended.
Consider:
* Any value returned my malloc() must be able to be passed to free().
* malloc() can return NULL.
So, unless you were to write the standard to say, in effect, "you can only
pass NULL to free() if malloc() has previously returned NULL". And then you
have to take into account the case of multiple NULL returns from malloc().
After all, the following must be legal:
char *foo = malloc(1000000); /* Returns NULL in our example */
char *bar = malloc(1000000); /* Also returns NULL */
free(foo);
free(bar);
Consider the ramifications of not explicitly allowing free(NULL).
--
Kenneth Brody
|
|
0
|
|
|
|
Reply
|
Kenneth
|
5/18/2010 10:31:08 PM
|
|
sandeep wrote:
> What would you think of passing
> a NULL pointer to strlen or fclose?
It's real simple:
strlen takes a pointer to a string.
A strlen call any other argument causes undefined behavior.
The fclose function takes a pointer to an open file.
The free function takes any value returned by malloc.
NULL is one of the values which can be returned by malloc.
> Even
> if you can get away with it in the case of free,
> it is sloppy programming.
Your subject line is about trying get away with undefined
behavior because you're too lazy to check the return value of malloc.
Trying to get away with sloppy programming
is what you are all about.
--
pete
|
|
0
|
|
|
|
Reply
|
pete
|
5/18/2010 10:31:24 PM
|
|
Kenneth Brody <kenbrody@spamcop.net> writes:
> On 5/18/2010 2:05 PM, Richard Heathfield wrote:
>> sandeep wrote:
>>> Seebs writes:
> [... Passing NUL to free() ...]
>>>> It doesn't matter how easy or hard it is for the implementor to comply.
>>>> They are *REQUIRED* to correctly do nothing without crashing if a null
>>>> pointer is passed to free(). As it happens, this is very easy.
>>>
>>> You may get away with it,
>>
>> No, the language standard *requires* that you can get away with it.
>> There's no "may" about it.
>>
>>> but passing a NULL pointer to free is a mark of careless code, it
>>> should never happen.
>>
>> There, you have half a point. IIRC it's the first point you've made that
>> might reasonably be defended.
>
> Consider:
>
> * Any value returned my malloc() must be able to be passed to free().
> * malloc() can return NULL.
The first is an assumption, and I can't think of any strong
justification for it. Standard C happens to satisfy that assumption,
but it didn't need to.
> So, unless you were to write the standard to say, in effect, "you can
> only pass NULL to free() if malloc() has previously returned NULL".
> And then you have to take into account the case of multiple NULL
> returns from malloc().
Not at all. The standard could easily have been written to say
that free(NULL) invokes undefined behavior.
> After all, the following must be legal:
>
> char *foo = malloc(1000000); /* Returns NULL in our example */
> char *bar = malloc(1000000); /* Also returns NULL */
> free(foo);
> free(bar);
Why? I mean, it certainly is in standard C, but why *must* it be?
> Consider the ramifications of not explicitly allowing free(NULL).
The ramification is simply that you'd have to write:
char *foo = malloc(1000000);
char *bar = malloc(1000000);
if (foo != NULL) free(foo);
if (bar != NULL) free(bar);
or equivalent.
Consider, for example, that fopen() may return a null pointer, but
fclose(NULL) invokes undefined behavior. This is an inconsistency
in the language, but not a horrible one.
The real point, of course, is that *given* the fact that the C
standard guarantees that free(NULL) is harmless, and that there
are almost certainly no remaining C implementations in common use
where free(NULL) does anything bad, there's no good reason to go
out of your way to avoid calling free() with a null pointer argument.
On the other hand, if you need to test it anyway for some other
reason, you might as well make the free() call conditional on the
result of the test; there's no point in free()ing a pointer that
you *know* is null.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/18/2010 11:12:04 PM
|
|
On 2010-05-18, sandeep <nospam@nospam.com> wrote:
> Eric Sosman writes:
>> The ability to free(NULL) is convenient, not usually in that
>> exact guise but in a realloc(NULL,...) setting:
> I think you have a serious misunderstanding here.
You are, as usual, wrong about this.
> realloc does not always
> call free,
No one asserted that it did. However, there is no more reason for free(NULL)
to be a problem than for realloc(NULL, size) to be a problem.
> for example if it is passed a smaller size to reallocate. It
> is nice to be able to pass NULL to realloc to get extra functionality,
> with free there is no new functionality from passing NULL.
In neither case does it give "new functionality". What it gives you is
the ability to use a null pointer to represent a zero-size/unused allocation,
and "free" it so the code path is simpler.
>> The C Standard does not forbid either of these types of checks.
>> Indeed, division by zero *is* checked on *every* C implementation I've
>> used in the last three and a half decades.
> You may not have used many implementations then. Maybe the division is
> getting optimized out - try volatile. For example on gcc
>
> main()
> {
> volatile a=0;
> a=1/a;
> }
>
> Running this on Linux produces,
> Floating point exception
That sounds like it's checking! :)
> What would you think of passing a NULL pointer to strlen or fclose?
That would be undefined behavior.
> Even
> if you can get away with it in the case of free, it is sloppy programming.
No, it isn't. It's a supported, standard, feature, and there's no reason
not to use it. There's no particular reason to check for null pointers
before calling a function which has well-defined behavior with null pointers.
Consider:
/* error out, printing a message if one is provided */
void
err(char *s) {
fprintf(stderr, "an error occurred");
if (s) {
fprintf(stderr, ": %s\n", s);
}
fprintf(stderr, "\n");
exit(1);
}
Do you consider it "sloppy programming" to call this function with a
null pointer? It's *designed* to accept a null pointer. It is safe,
reliable, and clearly documented as to what it will do.
How about:
x = time(NULL);
It seems to me that you have one of the most dangerous flaws a novice
programmer can have -- you're too proud to admit mistakes. You made the
claim that it was unsafe to call free(NULL). You were wrong. Rather than
admit that you were wrong, you argued about reasons for which you thought
it was actually unsafe, going so far as to claim that the *language
definition* was incorrect and overlooked important things about how free()
was implemented.
And now, rather than admit that you were completely wrong, you're explaining
that it's "sloppy" of people to use a language feature as intended, and
arguing with people at length about it.
Start by admitting that you were wrong, and that you did not understand
the significance of something being the language definition, and if you want
to argue that it's bad style to call free() with a null pointer, it'll be an
interesting discussion. I disagree with your position, but it is legitimately
a matter of opinion. But as long as you seem to be using that style point
solely as a way to avoid admitting that you said things that were simply,
flatly, untrue, you're going to have a credibility gap.
You have to stop minding that sometimes you're wrong. It's great to try to
improve and to stop making mistakes. It's a bad thing to be unwilling to
admit that you made mistakes. Trust me! I am an *expert* in making
stupid mistakes when posting to Usenet!
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/19/2010 1:03:52 AM
|
|
"Phil Carmody" <thefatphil_demunged@yahoo.co.uk> wrote in message
news:878w7hbmsz.fsf@kilospaz.fatphil.org...
> Richard Heathfield <rjh@see.sig.invalid> writes:
>> Phil Carmody wrote:
>>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>>> Phil Carmody wrote:
>>>> <snip>
>>>>> An object being 'constructed'? Are you sure you were using C?
>>>> I construct objects very often in C. What's the problem?
>>>
>>> I don't remember using the word 'problem'.
>>
>> Are you being deliberately obtuse, or is this genuinely a maze of
>> twisty little misunderstandings, all different?
>
> One might say my understanding has been almost preternatural.
> I correctly worked out what language he was using, a language
> to which this newsgroup is not dedicated, despite the fact
> that he never explicitly stated it.
I didn't think it really needed to be mentioned, as it was not particularly
germane to the point I was making, as regards micro-optimizations.
Being more concerned about something like function call overhead, when
reworking one's algorithms can have much greater impact.
That's a concern independent of language.
Dennis
|
|
0
|
|
|
|
Reply
|
Dennis
|
5/19/2010 2:13:52 AM
|
|
Nisse Engstr�m wrote:
> On Mon, 17 May 2010 07:30:18 +0100, Richard Heathfield wrote:
>
>> In the UK, at a STOP sign (black text in red triangle on white
>> background), you are *required* to use your hand-brake, since you must
>> bring the vehicle to a complete and safe stop. (But of course you don't
>> use it /instead/ of the regular brakes - you use the footbrake to stop
>> the car, and then the handbrake to keep it stopped.)
>
> Are you sure about this?
Yes (except for the physical description, which I got hopelessly wrong),
unless you are an exceptionally fine driver.
> I don't recall reading anything
> about the hand-brake and Stop signs in the Highway Code,
Well, the Highway Code is not a full definition of the law, but it does
say "You MUST stop behind the line at a junction with a �Stop� sign and
a solid white line across the road" (para171). If you do not apply your
handbrake, you are basing your claim to having "stopped" on your ability
to hold the vehicle exactly on the biting point. Most people can't even
turn right properly, let alone keep a vehicle stationary on even a
fairly gentle slope - so any driving instructor worth his salt will beat
you about the head with a gearstick until you learn to apply the
handbrake at Stop signs *every time*. With the handbrake applied, you
know you've stopped. Otherwise, you're just guessing.
Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
your UK driving test, that's an automatic fail.
<snip>
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/19/2010 4:58:47 AM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> Nisse Engstr�m wrote:
[...]
>> I don't recall reading anything
>> about the hand-brake and Stop signs in the Highway Code,
>
> Well, the Highway Code is not a full definition of the law, but
> it does say "You MUST stop behind the line at a junction with a
> �Stop� sign and a solid white line across the road" (para171). If
> you do not apply your handbrake, you are basing your claim to
> having "stopped" on your ability to hold the vehicle exactly on
> the biting point. Most people can't even turn right properly, let
> alone keep a vehicle stationary on even a fairly gentle slope -
> so any driving instructor worth his salt will beat you about the
> head with a gearstick until you learn to apply the handbrake at
> Stop signs *every time*. With the handbrake applied, you know
> you've stopped. Otherwise, you're just guessing.
Do British cars have lousy brake pedals?
When I stop at a stop sign or a red light, I just press down on the
brake pedal firmly enough to keep the car from moving. No particular
guessing or finesse is required, even on a slope; it's not difficult
to apply sustantially more pressure than is required to stop the car.
> Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
> your UK driving test, that's an automatic fail.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/19/2010 7:18:21 AM
|
|
In article <lnhbm49x6q.fsf_-_@nuthaus.mib.org>, kst-u@mib.org says...
>
> Richard Heathfield <rjh@see.sig.invalid> writes:
> > Nisse Engstr�m wrote:
> [...]
> >> I don't recall reading anything
> >> about the hand-brake and Stop signs in the Highway Code,
> >
> > Well, the Highway Code is not a full definition of the law, but
> > it does say "You MUST stop behind the line at a junction with a
> > ?Stop? sign and a solid white line across the road" (para171). If
> > you do not apply your handbrake, you are basing your claim to
> > having "stopped" on your ability to hold the vehicle exactly on
> > the biting point. Most people can't even turn right properly, let
> > alone keep a vehicle stationary on even a fairly gentle slope -
> > so any driving instructor worth his salt will beat you about the
> > head with a gearstick until you learn to apply the handbrake at
> > Stop signs *every time*. With the handbrake applied, you know
> > you've stopped. Otherwise, you're just guessing.
>
> Do British cars have lousy brake pedals?
>
> When I stop at a stop sign or a red light, I just press down on the
> brake pedal firmly enough to keep the car from moving. No particular
> guessing or finesse is required, even on a slope; it's not difficult
> to apply sustantially more pressure than is required to stop the car.
>
> > Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
> > your UK driving test, that's an automatic fail.
We could be even safer by putting the transmission into park and turning
off the engine.
Then we can get out and chock the wheels.
;-)
|
|
0
|
|
|
|
Reply
|
Dann
|
5/19/2010 7:22:41 AM
|
|
"Dennis \(Icarus\)" <nojunkmail@ever.invalid> writes:
> "Phil Carmody" <thefatphil_demunged@yahoo.co.uk> wrote in message
> news:878w7hbmsz.fsf@kilospaz.fatphil.org...
>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>> Phil Carmody wrote:
>>>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>>>> Phil Carmody wrote:
>>>>> <snip>
>>>>>> An object being 'constructed'? Are you sure you were using C?
>>>>> I construct objects very often in C. What's the problem?
>>>>
>>>> I don't remember using the word 'problem'.
>>>
>>> Are you being deliberately obtuse, or is this genuinely a maze of
>>> twisty little misunderstandings, all different?
>>
>> One might say my understanding has been almost preternatural.
>> I correctly worked out what language he was using, a language
>> to which this newsgroup is not dedicated, despite the fact
>> that he never explicitly stated it.
>
> I didn't think it really needed to be mentioned, as it was not particularly
> germane to the point I was making, as regards micro-optimizations.
> Being more concerned about something like function call overhead, when
> reworking one's algorithms can have much greater impact.
> That's a concern independent of language.
If you consider the problem of arbitrarily-complicated functions
being automagically called when there's no explicit function call
in the code and where such processing may be undesirable to be
one of merely a 'function call overhead', then you have been
indoctrinated way too much.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/19/2010 7:50:19 AM
|
|
Keith Thompson wrote:
> Richard Heathfield <rjh@see.sig.invalid> writes:
<snip>
>> so any driving instructor worth his salt will beat you about the
>> head with a gearstick until you learn to apply the handbrake at
>> Stop signs *every time*. With the handbrake applied, you know
>> you've stopped. Otherwise, you're just guessing.
>
> Do British cars have lousy brake pedals?
No. (Well, no doubt some do!)
> When I stop at a stop sign or a red light, I just press down on the
> brake pedal firmly enough to keep the car from moving.
In the UK, driving instructors frown on this practice, because you're
not ready to move off (i.e. right foot poised over accelerator). This
would count, in the UK at least, as "inconsiderate driving", especially
if you're on a slight upward slope (so that the car starts to roll
backwards when you remove your foot from the brake to move it over to
the accelerator).
This would appear to be a real difference between ANSI driving and BSI
driving. In the UK, the practice you espouse is seen as poor style, and
*will* mean you fail if you do it on the UK driving test.
<snip>
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/19/2010 8:17:05 AM
|
|
Dann Corbit wrote:
> In article <lnhbm49x6q.fsf_-_@nuthaus.mib.org>, kst-u@mib.org says...
>> Richard Heathfield <rjh@see.sig.invalid> writes:
<snip>
>>> Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
>>> your UK driving test, that's an automatic fail.
>
> We could be even safer by putting the transmission into park and turning
> off the engine.
>
> Then we can get out and chock the wheels.
And we wouldn't get anywhere. But applying the handbrake suffices.
Similarly, we can be even safer with malloc by switching off the machine
and taking it back to the shop. But checking the return value suffices.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/19/2010 8:19:49 AM
|
|
Keith Thompson ha scritto:
> Richard Heathfield <rjh@see.sig.invalid> writes:
>> Nisse Engstr�m wrote:
> [...]
>>> I don't recall reading anything
>>> about the hand-brake and Stop signs in the Highway Code,
>> Well, the Highway Code is not a full definition of the law, but
>> it does say "You MUST stop behind the line at a junction with a
>> �Stop� sign and a solid white line across the road" (para171). If
>> you do not apply your handbrake, you are basing your claim to
>> having "stopped" on your ability to hold the vehicle exactly on
>> the biting point. Most people can't even turn right properly, let
>> alone keep a vehicle stationary on even a fairly gentle slope -
>> so any driving instructor worth his salt will beat you about the
>> head with a gearstick until you learn to apply the handbrake at
>> Stop signs *every time*. With the handbrake applied, you know
>> you've stopped. Otherwise, you're just guessing.
>
> Do British cars have lousy brake pedals?
>
> When I stop at a stop sign or a red light, I just press down on the
> brake pedal firmly enough to keep the car from moving. No particular
> guessing or finesse is required, even on a slope; it's not difficult
> to apply sustantially more pressure than is required to stop the car.
>
i dont know if in italy there is such a requirement to use the
handbrake, but my instructor taught me to stop at an upward sloping
crossing with footbrake *and* then to stay in position waiting for the
moment to move again using neither brakes, but simply using the minimum
force from the engine to avoid rolling down backawds :-)
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/19/2010 8:30:13 AM
|
|
superpollo ha scritto:
> Keith Thompson ha scritto:
>> Richard Heathfield <rjh@see.sig.invalid> writes:
>>> Nisse Engstr�m wrote:
>> [...]
>>>> I don't recall reading anything
>>>> about the hand-brake and Stop signs in the Highway Code,
>>> Well, the Highway Code is not a full definition of the law, but
>>> it does say "You MUST stop behind the line at a junction with a
>>> �Stop� sign and a solid white line across the road" (para171). If
>>> you do not apply your handbrake, you are basing your claim to
>>> having "stopped" on your ability to hold the vehicle exactly on
>>> the biting point. Most people can't even turn right properly, let
>>> alone keep a vehicle stationary on even a fairly gentle slope -
>>> so any driving instructor worth his salt will beat you about the
>>> head with a gearstick until you learn to apply the handbrake at
>>> Stop signs *every time*. With the handbrake applied, you know
>>> you've stopped. Otherwise, you're just guessing.
>>
>> Do British cars have lousy brake pedals?
>>
>> When I stop at a stop sign or a red light, I just press down on the
>> brake pedal firmly enough to keep the car from moving. No particular
>> guessing or finesse is required, even on a slope; it's not difficult
>> to apply sustantially more pressure than is required to stop the car.
>>
>
> i dont know if in italy there is such a requirement to use the
> handbrake, but my instructor taught me to stop at an upward sloping
> crossing with footbrake *and* then to stay in position waiting for the
> moment to move again using neither brakes, but simply using the minimum
> force from the engine to avoid rolling down backawds :-)
>
the point being, that doing so you can move off quickier when the time
comes.
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/19/2010 8:34:55 AM
|
|
On 05/19/10 08:30 PM, superpollo wrote:
> Keith Thompson ha scritto:
>>
>> When I stop at a stop sign or a red light, I just press down on the
>> brake pedal firmly enough to keep the car from moving. No particular
>> guessing or finesse is required, even on a slope; it's not difficult
>> to apply sustantially more pressure than is required to stop the car.
>>
>
> i dont know if in italy there is such a requirement to use the
> handbrake, but my instructor taught me to stop at an upward sloping
> crossing with footbrake *and* then to stay in position waiting for the
> moment to move again using neither brakes, but simply using the minimum
> force from the engine to avoid rolling down backawds :-)
Now you know why the Brits joke about Italian drivers!
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
Ian
|
5/19/2010 8:53:42 AM
|
|
On May 19, 2:18=A0am, Keith Thompson <ks...@mib.org> wrote:
> Richard Heathfield <r...@see.sig.invalid> writes:
> > Nisse Engstr=F6m wrote:
> [...]
> >> I don't recall reading anything
> >> about the hand-brake and Stop signs in the Highway Code,
>
> > Well, the Highway Code is not a full definition of the law, but
> > it does say "You MUST stop behind the line at a junction with a
> > =91Stop=92 sign and a solid white line across the road" (para171). If
> > you do not apply your handbrake, you are basing your claim to
> > having "stopped" on your ability to hold the vehicle exactly on
> > the biting point. Most people can't even turn right properly, let
> > alone keep a vehicle stationary on even a fairly gentle slope -
> > so any driving instructor worth his salt will beat you about the
> > head with a gearstick until you learn to apply the handbrake at
> > Stop signs *every time*. With the handbrake applied, you know
> > you've stopped. Otherwise, you're just guessing.
>
> Do British cars have lousy brake pedals?
>
> When I stop at a stop sign or a red light, I just press down on the
> brake pedal firmly enough to keep the car from moving. =A0No particular
> guessing or finesse is required, even on a slope; it's not difficult
> to apply sustantially more pressure than is required to stop the car.
No, they just have vastly more cars with manual transmissions (in the
US manual transmissions account for about 8-9% of vehicle sales - the
majority going into sports cars and ultra low end cars, in Europe,
OTOH, something like 75% of cars sold have manual transmissions). Nor
is using the handbrake for a stop on an incline really taught in the
US, except (occasionally) as a beginner technique for a standard
transmission, although I suspect in places like San Francisco it's
rather more popular.
Or you could just learn to heel-and-toe. ;-)
|
|
0
|
|
|
|
Reply
|
robertwessel2
|
5/19/2010 9:07:03 AM
|
|
On 17 May, 17:27, Richard Heathfield <r...@see.sig.invalid> wrote:
> io_x wrote:
<snip>
> > programming in about reduce text size
go Forth and, er Forth
> That's an interesting perspective, but not one that would keep you
> employed for very long. Other than as an interesting intellectual
> exercise (or, perhaps, as an IOCCC entry), a program that is optimised
> for source code length is of no use whatsoever. Programs should be
> readable, and readability *requires* redundancy.
isn't this the guy that used to post programs full of syntax
compressing macros?
#define I if
#define F for
#define M main
#define P printf
#define X exit (0)
M () {P("hello"); X;}
> > it is to say to the computer many little precise thing all togheter
>
> > if "?:" reduce the size of the prog at last of one char: it is ok
>
> Not if it is at the expense (and I do mean *expense*) of a reduction in
> readability.
so no APL then?
--
The beginning of wisdom for a [software engineer] is to recognize the
difference between getting a program to work, and getting it right.
-- M A Jackson, 1975
|
|
0
|
|
|
|
Reply
|
Nick
|
5/19/2010 9:19:00 AM
|
|
superpollo wrote:
) i dont know if in italy there is such a requirement to use the
) handbrake, but my instructor taught me to stop at an upward sloping
) crossing with footbrake *and* then to stay in position waiting for the
) moment to move again using neither brakes, but simply using the minimum
) force from the engine to avoid rolling down backawds :-)
With a manual transmission ? The mind boggles. Although I've seen
people do that in .nl as well; constantly rolling up and down trying
to stay in the same place, very annoying to look at.
How often do you have to replace the clutch plates in Italian cars ?
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
|
|
0
|
|
|
|
Reply
|
Willem
|
5/19/2010 9:31:05 AM
|
|
Willem ha scritto:
> superpollo wrote:
> ) i dont know if in italy there is such a requirement to use the
> ) handbrake, but my instructor taught me to stop at an upward sloping
> ) crossing with footbrake *and* then to stay in position waiting for the
> ) moment to move again using neither brakes, but simply using the minimum
> ) force from the engine to avoid rolling down backawds :-)
>
> With a manual transmission ? The mind boggles. Although I've seen
> people do that in .nl as well; constantly rolling up and down trying
> to stay in the same place, very annoying to look at.
that's why italians make jokes at nl drivers ;-)
> How often do you have to replace the clutch plates in Italian cars ?
errr... quite often i presume ... i should've changed them two or three
years ago, but i did not (to save money since i seldom use car) ... i
bought the car in 1993 and never replaced 'em ever since
bye
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/19/2010 10:01:44 AM
|
|
On 18 May, 19:05, Richard Heathfield <r...@see.sig.invalid> wrote:
> sandeep wrote:
> > Seebs writes:
> >> On 2010-05-17, sandeep <nos...@nospam.com> wrote:
<snip>
> > but passing a NULL pointer to free is a mark of
> > careless code, it should never happen.
>
> There, you have half a point. IIRC it's the first point you've made that
> might reasonably be defended.
#include <stdlib.h>
typedef struct
{
int id;
double param;
int busy;
} Equip;
Equip* allocate_stuff (int n)
{
int i;
Equip **array;
if ((array = malloc(n * sizeof(Equip*))) == NULL)
goto cleanup;
for (i = 0; i < n; i++)
if ((array[i] = malloc(sizeof(Equip))) == NULL)
goto cleanup;
return *array;
cleanup:
for (i = 0; i < n; i++)
free (array[i]);
free (array);
return NULL;
}
int main (void)
{
Equip *equip_list = allocate_stuff (100);
return equip_list ? 0 : 1;
}
[for extra points: how many deviations from the clc programming
standards are there in that code?]
|
|
0
|
|
|
|
Reply
|
Nick
|
5/19/2010 10:16:08 AM
|
|
sandeep <nospam@nospam.com> wrote:
> Seebs writes:
> > It doesn't matter how easy or hard it is for the implementor to comply.
> > They are *REQUIRED* to correctly do nothing without crashing if a null
> > pointer is passed to free(). As it happens, this is very easy.
>
> You may get away with it, but passing a NULL pointer to free is a mark of
> careless code, it should never happen.
Either you _are_ a troll, or you are simply ignorant about real C. In
either case, stop trying to tell your elders and betters how to suck
hard-boiled eggs.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/19/2010 10:29:14 AM
|
|
Keith Thompson <kst-u@mib.org> wrote:
> Kenneth Brody <kenbrody@spamcop.net> writes:
> > Consider:
> >
> > * Any value returned my malloc() must be able to be passed to free().
> > * malloc() can return NULL.
>
> The first is an assumption, and I can't think of any strong
> justification for it. Standard C happens to satisfy that assumption,
> but it didn't need to.
It is more elegant, and allows for more elegant code. Granted, it's not
a _compelling_ argument, but IMAO it's strong enough.
> Consider, for example, that fopen() may return a null pointer, but
> fclose(NULL) invokes undefined behavior. This is an inconsistency
> in the language, but not a horrible one.
And, indeed, it would have been more elegant if fclose(NULL) did
nothing. Presumably the reason why the Standard doesn't require this is
"existing implementations did not", but it would have been more elegant.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/19/2010 10:29:15 AM
|
|
Nick Keighley <nick_keighley_nospam@hotmail.com> writes:
> On 18 May, 19:05, Richard Heathfield <r...@see.sig.invalid> wrote:
>> sandeep wrote:
>> > Seebs writes:
>> >> On 2010-05-17, sandeep <nos...@nospam.com> wrote:
> <snip>
>> > but passing a NULL pointer to free is a mark of
>> > careless code, it should never happen.
>>
>> There, you have half a point. IIRC it's the first point you've made that
>> might reasonably be defended.
<snip>
> Equip* allocate_stuff (int n)
> {
> int i;
> Equip **array;
>
> if ((array = malloc(n * sizeof(Equip*))) == NULL)
> goto cleanup;
>
> for (i = 0; i < n; i++)
> if ((array[i] = malloc(sizeof(Equip))) == NULL)
> goto cleanup;
>
> return *array;
>
> cleanup:
> for (i = 0; i < n; i++)
> free (array[i]);
>
> free (array);
> return NULL;
> }
<snip>
> [for extra points: how many deviations from the clc programming
> standards are there in that code?]
Hmm... no idea, but it's undefined behaviour (in cleanup, array[i] is UB
when array == NULL and array[x] is UB for all x > i at the same point).
I do get your point (that it's easier just to free all the second-level
pointers) but an obvious alternative presents itself: i tells you how
many valid pointers there are. Just don't re-use it for the free loop.
Aside: this is one reason I like C99's 'for (int x = 0; ...)'. If used
consistently it can tell you more about the purpose of counters. The
first malloc loop would not use it, thereby indicating that it's counter
is used later.
--
Ben.
|
|
0
|
|
|
|
Reply
|
Ben
|
5/19/2010 11:15:34 AM
|
|
Nick Keighley wrote:
<snip>
> #include <stdlib.h>
>
> typedef struct
> {
> int id;
> double param;
> int busy;
> } Equip;
>
> Equip* allocate_stuff (int n)
> {
> int i;
> Equip **array;
>
> if ((array = malloc(n * sizeof(Equip*))) == NULL)
> goto cleanup;
>
> for (i = 0; i < n; i++)
> if ((array[i] = malloc(sizeof(Equip))) == NULL)
> goto cleanup;
>
> return *array;
>
> cleanup:
> for (i = 0; i < n; i++)
> free (array[i]);
Stop right there.
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/19/2010 11:42:51 AM
|
|
On Wed, 19 May 2010 00:18:21 -0700, Keith Thompson wrote:
> Richard Heathfield <rjh@see.sig.invalid> writes:
>>
>> Well, the Highway Code is not a full definition of the law, but
>> it does say "You MUST stop behind the line at a junction with a
>> �Stop� sign and a solid white line across the road" (para171). If
>> you do not apply your handbrake, you are basing your claim to
>> having "stopped" on your ability to hold the vehicle exactly on
>> the biting point. Most people can't even turn right properly, let
>> alone keep a vehicle stationary on even a fairly gentle slope -
>> so any driving instructor worth his salt will beat you about the
>> head with a gearstick until you learn to apply the handbrake at
>> Stop signs *every time*. With the handbrake applied, you know
>> you've stopped. Otherwise, you're just guessing.
>
> When I stop at a stop sign or a red light, I just press down on the
> brake pedal firmly enough to keep the car from moving. No particular
> guessing or finesse is required, even on a slope; it's not difficult
> to apply sustantially more pressure than is required to stop the car.
That's what we do, clutch and foot-break. Unless, of course,
you're stopping on a slope, but that's got nothing to do
with stop signs. We have plenty of stop signs in Sweden. In
particularly difficult or accident-prone locations, you may
even find junctions with stop signs in all directions.
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/19/2010 12:45:11 PM
|
|
On Wed, 19 May 2010 09:31:05 +0000 (UTC), Willem wrote:
> With a manual transmission ? The mind boggles. Although I've seen
> people do that in .nl as well; constantly rolling up and down trying
> to stay in the same place, very annoying to look at.
>
> How often do you have to replace the clutch plates in Italian cars ?
When I did my round of national service, my driving
instructor taught me how to shift gears without using
the clutch, but that only worked on their oldest
vehicles. :-)
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/19/2010 12:48:32 PM
|
|
Richard Heathfield wrote:
> Keith Thompson wrote:
>> Richard Heathfield <rjh@see.sig.invalid> writes:
>
> <snip>
>
>>> so any driving instructor worth his salt will beat you about the
>>> head with a gearstick until you learn to apply the handbrake at
>>> Stop signs *every time*. With the handbrake applied, you know
>>> you've stopped. Otherwise, you're just guessing.
....
>> When I stop at a stop sign or a red light, I just press down on the
>> brake pedal firmly enough to keep the car from moving.
>
> In the UK, driving instructors frown on this practice, because you're
> not ready to move off (i.e. right foot poised over accelerator).
Odd - that's precisely the reason why fully depressing the pedal was
favored by my driving instructors. By forcing you to take the time to
switch pedals, it avoids "jack-rabbit" starts, which can be dangerous if
some idiot runs the red light across your path right after your own
light turns green.
> ... This
> would count, in the UK at least, as "inconsiderate driving", ...
Again, odd - "jack-rabbit" starts are considered "inconsiderate driving"
in this country - which is not meant to imply that they're rare.
> ... especially
> if you're on a slight upward slope (so that the car starts to roll
> backwards when you remove your foot from the brake to move it over to
> the accelerator).
That doesn't happen with the automatic transmission vehicles I've used;
they always apply sufficient torque to prevent rolling backwards, even
before you hit the gas pedal. My own car has a manual transmission, and
learning how to avoid that backward roll was one of the most
ridiculously difficult things I had to learn during my first month or so
of using a manual transmission. However, it's now something I do so
automatically, I can't even explain how I do it - but it's not hard at
all to do except on the very steepest slopes. Slopes that steep are
quite rare in most of the areas I've driven (Los Angeles, and the
Washington DC metropolitan area).
|
|
0
|
|
|
|
Reply
|
James
|
5/19/2010 12:56:05 PM
|
|
On Wed, 19 May 2010, superpollo wrote:
> superpollo ha scritto:
>>
>> i dont know if in italy there is such a requirement to use the
>> handbrake, but my instructor taught me to stop at an upward sloping
>> crossing with footbrake *and* then to stay in position waiting for the
>> moment to move again using neither brakes, but simply using the minimum
>> force from the engine to avoid rolling down backawds :-)
The clutch friction disc industry must flourish in Italy.
> the point being, that doing so you can move off quickier when the time
> comes.
So this is what the expression "jack-rabbit start" means.
I knew driving styles differ from country to country, but I didn't imagine
local style would be ingrained in the driving curriculum this deeply.
(Personally I think Germans have the best cars, the best roads, and the
best driving style.)
Cheers,
lacos
|
|
0
|
|
|
|
Reply
|
Ersek
|
5/19/2010 1:19:10 PM
|
|
In article <Pine.LNX.4.64.1005191510180.23036@login01.caesar.elte.hu>,
"Ersek, Laszlo" <lacos@caesar.elte.hu> wrote:
> On Wed, 19 May 2010, superpollo wrote:
>
> > superpollo ha scritto:
> >>
> >> i dont know if in italy there is such a requirement to use the
> >> handbrake, but my instructor taught me to stop at an upward sloping
> >> crossing with footbrake *and* then to stay in position waiting for the
> >> moment to move again using neither brakes, but simply using the minimum
> >> force from the engine to avoid rolling down backawds :-)
>
> The clutch friction disc industry must flourish in Italy.
>
>
> > the point being, that doing so you can move off quickier when the time
> > comes.
>
> So this is what the expression "jack-rabbit start" means.
>
> I knew driving styles differ from country to country, but I didn't imagine
> local style would be ingrained in the driving curriculum this deeply.
>
> (Personally I think Germans have the best cars, the best roads, and the
> best driving style.)
Oh sure. That must be why I stepped out of a hotel in a German city a
few years ago, and watched a woman do a U-turn and smash straight into a
car coming the other way. I saw two other accidents during the total of
three days I was there.
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/19/2010 1:32:20 PM
|
|
In article <1ubvqgdl40aly.dlg@luden.se>,
Nisse Engstrom <news.NOSPAM.idf0Z@luden.se> wrote:
> On Wed, 19 May 2010 00:18:21 -0700, Keith Thompson wrote:
>
> > Richard Heathfield <rjh@see.sig.invalid> writes:
> >>
> >> Well, the Highway Code is not a full definition of the law, but
> >> it does say "You MUST stop behind the line at a junction with a
> >> ?Stop? sign and a solid white line across the road" (para171). If
> >> you do not apply your handbrake, you are basing your claim to
> >> having "stopped" on your ability to hold the vehicle exactly on
> >> the biting point. Most people can't even turn right properly, let
> >> alone keep a vehicle stationary on even a fairly gentle slope -
> >> so any driving instructor worth his salt will beat you about the
> >> head with a gearstick until you learn to apply the handbrake at
> >> Stop signs *every time*. With the handbrake applied, you know
> >> you've stopped. Otherwise, you're just guessing.
> >
> > When I stop at a stop sign or a red light, I just press down on the
> > brake pedal firmly enough to keep the car from moving. No particular
> > guessing or finesse is required, even on a slope; it's not difficult
> > to apply sustantially more pressure than is required to stop the car.
>
> That's what we do, clutch and foot-break. Unless, of course,
> you're stopping on a slope, but that's got nothing to do
> with stop signs. We have plenty of stop signs in Sweden. In
> particularly difficult or accident-prone locations, you may
> even find junctions with stop signs in all directions.
By and large stop signs are unnecessary, which is why we have so few of
them in the UK, and a very low accident rate. Road signs in the US
appeared to be intended to raise revenue, far as I could tell.
--
Tim
"That excessive bail ought not to be required, nor excessive fines imposed,
nor cruel and unusual punishments inflicted" -- Bill of Rights 1689
|
|
0
|
|
|
|
Reply
|
Tim
|
5/19/2010 1:39:43 PM
|
|
On 16 May 2010 09:38:42 GMT, Seebs wrote:
> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>
>> void* safeMalloc(size_t x)
>> {
>> static void* emrgcy=0;
>> void* x1;
>> #define ESIZE 0x40000000uLL
>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>> #undef ESIZE
>
> 4. Don't use "uLL" on a constant that's unambiguously within the size
> range of an ordinary signed long. You don't need any qualifier at all,
> although in theory a system could exist where that value is too big for
> size_t, in which case you'd be allocating 0 bytes.
The "uLL" suffix ensures that any integer arithmetic
performed on ESIZE is done using an unsigned type
(unless there are wider types). Without the suffix,
you may end up using signed arithmetic which might
cause unnecessary overflows.
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/19/2010 1:52:57 PM
|
|
Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:
> On 18 May, 19:05, Richard Heathfield <r...@see.sig.invalid> wrote:
> > sandeep wrote:
> > > but passing a NULL pointer to free is a mark of
> > > careless code, it should never happen.
> >
> > There, you have half a point. IIRC it's the first point you've made that
> > might reasonably be defended.
> typedef struct
> {
> } Equip;
>
> Equip* allocate_stuff (int n)
> {
> int i;
> Equip **array;
>
> if ((array = malloc(n * sizeof(Equip*))) == NULL)
> goto cleanup;
> cleanup:
> for (i = 0; i < n; i++)
> free (array[i]);
*Bzzt!* That _does_ deref a null pointer, if the first malloc() failed.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/19/2010 2:32:27 PM
|
|
On 2010-05-19, Nisse Engstr�m <news.NOSPAM.idf0Z@luden.se> wrote:
> On 16 May 2010 09:38:42 GMT, Seebs wrote:
>> On 2010-05-16, sandeep <nospam@nospam.com> wrote:
>>> void* safeMalloc(size_t x)
>>> {
>>> static void* emrgcy=0;
>>> void* x1;
>>> #define ESIZE 0x40000000uLL
>>> if(!(emrgcy=emrgcy?emrgcy:malloc(ESIZE)))
>>> #undef ESIZE
>> 4. Don't use "uLL" on a constant that's unambiguously within the size
>> range of an ordinary signed long. You don't need any qualifier at all,
>> although in theory a system could exist where that value is too big for
>> size_t, in which case you'd be allocating 0 bytes.
> The "uLL" suffix ensures that any integer arithmetic
> performed on ESIZE is done using an unsigned type
> (unless there are wider types). Without the suffix,
> you may end up using signed arithmetic which might
> cause unnecessary overflows.
If any arithmetic were ever to be performed on the value, this might
be a significant consideration. However, the key point I was making was
not about the U, which is reasonable, but about the LL, which is not.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/19/2010 2:32:53 PM
|
|
"Ersek, Laszlo" <lacos@caesar.elte.hu> wrote:
> (Personally I think Germans have the best cars, the best roads, and the
> best driving style.)
Hah! When I'm being tailgated, in 99% of the cases, it's either a German
make or a German plate.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/19/2010 4:19:11 PM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Do you think I'm the kind of idiot who would waste time doing that?
Why would _we_ be the kind of idiots who would waste time trying to
figure out what you mean when you speak in riddles - for the thatmuchth
time? Speak your mind, and speak it clearly, if you want to be
understood.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/19/2010 4:19:11 PM
|
|
Tim Streater ha scritto:
> In article <Pine.LNX.4.64.1005191510180.23036@login01.caesar.elte.hu>,
> "Ersek, Laszlo" <lacos@caesar.elte.hu> wrote:
>
>> On Wed, 19 May 2010, superpollo wrote:
>>
>>> superpollo ha scritto:
>>>> i dont know if in italy there is such a requirement to use the
>>>> handbrake, but my instructor taught me to stop at an upward sloping
>>>> crossing with footbrake *and* then to stay in position waiting for the
>>>> moment to move again using neither brakes, but simply using the minimum
>>>> force from the engine to avoid rolling down backawds :-)
>> The clutch friction disc industry must flourish in Italy.
>>
>>
>>> the point being, that doing so you can move off quickier when the time
>>> comes.
>> So this is what the expression "jack-rabbit start" means.
>>
>> I knew driving styles differ from country to country, but I didn't imagine
>> local style would be ingrained in the driving curriculum this deeply.
>>
>> (Personally I think Germans have the best cars, the best roads, and the
>> best driving style.)
>
> Oh sure. That must be why I stepped out of a hotel in a German city a
> few years ago, and watched a woman do a U-turn and smash straight into a
> car coming the other way. I saw two other accidents during the total of
> three days I was there.
maybe they were italians.
|
|
0
|
|
|
|
Reply
|
superpollo
|
5/19/2010 4:27:49 PM
|
|
Richard Bos wrote:
> "Ersek, Laszlo" <lacos@caesar.elte.hu> wrote:
>
>> (Personally I think Germans have the best cars, the best roads, and
>> the best driving style.)
>
> Hah! When I'm being tailgated, in 99% of the cases, it's either a
> German make or a German plate.
You know what happens if a Dutch fails driving test the 3rd time? He gets
the license, but has to drive a car with a yellow number plate... ;-)
Bye, Jojo
|
|
0
|
|
|
|
Reply
|
Joachim
|
5/19/2010 4:27:51 PM
|
|
On 2010-05-19, Joachim Schmitz <nospam.jojo@schmitz-digital.de> wrote:
> You know what happens if a Dutch fails driving test the 3rd time? He gets
> the license, but has to drive a car with a yellow number plate... ;-)
Driving tests scare me. Around here, you need a score of 70% to pass.
In theory, that ought to imply that, at any given time, there is a 30%
chance that any driver you see is about to swerve unexpectedly into you.
While in fact it doesn't quite work out that way, it pays off to be about
that cautious about other drivers.
.... Dragging this on topic: The same mostly goes for code. I find it very
rewarding to write code as though the people reading my code are likely to
be fairly inexperienced and a little careless. If nothing else, this saves
a lot of time if I have to work on the code later, because I am nearly
always a little careless. I have found extremes I'm not willing to go
to (I'm not going to try to accommodate people who don't know that C
statements end with semicolons, or who don't know what "extern" means),
but in general, as I get more experienced and better, I tend to write
more and more simply and clearly.
I gotta track down a thing I found recently, an actual roguelike game I
wrote in college, which is atrocious. Up to and including "void main".
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/19/2010 4:50:44 PM
|
|
On 19 May, 05:58, Richard Heathfield <r...@see.sig.invalid> wrote:
> Nisse Engstr=F6m wrote:
> > On Mon, 17 May 2010 07:30:18 +0100, Richard Heathfield wrote:
>
> >> In the UK, at a STOP sign (black text in red triangle on white
> >> background), you are *required* to use your hand-brake, since you must
> >> bring the vehicle to a complete and safe stop. (But of course you don'=
t
> >> use it /instead/ of the regular brakes - you use the footbrake to stop
> >> the car, and then the handbrake to keep it stopped.)
>
> > Are you sure about this?
>
> Yes (except for the physical description, which I got hopelessly wrong),
> unless you are an exceptionally fine driver.
>
> =A0> I don't recall reading anything
>
> > about the hand-brake and Stop signs in the Highway Code,
>
> Well, the Highway Code is not a full definition of the law, but it does
> say "You MUST stop behind the line at a junction with a =91Stop=92 sign a=
nd
> a solid white line across the road" (para171). If you do not apply your
> handbrake, you are basing your claim to having "stopped" on your ability
> to hold the vehicle exactly on the biting point. Most people can't even
> turn right properly, let alone keep a vehicle stationary on even a
> fairly gentle slope - so any driving instructor worth his salt will beat
> you about the head with a gearstick until you learn to apply the
> handbrake at Stop signs *every time*. With the handbrake applied, you
> know you've stopped. Otherwise, you're just guessing.
>
> Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
> your UK driving test, that's an automatic fail.
I don't agree with much of your post about UK expectations but the
last assertion is the worst! :-( Can you substantiate that last
(outrageous) statement?
James
|
|
0
|
|
|
|
Reply
|
James
|
5/19/2010 6:03:24 PM
|
|
James Harris wrote:
> On 19 May, 05:58, Richard Heathfield <r...@see.sig.invalid> wrote:
<snip>
>> Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
>> your UK driving test, that's an automatic fail.
>
> I don't agree with much of your post about UK expectations but the
> last assertion is the worst! :-( Can you substantiate that last
> (outrageous) statement?
I can't, but my driving examiner did!
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/19/2010 6:06:09 PM
|
|
io_x wrote:
> "Geoff" <geoff@invalid.invalid> ha scritto nel messaggio
<snip>
>> Programming is about correctness of the solution to a problem, not
>> about terseness.
>
> wrong, readability is due to terseness
Why did you put spaces in that sentence?
<snip>
--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within
|
|
0
|
|
|
|
Reply
|
Richard
|
5/19/2010 6:08:42 PM
|
|
"Geoff" <geoff@invalid.invalid> ha scritto nel messaggio
news:u292v5d98m538lgd49a971kv2m66is406g@4ax.com...
> On Mon, 17 May 2010 12:16:58 +0200, "io_x" <a@b.c.invalid> wrote:
>
>>
>>"Tim Harig" <usernet@ilthio.net> ha scritto nel messaggio > Note that
>>minimizing
>>code density doesn't create a smaller or faster
>>> binary. A concisely written ?: operator on a single line generates the
>>> same code as the equivilant if/else operators spread across multiple lines.
>>> The if/else version is almost always easier to read. "?:"'s are almost
>>> always the sign of an amateur programmer trying to show off their "l337
>>> skilz." Seasoned programmers have learned to avoid them.
>>
>>programming in about reduce text size
>>it is to say to the computer many little precise thing all togheter
>
> Programming is about correctness of the solution to a problem, not
> about terseness.
wrong, readability is due to terseness
> To obtain correctness and ease of maintenance the
> programmer should write it as though someone unfamiliar with the
> solution will be maintaining the code.
if someone has to modify unfamiliar code: better not put the hends on it
> The compiler will optimize the
> code. Yes, there are occasions when the solution has bottlenecks, but
> these are conceptual problems with the implementation of the solution,
> not the size of the code.
programmer is like one artisan, and the size of code should be important
(less code to debug)
>>if "?:" reduce the size of the prog at last of one char: it is ok
>>
>
> Simply false. As Tim stated above, if/else generates essentially the
> same machine code as ?: yet the latter is harder to read and maintain.
but the eye can see better one well indented "?:"
|
|
0
|
|
|
|
Reply
|
io_x
|
5/19/2010 6:11:37 PM
|
|
"Nick Keighley" <nick_keighley_nospam@hotmail.com> ha scritto nel messaggio
news:f91c296b-868e-4ee6-a6e2-91b06ab63058@i31g2000vbt.googlegroups.com...
> On 18 May, 19:05, Richard Heathfield <r...@see.sig.invalid> wrote:
>> sandeep wrote:
>> > Seebs writes:
>> >> On 2010-05-17, sandeep <nos...@nospam.com> wrote:
>
> <snip>
>
>> > but passing a NULL pointer to free is a mark of
>> > careless code, it should never happen.
>>
>> There, you have half a point. IIRC it's the first point you've made that
>> might reasonably be defended.
>
> #include <stdlib.h>
>
> typedef struct
> {
> int id;
> double param;
> int busy;
> } Equip;
>
> Equip* allocate_stuff (int n)
> {
> int i;
> Equip **array;
>
> if ((array = malloc(n * sizeof(Equip*))) == NULL)
> goto cleanup;
>
> for (i = 0; i < n; i++)
> if ((array[i] = malloc(sizeof(Equip))) == NULL)
> goto cleanup;
>
> return *array;
>
> cleanup:
> for (i = 0; i < n; i++)
> free (array[i]);
this is wrong
how do you know
1) that the fail is in the first
" if ((array = malloc(n * sizeof(Equip*))) == NULL)
goto cleanup;
"
2) if the first allocation is ok but for i==n-2
((array[i] = malloc(sizeof(Equip)))
and malloc fail how do you know that array[n-1]
is zero?
> free (array);
> return NULL;
> }
>
> int main (void)
> {
> Equip *equip_list = allocate_stuff (100);
> return equip_list ? 0 : 1;
> }
>
> [for extra points: how many deviations from the clc programming
> standards are there in that code?]
#include <stdlib.h>
typedef struct{
double param;
int id;
int busy;
} Equip;
Equip* allocate_stuff(int n)
{int i;
Equip **array;
i=0;
if ((array = malloc(n * sizeof(Equip*)))== NULL)
goto cleanup;
for(; i<n; ++i)
if((array[i] = malloc(sizeof(Equip))) == NULL)
goto cleanup;
return *array;
cleanup:
for(--i; i>=0 ; i--)
free(array[i]);
free(array);
return NULL;
}
int main(void)
{Equip *equip_list = allocate_stuff(100);
return equip_list ? 0 : 1;
}
but don't know if it is right
|
|
0
|
|
|
|
Reply
|
io_x
|
5/19/2010 6:11:45 PM
|
|
"Nick Keighley" <nick_keighley_nospam@hotmail.com> ha scritto nel messaggio
news:36150851-b242-4945-8dd7-c620ed8cfc3d@z17g2000vbd.googlegroups.com...
> On 17 May, 17:27, Richard Heathfield <r...@see.sig.invalid> wrote:
>> io_x wrote:
>
> <snip>
>
>> > programming in about reduce text size
>
> go Forth and, er Forth
>
>> That's an interesting perspective, but not one that would keep you
>> employed for very long.
so i'm free and you have to follow your bos that you never choose
>> Other than as an interesting intellectual
>> exercise (or, perhaps, as an IOCCC entry), a program that is optimised
>> for source code length is of no use whatsoever. Programs should be
>> readable, and readability *requires* redundancy.
>
> isn't this the guy that used to post programs full of syntax
> compressing macros?
>
> #define I if
> #define F for
> #define M main
> #define P printf
> #define X exit (0)
>
> M () {P("hello"); X;}
if you use above number 5 macros for one week you will see that you can read
them better than the formed words
>> > it is to say to the computer many little precise thing all togheter
>>
>> > if "?:" reduce the size of the prog at last of one char: it is ok
>>
>> Not if it is at the expense (and I do mean *expense*) of a reduction in
>> readability.
>
> so no APL then?
APL? is it a language with many symbols? how many are them?
perhaps they are too much, for the poor memory of the programmer
> --
> The beginning of wisdom for a [software engineer] is to recognize the
> difference between getting a program to work, and getting it right.
> -- M A Jackson, 1975
|
|
0
|
|
|
|
Reply
|
io_x
|
5/19/2010 6:11:56 PM
|
|
"Eric Sosman" ha scritto nel messaggio
news:hsv2hf$6s0$1@news.eternal-september.org...
.....
> Also, some realloc() implementations will *always* move the region,
> even when shrinking it or when there happens to be a "gap" right after
> it. And then they'll fill the old region with 0xDEADBEEF or some such.
> Can you guess why a realloc() implementor would choose to do this?
is it because someone in a debugger can see the memory is not free?
(it seems useful because if free() not release the memory to the OS
that memory here not seg-fault if read or write, even if it should be not used)
|
|
0
|
|
|
|
Reply
|
io_x
|
5/19/2010 6:12:02 PM
|
|
Richard Heathfield <rjh@see.sig.invalid> writes:
> io_x wrote:
>> "Geoff" <geoff@invalid.invalid> ha scritto nel messaggio
> <snip>
>
>>> Programming is about correctness of the solution to a problem, not
>>> about terseness.
>>
>> wrong, readability is due to terseness
>
> Why did you put spaces in that sentence?
>
> <snip>
And vowels?
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/19/2010 6:39:11 PM
|
|
On 19 May, 19:06, Richard Heathfield <r...@see.sig.invalid> wrote:
> James Harris wrote:
> > On 19 May, 05:58, Richard Heathfield <r...@see.sig.invalid> wrote:
>
> <snip>
>
> >> Oh, by the way - if you *don't* apply your handbrake at a Stop sign on
> >> your UK driving test, that's an automatic fail.
>
> > I don't agree with much of your post about UK expectations but the
> > last assertion is the worst! :-( Can you substantiate that last
> > (outrageous) statement?
>
> I can't, but my driving examiner did!
LOL
FWIW according to
http://www.direct.gov.uk/en/Motoring/LearnerAndNewDrivers/PracticalTest/DG_4022540
"You can make up to 15 driving faults and still pass the test. 16 or
more faults results in failure. If you commit one serious or dangerous
fault you will fail the test."
Fifteen sounds quite generous!
James
|
|
0
|
|
|
|
Reply
|
James
|
5/19/2010 7:03:09 PM
|
|
On 5/19/2010 2:12 PM, io_x wrote:
> "Eric Sosman" ha scritto nel messaggio
> news:hsv2hf$6s0$1@news.eternal-september.org...
> ....
>> Also, some realloc() implementations will *always* move the region,
>> even when shrinking it or when there happens to be a "gap" right after
>> it. And then they'll fill the old region with 0xDEADBEEF or some such.
>> Can you guess why a realloc() implementor would choose to do this?
>
> is it because someone in a debugger can see the memory is not free?
> (it seems useful because if free() not release the memory to the OS
> that memory here not seg-fault if read or write, even if it should be not used)
You're the "terseness is readability" guy, right?
thatsthegeneralideayesthegoalistoexposeanydanglingpointersthat
mightstillbepointingtothememoryasitwasbeforereallocbymovingthe
blockunconditionallyreallocinvalidatesthestalepointerseverytime
andbywipingoutwhateverformerlyinhabitedtheoldblockreallocmakes
itmorelikelythatastalepointeraccessingthatdatawillcausevisible
troublehopethisisterseenoughforyou
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/19/2010 7:49:35 PM
|
|
On 2010-05-17, bart.c <bartc@freeuk.com> wrote:
> "Geoff" <geoff@invalid.invalid> wrote in message
> news:u292v5d98m538lgd49a971kv2m66is406g@4ax.com...
>> On Mon, 17 May 2010 12:16:58 +0200, "io_x" <a@b.c.invalid> wrote:
>>>if "?:" reduce the size of the prog at last of one char: it is ok
>> Simply false. As Tim stated above, if/else generates essentially the
>> same machine code as ?: yet the latter is harder to read and maintain.
> And when ?: is properly embedded in an expression:
>
> f( p+(a?b:c)+q*(d?e:g))
>
> then I'd really like to see your version using if/else.
Ignoring the visual clues provided by the indentation that there is a
divergence of execution control, your complex statment, while contrived,
doesn't explain why you are doing what you are doing. Why is it that you
might need to total what amounts to different values under *four* different
circumstances?
/* explanation 1 */
if (descriptive_condition_1)
descriptive_name_1 = descriptive_name_2;
else
descriptive_name_1 = descriptive_name_3;
/* explanation 2 */
if (descriptive_condtion_2)
descriptive_name_4 = descriptive_name_5;
else
descriptive_name_4 = descriptive_name_6;
/* explaination 3 */
descriptive_name_7 = descriptive_name2 * descriptive_name_8
vert_object(descriptive_name_9 + descriptive_name_1 + descriptive_name_7);
Notice how much more room there is for comments. Each operation is
logically split into smaller units that can be individually commented
and understood before preceding. Furthermore, the additional and constructive
use of variable names provides even more information about the values that
we are using. This approaches self documentation.
You are so impressed that you saved a little bit of typing; but, what
happens down the road when we find that there are three numbers that could
be associated with x or that if a, we really need to do three things to
know that x should be? Then you have rearange all of this section of
code (not to mention the comments) to accomadate the changes instead of
just adding additional steps to the single unit that is functionally
affected by the change.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 2:02:48 AM
|
|
On 2010-05-18, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> "bart.c" <bartc@freeuk.com> writes:
> Why? If the thing that makes the line unreadable is the
> fact that it's *embedded in an expression*, then the solution
> is to simply *not embed it in an expression*.
>
> int base_foo = a?b:c; /* or ``a ? b : c'' if you like airy code */
> int scaled_foo = d?e:g;
> f(p + base_foo + q*scaled_foo);
Then what have you gained over just using if/else? A couple of lines in a
source code file? A couple hours pay finding a bug because somebody
overlooked the conditional as a simple statement, while quickly scanning
the code?
> There's nothing intrinsically less readable about the ternary ? :
> operator than multiple assignments in an if/else.
Intrinsically, maybe not. Practically, the difference can be rather
profound. The simple fact of the matter is that people are much more used
to seeing if/else syntax then ?: and they have become better at visually
processing it. if/else can do things that cannot be legibly done with ?:
so why require somebody reading your code to read two different types of
constructs when one will handle them both.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 2:21:08 AM
|
|
"Eric Sosman" ha scritto nel messaggio
news:ht1fdl$do2$1@news.eternal-september.org...
> On 5/19/2010 2:12 PM, io_x wrote:
>> "Eric Sosman" ha scritto nel messaggio
>> news:hsv2hf$6s0$1@news.eternal-september.org...
>> ....
>>> Also, some realloc() implementations will *always* move the region,
>>> even when shrinking it or when there happens to be a "gap" right after
>>> it. And then they'll fill the old region with 0xDEADBEEF or some such.
>>> Can you guess why a realloc() implementor would choose to do this?
>>
>> is it because someone in a debugger can see the memory is not free?
>> (it seems useful because if free() not release the memory to the OS
>> that memory here not seg-fault if read or write, even if it should be not
>> used)
>
> You're the "terseness is readability" guy, right?
the conventions for speak languge win on what could be;
here a traslation:
> thats the general idea yes the goal isto expose any dangling pointers that
> might still be pointing to them emorya sit was beforere alloc bymoving the
> block unconditionally realloc invalidates thes talepointerse very time
> and by wipin gout what ever formerly inhabited theol dblock realloc makes
> it morelike lythata stale pointer accessing that datawill cause visible
> trouble hope this isterse enough for you
>
> --
> Eric Sosman
> esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
io_x
|
5/20/2010 6:23:32 AM
|
|
"io_x" <a@b.c.invalid> ha scritto nel messaggio
news:4bf427a4$0$12125$4fafbaef@reader4.news.tin.it...
>
> "Nick Keighley" <nick_keighley_nospam@hotmail.com> ha scritto nel messaggio
> news:f91c296b-868e-4ee6-a6e2-91b06ab63058@i31g2000vbt.googlegroups.com...
>> On 18 May, 19:05, Richard Heathfield <r...@see.sig.invalid> wrote:
>>> sandeep wrote:
>>> > Seebs writes:
>>> >> On 2010-05-17, sandeep <nos...@nospam.com> wrote:
>>
>> <snip>
>>
>>> > but passing a NULL pointer to free is a mark of
>>> > careless code, it should never happen.
>>>
>>> There, you have half a point. IIRC it's the first point you've made that
>>> might reasonably be defended.
>>
>> #include <stdlib.h>
>>
>> typedef struct
>> {
>> int id;
>> double param;
>> int busy;
>> } Equip;
>>
>> Equip* allocate_stuff (int n)
>> {
>> int i;
>> Equip **array;
>>
>> if ((array = malloc(n * sizeof(Equip*))) == NULL)
>> goto cleanup;
>>
>> for (i = 0; i < n; i++)
>> if ((array[i] = malloc(sizeof(Equip))) == NULL)
>> goto cleanup;
>>
>> return *array;
>>
>> cleanup:
>> for (i = 0; i < n; i++)
>> free (array[i]);
>
> this is wrong
> how do you know
> 1) that the fail is in the first
> " if ((array = malloc(n * sizeof(Equip*))) == NULL)
> goto cleanup;
> "
>
> 2) if the first allocation is ok but for i==n-2
> ((array[i] = malloc(sizeof(Equip)))
> and malloc fail how do you know that array[n-1]
> is zero?
>
>> free (array);
>> return NULL;
>> }
>>
>> int main (void)
>> {
>> Equip *equip_list = allocate_stuff (100);
here seems equip_list is not a useful address, right?
>> return equip_list ? 0 : 1;
>> }
>>
here i would write one of these choices:
------------------------------
#include <stdlib.h>
typedef struct{
double param;
int id;
int busy;
} Equip;
Equip* allocate_stuff(int n)
{return malloc(n* sizeof(Equip));}
int main(void)
{int i;
Equip *equip_list;
equip_list=allocate_stuff(100);
if(equip_list==0) return 1;
for(i=0; i<100; ++i) // how access it
equip_list[i].id=9;
return 0;
}
-----------------
#include <stdlib.h>
typedef struct{
double param;
int id;
int busy;
} Equip;
Equip** allocate_stuff(int n)
{int i;
Equip **array;
i=0;
if ((array = malloc(n * sizeof(Equip*)))== NULL)
goto cleanup;
for(; i<n; ++i)
if((array[i]=malloc(sizeof(Equip))) == NULL)
goto cleanup;
return array;
cleanup:
for(--i; i>=0 ; i--)
free(array[i]);
free(array);
return NULL;
}
int main(void)
{int i;
Equip **equip_list;
equip_list=allocate_stuff(100);
if(equip_list==0) return 1;
for(i=0; i<100; ++i) // how access it
equip_list[i]->id=9;
return 0;
}
|
|
0
|
|
|
|
Reply
|
io_x
|
5/20/2010 6:23:32 AM
|
|
On 20 May, 03:21, Tim Harig <user...@ilthio.net> wrote:
> On 2010-05-18, Phil Carmody <thefatphil_demun...@yahoo.co.uk> wrote:
> > "bart.c" <ba...@freeuk.com> writes:
> > Why? If the thing that makes the line unreadable is the
> > fact that it's *embedded in an expression*, then the solution
> > is to simply *not embed it in an expression*.
>
> > int base_foo =3D a?b:c; /* or ``a ? b : c'' if you like airy code */
I do find the airy code easier on the eye
> > int scaled_foo =3D d?e:g;
> > f(p + base_foo + q*scaled_foo);
>
> Then what have you gained over just using if/else? =A0
elegance?
> A couple of lines in a
> source code file? =A0A couple hours pay finding a bug because somebody
> overlooked the conditional as a simple statement, while quickly scanning
> the code?
you might as well argue that { and } and we should all code in Pascal
(actually I /do/ think we should all code in pascal, but that's
another issue :-) )
> > There's nothing intrinsically less readable about the ternary ? :
> > operator than multiple assignments in an if/else.
>
> Intrinsically, maybe not. =A0Practically, the difference can be rather
> profound. =A0The simple fact of the matter is that people are much more u=
sed
> to seeing if/else syntax then ?:
rather depends on the code base doesn't it?
> and they have become better at visually processing it.
do you have figures from cognitive scientists and psychologists?
>=A0if/else can do things that cannot be legibly done with ?:
and vice versa. They are different constructs
> so why require somebody reading your code to read two different types of
> constructs when one will handle them both.
not really. I was always a fan of Algol-60s (and Coral's)
x :=3D if a =3D b then 1 else 2;
that is they used the same keywords for both if/elses. Only yesterday
I was arguing with someone who said this was a bad idea and the two
forms /should/ be distinguished.
--
The use of the Chomsky formalism is also responsible for the term
"programming language", because programming languages seemed to
exhibit a strucure similar to spoken languages. We believe that
this term is rather unfortunate on the whole, because a programming
language is not spoken, and therefore is not a language in the true
sense of the word. Formalism or formal notation would have been
more appropriate terms.
Niklaus Wirth
|
|
0
|
|
|
|
Reply
|
Nick
|
5/20/2010 6:57:37 AM
|
|
Tim Harig <usernet@ilthio.net> writes:
> On 2010-05-18, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>> "bart.c" <bartc@freeuk.com> writes:
>> Why? If the thing that makes the line unreadable is the
>> fact that it's *embedded in an expression*, then the solution
>> is to simply *not embed it in an expression*.
>>
>> int base_foo = a?b:c; /* or ``a ? b : c'' if you like airy code */
>> int scaled_foo = d?e:g;
>> f(p + base_foo + q*scaled_foo);
>
> Then what have you gained over just using if/else?
Simpler, more readable code, as there's only one assignment to
each of the intermediate values rather than two.
Simpler, more readable code, as the definition of the intermediate
values is accompanied by the initialisation thereof.
> A couple of lines in a
> source code file? A couple hours pay finding a bug because somebody
> overlooked the conditional as a simple statement
The whole point of the statement is the conditional expression -
anyone who's going to overlook that has no right to be looking
at the code.
> , while quickly scanning the code?
>
>> There's nothing intrinsically less readable about the ternary ? :
>> operator than multiple assignments in an if/else.
>
> Intrinsically, maybe not. Practically, the difference can be rather
> profound. The simple fact of the matter is that people are much more used
> to seeing if/else syntax then ?: and they have become better at visually
> processing it.
Care to cite any scientific studies that back up that claim?
You may get confused by ?:, but I've got a multimegaline codebase
at $DAYJOB which proves that your weakness isn't shared by too
many people.
> if/else can do things that cannot be legibly done with ?:
> so why require somebody reading your code to read two different types of
> constructs when one will handle them both.
Why require someone to match the targets of two separate assignments?
And why require people to separate definition from initialisation?
Goto can do things that cannot legibly be done with if/else,
so why require someone reading your code to read two different
types of control flow when one will handle them both. (Included
ironically purely to show how flawed your logic is.)
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/20/2010 7:12:04 AM
|
|
On 20 May, 07:57, Nick Keighley <nick_keighley_nos...@hotmail.com>
wrote:
> you might as well argue that { and }
[should be replaced with "begin" and "end"]
> and we should all code in Pascal
> (actually I /do/ think we should all code in pascal, but that's
> another issue :-) )
read what I meant to write...
|
|
0
|
|
|
|
Reply
|
Nick
|
5/20/2010 7:35:41 AM
|
|
On 19 May 2010 14:32:53 GMT, Seebs wrote:
> On 2010-05-19, Nisse Engström <news.NOSPAM.idf0Z@luden.se> wrote:
>
>> The "uLL" suffix ensures that any integer arithmetic
>> performed on ESIZE is done using an unsigned type
>> (unless there are wider types). Without the suffix,
>> you may end up using signed arithmetic which might
>> cause unnecessary overflows.
>
> If any arithmetic were ever to be performed on the value, this might
> be a significant consideration. However, the key point I was making was
> not about the U, which is reasonable, but about the LL, which is not.
I tend to agree. I think I was thinking "unsigned long"
perhaps.
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/20/2010 10:15:30 AM
|
|
"Tim Harig" <usernet@ilthio.net> wrote in message
news:slrnhv95vm.64j.usernet@rutherford.ilthio.net...
> On 2010-05-17, bart.c <bartc@freeuk.com> wrote:
>> "Geoff" <geoff@invalid.invalid> wrote in message
>> news:u292v5d98m538lgd49a971kv2m66is406g@4ax.com...
>>> On Mon, 17 May 2010 12:16:58 +0200, "io_x" <a@b.c.invalid> wrote:
>>>>if "?:" reduce the size of the prog at last of one char: it is ok
>>> Simply false. As Tim stated above, if/else generates essentially the
>>> same machine code as ?: yet the latter is harder to read and maintain.
>> And when ?: is properly embedded in an expression:
>>
>> f( p+(a?b:c)+q*(d?e:g))
>>
>> then I'd really like to see your version using if/else.
>
> Ignoring the visual clues provided by the indentation that there is a
> divergence of execution control, your complex statment, while contrived,
> doesn't explain why you are doing what you are doing. Why is it that you
> might need to total what amounts to different values under *four*
> different
> circumstances?
I use this construction all the time, although mostly in a different
language and mostly associated with strings (when there are two or more in
the same expression). An example in C might be:
printf("%d %s in %d
Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
> Notice how much more room there is for comments.
What comments would be needed in examples such as above?
> Each operation is
> logically split into smaller units that can be individually commented
> and understood before preceding. Furthermore, the additional and
> constructive
> use of variable names provides even more information about the values that
> we are using. This approaches self documentation.
> You are so impressed that you saved a little bit of typing; but, what
> happens down the road when we find that there are three numbers that could
> be associated with x or that if a, we really need to do three things to
> know that x should be?
Well in another language I would use: m:=(n|"One","Two","Three"|"Other")...
In C, you might use: m = (n>=1 && n<=3 ? table[n] : "Other");
But, this involves setting up the table[] data, which is a nuisance if it is
only used in this one place. So this is more an argument for extending the
?: feature into a multi-selection operator...
Your solution would presumably be:
switch (n) {
case 1: m="One"; break
case 2: m="Two"; break
case 3: m="Three"; break
default: m="Other";
}
Which would be a major distraction when your code has bigger fish to fry
than just setting a variable to one of 4 values...
--
Bartc
|
|
0
|
|
|
|
Reply
|
bart
|
5/20/2010 12:02:44 PM
|
|
On Tue, 18 May 2010 20:11:58 +0000 (UTC), sandeep wrote:
> with free there is no new functionality from passing NULL.
It allows you to write cleaner code:
p = malloc (...);
q = malloc (...);
r = malloc (...);
s = malloc (...);
if (p && q && r && s) {
/* ok */
} else {
/* error */
}
free (p);
free (q);
free (r);
free (s);
The cleanup would be a lot bulkier (to me) if I had to
add an if() statement before every free().
> What would you think of passing a NULL pointer to strlen or fclose? Even
> if you can get away with it in the case of free, it is sloppy programming.
I wish fclose(NULL) had the same semantics as free(NULL),
but unfortunately it doesn't. I try to always follow the
free(NULL) pattern when I write code that allocates/frees
some kind of resource. There's nothing sloppy about it.
It's just the cleanest and most obvious way to do it (in
my mind).
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/20/2010 12:19:19 PM
|
|
On 2010-05-20, Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:
> On 20 May, 03:21, Tim Harig <user...@ilthio.net> wrote:
>> On 2010-05-18, Phil Carmody <thefatphil_demun...@yahoo.co.uk> wrote:
>> > "bart.c" <ba...@freeuk.com> writes:
>> > Why? If the thing that makes the line unreadable is the
>> > fact that it's *embedded in an expression*, then the solution
>> > is to simply *not embed it in an expression*.
>>
>> > int base_foo = a?b:c; /* or ``a ? b : c'' if you like airy code */
>> > int scaled_foo = d?e:g;
>> > f(p + base_foo + q*scaled_foo);
>>
>> Then what have you gained over just using if/else? �
>
> elegance?
Funny that you do not make any comments where I placed my counter-example
at <slrnhv95vm.64j.usernet@rutherford.ilthio.net>. I made the main part of
my argument there.
>> A couple of lines in a
>> source code file? �A couple hours pay finding a bug because somebody
>> overlooked the conditional as a simple statement, while quickly scanning
>> the code?
>
> you might as well argue that { and } and we should all code in Pascal
> (actually I /do/ think we should all code in pascal, but that's
> another issue :-) )
I also code in Ada. I have not problems with begin/end style syntax; but,
{} works just as well for me. The difference is that I see less abuse of
{} then I do of ?:.
>> > There's nothing intrinsically less readable about the ternary ? :
>> > operator than multiple assignments in an if/else.
>>
>> Intrinsically, maybe not. �Practically, the difference can be rather
>> profound. �The simple fact of the matter is that people are much more used
>> to seeing if/else syntax then ?:
>
> rather depends on the code base doesn't it?
1. Reference: _Writing Solid Code_ by Steve Maguire, "The ?: is an If
Statement Too", p.127-130 and "APL Syndrome", p.161-162
2. I have too much experience with programmers abusing ?: only to write
errors. When I rewrite the code using if/else syntax and give them both
copies, they see their own errors much faster using the normal if/else
syntax.
>> and they have become better at visually processing it.
> do you have figures from cognitive scientists and psychologists?
I have 1 expert opinion, quite a bit of person experience with poorly
written ?: constructs, and another simple demonstration. Properly blocked
and indented code, even smeared out so the text and operators are not
visual, provides a number of clues about the code. This technique has been
used in studies for testing the readability of different indent styles and
block layouts.
See _Code Complete_ by Steve McConnell, section 18.2-4 "Layout
Techniques", p.408-410; section 18-3 "Layout Styles", p.410-417; and
section 18.4 "Laying Out Control Structures" p.417-425.
Using this method if/if else/else control structures are clearly visible in
the code even though the actual operators are obscured. ?:'s on a single
line just look like statements. Furthermore, the indentation style is
consistant with the style of the rest of the control statements used in the
code. ?: breaks this consistancy.
>> constructs when one will handle them both.
> not really. I was always a fan of Algol-60s (and Coral's)
>
> x := if a = b then 1 else 2;
>
> that is they used the same keywords for both if/elses. Only yesterday
> I was arguing with someone who said this was a bad idea and the two
> forms /should/ be distinguished.
The ?: implies that a value is changing. That isn't always the case.
Sometimes you just want to use a different set of steps based on the
conditional. ?:, because it implys this changing value, doesn't work
clearly in this condition. If/else handles this flawlessly.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 12:22:36 PM
|
|
"bart.c" <bartc@freeuk.com> wrote:
> "Tim Harig" <usernet@ilthio.net> wrote in message
> > Ignoring the visual clues provided by the indentation that there is a
> > divergence of execution control, your complex statment, while contrived,
> > doesn't explain why you are doing what you are doing. Why is it that you
> > might need to total what amounts to different values under *four*
> > different circumstances?
>
> I use this construction all the time, although mostly in a different
> language and mostly associated with strings (when there are two or more in
> the same expression). An example in C might be:
>
> printf("%d %s in %d
> Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
Yes, but in that case - at least, with saner spatiation - the meaning of
the ?:s is directly apparent. Each of them is a simple, obvious binary
choice. I'd prefer to see it written like this, though:
printf("%d File%s in %d Director%s\n", nfiles, nfiles=1?"" :"s",
ndirs, ndirs=1 ?"y":"ies");
(although my indentation is, in this case, geared specifically towards
this one case; if the two ?:s weren't so directly parallel, I'd move
ndirs et seq. further left.)
The point is, though, that this is much different from a more involved
and seemingly random
f( p+(a?b:c)+q*(d?e:g))
which does _not_ tell you what the ?:s mean at all.
Richard
|
|
0
|
|
|
|
Reply
|
raltbos
|
5/20/2010 12:22:40 PM
|
|
On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Tim Harig <usernet@ilthio.net> writes:
>> On 2010-05-18, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>> "bart.c" <bartc@freeuk.com> writes:
>>> Why? If the thing that makes the line unreadable is the
>>> fact that it's *embedded in an expression*, then the solution
>>> is to simply *not embed it in an expression*.
>>>
>>> int base_foo = a?b:c; /* or ``a ? b : c'' if you like airy code */
>>> int scaled_foo = d?e:g;
>>> f(p + base_foo + q*scaled_foo);
>>
>> Then what have you gained over just using if/else?
>
> Simpler, more readable code, as there's only one assignment to
> each of the intermediate values rather than two.
Here you have small point; however, it doesn't overcome the list of other
points against it such as lack of layout consistency, the requirement for
reading separate operators, the missuse of code packed to tightly as part
of "APL Syndrome", the lack of clear established reason when being used as
part of complex statements.
> Simpler, more readable code, as the definition of the intermediate
> values is accompanied by the initialisation thereof.
Nobody commented on my main argument at
<slrnhv95vm.64j.usernet@rutherford.ilthio.net> where this was addressed.
People tend to use ?: as part of expressions without proper documentation
as to why they are using it. This is less likely to happen when used as a
separate block unit which can understood separately and is more likely to
be commented properly.
>> A couple of lines in a
>> source code file? A couple hours pay finding a bug because somebody
>> overlooked the conditional as a simple statement
[rearranged back to original form]
>> , while quickly scanning the code?
>
> The whole point of the statement is the conditional expression -
> anyone who's going to overlook that has no right to be looking
> at the code.
Even looking at properly formated source code which has been blacked
out so that only the whitespace is visible, it is possible to see the
conditionals because of their indent styles. ?: breaks this; Using the
same method, ?: will almost always be confused for a simple statement
because it does not follow the same indent pattern. Consistency is
desireable in its own right; but, in this case it is easy and I have seen
good programmers miss ?:'s while scanning code because it doesn't have the
same indented block pattern as every other control structure.
>>> There's nothing intrinsically less readable about the ternary ? :
>>> operator than multiple assignments in an if/else.
>>
>> Intrinsically, maybe not. Practically, the difference can be rather
>> profound. The simple fact of the matter is that people are much more used
>> to seeing if/else syntax then ?: and they have become better at visually
>> processing it.
>
> Care to cite any scientific studies that back up that claim?
> You may get confused by ?:, but I've got a multimegaline codebase
> at $DAYJOB which proves that your weakness isn't shared by too
> many people.
I referenced sections of Code Complete in
<slrnhvaa9p.43v.usernet@rutherford.ilthio.net> regarding the benefits of
if/else's consistancy of layout style. The sections referenced themselves
reference several studies on the benefits of block layout and indention.
Since ?: is almost always used without this layout structure, it looses
those benefits.
As for being used in your codebase, I am saying ?: is impossible to parse;
but, that doesn't mean its use is any clearer then just using if/else.
>> if/else can do things that cannot be legibly done with ?:
>> so why require somebody reading your code to read two different types of
>> constructs when one will handle them both.
>
> Why require someone to match the targets of two separate assignments?
> And why require people to separate definition from initialisation?
I actually don't have as much of a problem with ?: being used in an
initialization where the variable is declared *if* the declarations are in
the proximity of the code block where it is used as it is possible to do
using C99 to avoid variable persistance. When used at the top of functions as
is required by C89, it would be placing a conditional well away from the
rest of the code where the variable is used which is a bad thing. I will
tentatively accept this; because, have not seen it abused whereas I have seen ?:
fully abused in code blocks. I reserve the right to change my mind if I do
ever see it overly abused. Personally, I am not really sure why you need
to conditionally initialize a variable when it is declared. Generally, you
don't change the initialization, you change it when it becomes necessary to
do so in your code blocks.
> Goto can do things that cannot legibly be done with if/else,
> so why require someone reading your code to read two different
> types of control flow when one will handle them both. (Included
> ironically purely to show how flawed your logic is.)
I don't have as much of a problem with goto used properly. No, it should
not be used to implement loops; but, it is useful for handling errors in
nested loops. Goto is not conditional so it cannot be used for if/else
replacement even if there was some good reason to do so.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 1:02:04 PM
|
|
"Richard Bos" <raltbos@xs4all.nl> wrote in message
news:4bf527ee.4698312@news.xs4all.nl...
> "bart.c" <bartc@freeuk.com> wrote:
>> printf("%d %s in %d
>> Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
>
> Yes, but in that case - at least, with saner spatiation - the meaning of
> the ?:s is directly apparent. Each of them is a simple, obvious binary
> choice. I'd prefer to see it written like this, though:
>
> printf("%d File%s in %d Director%s\n", nfiles, nfiles=1?"" :"s",
> ndirs, ndirs=1 ?"y":"ies");
>
> The point is, though, that this is much different from a more involved
> and seemingly random
>
> f( p+(a?b:c)+q*(d?e:g))
>
> which does _not_ tell you what the ?:s mean at all.
It wasn't meant to be meaningful, only to be an example awkward to convert
into if-else statements. I'm sure there must be real expressions with more
than one ?: operation.
As for my printf example being a series of binary selections: that example
was derived from this non-C fragment that used *3* of these constructs, but
I couldn't convert the first bit into C cleanly:
display((nfiles|str(nfiles)|"No") + (nfiles=1|" File"|" Files") + " in " +
str(ndirs) + " Director"+(ndirs=1|"y"|"ies"))
(Here, (a|b|c) just means a?b:c.)
It's clearly just a status message being constructed, nothing momentous.
It could be decomposed but there seems little point.
--
Bartc
|
|
0
|
|
|
|
Reply
|
bart
|
5/20/2010 1:09:31 PM
|
|
On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>
> "Tim Harig" <usernet@ilthio.net> wrote in message
> news:slrnhv95vm.64j.usernet@rutherford.ilthio.net...
>> On 2010-05-17, bart.c <bartc@freeuk.com> wrote:
>>> "Geoff" <geoff@invalid.invalid> wrote in message
>>> news:u292v5d98m538lgd49a971kv2m66is406g@4ax.com...
>>>> On Mon, 17 May 2010 12:16:58 +0200, "io_x" <a@b.c.invalid> wrote:
>>>>>if "?:" reduce the size of the prog at last of one char: it is ok
>>>> Simply false. As Tim stated above, if/else generates essentially the
>>>> same machine code as ?: yet the latter is harder to read and maintain.
>>> And when ?: is properly embedded in an expression:
>>>
>>> f( p+(a?b:c)+q*(d?e:g))
Note that this changes the way something is calculated which will be used
somewhere else in the execution of the program. (see below)
>>>
>>> then I'd really like to see your version using if/else.
>>
>> Ignoring the visual clues provided by the indentation that there is a
>> divergence of execution control, your complex statment, while contrived,
>> doesn't explain why you are doing what you are doing. Why is it that you
>> might need to total what amounts to different values under *four*
>> different
>> circumstances?
>
> I use this construction all the time, although mostly in a different
> language and mostly associated with strings (when there are two or more in
> the same expression). An example in C might be:
>
> printf("%d %s in %d
> Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
Fair enough, this is a different usage then above because you are not
actually going to be changing any persistant values which may be used later
in the program. I would accept this; although, I am assuming that you have
already check for 0 and negative values elsewhere in your code.
>> Notice how much more room there is for comments.
>
> What comments would be needed in examples such as above?
I would agree that this is pretty self explanitory. The example where you
used conditionals inside of a calculation statement was not.
>> You are so impressed that you saved a little bit of typing; but, what
>> happens down the road when we find that there are three numbers that could
>> be associated with x or that if a, we really need to do three things to
>> know that x should be?
>
> Well in another language I would use: m:=(n|"One","Two","Three"|"Other")...
>
> In C, you might use: m = (n>=1 && n<=3 ? table[n] : "Other");
>
> But, this involves setting up the table[] data, which is a nuisance if it is
> only used in this one place. So this is more an argument for extending the
> ?: feature into a multi-selection operator...
1. This only works for the multivalue case; not where you have to insert
more code because it now requires more then a simple assignment.
2. C does not have a multi-selection operator so the point is mute.
> Your solution would presumably be:
>
> switch (n) {
> case 1: m="One"; break
> case 2: m="Two"; break
> case 3: m="Three"; break
> default: m="Other";
> }
> Which would be a major distraction when your code has bigger fish to fry
> than just setting a variable to one of 4 values...
Since you did not provide an example in C, I can only speculate that your
solution would have included nesting ?: statements. Once again this isn't
scalable and looses the consitancy and clarity that comes with if/else (or
a *properly indented* switch/case).
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 1:22:23 PM
|
|
"Tim Harig" <usernet@ilthio.net> wrote in message
news:slrnhvadpt.43v.usernet@rutherford.ilthio.net...
> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>>
>> "Tim Harig" <usernet@ilthio.net> wrote in message
>> news:slrnhv95vm.64j.usernet@rutherford.ilthio.net...
>>> On 2010-05-17, bart.c <bartc@freeuk.com> wrote:
>>>> f( p+(a?b:c)+q*(d?e:g))
>>>> then I'd really like to see your version using if/else.
>>>
>>> Ignoring the visual clues provided by the indentation that there is a
>>> divergence of execution control, your complex statment, while contrived,
>>> doesn't explain why you are doing what you are doing. Why is it that
>>> you
>>> might need to total what amounts to different values under *four*
>>> different
>>> circumstances?
Perhaps I should have explained better: a?b:c is little different from
table[a]. So you are also asking why I need to total values under M*N
different circumstances, which is nonsense.
>> printf("%d %s in %d
>> Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
>
> I would accept this; although, I am assuming that you have
> already check for 0 and negative values elsewhere in your code.
0 is OK ("0 files"; "No files" is better but more fiddly to achieve in one
line)
-N is also OK ("-5 files", which just indicates a problem elsewhere)
>>> what
>>> happens down the road when we find that there are three numbers that
>>> could
>>> be associated with x or that if a, we really need to do three things to
>>> know that x should be?
>>
>> Well in another language I would use:
>> m:=(n|"One","Two","Three"|"Other")...
>>
>> In C, you might use: m = (n>=1 && n<=3 ? table[n] : "Other");
>> Your solution would presumably be:
>>
>> switch (n) {
....
> Since you did not provide an example in C, I can only speculate that your
> solution would have included nesting ?: statements.
I gave my C version above for an indexed selection, but using an array (and
needing to explicitly check range) instead of being to express all the
possibilities in situ.
Where nested ?: are called for, it will be complicated, but if/else
versions, especially with distinct ?: blocks on the same line, will also be.
Some judgement is needed as to the best approach. Just outlawing ?:
completely doesn't seem sensible.
> Once again this isn't
> scalable and looses the consitancy and clarity that comes with if/else (or
> a *properly indented* switch/case).
?: chains can also be laid out with indentation. As I said, judgement is
used when writing code.
--
Bart
|
|
0
|
|
|
|
Reply
|
bart
|
5/20/2010 3:53:44 PM
|
|
On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>
> "Tim Harig" <usernet@ilthio.net> wrote in message
> news:slrnhvadpt.43v.usernet@rutherford.ilthio.net...
>> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>>>
>>> "Tim Harig" <usernet@ilthio.net> wrote in message
>>> news:slrnhv95vm.64j.usernet@rutherford.ilthio.net...
>>>> On 2010-05-17, bart.c <bartc@freeuk.com> wrote:
>
>>>>> f( p+(a?b:c)+q*(d?e:g))
>
>>>>> then I'd really like to see your version using if/else.
>>>>
>>>> Ignoring the visual clues provided by the indentation that there is a
>>>> divergence of execution control, your complex statment, while contrived,
>>>> doesn't explain why you are doing what you are doing. Why is it that
>>>> you
>>>> might need to total what amounts to different values under *four*
>>>> different
>>>> circumstances?
>
> Perhaps I should have explained better: a?b:c is little different from
> table[a]. So you are also asking why I need to total values under M*N
> different circumstances, which is nonsense.
If you had used table[a] it would have made your code clearer and you would
not have had to give the explanation that you are now. I would have known
from your descriptive variable name table that the operation is simply
taking something from a table. If table is well named
ie, "plural_eqivilants_table" then I understand exactly why I could
by using different values in the first place.
The variable name "a" is still ambiguous, I don't really know why I am
choosing one value or another out of the table; but, that is a different
problem altogether.
?: shows what you are doing. Using a table tells me not only what you are
doing but why you are doing it.
>>> printf("%d %s in %d
>>> Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
>>
>> I would accept this; although, I am assuming that you have
>> already check for 0 and negative values elsewhere in your code.
>
> 0 is OK ("0 files"; "No files" is better but more fiddly to achieve in one
> line)
> -N is also OK ("-5 files", which just indicates a problem elsewhere)
If negative numbers indicates a problem then it should have been check as
an error; or, it would indicate an error in the code. If it indicates an
error in the code, it would be wise to add an assertion to make sure it is
detected should that problem ever exist.
>>>> what
>>>> happens down the road when we find that there are three numbers that
>>>> could
>>>> be associated with x or that if a, we really need to do three things to
>>>> know that x should be?
>>>
>>> Well in another language I would use:
>>> m:=(n|"One","Two","Three"|"Other")...
>>>
>>> In C, you might use: m = (n>=1 && n<=3 ? table[n] : "Other");
>
>>> Your solution would presumably be:
>>>
>>> switch (n) {
> ...
>
>> Since you did not provide an example in C, I can only speculate that your
>> solution would have included nesting ?: statements.
>
> I gave my C version above for an indexed selection, but using an array (and
> needing to explicitly check range) instead of being to express all the
> possibilities in situ.
So, when this happened you needed to revert to using another method. If
you had been using the table method (or even a chained if/else if/else)
this would not require any more work then adding a value to the table.
That was my point in the first place.
You still did not address the situation where the statement requires more
operations then a simple assignment. In this situation, a table may not be
sufficient to handle all of the possible execution paths. This is an
equally relavant change that is would require the modification of the
statement since it is tied to the conditional.
> Where nested ?: are called for, it will be complicated, but if/else
> versions, especially with distinct ?: blocks on the same line, will also be.
I *never* advocate using if/else on the same line. I don't advocate ?:
anywhere. I never would have had this issue in the first place because I
never used the ?: to start with.
> Some judgement is needed as to the best approach. Just outlawing ?:
> completely doesn't seem sensible.
Since if/else can do everything ?: can I don't see any reason to advocated
the use of ?:. I have never seen an example where if/else could not be
used or where it was not at least as clear is ?:. If ?: could do
everything as clearly as ?:, then I would say choose one of the two and
stick with it. There is no need to have code littered with both. As it
is, if/else is more general. Since we only need on, I choose the more
general of the two.
>> Once again this isn't
>> scalable and looses the consitancy and clarity that comes with if/else (or
>> a *properly indented* switch/case).
>
> ?: chains can also be laid out with indentation. As I said, judgement is
> used when writing code.
Yes they can, but what have you gained. All that you really have done is
chagned if to ? and else to :. Furthermore, ?: *implies* an assignment.
What about code where not assignemnt takes place?
if (condition) {
/* do something without an assignment */
print_report();
else
/* do something else without an assignment */
print_error_report()
Yes you could use ?:; but it would be an abuse of the nature of the
operators and therefore misleading to anybody who has to read the code.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 4:37:34 PM
|
|
"Tim Harig" <usernet@ilthio.net> wrote in message
news:slrnhvap7s.43v.usernet@rutherford.ilthio.net...
> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>>
>> "Tim Harig" <usernet@ilthio.net> wrote in message
>> news:slrnhvadpt.43v.usernet@rutherford.ilthio.net...
>>> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>>>>
>>>> "Tim Harig" <usernet@ilthio.net> wrote in message
>>>> news:slrnhv95vm.64j.usernet@rutherford.ilthio.net...
>>>>> On 2010-05-17, bart.c <bartc@freeuk.com> wrote:
>>
>>>>>> f( p+(a?b:c)+q*(d?e:g))
>>
>>>>>> then I'd really like to see your version using if/else.
>>>>>
>>>>> Ignoring the visual clues provided by the indentation that there is a
>>>>> divergence of execution control, your complex statment, while
>>>>> contrived,
>>>>> doesn't explain why you are doing what you are doing. Why is it that
>>>>> you
>>>>> might need to total what amounts to different values under *four*
>>>>> different
>>>>> circumstances?
>>
>> Perhaps I should have explained better: a?b:c is little different from
>> table[a]. So you are also asking why I need to total values under M*N
>> different circumstances, which is nonsense.
>
> If you had used table[a] it would have made your code clearer and you
> would
> not have had to give the explanation that you are now.
I disagree. a?b:c can be done as an array (assuming for a minute that a=0 or
1 and that b and c are constants):
int table[2]={b,c};
x=table[a]; /* instead of x=a?b:c; */
But isn't it much clearer when all the possibilies for table are laid out
for you directly in the expression?
> ?: shows what you are doing. Using a table tells me not only what you are
> doing but why you are doing it.
You're getting extra information from the superfluous table name, but also
hiding the contents of the table as well as generating extra clutter. You
might as well insert an embedded comment.
>> Where nested ?: are called for, it will be complicated, but if/else
>> versions, especially with distinct ?: blocks on the same line, will also
>> be.
>
> I *never* advocate using if/else on the same line. I don't advocate ?:
> anywhere.
I meant when an expression has multiple lots of possibly nested ?:, the
if/else equivalents would be equally tortured code.
>> ?: chains can also be laid out with indentation. As I said, judgement is
>> used when writing code.
>
> Yes they can, but what have you gained. All that you really have done is
> chagned if to ? and else to :. Furthermore, ?: *implies* an assignment.
> What about code where not assignemnt takes place?
Huh? Where does it say that ?: is associated with assignments? ?: is used
just like any other operator, while indentation might be used like this
(imagine a,b,c are more complex than they are).:
p[i]->q->r = a
?b
:c
> if (condition) {
> /* do something without an assignment */
> print_report();
> else
> /* do something else without an assignment */
> print_error_report()
>
> Yes you could use ?:; but it would be an abuse of the nature of the
> operators and therefore misleading to anybody who has to read the code.
There's no point using ?: here, as they confer little benefit. But there are
plenty if situations where they *are* useful:
p[sourceix?i:j] = q[destix?i:j];
--
Bartc
|
|
0
|
|
|
|
Reply
|
bart
|
5/20/2010 5:23:15 PM
|
|
Eric Sosman writes:
> Also, some realloc() implementations will *always* move the region,
> even when shrinking it or when there happens to be a "gap" right after
> it. And then they'll fill the old region with 0xDEADBEEF or some such.
> Can you guess why a realloc() implementor would choose to do this?
Please do not patronize me Mr Susman.
> Ponder this, Sandeep: How did the computer know it should emit
> a "Floating point exception" message? What prompted it do do so?
A hardware trap led to a SIGFPE signal being sent by the kernel. I do not
believe the compiler generated any code to catch division by 0 in
software.
> All of these functions have well-defined behaviors when the
> argument/operand is a null pointer. So does free().
If so then in my opinion this is a deficiency in the definition of C.
Passing a NULL pointer to free should yield an undefined behavior in my
opinion.
|
|
0
|
|
|
|
Reply
|
sandeep
|
5/20/2010 6:17:57 PM
|
|
On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
> "Tim Harig" <usernet@ilthio.net> wrote in message
> news:slrnhvap7s.43v.usernet@rutherford.ilthio.net...
>> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>>> Perhaps I should have explained better: a?b:c is little different from
>>> table[a]. So you are also asking why I need to total values under M*N
>>> different circumstances, which is nonsense.
>>
>> If you had used table[a] it would have made your code clearer and you
>> would
>> not have had to give the explanation that you are now.
>
> I disagree. a?b:c can be done as an array (assuming for a minute that a=0 or
> 1 and that b and c are constants):
>
> int table[2]={b,c};
>
> x=table[a]; /* instead of x=a?b:c; */
>
> But isn't it much clearer when all the possibilies for table are laid out
> for you directly in the expression?
Not necessarily, if the function of the table is obvious I should not need
to know what it contains. Moreover, I shouldn't need to consern myself
if it changes.
b and c need not be constants. In your singular vs plural example, they
could easily be redefined for new languages etc.
Note that the table was your idea and one that it turns out that I really
do like. I was simply going to stick with an if/else; but, I like your
idea better. There is one raw edge. Because you used an array for your
table, your code is subject overflow if a is set incorrectly. This is easy
to fix with a simple function wrapper for table[a] that asserts a is within
the range that it should be.
I really like this. We have turned an obscure operation into a scalable,
flexible, descriptive abstraction that has the added benefit of helping
us in our code testing. What could be better?
>> ?: shows what you are doing. Using a table tells me not only what you are
>> doing but why you are doing it.
>
> You're getting extra information from the superfluous table name, but also
> hiding the contents of the table as well as generating extra clutter. You
> might as well insert an embedded comment.
On the contrary, moving implementation details behind abstractions so
that they only need to thought of or modified in one place is very much
the spirit behind structured programming.
A comment is not optional here; but, comments have a bad habit of not being
updated and they do not excuse poor code.
>>> Where nested ?: are called for, it will be complicated, but if/else
>>> versions, especially with distinct ?: blocks on the same line, will also
>>> be.
>>
>> I *never* advocate using if/else on the same line. I don't advocate ?:
>> anywhere.
>
> I meant when an expression has multiple lots of possibly nested ?:, the
> if/else equivalents would be equally tortured code.
A properly written and formated if/else chain is not "tortured;" but, yes
if the ?: chain is broken down over multiple lines and it only leads to
assignments then they are not much different. This has been one of my
large themes since the beginning. You are not really gaining anything with
?: but you can loose with it.
>>> ?: chains can also be laid out with indentation. As I said, judgement is
>>> used when writing code.
>>
>> Yes they can, but what have you gained. All that you really have done is
>> chagned if to ? and else to :. Furthermore, ?: *implies* an assignment.
>> What about code where not assignemnt takes place?
>
> Huh? Where does it say that ?: is associated with assignments? ?: is used
> just like any other operator, while indentation might be used like this
> (imagine a,b,c are more complex than they are).:
>
> p[i]->q->r = a
> ?b
> :c
Notice that r is being assigned a value here. My point was, what if there
is no assignment being made? The code just branches to do different tasks.
The code below was designed as an example.
>> if (condition) {
>> /* do something without an assignment */
>> print_report();
>> else
>> /* do something else without an assignment */
>> print_error_report()
>>
>> Yes you could use ?:; but it would be an abuse of the nature of the
>> operators and therefore misleading to anybody who has to read the code.
>
> There's no point using ?: here, as they confer little benefit. But there are
> plenty if situations where they *are* useful:
Here we agree and it makes my point directly above.
> p[sourceix?i:j] = q[destix?i:j];
Once again, I am left without any evident clues as to why we might be using
branching values for these indexes. You have saved a few lines at the cost
of code clarity. This is back where we started in the first place. Yes,
its true that ?: isn't always unreadable; but, it doesn't really save you
anything except a little typing even where these conditions occur.
I have already shown you that even where there is no clarity breakdown, and
these are few (definitly *not* the preceding example), it makes the code
harder to modify. There is no doubt that it breaks the consistancy of
conditional indenting. (Remember that many languages do not support ?: but
that almost every language supports an if/else.)
No, ?: is no more evil then goto. A properly written goto can *enhance*
readablity and the logical flow; but, we also know that the true niche for
a goto is rare. It actually provides a benefit that does not exist in any
other construct. Thanks to the stigma that surrounds it, it is much less
abused. We have learned to make sure that there is very careful
consideration before using it because, while it is not inherently evil, we
have learned to distrust the individual programmer's disgression of when it
is beneficial to use and when it is not.
Where goto provides additonal functionality, ?: does not provide any
real benefit over a traditional if/else construct. It is used simply to
save fingers from typing. Yes it can be spread over multiple lines; but,
people rarely do. Yes there are times when it make make sense to use; but,
I have learned programmer disgression is not so great at making these
descisions.
While we cannot replace the functionality we have received with goto, the
same cannot be said of ?:. Since it is redundant, since is well known to
create clarity issues without significant benefit, since it is less
scalable and more poorly used, since it is replacable; I say avoid it.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 6:38:03 PM
|
|
On 2010-05-20, sandeep <nospam@nospam.com> wrote:
> Eric Sosman writes:
>> Also, some realloc() implementations will *always* move the region,
>> even when shrinking it or when there happens to be a "gap" right after
>> it. And then they'll fill the old region with 0xDEADBEEF or some such.
>> Can you guess why a realloc() implementor would choose to do this?
> Please do not patronize me Mr Susman.
And yet, you can't spell his name, and you regularly say things that are
frankly pretty dumb.
>> Ponder this, Sandeep: How did the computer know it should emit
>> a "Floating point exception" message? What prompted it do do so?
> A hardware trap led to a SIGFPE signal being sent by the kernel. I do not
> believe the compiler generated any code to catch division by 0 in
> software.
Maybe, maybe not. So what?
>> All of these functions have well-defined behaviors when the
>> argument/operand is a null pointer. So does free().
> If so then in my opinion this is a deficiency in the definition of C.
Are you a sock for Nilges, that you think that when you're flatly wrong
about C, that's a defect in the language?
> Passing a NULL pointer to free should yield an undefined behavior in my
> opinion.
And yet, you've never supported this opinion. There's a number of good
reasons for the current behavior. Some early C implementations (pre-standard)
did in fact crash on free(NULL). It was a serious pain, because it
dramatically amplified a fairly common class of bugs, and didn't really
offer any benefits.
You haven't offered any real explanation for why free(NULL) shouldn't be
defined to just do nothing. The only explanation on offer is that it's
purely a matter of your ego -- you are too proud to admit that you were
wrong.
This makes it pretty pointless for anyone to try to help you learn C; you've
already committed yourself to not learning.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/20/2010 6:43:43 PM
|
|
sandeep <nospam@nospam.com> writes:
> Eric Sosman writes:
>> Also, some realloc() implementations will *always* move the region,
>> even when shrinking it or when there happens to be a "gap" right after
>> it. And then they'll fill the old region with 0xDEADBEEF or some such.
>> Can you guess why a realloc() implementor would choose to do this?
>
> Please do not patronize me Mr Susman.
Please do not patronize the rest of us. (And his name is Sosman,
not Susman.)
>> Ponder this, Sandeep: How did the computer know it should emit
>> a "Floating point exception" message? What prompted it do do so?
>
> A hardware trap led to a SIGFPE signal being sent by the kernel. I do not
> believe the compiler generated any code to catch division by 0 in
> software.
It doesn't matter whether the compiler generated code to catch it.
The compiler generated code that resulted in the division by zero
being caught. The implementation is more than the compiler, and the
compiler is perfectly free to take advantage of the characteristics
of the underlying system (in fact it must do so).
Of course there's no requirement in C for checks on floating-point
operations (except, I suppose, in certain IEEE modes; I don't
recall the details). The point is that, in the example you chose
to support your claim that there was no check for division by zero,
*there was a check for division by zero*.
>> All of these functions have well-defined behaviors when the
>> argument/operand is a null pointer. So does free().
>
> If so then in my opinion this is a deficiency in the definition of C.
> Passing a NULL pointer to free should yield an undefined behavior in my
> opinion.
And you're certainly entitled to that opinion. But please sit
back for a moment and ponder the fact that a number of people who
have far more C experience than you strongly disagree with you.
Does it even occur to you that you might be wrong?
Once you've done that, you might want to answer the question I
asked you in Message-ID: <lnbpcdard2.fsf@nuthaus.mib.org>,
<http://groups.google.com/group/comp.lang.c/msg/bddfd7151e3ca10f>.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/20/2010 6:55:04 PM
|
|
On 5/20/2010 2:17 PM, sandeep wrote:
> Eric Sosman writes:
>
>> Ponder this, Sandeep: How did the computer know it should emit
>> a "Floating point exception" message? What prompted it do do so?
>
> A hardware trap led to a SIGFPE signal being sent by the kernel. I do not
> believe the compiler generated any code to catch division by 0 in
> software.
Who said anything about *the compiler* doing something? The
claim I made -- and stand by -- is that *the implementation* made
a check for division by zero, detected it, and took action. It
matters not one whit whether the check is done by hardware, or by
a division subroutine (lots of machines nowadays have no integer
divide instruction), or by the equivalent of
if (divisor == 0) abort();
quotient = dividend / divisor;
The check is made, somehow, on every *implementation* I've seen in
three and a half decades of using C. C does not require the check
("undefined behavior" is all), but every implementation of C and
every implementation of every other language I've ever used has made
some kind of zero-division check and handled it as a special case.
Hardware, software, firmware, vaporware, somehow a check is made.
>> All of these functions have well-defined behaviors when the
>> argument/operand is a null pointer. So does free().
>
> If so then in my opinion this is a deficiency in the definition of C.
> Passing a NULL pointer to free should yield an undefined behavior in my
> opinion.
Submit a bug report to the Committee, then.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
Eric
|
5/20/2010 7:02:54 PM
|
|
On Thu, 20 May 2010 18:38:03 +0000, Tim Harig wrote:
> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>> "Tim Harig" <usernet@ilthio.net> wrote in message
>> news:slrnhvap7s.43v.usernet@rutherford.ilthio.net...
>>> On 2010-05-20, bart.c <bartc@freeuk.com> wrote:
>>
>> Huh? Where does it say that ?: is associated with assignments? ?: is
>> used just like any other operator, while indentation might be used like
>> this (imagine a,b,c are more complex than they are).:
>>
>> p[i]->q->r = a
>> ?b
>> :c
>
> Notice that r is being assigned a value here. My point was, what if
> there is no assignment being made? The code just branches to do
> different tasks. The code below was designed as an example.
>
A pattern where the ?: can be used as a pure expression
(without using it an assignment) is:
switch (p? p->status : -1) {
case -1:
...
break;
case STATUS_OUT_OF_PAPER:
...
break;
case FILE_NOT_FOUND:
...
break;
case WATER_IN_DRIVE_A:
start_pumping();
break;
....
}
Similar uses can be constructed with if(), for() and while(),
but there a short circuit evaluation is probably more readable.
(and more common idiom)
HTH,
AvK
|
|
0
|
|
|
|
Reply
|
Moi
|
5/20/2010 7:02:56 PM
|
|
On May 20, 6:19=A0am, Nisse Engstr=F6m <news.NOSPAM.id...@luden.se> wrote:
> On Tue, 18 May 2010 20:11:58 +0000 (UTC), sandeep wrote:
> > with free there is no new functionality from passing NULL.
>
> It allows you to write cleaner code:
>
> =A0 p =3D malloc (...);
> =A0 q =3D malloc (...);
> =A0 r =3D malloc (...);
> =A0 s =3D malloc (...);
>
> =A0 if (p && q && r && s) {
> =A0 =A0 /* ok */
> =A0 } else {
> =A0 =A0 /* error */
> =A0 }
>
> =A0 free (p);
> =A0 free (q);
> =A0 free (r);
> =A0 free (s);
>
> The cleanup would be a lot bulkier (to me) if I had to
> add an if() statement before every free().
This is where I'd do it quite differently, more like:
char *p =3D malloc(...);
if ( !p )
/* error */
else
{
int *q =3D malloc(...);
if ( !q )
/* error */
else
{
mystruct *r =3D malloc(...);
if ( !r )
/* error */
else
{
char *s =3D malloc(...);
if ( !s )
/* error */
else
{
/* ok */
free(s);
} //allocated s
free(r);
} //allocated r
free(q);
} //allocated q
free(p);
} //allocated p
To me, the beauty of free(NULL) being guaranteed to work (for some
definition of "work") is that either of us can understand the other
approach, and know what it's doing. I generally avoid freeing NULL
pointers, just as I generally avoid multiple returns from a function,
but recognize this as a style preference, not a holy command. My
objection to your approach is more that you don't know which
allocation failed (it may be pointless to differentiate), not that
there's anything "sloppy" or "wrong" with it.
>
> > What would you think of passing a NULL pointer to strlen or fclose? Eve=
n
> > if you can get away with it in the case of free, it is sloppy programmi=
ng.
>
> I wish fclose(NULL) had the same semantics as free(NULL),
> but unfortunately it doesn't. I try to always follow the
> free(NULL) pattern when I write code that allocates/frees
> some kind of resource. There's nothing sloppy about it.
> It's just the cleanest and most obvious way to do it (in
> my mind).
I agree about fclose(). I would be very unlikely to use that feature,
but to my way of thinking it would still be a better interface if it
worked that way. Then again, I'd also prefer an fclose() that NULLed
the FILE * [i.e. fclose(&myfileptr); ], and a free that took a pointer
to the pointer and set it NULL after releasing the memory. Then you'd
only have to be careful of secondary pointers. I could just do
myptr =3D realloc(myptr, 0);
instead of
free(myptr);
myptr =3D NULL;
but that seems a bit contrived and "cute", and more likely to confuse
than to communicate.
|
|
0
|
|
|
|
Reply
|
BruceS
|
5/20/2010 7:26:27 PM
|
|
BruceS <bruces42@hotmail.com> writes:
[...]
> I agree about fclose(). I would be very unlikely to use that feature,
> but to my way of thinking it would still be a better interface if it
> worked that way. Then again, I'd also prefer an fclose() that NULLed
> the FILE * [i.e. fclose(&myfileptr); ], and a free that took a pointer
> to the pointer and set it NULL after releasing the memory. Then you'd
> only have to be careful of secondary pointers. I could just do
>
> myptr = realloc(myptr, 0);
>
> instead of
>
> free(myptr);
> myptr = NULL;
>
> but that seems a bit contrived and "cute", and more likely to confuse
> than to communicate.
There are two problems with having free() take a pointer-to-pointer
argument. One is that, though void* is a generic pointer type, there is
no generic pointer-to-pointer type. You can't write a myfree() function
for which both of these calls:
int *iptr = malloc(sizeof *iptr);
double *dptr = malloc(sizeof *dptr);
myfree(&iptr);
myfree(&dptr);
are legal.
Second, it doesn't solve the whole problem:
int *p = malloc(sizeof *p);
int *copy_of_p = p;
free(p);
p = NULL;
/* copy_of_p is still a dangling pointer */
Perhaps a partial solution is better than none at all -- but see the
first point.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/20/2010 7:48:10 PM
|
|
Tim Harig <usernet@ilthio.net> writes:
> The ?: implies that a value is changing.
At first, I thought you were young and naive regarding programming.
I was wrong - you're simply full of crap. No :? ever implies that
a value is changing. Please put down the crack pipe.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/20/2010 8:41:11 PM
|
|
On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Tim Harig <usernet@ilthio.net> writes:
>> The ?: implies that a value is changing.
Sorry, poor wording. I don't always speak the best English. What I was
referring to is the fact that ?: implies an assignment (ie, the first
argument).
> At first, I thought you were young and naive regarding programming.
I wish I was young :(
> I was wrong - you're simply full of crap. No :? ever implies that
> a value is changing. Please put down the crack pipe.
Would a crack pipe help diminish my growing gut? Where can I get one! :)
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 9:20:39 PM
|
|
On 2010-05-20, Moi <root@invalid.address.org> wrote:
> On Thu, 20 May 2010 18:38:03 +0000, Tim Harig wrote:
>> Notice that r is being assigned a value here. My point was, what if
>> there is no assignment being made? The code just branches to do
>> different tasks. The code below was designed as an example.
>
> A pattern where the ?: can be used as a pure expression
> (without using it an assignment) is:
>
> switch (p? p->status : -1) {
> case -1:
> ...
> break;
> case STATUS_OUT_OF_PAPER:
> ...
> break;
> case FILE_NOT_FOUND:
> ...
> break;
> case WATER_IN_DRIVE_A:
> start_pumping();
> break;
> ...
> }
Okay, assignment isn't the proper word. A value is still being returned
here and it is still being used; it just isn't being assigned to a varable.
My question would be why p hasn't already been checked against a NULL
condition? One would that that this would be done when it was first
allocated; before one would assume any of its contents would have any
sanity. Notice that this isn't all that descriptive either. Your testing
two non-dependant condtions at the same time. This alone raises a flag in
my mind.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/20/2010 9:32:41 PM
|
|
BruceS wrote:
) I agree about fclose(). I would be very unlikely to use that feature,
) but to my way of thinking it would still be a better interface if it
) worked that way. Then again, I'd also prefer an fclose() that NULLed
) the FILE * [i.e. fclose(&myfileptr); ], and a free that took a pointer
) to the pointer and set it NULL after releasing the memory. Then you'd
) only have to be careful of secondary pointers. I could just do
)
) myptr = realloc(myptr, 0);
)
) instead of
)
) free(myptr);
) myptr = NULL;
)
) but that seems a bit contrived and "cute", and more likely to confuse
) than to communicate.
How about making free() always return NULL ?
Then you can simply do: myptr = free(myptr);
Which is nicely parallel to the realloc() version.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
|
|
0
|
|
|
|
Reply
|
Willem
|
5/20/2010 9:38:47 PM
|
|
On Thu, 20 May 2010 21:20:39 +0000, Tim Harig wrote:
> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>> Tim Harig <usernet@ilthio.net> writes:
>>> The ?: implies that a value is changing.
>
> Sorry, poor wording. I don't always speak the best English. What I was
> referring to is the fact that ?: implies an assignment (ie, the first
> argument).
No, this is not a fact.
See may orher post with
"switch (p ? p->status : -1 ) { ...}"
No assignment.
HTH,
AvK
|
|
0
|
|
|
|
Reply
|
Moi
|
5/20/2010 9:44:22 PM
|
|
On May 20, 1:48=A0pm, Keith Thompson <ks...@mib.org> wrote:
> BruceS <bruce...@hotmail.com> writes:
>
> [...]
>
>
>
> > I agree about fclose(). =A0I would be very unlikely to use that feature=
,
> > but to my way of thinking it would still be a better interface if it
> > worked that way. =A0Then again, I'd also prefer an fclose() that NULLed
> > the FILE * [i.e. fclose(&myfileptr); ], and a free that took a pointer
> > to the pointer and set it NULL after releasing the memory. =A0Then you'=
d
> > only have to be careful of secondary pointers. =A0I could just do
>
> > myptr =3D realloc(myptr, 0);
>
> > instead of
>
> > free(myptr);
> > myptr =3D NULL;
>
> > but that seems a bit contrived and "cute", and more likely to confuse
> > than to communicate.
>
> There are two problems with having free() take a pointer-to-pointer
> argument. =A0One is that, though void* is a generic pointer type, there i=
s
> no generic pointer-to-pointer type. =A0You can't write a myfree() functio=
n
> for which both of these calls:
>
> =A0 =A0 int =A0 =A0*iptr =3D malloc(sizeof *iptr);
> =A0 =A0 double *dptr =3D malloc(sizeof *dptr);
> =A0 =A0 myfree(&iptr);
> =A0 =A0 myfree(&dptr);
>
> are legal.
See, that's what I get for not attempting to implement my improved
free()---I would have run into that.
> Second, it doesn't solve the whole problem:
>
> =A0 =A0 int *p =3D malloc(sizeof *p);
> =A0 =A0 int *copy_of_p =3D p;
> =A0 =A0 free(p);
> =A0 =A0 p =3D NULL;
> =A0 =A0 /* copy_of_p is still a dangling pointer */
That's what I meant by secondary pointers. Any time you have >1
pointer to the same piece of memory, you have extra work to manage
them. I can't offhand think of a reasonable way to get around this.
> Perhaps a partial solution is better than none at all -- but see the
> first point.
One solid hit does the trick. The second just wings, but it looks
like the target is down anyway.
|
|
0
|
|
|
|
Reply
|
BruceS
|
5/20/2010 9:46:51 PM
|
|
On May 20, 3:38=A0pm, Willem <wil...@turtle.stack.nl> wrote:
> BruceS wrote:
>
> ) I agree about fclose(). =A0I would be very unlikely to use that feature=
,
> ) but to my way of thinking it would still be a better interface if it
> ) worked that way. =A0Then again, I'd also prefer an fclose() that NULLed
> ) the FILE * [i.e. fclose(&myfileptr); ], and a free that took a pointer
> ) to the pointer and set it NULL after releasing the memory. =A0Then you'=
d
> ) only have to be careful of secondary pointers. =A0I could just do
> )
> ) myptr =3D realloc(myptr, 0);
> )
> ) instead of
> )
> ) free(myptr);
> ) myptr =3D NULL;
> )
> ) but that seems a bit contrived and "cute", and more likely to confuse
> ) than to communicate.
>
> How about making free() always return NULL ?
>
> Then you can simply do: =A0myptr =3D free(myptr);
> Which is nicely parallel to the realloc() version.
I like that. The same could be done with fclose(), except that it
loses the ability to indicate an error by returning EOF. Of course,
this can be done with a simple macro without changing the language.
|
|
0
|
|
|
|
Reply
|
BruceS
|
5/20/2010 9:51:09 PM
|
|
On Thu, 20 May 2010 21:32:41 +0000, Tim Harig wrote:
> On 2010-05-20, Moi <root@invalid.address.org> wrote:
>> On Thu, 20 May 2010 18:38:03 +0000, Tim Harig wrote:
>>> Notice that r is being assigned a value here. My point was, what if
>>> there is no assignment being made? The code just branches to do
>>> different tasks. The code below was designed as an example.
>>
>> A pattern where the ?: can be used as a pure expression (without using
>> it an assignment) is:
>>
>> switch (p? p->status : -1) {
>> case -1:
>> ...
>> break;
>> case STATUS_OUT_OF_PAPER:
>> ...
>> break;
>> case FILE_NOT_FOUND:
>> ...
>> break;
>> case WATER_IN_DRIVE_A:
>> start_pumping();
>> break;
>> ...
>> }
>
> Okay, assignment isn't the proper word. A value is still being returned
> here and it is still being used; it just isn't being assigned to a
> varable.
>
> My question would be why p hasn't already been checked against a NULL
> condition? One would that that this would be done when it was first
> allocated; before one would assume any of its contents would have any
> sanity. Notice that this isn't all that descriptive either. Your
> testing two non-dependant condtions at the same time. This alone raises
> a flag in my mind.
Well, it is a matter of taste. Some people sometimes prefer grouping
*all the possibilities* into one big switch. It avoids extra nesting
(and "ugly tails"), such as:
if (p) {
switch (p->status) {
...
...
... 30 lines of blabla...
...
...
}
}else {
... oops there was no p ...
}
That's all,
AvK
|
|
0
|
|
|
|
Reply
|
Moi
|
5/20/2010 9:52:43 PM
|
|
Tim Harig <usernet@ilthio.net> writes:
> Sorry, poor wording. I don't always speak the best English. What I was
> referring to is the fact that ?: implies an assignment (ie, the first
> argument).
Here's a counterexample from some code of mine:
if (row->old
? !ovsdb_datum_equals(&row->old[idx], &row->new[idx], &column->type)
: !ovsdb_datum_is_default(&row->new[idx], &column->type)) {
...
}
--
Ben Pfaff
http://benpfaff.org
|
|
0
|
|
|
|
Reply
|
Ben
|
5/20/2010 10:17:22 PM
|
|
Tim Harig <usernet@ilthio.net> writes:
> On 2010-05-20, Moi <root@invalid.address.org> wrote:
>> On Thu, 20 May 2010 18:38:03 +0000, Tim Harig wrote:
>>> Notice that r is being assigned a value here. My point was, what if
>>> there is no assignment being made? The code just branches to do
>>> different tasks. The code below was designed as an example.
>>
>> A pattern where the ?: can be used as a pure expression
>> (without using it an assignment) is:
>>
>> switch (p? p->status : -1) {
>> case -1:
>> ...
>> break;
>> case STATUS_OUT_OF_PAPER:
>> ...
>> break;
>> case FILE_NOT_FOUND:
>> ...
>> break;
>> case WATER_IN_DRIVE_A:
>> start_pumping();
>> break;
>> ...
>> }
>
> Okay, assignment isn't the proper word. A value is still being returned
> here and it is still being used; it just isn't being assigned to a varable.
Then I'm not sure what you're trying to say.
> My question would be why p hasn't already been checked against a NULL
> condition? One would that that this would be done when it was first
> allocated; before one would assume any of its contents would have any
> sanity. Notice that this isn't all that descriptive either. Your testing
> two non-dependant condtions at the same time. This alone raises a flag in
> my mind.
Certainly you'd check whether p == NULL immediately after allocating it
(assuming it points to allocated memory; it could point to anything).
But a null pointer might still be a valid state for p.
In this case, p appears to be a pointer to a structure that has
information about a printer. If you don't have a printer (or
haven't selected one, or whatever), then p will be null; it's not
necessarily an error. The "case -1:" code might ask the user
to configure a printer, for example.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/20/2010 10:25:36 PM
|
|
On Thu, 20 May 2010 15:25:36 -0700, Keith Thompson wrote:
> Tim Harig <usernet@ilthio.net> writes:
>> On 2010-05-20, Moi <root@invalid.address.org> wrote:
>>> On Thu, 20 May 2010 18:38:03 +0000, Tim Harig wrote:
>>>> Notice that r is being assigned a value here. My point was, what if
>>>> there is no assignment being made? The code just branches to do
>>>> different tasks. The code below was designed as an example.
>>>
>>> A pattern where the ?: can be used as a pure expression (without using
>>> it an assignment) is:
>>>
> Certainly you'd check whether p == NULL immediately after allocating it
> (assuming it points to allocated memory; it could point to anything).
> But a null pointer might still be a valid state for p.
>
> In this case, p appears to be a pointer to a structure that has
> information about a printer. If you don't have a printer (or haven't
> selected one, or whatever), then p will be null; it's not necessarily an
> error. The "case -1:" code might ask the user to configure a printer,
> for example.
again:
switch (p? p->status : -1) {
case -1:
p = configure_printer(...);
if (p) goto again;
break;
case STATUS_OUT_OF_PAPER:
...
break;
case FILE_NOT_FOUND:
...
break;
case WATER_IN_DRIVE_A:
start_pumping();
break;
...
}
/cross-topic
FTFY ;-)
AvK
|
|
0
|
|
|
|
Reply
|
Moi
|
5/20/2010 10:32:10 PM
|
|
BruceS <bruces42@hotmail.com> writes:
> On May 20, 6:19 am, Nisse Engström <news.NOSPAM.id...@luden.se> wrote:
>> On Tue, 18 May 2010 20:11:58 +0000 (UTC), sandeep wrote:
>> > with free there is no new functionality from passing NULL.
>>
>> It allows you to write cleaner code:
>>
>> p = malloc (...);
>> q = malloc (...);
>> r = malloc (...);
>> s = malloc (...);
>>
>> if (p && q && r && s) {
>> /* ok */
>> } else {
>> /* error */
>> }
>>
>> free (p);
>> free (q);
>> free (r);
>> free (s);
>>
>> The cleanup would be a lot bulkier (to me) if I had to
>> add an if() statement before every free().
>
> This is where I'd do it quite differently, more like:
>
> char *p = malloc(...);
> if ( !p )
> /* error */
> else
> {
> int *q = malloc(...);
> if ( !q )
> /* error */
> else
> {
> mystruct *r = malloc(...);
> if ( !r )
> /* error */
> else
> {
> char *s = malloc(...);
> if ( !s )
> /* error */
> else
> {
> /* ok */
> free(s);
> } //allocated s
> free(r);
> } //allocated r
> free(q);
> } //allocated q
> free(p);
> } //allocated p
Well, each to their own, but this is much too much for my taste. It can
be tricky making the "no error" path clear and obvious in a language
like C with in-band error signalling, but it is particularly buried in
your version. Also, the error code needs to be written four times.
<snip>
--
Ben.
|
|
0
|
|
|
|
Reply
|
Ben
|
5/20/2010 10:49:47 PM
|
|
On 2010-05-20, Ben Pfaff <blp@cs.stanford.edu> wrote:
> Tim Harig <usernet@ilthio.net> writes:
>
>> Sorry, poor wording. I don't always speak the best English. What I was
>> referring to is the fact that ?: implies an assignment (ie, the first
>> argument).
>
> Here's a counterexample from some code of mine:
>
> if (row->old
> ? !ovsdb_datum_equals(&row->old[idx], &row->new[idx], &column->type)
> : !ovsdb_datum_is_default(&row->new[idx], &column->type)) {
> ...
> }
As I have already mentioned to Moi, in an almost identical example, while
yes you are not actually assigning the value returned by the ?: statement,
you are still using it. You don't often see it used in isolation without
making use of its value, ie:
(row->old
? !ovsdb_datum_equals(&row->old[idx], &row->new[idx], &column->type)
: !ovsdb_datum_is_default(&row->new[idx], &column->type)
Since the it tends to imply a this or that quantity rather then a general
if/else, you would tend to wonder why the value is not being used. In that
sense, using it in this way is somewhat misleading.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 12:03:12 AM
|
|
On 2010-05-20, Keith Thompson <kst-u@mib.org> wrote:
> Tim Harig <usernet@ilthio.net> writes:
>> On 2010-05-20, Moi <root@invalid.address.org> wrote:
>>> On Thu, 20 May 2010 18:38:03 +0000, Tim Harig wrote:
>>>> Notice that r is being assigned a value here. My point was, what if
>>>> there is no assignment being made? The code just branches to do
>>>> different tasks. The code below was designed as an example.
>>>
>>> A pattern where the ?: can be used as a pure expression
>>> (without using it an assignment) is:
>>>
>>> switch (p? p->status : -1) {
>>> case -1:
>>> ...
>>> break;
>>> case STATUS_OUT_OF_PAPER:
>>> ...
>>> break;
>>> case FILE_NOT_FOUND:
>>> ...
>>> break;
>>> case WATER_IN_DRIVE_A:
>>> start_pumping();
>>> break;
>>> ...
>>> }
>>
>> Okay, assignment isn't the proper word. A value is still being returned
>> here and it is still being used; it just isn't being assigned to a varable.
>
> Then I'm not sure what you're trying to say.
This really is a small point among the rest that I have made.
What I mean is that you do not normally see ?: operators where no value
from the statements are used:
(a?b:c)
As the the ?: construct was designed as an explicit this or that value
construct, you don't see it used in isolation as above. If you did, you
would wonder why the value it returns is not being used. It is in this way
misleading. If/else doesn't necessarily have this limitation:
if (condition)
/* do something */
else
/* do something else */
It doesn't stand to reason that any value changes are actually preserved
(though they might) with the if/else blocks.
>> My question would be why p hasn't already been checked against a NULL
>> condition? One would that that this would be done when it was first
>> allocated; before one would assume any of its contents would have any
>> sanity. Notice that this isn't all that descriptive either. Your testing
>> two non-dependant condtions at the same time. This alone raises a flag in
>> my mind.
>
> Certainly you'd check whether p == NULL immediately after allocating it
> (assuming it points to allocated memory; it could point to anything).
> But a null pointer might still be a valid state for p.
So you are essentially overloading p. It points to a print_dev struct if
one is available or NULL if it is not.
> In this case, p appears to be a pointer to a structure that has
> information about a printer. If you don't have a printer (or
> haven't selected one, or whatever), then p will be null; it's not
> necessarily an error. The "case -1:" code might ask the user
> to configure a printer, for example.
Okay, I can see now where Moi is coming from; but, in my opinion, if
there are no printers then *no* printer handling code should be called in
the first place. As Moi has it, the printing codes all must be able to
handle the possibility that the printer may not exist. This is a
requirement for *everything* that attempts to dereference p. Everybody who
works with the code must be aware of this fact.
I would much rather handle a missing printer as soon as the printer is
allocated or selected. NULL might be okay to pass back as a flag from the
create_printer() function; but, once it has failed I want a more explicit
indication of it If a valid printer is saved to p then a printer_count
variable is incremented or possibly the printer is placed into some
kind of collection where the length can be counted. I would prevent
any printer handling code from running at all if p is not a valid
printer_dev struct.
Even using NULL as a flag for a valid printer , I feel that checking
whether or not the printer exists is logically distinct from what
errors a valid printer may have. In both cases, I would have formated
it as two blocks. One check that a valid printer is present, either by
checking the printer count or in Moi's case whether p was NULL, and the
other to check for the status of a valid printer struct. This seems more
explicit to me.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 12:54:00 AM
|
|
On 2010-05-20, Moi <root@invalid.address.org> wrote:
> On Thu, 20 May 2010 21:32:41 +0000, Tim Harig wrote:
>> My question would be why p hasn't already been checked against a NULL
>> condition? One would that that this would be done when it was first
>> allocated; before one would assume any of its contents would have any
>> sanity. Notice that this isn't all that descriptive either. Your
>> testing two non-dependant condtions at the same time. This alone raises
>> a flag in my mind.
Alright, Keith Thompson let me in on a little bit of your thinking. You
are effectively overloading p to act both as a valid struct or as NULL if
was not valid. Returning NULL from the function that creates the struct is
sufficient; but, after that I would want a more explicit indication that
creating the stuct failed. I would not allow any printer handling code,
including your switch/case statement execute unless as valid struct was
created.
> Well, it is a matter of taste. Some people sometimes prefer grouping
> *all the possibilities* into one big switch. It avoids extra nesting
Separating the blocks into distinct functional units does have the
potential to create an extra nest; but, in practice I would expect the code
that creates your struct object to be in a distinct function from your
switch/case statement that is checking for its status.
> (and "ugly tails"), such as:
I would not have permitted such an "ugly tail." In my code, none of your
switch case statement would be allowed to execute unless a valid p
structure had been created. If and only if struct p had been created would
I worry about checking the status of p. In any case, testing whether p is
valid and the status of a valid p are, to me, two functionally distinct
operations and I would have placed them in separate blocks.
> if (p) {
> switch (p->status) {
> ...
> ...
> ... 30 lines of blabla...
> ...
> ...
> }
> }else {
>
> ... oops there was no p ...
> }
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 1:07:19 AM
|
|
On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
> What I mean is that you do not normally see ?: operators where no value
> from the statements are used:
"expressions", not "statements", but I think this is probably true.
You similarly don't tend to see many addition operators where their value
isn't used. In general, operators are used only when we want their
values, except when they have side effects.
>> Certainly you'd check whether p == NULL immediately after allocating it
>> (assuming it points to allocated memory; it could point to anything).
>> But a null pointer might still be a valid state for p.
> So you are essentially overloading p. It points to a print_dev struct if
> one is available or NULL if it is not.
That's not really "overloading".
> Okay, I can see now where Moi is coming from; but, in my opinion, if
> there are no printers then *no* printer handling code should be called in
> the first place.
That seems much, much, harder to arrange.
> As Moi has it, the printing codes all must be able to
> handle the possibility that the printer may not exist. This is a
> requirement for *everything* that attempts to dereference p. Everybody who
> works with the code must be aware of this fact.
No, it's only a requirement for all the places at which you could enter
the code. If the only interface provided is
do_print(printer *p, struct print_me *data);
then only do_print has to handle this.
> I would much rather handle a missing printer as soon as the printer is
> allocated or selected.
Yes, but the way you handle it is by storing a null pointer to indicate
that there isn't anything to point to.
> NULL might be okay to pass back as a flag from the
> create_printer() function; but, once it has failed I want a more explicit
> indication of it If a valid printer is saved to p then a printer_count
> variable is incremented or possibly the printer is placed into some
> kind of collection where the length can be counted. I would prevent
> any printer handling code from running at all if p is not a valid
> printer_dev struct.
That seems a LOT more difficult. You then have to have every single place
that might want to ask that someone do something involving a printer check
that, which is more work.
Which is easier?
Case #1:
int do_print(printer *p, struct print_me *data) {
if (!p)
return -1;
/* code using p */
}
...
do_print(default_printer, file_data);
...
do_print(alternate_printer, file_data);
...
do_print(color_printer, stored_entries);
Case #2:
int do_print(printer *p, struct print_me *data) {
/* code using p */
}
...
if (default_printer)
do_print(default_printer, file_data);
...
if (alternate_printer)
do_print(alternate_printer, file_data);
...
if (color_printer)
do_print(color_printer, stored_entries);
I think the former makes more sense, and is also more robust.
> Even using NULL as a flag for a valid printer , I feel that checking
> whether or not the printer exists is logically distinct from what
> errors a valid printer may have.
That it might be, but it is easy to imagine a case where the
simplest thing to do is to merge the cases since "no printer exists"
is clearly very much like errors a printer could have.
> In both cases, I would have formated
> it as two blocks. One check that a valid printer is present, either by
> checking the printer count or in Moi's case whether p was NULL, and the
> other to check for the status of a valid printer struct. This seems more
> explicit to me.
It might be, but if the handling for error conditions is reasonably elaborate,
being able to route both cases through a single hunk of code may be easier
to maintain. Minimize special cases early and often!
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/21/2010 2:21:43 AM
|
|
>This was quite a long rant!! I think I see your point but you have to
>imagine your grannie when Word crashes. I think she will not like to see
>a message like
>"malloc failed at src\lib\unicode\mapper\table.c:762"
>I think that will confuse her!
I can put the above error message into Google and possibly get
details about the error from other users having problems with it.
If you give a generic message like "damn user, I ain't going to
tell you what's wrong" for everything, I cannot.
For an interactive program like Word, the program should try to save
the user's work or give the user a chance to do so before exiting.
If you are seriously concerned that the error message will confuse the
user, then:
(1) prompt the user for a telephone number where s/he can be
reached.
(2) Send an email with all the details of the problem you can
to your tech support.
(3) Have tech support call the user and explain the problem,
preferably within 10 minutes.
If you are seriously worried about the user getting hold of (2),
encrypt it. This will probably raise suspicion that your program
is spyware and is attempting to steal personal information.
Error messages should include:
(1) The attempted operation (e.g. "allocate memory", "open file",
"write file")
(2) The object the operation was targetting (e.g. the file name of
the file). In the case of memory allocation, how much memory
was being allocated (it is not unheard of to try to allocate
a negative amount of memory, due to a bug in program logic)?
(3) The reason from the operating system or code why the operation
failed. This may be the output of strerror(errno), which
you should include unless there is no possibility that it
reflects the problem. Include a complicated legal disclaimer,
if needed, that this reason may not be accurate. Yes, you
should include the output of strerror(errno) if a call to fopen()
fails, even though the C standard does not require fopen() to
set errno, it doesn't prohibit it either. And some operating
systems do set errno to a useful value most of the time when
fopen() fails.
(4) If it's commercial software, the phone number or email address
of tech support.
Your error messages shouldn't make unwarranted assumptions about
the way to fix an error, although it can suggest possibilities.
While "needs a restart" may seem like an obvious solution for
software running on an OS that leaks memory like a sieve, if the
program needs 129GB to run, many computers don't have that much
memory and cannot be expanded to have that much. Sometimes the OS
cannot handle that much memory even if it were installed.
|
|
0
|
|
|
|
Reply
|
gordonb
|
5/21/2010 6:43:49 AM
|
|
On 20 May, 22:20, Tim Harig <user...@ilthio.net> wrote:
> On 2010-05-20, Phil Carmody <thefatphil_demun...@yahoo.co.uk> wrote:
> > Tim Harig <user...@ilthio.net> writes:
> >> The ?: implies that a value is changing.
>
> Sorry, poor wording. =A0I don't always speak the best English. =A0What I =
was
> referring to is the fact that ?: implies an assignment (ie, the first
> argument).
no it doesn't. Maybe you'd drop your predjudice about ?: if you worked
out what it did... In what sense is ?: different from any other
operator?
<snip>
|
|
0
|
|
|
|
Reply
|
Nick
|
5/21/2010 7:15:01 AM
|
|
On 21 May, 01:03, Tim Harig <user...@ilthio.net> wrote:
> On 2010-05-20, Ben Pfaff <b...@cs.stanford.edu> wrote:
> > Tim Harig <user...@ilthio.net> writes:
> >> Sorry, poor wording. =A0I don't always speak the best English. =A0What=
I was
> >> referring to is the fact that ?: implies an assignment (ie, the first
> >> argument).
>
> > Here's a counterexample from some code of mine:
>
> > =A0 =A0 =A0 if (row->old
> > =A0 =A0 =A0 =A0 =A0 ? !ovsdb_datum_equals(&row->old[idx], &row->new[idx=
], &column->type)
> > =A0 =A0 =A0 =A0 =A0 : !ovsdb_datum_is_default(&row->new[idx], &column->=
type)) {
> > =A0 =A0 =A0 =A0 =A0 ...
> > =A0 =A0 =A0 }
>
> As I have already mentioned to Moi, in an almost identical example, while
> yes you are not actually assigning the value returned by the ?: statement=
,
> you are still using it. =A0You don't often see it used in isolation witho=
ut
> making use of its value, ie:
you don't often see + used in isolation without making use of its
value
> =A0 =A0 =A0 =A0 (row->old
> =A0 =A0 =A0 =A0 =A0 =A0? !ovsdb_datum_equals(&row->old[idx], &row->new[id=
x], &column->type)
> =A0 =A0 =A0 =A0 =A0 =A0: !ovsdb_datum_is_default(&row->new[idx], &column-=
>type)
>
> Since the it tends to imply a this or that quantity rather then a general
> if/else, you would tend to wonder why the value is not being used. =A0In =
that
> sense, using it in this way is somewhat misleading.
not really. In fact not at all
|
|
0
|
|
|
|
Reply
|
Nick
|
5/21/2010 7:16:57 AM
|
|
Tim Harig <usernet@ilthio.net> writes:
> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>> Tim Harig <usernet@ilthio.net> writes:
>>> The ?: implies that a value is changing.
>
> Sorry, poor wording. I don't always speak the best English. What I was
> referring to is the fact that ?: implies an assignment (ie, the first
> argument).
I have written many a printk that contains conditional terms. But
it works well for arbitrary function's parameters.
prepare_mem(p->buf, p->is_big ? BIG : SMALL);
So again, I dispute your so-called facts, no matter how they are
worded.
>> At first, I thought you were young and naive regarding programming.
>
> I wish I was young :(
I wish I hadn't found out yesterday that one of my managers has only
half the work experience that I have. And he's in a role where he is
supposed to make decisions about what I work on.
Your above assertion does support the hypothesis that you've simply
not seen enough code. So if you're trying to swing things back in
favour of just being naive, you're making progress.
>> I was wrong - you're simply full of crap. No :? ever implies that
>> a value is changing. Please put down the crack pipe.
>
> Would a crack pipe help diminish my growing gut? Where can I get one! :)
I can't say I've never known any crack users, but from what I've seen
in the trashy media, weight loss may occur. Beer, on the other hand,
can be healthy (so you want it live and not too heavily filtered), so
will only give gut-friendly, but of course gut-unfriendly, advice.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/21/2010 8:09:35 AM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> Tim Harig <usernet@ilthio.net> writes:
>> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>> Tim Harig <usernet@ilthio.net> writes:
>>>> The ?: implies that a value is changing.
>>
>> Sorry, poor wording. I don't always speak the best English. What I was
>> referring to is the fact that ?: implies an assignment (ie, the first
>> argument).
>
> I have written many a printk that contains conditional terms. But
> it works well for arbitrary function's parameters.
> prepare_mem(p->buf, p->is_big ? BIG : SMALL);
> So again, I dispute your so-called facts, no matter how they are
> worded.
What Tim meant, as he clarified in a later followup, is that the
use of ?: implies, or should imply, that the resulting value is
going to be used, i.e., not discarded. I tend to agree; if you're
going to discard the result, you might as well use an if statement.
Obviously "assignment" wasn't the righ word for this, but one can
very loosely think of the result of an expression being "assigned"
to the enclosing context.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
Keith
|
5/21/2010 3:24:00 PM
|
|
On Thu, 20 May 2010 15:17:22 -0700, Ben Pfaff wrote:
> Tim Harig <usernet@ilthio.net> writes:
>
>> Sorry, poor wording. I don't always speak the best English. What I was
>> referring to is the fact that ?: implies an assignment (ie, the first
>> argument).
>
> Here's a counterexample from some code of mine:
>
> if (row->old
> ? !ovsdb_datum_equals(&row->old[idx], &row->new[idx], &column->type)
> : !ovsdb_datum_is_default(&row->new[idx], &column->type)) {
> ...
> }
Interesting. I both like and dislike this one at the same
time. Counter-example from some old code of mine:
(offLmod == offRmod ? ror_d0 :
minlen <= BITS ? ror_short :
offLmod < offRmod ? ror_long_dn :
ror_long_up )
(bsL->data+offLdiv, bsR->data+offRdiv, offLmod, offRmod, minlen);
/Nisse
|
|
0
|
|
|
|
Reply
|
Nisse
|
5/21/2010 3:26:09 PM
|
|
On 2010-05-21, Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:
> On 21 May, 01:03, Tim Harig <user...@ilthio.net> wrote:
>> On 2010-05-20, Ben Pfaff <b...@cs.stanford.edu> wrote:
>> As I have already mentioned to Moi, in an almost identical example, while
>> yes you are not actually assigning the value returned by the ?: statement,
>> you are still using it. �You don't often see it used in isolation without
>> making use of its value, ie:
>
> you don't often see + used in isolation without making use of its
> value
And that is exaclty the point. The same is not true for if/else. It is
entirely conceivable that you would write an if/else without any
assignments that persist after the if/else.
if (condition)
/* enable bus */
else
/* print error */
>> Since the it tends to imply a this or that quantity rather then a general
>> if/else, you would tend to wonder why the value is not being used. �In that
>> sense, using it in this way is somewhat misleading.
>
> not really. In fact not at all
?: is more properly written as a this or that statement. While it doesn't
require the value that it returns to be used, that *is* the way that it was
designed to be used and the expectation that many programmers are going to
have when they see it. The first thing I am going to wonder if I see an
isolated ?: is why they forgot to use the value. An if/else does not
violate these expectations.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 4:58:30 PM
|
|
On 2010-05-21, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Tim Harig <usernet@ilthio.net> writes:
>> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>> Tim Harig <usernet@ilthio.net> writes:
>> Sorry, poor wording. I don't always speak the best English. What I was
>> referring to is the fact that ?: implies an assignment (ie, the first
>> argument).
>
> I have written many a printk that contains conditional terms. But
> it works well for arbitrary function's parameters.
> prepare_mem(p->buf, p->is_big ? BIG : SMALL);
> So again, I dispute your so-called facts, no matter how they are
> worded.
1. I have already said that I don't mind as much when used in an output
statement because it doesn't alter the control flow structure of
the program.
2. Your example is non-sequator to the argumentation that followed it.
3. It would seem obvious to me to either change the function declaration so
that it uses the same values as p->is_big or modify the struct
definition so that it uses the same values expected by the
function. Personally, p->is_big should have been an enumeration
in the first place. This isn't a ?: problem, it is a design
problem.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 5:07:00 PM
|
|
On 2010-05-21, Keith Thompson <kst-u@mib.org> wrote:
> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>> Tim Harig <usernet@ilthio.net> writes:
>>> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>>> Tim Harig <usernet@ilthio.net> writes:
>>>>> The ?: implies that a value is changing.
>>>
>>> Sorry, poor wording. I don't always speak the best English. What I was
>>> referring to is the fact that ?: implies an assignment (ie, the first
>>> argument).
>>
>> I have written many a printk that contains conditional terms. But
>> it works well for arbitrary function's parameters.
>> prepare_mem(p->buf, p->is_big ? BIG : SMALL);
>> So again, I dispute your so-called facts, no matter how they are
>> worded.
>
> What Tim meant, as he clarified in a later followup, is that the
> use of ?: implies, or should imply, that the resulting value is
> going to be used, i.e., not discarded. I tend to agree; if you're
> going to discard the result, you might as well use an if statement.
> Obviously "assignment" wasn't the righ word for this, but one can
> very loosely think of the result of an expression being "assigned"
> to the enclosing context.
Thank-you.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 5:08:00 PM
|
|
On 2010-05-21, Seebs <usenet-nospam@seebs.net> wrote:
> On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
>> What I mean is that you do not normally see ?: operators where no value
>> from the statements are used:
> "expressions", not "statements", but I think this is probably true.
> You similarly don't tend to see many addition operators where their value
> isn't used. In general, operators are used only when we want their
> values, except when they have side effects.
Exactly. The same is not however true for if/else statements. It is quite
reasonable to see an if/else statement where no value is assigned that
persists outside of the if/else block.
if (condition)
/* enable bus */
else
/* print error */
>>> Certainly you'd check whether p == NULL immediately after allocating it
>>> (assuming it points to allocated memory; it could point to anything).
>>> But a null pointer might still be a valid state for p.
>
>> So you are essentially overloading p. It points to a print_dev struct if
>> one is available or NULL if it is not.
>
> That's not really "overloading".
No, I didn't have a more exact term to describe it, and since C does not
allow overloading, I thought that it might be descriptive in this case.
The fact is that you are using p in two different ways to send two
different kinds of information. This isn't really all that different from
the ways many standard functions like fgetc() return an int for what is
expected to be a char value so that they can also send error information
along with any values. This is questionable practice that tends to cause
lots of trouble (in this case particularly to newcomers.)
In general, it is a much better practice to explicitly separate the data
outputs. The structure is sent in channel while the error code is sent
separately out of channel. If I was going to write a function that creates
p, I often use something like this:
typedef enum fulfillment_type {SUCCESS, FAILURE} fulfillment;
/*
creates a valid printer_dev at the buffer pointed to by printer -- returns
SUCCESS or FAILURE depending on whether a valid pring_dev could actually be
created
*/
fulfillment create_printer(printer_dev* printer);
In this way, I have an explicitly differentiated the error information from
the data itself.
Furthermore, I have allowed the possibility of later expanding the
interface to include values other then success or failure which may help
with error handling down the road. As somebody pointed out, there are
different reasons why the printer may not be available. NULL only
allows you to say that it isn't available; but, provides no information as
to why. It could be that the printer is not physically available or that
there are printers available but the user has not choosen which to use for
this operation. By using an external error indicator, I might have my
error handing code call a printer select dialog if printers are available
but none have been chosen.
>> Okay, I can see now where Moi is coming from; but, in my opinion, if
>> there are no printers then *no* printer handling code should be called in
>> the first place.
>
> That seems much, much, harder to arrange.
Not really. There may be several layers on top of the extra printer
handling functionality. In this example, one would expect to see code
which confirms that there is valid data to print, converts the data into a
format that the printer understands, then it actually calls the printer
handling code to send data to the printer. If the code checks for a valid
printer when a printing operation is requested, then non of this needs to
be called.
>> As Moi has it, the printing codes all must be able to
>> handle the possibility that the printer may not exist. This is a
>> requirement for *everything* that attempts to dereference p. Everybody who
>> works with the code must be aware of this fact.
>
> No, it's only a requirement for all the places at which you could enter
> the code. If the only interface provided is
> do_print(printer *p, struct print_me *data);
> then only do_print has to handle this.
Agreed, but the code that calls do_printer still must be able to handle the
differing ways in which do_printer might fail.
>> I would much rather handle a missing printer as soon as the printer is
>> allocated or selected.
>
> Yes, but the way you handle it is by storing a null pointer to indicate
> that there isn't anything to point to.
As I have already said, a NULL pointer indicates that there is an error.
It doesn't explain why. Even with an absence of requirements that different
errors handled differently, it is much more descriptive to say:
if (is_printer_available())
/* ... */
else
/* error code */
then:
if (printer != NULL)
/* ... */
else
/* error code */
The second only tells me that there is a problem. The second tells me that
there is an error because no printer is available.
>> NULL might be okay to pass back as a flag from the
>> create_printer() function; but, once it has failed I want a more explicit
>> indication of it If a valid printer is saved to p then a printer_count
>> variable is incremented or possibly the printer is placed into some
>> kind of collection where the length can be counted. I would prevent
>> any printer handling code from running at all if p is not a valid
>> printer_dev struct.
>
> That seems a LOT more difficult. You then have to have every single place
> that might want to ask that someone do something involving a printer check
> that, which is more work.
>
> Which is easier?
>
> Case #1:
> int do_print(printer *p, struct print_me *data) {
> if (!p)
> return -1;
> /* code using p */
> }
>
> ...
> do_print(default_printer, file_data);
> ...
> do_print(alternate_printer, file_data);
> ...
> do_print(color_printer, stored_entries);
>
> Case #2:
> int do_print(printer *p, struct print_me *data) {
> /* code using p */
> }
> ...
> if (default_printer)
> do_print(default_printer, file_data);
> ...
> if (alternate_printer)
> do_print(alternate_printer, file_data);
> ...
> if (color_printer)
> do_print(color_printer, stored_entries);
You mistaken in my reasoning. Likely, the test will be performed much
higher up before the system even attempts to generate information to send
to the printer. It makes no sense to do this much if there is not going to
be an printer to send it to. Secondly, the test will likely only need to
be in a single place inside of the function interface that actually
initiates any printer operations.
> I think the former makes more sense, and is also more robust.
First see above, since test only needs to be called at the first common
entry into the printing system it is no less robust.
Second, while allowing an error to propogate through the code, there are
many more places that it could fail. Allowing a NULL printer value not
only creates problems in the actual printer handling code but could cause
issues where the program has to decide what output to send to the printer
etc.
In my model, it is illegal to call print handling code if the printer is
not valid in the first place. This would be enforced by assertions to make
sure the code is never used in this way. Since the code is never even
called for an invalid printer, there is no way that any piece of code which
was not properly designed to handle the missing printer could cause any
issues. I call this more robust.
>> Even using NULL as a flag for a valid printer , I feel that checking
>> whether or not the printer exists is logically distinct from what
>> errors a valid printer may have.
>
> That it might be, but it is easy to imagine a case where the
> simplest thing to do is to merge the cases since "no printer exists"
> is clearly very much like errors a printer could have.
The simplest thing to do isn't always the best thing to do.
>> In both cases, I would have formated
>> it as two blocks. One check that a valid printer is present, either by
>> checking the printer count or in Moi's case whether p was NULL, and the
>> other to check for the status of a valid printer struct. This seems more
>> explicit to me.
>
> It might be, but if the handling for error conditions is reasonably elaborate,
> being able to route both cases through a single hunk of code may be easier
> to maintain. Minimize special cases early and often!
Congratulations, this is the first decent argument that I have seen used in
this sub-thread. I do however disagree that this is a problem.
1. I would not even enounter this problem because I have made sure this
incident never arises as in my model, this code never would have
been called.
2. I don't think this really adds any significant complication and omits
the "special case" far earlier so that the same bit of code is not
complicated in having to use a value in two different ways.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 6:05:14 PM
|
|
On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
> On 2010-05-21, Seebs <usenet-nospam@seebs.net> wrote:
>> That's not really "overloading".
> No, I didn't have a more exact term to describe it, and since C does not
> allow overloading, I thought that it might be descriptive in this case.
Hmm.
> The fact is that you are using p in two different ways to send two
> different kinds of information. This isn't really all that different from
> the ways many standard functions like fgetc() return an int for what is
> expected to be a char value so that they can also send error information
> along with any values. This is questionable practice that tends to cause
> lots of trouble (in this case particularly to newcomers.)
Oh, certainly it's questionable. Go ahead and question it.
> In general, it is a much better practice to explicitly separate the data
> outputs. The structure is sent in channel while the error code is sent
> separately out of channel. If I was going to write a function that creates
> p, I often use something like this:
I am not sure this is actually that much better a practice, especially in C,
where it's pretty much idiomatic to use a null pointer to indicate "we don't
have a foo".
> /*
> creates a valid printer_dev at the buffer pointed to by printer -- returns
> SUCCESS or FAILURE depending on whether a valid pring_dev could actually be
> created
> */
> fulfillment create_printer(printer_dev* printer);
> In this way, I have an explicitly differentiated the error information from
> the data itself.
And also forced the user to know how to allocate a printer, which does nothing
but push the issue somewhere else. Now the user has to write:
printer_dev *p = get_new_printer();
if (p)...
So you're right back where you started.
You can, of course, do:
fulfillment create_printer(printer_dev **printer);
and declare that, if it doesn't return SUCCESS, it hasn't done anything to
"printer".
But that's annoying! Furthermore, even if you've done this, you STILL have
the possibility that "printer" will be a null pointer.
You can never eliminate the possibility of a pointer being null-or-invalid
unless it's the address of a static object. For most real-world cases,
dynamic allocation is necessary. You can't detect an invalid pointer, so
you have to use null as the "we don't have one" state.
Since you MUST do that, it is ridiculous to then attach a second status that
isn't the pointer's status, because the moment you do that, you've created
two pieces of information which are supposed to reflect the same thing, and
thus the possibility for them to be out of sync.
> As somebody pointed out, there are
> different reasons why the printer may not be available. NULL only
> allows you to say that it isn't available; but, provides no information as
> to why.
Right, because at the point where you'd be *using* the printer, you don't
care why. It's only at the point that an attempt was made to *allocate*
a printer that you care why, and the error handling for that belongs
there.
>>> Okay, I can see now where Moi is coming from; but, in my opinion, if
>>> there are no printers then *no* printer handling code should be called in
>>> the first place.
>> That seems much, much, harder to arrange.
> Not really. There may be several layers on top of the extra printer
> handling functionality.
That would be "much, much, harder to arrange". You're creating bloat in
code, data storage, and layers of abstraction, all to avoid something that
is clear, idiomatic, and useful.
>> No, it's only a requirement for all the places at which you could enter
>> the code. If the only interface provided is
>> do_print(printer *p, struct print_me *data);
>> then only do_print has to handle this.
> Agreed, but the code that calls do_printer still must be able to handle the
> differing ways in which do_printer might fail.
Yup. But "no printer available" is plenty complete for this point; the
point at which a more specific problem with printer availability would
have been addressed would have been when p was configured.
> As I have already said, a NULL pointer indicates that there is an error.
> It doesn't explain why.
It doesn't have to. The "why" was addressed when the pointer failed to
acquire a non-null value. Past that, all you need to know is that there
is not currently a printer.
> Even with an absence of requirements that different
> errors handled differently, it is much more descriptive to say:
>
> if (is_printer_available())
> /* ... */
> else
> /* error code */
>
> then:
>
> if (printer != NULL)
> /* ... */
> else
> /* error code */
>
> The second only tells me that there is a problem. The second tells me that
> there is an error because no printer is available.
They're completely identical. Both tell you that there is no printer
available. If there were a printer available, printer would not be a null
pointer.
> You mistaken in my reasoning. Likely, the test will be performed much
> higher up before the system even attempts to generate information to send
> to the printer. It makes no sense to do this much if there is not going to
> be an printer to send it to. Secondly, the test will likely only need to
> be in a single place inside of the function interface that actually
> initiates any printer operations.
This seems very speculative, and you've repeatedly talked about adding
additional layers of interfaces, which sounds like a lot of overhead to
satisfy a prejudice.
In C, a null pointer is idiomatically used to indicate the unavailability
of a thing that would otherwise have been pointed to. That's the idiom
the language standard uses, for everything from malloc() to fopen(). Using
any other idiom is gratuitously confusing.
> First see above, since test only needs to be called at the first common
> entry into the printing system it is no less robust.
I don't buy this notion of a "first common entry into the printing system".
The printing system is used by the rest of the program; every part of the
rest of the program that might wish to print has to know how to tell what
it can or can't do.
> Second, while allowing an error to propogate through the code, there are
> many more places that it could fail.
Not really. It's just as practical to have the "first common entry" treat
a null pointer as evidence that there's no printer as to have some other
elaborate scheme.
> Allowing a NULL printer value not
> only creates problems in the actual printer handling code but could cause
> issues where the program has to decide what output to send to the printer
> etc.
It's *at worst* no different at all from yours. There are many easily
constructed cases where it's better.
> In my model, it is illegal to call print handling code if the printer is
> not valid in the first place. This would be enforced by assertions to make
> sure the code is never used in this way. Since the code is never even
> called for an invalid printer, there is no way that any piece of code which
> was not properly designed to handle the missing printer could cause any
> issues. I call this more robust.
This is too vague and hand-wavey to constitute an argument or a design
description. Present a complete, self-contained, example program showing
how you think this would work. I'm not buying it at all; so far, it sounds
like you're inventing a huge amount of overhead in order to synchronize
two pieces of information, and thus introducing an entire new *class* of
errors that are impossible with the standard idiom.
>> That it might be, but it is easy to imagine a case where the
>> simplest thing to do is to merge the cases since "no printer exists"
>> is clearly very much like errors a printer could have.
> The simplest thing to do isn't always the best thing to do.
Not always, but until you have an explanation of something better which
is both concrete and coherent, and actually shows some kind of evidence
for why it's better other than your dislike of the completely standard
C idiom, which everyone is already used to and using and which works reliably
in billions of lines of code, I'm going to do something simple and reliable.
> Congratulations, this is the first decent argument that I have seen used in
> this sub-thread. I do however disagree that this is a problem.
> 1. I would not even enounter this problem because I have made sure this
> incident never arises as in my model, this code never would have
> been called.
You're just pushing the problem out a layer of abstraction without changing
it.
SOME code is called. Doesn't matter whether it's this code or your
completely gratuitous and probably buggy abstraction layer that you invented
to work around the standard C idiom. Some code is called, and must deal
with the possibility that no printer is available.
I'm using your printer layer. I want to print something. I must deal with
the possibility that there is no printer available. It doesn't matter
whether I deal with this by checking whether a pointer is null or doing
something else; I still have to deal with it.
> 2. I don't think this really adds any significant complication and omits
> the "special case" far earlier so that the same bit of code is not
> complicated in having to use a value in two different ways.
If sentinel values were some kind of incredibly arcane feature that no one
had ever heard of and that an ordinary C programmer might never have
encountered, this would be an argument.
They're not. It isn't.
There is no way to get away from the existence of null pointers, and the
possibility that a given pointer might be null when it's handed to your
code. You have to write the code for that anyway. By long-standing idiom,
that's the way to indicate "and we don't have one of these".
My solution:
void error_out(char *msg) {
fprintf(stderr, "fatal error: %s\n", msg ? msg : "unknown error");
exit(1);
}
...
error_out("couldn't open a file, sorry.");
...
error_out(NULL);
Your solution:
typedef enum error_message_availability {
ERROR_NOT_AVAILABLE,
ERROR_AVAILABLE,
} error_message_availability_t;
void error_out(error_message_availability_t avail, char *msg) {
if (avail == ERROR_AVAILABLE)
fprintf(stderr, "fatal error: %s\n", msg);
else
fprintf(stderr, "fatal error: unknown error\n");
exit(1);
}
...
error_out(ERROR_AVAILABLE, "couldn't open a file, sorry.");
...
error_out(ERROR_NOT_AVAILABLE, /* what goes here? */);
In your system, it is now possible to write:
error_out(ERROR_AVAILABLE, NULL);
and cause a null pointer dereference. In mine, it's not.
Furthermore, you still haven't suggested what should go in the "msg" slot
if there is no error. Or do you want us to do:
void error_out_with_message(char *msg) {
fprintf(stderr, "fatal error: %s\n", msg);
exit(1);
}
void error_out_without_message(void) {
fprintf(stderr, "fatal error: unknown error\n");
exit(1);
}
...
if (error_availability == ERROR_AVAILABLE)
error_out_with_message("couldn't open a file, sorry.");
else
error_out_without_message();
This is just ridiculous. You still have to deal with the fact that someone
can pass a pointer to your code, and that pointer could be a null pointer.
You can never escape that in any program which uses dynamic allocation. If
you allow users to specify the thing they want you to operate on by passing
a pointer to it, the pointer can be null. If you don't, you have a crippled
interface and people won't be able to make much use of it.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/21/2010 6:17:25 PM
|
|
On 2010-05-21, Seebs <usenet-nospam@seebs.net> wrote:
> On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
>> In general, it is a much better practice to explicitly separate the data
>> outputs. The structure is sent in channel while the error code is sent
>> separately out of channel. If I was going to write a function that creates
>> p, I often use something like this:
>
> I am not sure this is actually that much better a practice, especially in C,
> where it's pretty much idiomatic to use a null pointer to indicate "we don't
> have a foo".
Yes it is idomatic and common; but, in my experience it also contributes to
a number of bugs. I am not conserned how tradional something has become.
If it is creating bugs then, it is ineffective and the practice needs to be
replaced as much as possible to prevent said bugs.
>> /*
>> creates a valid printer_dev at the buffer pointed to by printer -- returns
>> SUCCESS or FAILURE depending on whether a valid pring_dev could actually be
>> created
>> */
>> fulfillment create_printer(printer_dev* printer);
>
>> In this way, I have an explicitly differentiated the error information from
>> the data itself.
>
> And also forced the user to know how to allocate a printer, which does nothing
> but push the issue somewhere else. Now the user has to write:
>
> printer_dev *p = get_new_printer();
> if (p)...
>
> So you're right back where you started.
I am assuming that whatever code creates a printer is wrapped in a
function. It should be. Code that accesses a printer should not have to
know anything about the printer structure any more then the programmer
should know about FILEs.
> You can, of course, do:
> fulfillment create_printer(printer_dev **printer);
> and declare that, if it doesn't return SUCCESS, it hasn't done anything to
> "printer".
No, then you have the same problem and you are gaining the worst of BOTH
implementations.
> You can never eliminate the possibility of a pointer being null-or-invalid
> unless it's the address of a static object. For most real-world cases,
> dynamic allocation is necessary. You can't detect an invalid pointer, so
> you have to use null as the "we don't have one" state.
I did assume dynamic allocation. The only difference is that the printer
handling code no longer does its own allocation. The calling code
allocates memory for the print_dev structure and is responsible for
releasing it when it is done.
> Since you MUST do that, it is ridiculous to then attach a second status that
> isn't the pointer's status, because the moment you do that, you've created
> two pieces of information which are supposed to reflect the same thing, and
> thus the possibility for them to be out of sync.
No, you have created one status that should be used. The stucture should
be unallocated and it is a good idea to change the pointer to NULL so that
the program segfaults if it is dereferenced rather then invoke undefined
behavior.
Personally, I would have saved all of the available printers to some kind
of collection. I would not have used a pointer after the printers were
allocated. Then, I would simply pass the pointer to the data structure
holding the printers, which may be empty.
>> As somebody pointed out, there are
>> different reasons why the printer may not be available. NULL only
>> allows you to say that it isn't available; but, provides no information as
>> to why.
>
> Right, because at the point where you'd be *using* the printer, you don't
> care why. It's only at the point that an attempt was made to *allocate*
> a printer that you care why, and the error handling for that belongs
> there.
You do when you attempt to initialize the printer. Did the printer fail
because there was no printer, because we do not permissions to access the
printer, etc. All of these different variations on the failure can either
be handled or at least logged for troubleshooting purposes.
>> Not really. There may be several layers on top of the extra printer
>> handling functionality.
>
> That would be "much, much, harder to arrange". You're creating bloat in
> code, data storage, and layers of abstraction, all to avoid something that
> is clear, idiomatic, and useful.
No, I gave my opinion of the code design because I also questioned it. If
you don't want to accept these suggestions you don't have to. It was not
part of the argument against ?:. This is why I had to split my comments
below.
It is not in fact much, much harder to arrange in real code. Yes, it
entails more then was given in the code fragment; but, in the real world
there would be error handling infrastructure to deal with these kinds of
problems, and others, already in place. This just uses what should already
be there.
> This seems very speculative, and you've repeatedly talked about adding
> additional layers of interfaces, which sounds like a lot of overhead to
> satisfy a prejudice.
>
> In C, a null pointer is idiomatically used to indicate the unavailability
> of a thing that would otherwise have been pointed to. That's the idiom
> the language standard uses, for everything from malloc() to fopen(). Using
> any other idiom is gratuitously confusing.
1. This would have been my recommendation up and above whether or not to
use the ?:.
2. Yes, the NULL pointer is idiomatic and tradional; but, also is a common
source of bugs. I don't care about being traditional. I care
about creating bugless code.
3. This is one of the coding standards that we use in house. It has been
largely effective. The decision only comes in to play when
creating the function interfaces. We have not had any problems
with people understanding how to use them. We have less problems
then we had before we added this to the coding standards.
>> First see above, since test only needs to be called at the first common
>> entry into the printing system it is no less robust.
>
> I don't buy this notion of a "first common entry into the printing system".
> The printing system is used by the rest of the program; every part of the
> rest of the program that might wish to print has to know how to tell what
> it can or can't do.
Given the example of a traditional GUI text editor. In my code, when the
app starts, it would initialize the printing system. If there are no
printers available, no option to print would be available. The only places
that need to test whether the printing system is properly established is
the dialog code that adds the ability to print to the menu and possibly the
wrapper function that is called when the user generates a keystroke
designated to print. If the printing system is not initialized, then the
menu item is not displayed and the function that wraps the keystroke
effectively becomes a noop.
Now, with the test in just two places, the system will never even allow the
possiblity of calling the printing code unless the printing system is
properly initialized.
>> Second, while allowing an error to propogate through the code, there are
>> many more places that it could fail.
>
> Not really. It's just as practical to have the "first common entry" treat
> a null pointer as evidence that there's no printer as to have some other
> elaborate scheme.
You could also use the NULL pointer. The operation is equivilant; as long as
it is done in the dialog code that allows printing and the keystroke
wrapper function the printer handling code need never be accessed.
>> Allowing a NULL printer value not
>> only creates problems in the actual printer handling code but could cause
>> issues where the program has to decide what output to send to the printer
>> etc.
>
> It's *at worst* no different at all from yours. There are many easily
> constructed cases where it's better.
Again, use NULL if you prefer that. We have found it to cause more issues
then separating state from data. This is all really irrelevant to my
points on ?:
>> In my model, it is illegal to call print handling code if the printer is
>> not valid in the first place. This would be enforced by assertions to make
>> sure the code is never used in this way. Since the code is never even
>> called for an invalid printer, there is no way that any piece of code which
>> was not properly designed to handle the missing printer could cause any
>> issues. I call this more robust.
>
> This is too vague and hand-wavey to constitute an argument or a design
> description. Present a complete, self-contained, example program showing
> how you think this would work. I'm not buying it at all; so far, it sounds
> like you're inventing a huge amount of overhead in order to synchronize
> two pieces of information, and thus introducing an entire new *class* of
> errors that are impossible with the standard idiom.
I already explained that the tests were in the dialog generation code and
the keystroke wrapper code. For all of the printer handling functions it
would be defined as illegal to pass them a NULL pointer for the printer
field. The very first lines in all of our functions are assert statements.
One of those asserts would be:
assert(printer != NULL);
Now, if any of these functions receive a NULL printer it is because there
is an error in the calling code. During testing, any errant calling code
that does pass a NULL pointer will cause the system to abort making it
clear that there is an error. Furthermore, it is easy to create unit tests
that deliberately return an unsuccessful printer intialization to see if
the code handles it correctly or if it passes the NULL on to the print
handling code which will abort to indicate something is wrong with that
unit of code.
> Not always, but until you have an explanation of something better which
> is both concrete and coherent, and actually shows some kind of evidence
> for why it's better other than your dislike of the completely standard
> C idiom, which everyone is already used to and using and which works reliably
> in billions of lines of code, I'm going to do something simple and reliable.
Please differentiate my suggestions from my arguments against ?:. As
suggestions I have stated:
1. Any problems properly allocating a printer should be handled as soon as
possible. The farther an invalid printer is propogated throught
the code, the more opportunities that somewhere it will not be
handled throughly.
2. To achieve 1, I recommend not calling the printer code if there is no
valid printer to be used. This can be prevented by placing
restrictions in the GUI menu and keystroke wrappers that call the
printer infrastructure.
3. Moving the error conditions outside of the communication channel used
for transfering data reduces the possiblity that the user will
forget to properly check the validity of the data received.
4. That by separating the data and error status information, you have more
flexibility in handling specific errors that may occur.
All of these (except 2 depends on 1), are independant suggestions that may
be evaluated individually. None of them have a direct bearing on the ?:
argument.
>> Congratulations, this is the first decent argument that I have seen used in
>> this sub-thread. I do however disagree that this is a problem.
>
>> 1. I would not even enounter this problem because I have made sure this
>> incident never arises as in my model, this code never would have
>> been called.
>
> You're just pushing the problem out a layer of abstraction without changing
> it.
Yes, but by doing so, far less of the code is in a position to mishandle an
error condition.
> SOME code is called. Doesn't matter whether it's this code or your
> completely gratuitous and probably buggy abstraction layer that you invented
> to work around the standard C idiom. Some code is called, and must deal
> with the possibility that no printer is available.
I didn't do this the remove the C idiom. I moved the error handling to the
top where there is less exposure in the code. If you prefer, this can
still be done using NULLs and ?:s. This is a completely separate point.
> I'm using your printer layer. I want to print something. I must deal with
> the possibility that there is no printer available. It doesn't matter
> whether I deal with this by checking whether a pointer is null or doing
> something else; I still have to deal with it.
You may want to print something; but, you cannot because there is no
printer available. You don't need to deal with it, because it has already
been delt with far higher up in the codebase. There is no way of getting
to where you are that you want to print something. The option to do so has
been removed.
>> 2. I don't think this really adds any significant complication and omits
>> the "special case" far earlier so that the same bit of code is not
>> complicated in having to use a value in two different ways.
>
> If sentinel values were some kind of incredibly arcane feature that no one
> had ever heard of and that an ordinary C programmer might never have
> encountered, this would be an argument.
>
> They're not. It isn't.
The point is that they are not necessary. The do not provide anything that
cannot be done using an if/else.
We added not using ?: to our coding standards because it created bugs that
were clearer to see when they had been converted to if/else syntax using
the exact same logic. I don't disagree that they can be used effectively.
What I disagree with is that too many people who think that they are
using them effectively are not by the fact that they have used them to
obfuscate bugs in their code.
Furthermore, since ?: is usually used on a single line, it violates the
expectation that control statements will occupy multiple commonly indented
lines. This means that it is easy to overlook this conditional when
scanning a piece of code looking for bugs.
If ?: actually provided something that if/else could not. We do allow goto
and, because of the stigma that most programmers have inhereted for it, we
see it abused far less. As it is, it does not. It is just a measure
to decrease typing. We do not see the cost of typing overwaying the cost
of adding bugs into our codebase.
> There is no way to get away from the existence of null pointers, and the
> possibility that a given pointer might be null when it's handed to your
> code. You have to write the code for that anyway. By long-standing idiom,
> that's the way to indicate "and we don't have one of these".
Yes, NULL pointers exist. If they are handled improperly, they cause bugs.
The longer they are allowed to permiate the code base, the longer that they
will be mishandled somewhere. Therefore, we have found it prudent to
handle them as soon as possible; even if it violates the long standing
idiom. Long standing idioms to not have to account for the bugs that I
create using them. I do.
> My solution:
> void error_out(char *msg) {
> fprintf(stderr, "fatal error: %s\n", msg ? msg : "unknown error");
> exit(1);
> }
> ...
> error_out("couldn't open a file, sorry.");
> ...
> error_out(NULL);
>
>
> Your solution:
> typedef enum error_message_availability {
> ERROR_NOT_AVAILABLE,
> ERROR_AVAILABLE,
> } error_message_availability_t;
>
> void error_out(error_message_availability_t avail, char *msg) {
> if (avail == ERROR_AVAILABLE)
> fprintf(stderr, "fatal error: %s\n", msg);
> else
> fprintf(stderr, "fatal error: unknown error\n");
> exit(1);
> }
> ...
> error_out(ERROR_AVAILABLE, "couldn't open a file, sorry.");
> ...
> error_out(ERROR_NOT_AVAILABLE, /* what goes here? */);
That would *NOT* be my solution. My solution would never call a function
outputing a message unless the message was available to begin with. I
would call a different function. Any inputs would be required to be valid
and they would be asserted to be so.
It is my philosophy that errors should be handled where they are found
rather then being pushed off to another function hoping that it can
handle the error cleanly. If you send data to any function; then,
you should have sanized it to make sure that it was valid.
When functions encounter errors, they should cleanly indicate that an error
has occured and if possible why.
> This is just ridiculous. You still have to deal with the fact that someone
> can pass a pointer to your code, and that pointer could be a null pointer.
Functions have specifications. If the specification of a function says
that the function should not be given a NULL pointer, then the programmer
who called the function with a NULL pointer is at fault. The function
should assert this and abort should it ever be passed a NULL pointer.
> You can never escape that in any program which uses dynamic allocation. If
I don't, I am assuming dynamic allocation.
> you allow users to specify the thing they want you to operate on by passing
> a pointer to it, the pointer can be null. If you don't, you have a crippled
> interface and people won't be able to make much use of it.
Our inteface makes use of this principle. When programmers violate it, the
asserts cause the code to abort making it *very* clear that they have done
something wrong. We write code that is designed to be *highly* stable and
we add far more infrastructure then most projects do in the process. The
coding guidlines that we have adopted help us to do that. NULL pointers
are a common source of issues; so, we have found ways of working around
them so that they are avoided as much as possible and if they are not
properly sanitized, we have taken offorts to assure that they are more
likely to crash during testing rather then escaping into shipping code.
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 8:51:35 PM
|
|
Keith Thompson <kst-u@mib.org> writes:
> Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
>> Tim Harig <usernet@ilthio.net> writes:
>>> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>>> Tim Harig <usernet@ilthio.net> writes:
>>>>> The ?: implies that a value is changing.
>>>
>>> Sorry, poor wording. I don't always speak the best English. What I was
>>> referring to is the fact that ?: implies an assignment (ie, the first
>>> argument).
>>
>> I have written many a printk that contains conditional terms. But
>> it works well for arbitrary function's parameters.
>> prepare_mem(p->buf, p->is_big ? BIG : SMALL);
>> So again, I dispute your so-called facts, no matter how they are
>> worded.
>
> What Tim meant, as he clarified in a later followup, is that the
> use of ?: implies, or should imply, that the resulting value is
> going to be used, i.e., not discarded. I tend to agree; if you're
> going to discard the result, you might as well use an if statement.
Agreed. So much agreed that it's a policy I work to. If you can
sniff out our core-review mailing list (ok.r.n.c) you'll see me
rejecting a ?: with an unused evaluation less than a week ago.
(Describing it as 'macho code' more suited to the IOCCC than the
linux kernel.)
/However/, if you are going to use the result, and you're the only
user of the test, then you might as well use the ? : operator, surely?
So they're both useful, for their own particular cases or niches?
If that's "yes, yes", then we're in complete agreement, and in
clear disagreement with TH.
> Obviously "assignment" wasn't the righ word for this, but one can
> very loosely think of the result of an expression being "assigned"
> to the enclosing context.
No wording would have made what he said correct in my eyes.
Why use both '+' and '-'? '-', in sufficient quantities, can
do everything that '+' can do! It's just bad logic.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/21/2010 9:40:44 PM
|
|
On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
> Yes it is idomatic and common; but, in my experience it also contributes to
> a number of bugs.
I am not convinced of this. Furthrermore, avoiding the standard way of doing
something creates a lot of bugs too.
> I am not conserned how tradional something has become.
> If it is creating bugs then, it is ineffective and the practice needs to be
> replaced as much as possible to prevent said bugs.
Nothing you've posted here has looked to me as though it could POSSIBLY
result in fewer bugs.
> I am assuming that whatever code creates a printer is wrapped in a
> function. It should be. Code that accesses a printer should not have to
> know anything about the printer structure any more then the programmer
> should know about FILEs.
Very good!
But "whether or not I have a printer" is not anything about the printer
structure.
Let's get more specific. In your implementation, do I ever see a
(printer_dev *)? If so, what is its value if I don't have a printer?
If your solution is "never call any code that could possibly have used
that structure unless there is a printer", we have interesting issues. How
do I decide whether or not to call the code that could have used that?
If I do want to use it, what do I do to tell that code what printer it
gets? If I pass a pointer to it, what happens if the pointer is a
null pointer? (And no matter what you think about planning, it WILL be
a null pointer sooner or later, because That Is What Happens. Code
defensively!)
>> You can, of course, do:
>> fulfillment create_printer(printer_dev **printer);
>> and declare that, if it doesn't return SUCCESS, it hasn't done anything to
>> "printer".
> No, then you have the same problem and you are gaining the worst of BOTH
> implementations.
Why, yes. But you haven't given me any way to get this thing that's like
a pointer but is never null yet.
> I did assume dynamic allocation. The only difference is that the printer
> handling code no longer does its own allocation. The calling code
> allocates memory for the print_dev structure and is responsible for
> releasing it when it is done.
If I might quote something:
> Code that accesses a printer should not have to
> know anything about the printer structure any more then the programmer
> should know about FILEs.
Remember that?
You are now requiring the CALLING CODE to do the allocation and release.
Meaning the calling code now needs to know what size a print_dev structure
is, and allocate it, and maintain it. That's EXACTLY what you just said
the calling code shouldn't have to do.
> No, you have created one status that should be used. The stucture should
> be unallocated and it is a good idea to change the pointer to NULL so that
> the program segfaults if it is dereferenced rather then invoke undefined
> behavior.
Uh.
First off, dereferecing null pointers *IS* undefined behavior -- and it is
*not* guaranteed to segfault. Secondly, you're missing my point.
If your status *is* the pointer, then it is impossible for you to think you
have a pointer when it's null.
If your status is *anything other than* the pointer, then it is possible for
the status to indicate that you have a printer, but the pointer is a null
pointer.
You've created a new point of failure.
> Personally, I would have saved all of the available printers to some kind
> of collection. I would not have used a pointer after the printers were
> allocated. Then, I would simply pass the pointer to the data structure
> holding the printers, which may be empty.
And what happens if that allocation fails? We're right back to "you have
to check whether the pointer is null".
>> Right, because at the point where you'd be *using* the printer, you don't
>> care why. It's only at the point that an attempt was made to *allocate*
>> a printer that you care why, and the error handling for that belongs
>> there.
> You do when you attempt to initialize the printer. Did the printer fail
> because there was no printer, because we do not permissions to access the
> printer, etc.
There are two possibilities:
1. These errors occurred during the attempt to create a pointer-to-printer,
and were logged/diagnosed at that time. We have already logged or diagnosed
that error, and the fact that we are trying to access an unavailable printer
is now a separate error which has nothing to do with why there is no
printer available.
2. These errors will be generated while interacting with a valid
pointer-to-printer which is not a null pointer.
> 2. Yes, the NULL pointer is idiomatic and tradional; but, also is a common
> source of bugs. I don't care about being traditional. I care
> about creating bugless code.
Which is why you've proposed a system which has several more possible failure
modes and requires more layers of allocation and bookkeeping, dramatically
increasing the probability of bugs.
> 3. This is one of the coding standards that we use in house. It has been
> largely effective. The decision only comes in to play when
> creating the function interfaces. We have not had any problems
> with people understanding how to use them. We have less problems
> then we had before we added this to the coding standards.
This does not show that the particular standard you picked is a good one,
only that paying attention to an issue reduces bugs involving it.
> Given the example of a traditional GUI text editor. In my code, when the
> app starts, it would initialize the printing system. If there are no
> printers available, no option to print would be available. The only places
> that need to test whether the printing system is properly established is
> the dialog code that adds the ability to print to the menu and possibly the
> wrapper function that is called when the user generates a keystroke
> designated to print. If the printing system is not initialized, then the
> menu item is not displayed and the function that wraps the keystroke
> effectively becomes a noop.
>
> Now, with the test in just two places, the system will never even allow the
> possiblity of calling the printing code unless the printing system is
> properly initialized.
Yes. I wrote a system very much like this, and in the system very much like
this I wrote, I do roughly that, only I use the null/non-null pointer to
determine whether a printer was found, and it works fine.
> You could also use the NULL pointer. The operation is equivilant; as long as
> it is done in the dialog code that allows printing and the keystroke
> wrapper function the printer handling code need never be accessed.
In other words, none of this has ever been an argument against using null
pointers as a sentinel to determine that no printer is available?
> Please differentiate my suggestions from my arguments against ?:. As
> suggestions I have stated:
Okay.
> 1. Any problems properly allocating a printer should be handled as soon as
> possible. The farther an invalid printer is propogated throught
> the code, the more opportunities that somewhere it will not be
> handled throughly.
Yup, agreed.
> 2. To achieve 1, I recommend not calling the printer code if there is no
> valid printer to be used. This can be prevented by placing
> restrictions in the GUI menu and keystroke wrappers that call the
> printer infrastructure.
Makes sense.
> 3. Moving the error conditions outside of the communication channel used
> for transfering data reduces the possiblity that the user will
> forget to properly check the validity of the data received.
But increases the possibility that the check will be handled incorrectly
resulting in a garbage or invalid pointer being used, which is a lot harder
to catch usually than a null pointer is.
> 4. That by separating the data and error status information, you have more
> flexibility in handling specific errors that may occur.
This, I haven't seen, though. By the point at which you get back the null
pointer, the error should ALREADY have been handled or diagnosed.
> All of these (except 2 depends on 1), are independant suggestions that may
> be evaluated individually. None of them have a direct bearing on the ?:
> argument.
Fair enough.
>> I'm using your printer layer. I want to print something. I must deal with
>> the possibility that there is no printer available. It doesn't matter
>> whether I deal with this by checking whether a pointer is null or doing
>> something else; I still have to deal with it.
> You may want to print something; but, you cannot because there is no
> printer available. You don't need to deal with it, because it has already
> been delt with far higher up in the codebase. There is no way of getting
> to where you are that you want to print something. The option to do so has
> been removed.
That doesn't make any sense. I'm writing a hunk of code. I think it'd be
fun to, when this code is hit, print something. I can call the printer
code at this point.
In short, removing the menu item doesn't remove the API. Yes, you can tell
people never to call the printer code unless the printer driver has been
initialized, but you can tell them that in any environment.
> We added not using ?: to our coding standards because it created bugs that
> were clearer to see when they had been converted to if/else syntax using
> the exact same logic.
This, I'd probably mostly agree with, but I'm not sure I'd always agree
with it.
There are a whole lot of things of the form:
printf("%s", x ? x : "<nil>");
which lose a lot of readability if you don't use ?:.
There are indeed a lot of places where if/else or a switch is clearer,
though.
> Yes, NULL pointers exist. If they are handled improperly, they cause bugs.
> The longer they are allowed to permiate the code base, the longer that they
> will be mishandled somewhere. Therefore, we have found it prudent to
> handle them as soon as possible; even if it violates the long standing
> idiom. Long standing idioms to not have to account for the bugs that I
> create using them. I do.
All you're doing is replacing the risk of null pointers with the risk of null
pointers amplified by the risk that your other piece of data which is supposed
to tell you whether or not the pointer is valid will be wrong.
> That would *NOT* be my solution. My solution would never call a function
> outputing a message unless the message was available to begin with. I
> would call a different function.
This seems like it creates a lot of code duplication in likely error
handlers. I'd rather have a single error handler which may or may not
receive a given piece of data, and reacts accordingly, than several different
error handlers each of which takes different inputs and tries to do otherwise
the same things.
Imagine that there are several paths which can lead to needing to
clean up a bunch of data structures. In some of them, there is a useful
diagnostic message to provide; in others, there is not. You seem to be
advocating duplicating the entire cleanup thing to avoid having something
to the effect of:
if (msg) {
fprintf(stderr, "message: %s\n", msg);
}
in the middle of a function.
> Any inputs would be required to be valid
> and they would be asserted to be so.
So we duplicate all functions for every possible combination of available
inputs.
>> This is just ridiculous. You still have to deal with the fact that someone
>> can pass a pointer to your code, and that pointer could be a null pointer.
> Functions have specifications. If the specification of a function says
> that the function should not be given a NULL pointer, then the programmer
> who called the function with a NULL pointer is at fault. The function
> should assert this and abort should it ever be passed a NULL pointer.
Yes, because nothing makes users happier than programs which coredump
without even a moment's consideration given to saving files or trying to
recover gracefully.
> Our inteface makes use of this principle. When programmers violate it, the
> asserts cause the code to abort making it *very* clear that they have done
> something wrong. We write code that is designed to be *highly* stable and
> we add far more infrastructure then most projects do in the process. The
> coding guidlines that we have adopted help us to do that. NULL pointers
> are a common source of issues; so, we have found ways of working around
> them so that they are avoided as much as possible and if they are not
> properly sanitized, we have taken offorts to assure that they are more
> likely to crash during testing rather then escaping into shipping code.
That overall sounds like it makes sense, but the specific examples you've
given have seemed like they'd introduce a large number of points of failure,
all different.
This seems like a case where, in order to absolutely eliminate a hundred bugs
which were all similar, you've introduced several hundred bugs all different
and hard to group together.
I'm all for programs asserting that inputs are not null if it doesn't make
sense for them to get called without an input, but there are a whole lot of
cases where an input could reasonably be optional, at which point, a pointer
which may be null is an extremely clear way to handle that.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/21/2010 9:52:24 PM
|
|
Tim Harig <usernet@ilthio.net> writes:
> On 2010-05-21, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>> Tim Harig <usernet@ilthio.net> writes:
>>> On 2010-05-20, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>>> Tim Harig <usernet@ilthio.net> writes:
>>> Sorry, poor wording. I don't always speak the best English. What I was
>>> referring to is the fact that ?: implies an assignment (ie, the first
>>> argument).
>>
>> I have written many a printk that contains conditional terms. But
>> it works well for arbitrary function's parameters.
>> prepare_mem(p->buf, p->is_big ? BIG : SMALL);
>> So again, I dispute your so-called facts, no matter how they are
>> worded.
>
> 1. I have already said that I don't mind as much when used in an output
> statement because it doesn't alter the control flow structure of
> the program.
If you have said that, you've not said it as clearly as that before,
and in fact quite the opposite, you've said some very mangled and
unclear things before while addressing whether ?: should be used.
> 2. Your example is non-sequator to the argumentation that followed it.
Hilarious.
> 3. It would seem obvious to me to either change the function declaration so
> that it uses the same values as p->is_big or modify the struct
> definition so that it uses the same values expected by the
> function. Personally, p->is_big should have been an enumeration
> in the first place.
It should be a boolean, and probably was in the absense of evidence
to the contrary.
> This isn't a ?: problem, it is a design problem.
Would you say that if I s/(BIG|SMALL)/$1_BUFFER_SIZE/g ?
If you have so little imagination that you can't imagine those two
tokens representing sizes, then you're very lacking in imagination.
Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1
|
|
0
|
|
|
|
Reply
|
Phil
|
5/21/2010 9:53:07 PM
|
|
On 2010-05-21, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Tim Harig <usernet@ilthio.net> writes:
>> On 2010-05-21, Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>>> Tim Harig <usernet@ilthio.net> writes:
>>>> Sorry, poor wording. I don't always speak the best English. What I was
>>>> referring to is the fact that ?: implies an assignment (ie, the first
>>>> argument).
>>>
>>> I have written many a printk that contains conditional terms. But
>>> it works well for arbitrary function's parameters.
>>> prepare_mem(p->buf, p->is_big ? BIG : SMALL);
>>> So again, I dispute your so-called facts, no matter how they are
>>> worded.
>>
>> 1. I have already said that I don't mind as much when used in an output
>> statement because it doesn't alter the control flow structure of
>> the program.
>
> If you have said that, you've not said it as clearly as that before,
> and in fact quite the opposite, you've said some very mangled and
> unclear things before while addressing whether ?: should be used.
In <slrnhvadpt.43v.usernet@rutherford.ilthio.net>:
-On 2010-05-20, bart.c <ba...@freeuk.com> wrote:
-> printf("%d %s in %d
-> Director%s\n",nfiles,(nfiles=1?"File":"Files"),ndirs,(ndirs=1?"y":"ies"));
-
-Fair enough, this is a different usage then above because you are not
-actually going to be changing any persistant values which may be used later
-in the program. I would accept this; although, I am assuming that you have
-already check for 0 and negative values elsewhere in your code.
I accept that is a valid use. That doesn't mean that I consider it
significant enough that I am willing to issue the statement that I condone
the use of ?:. It is still just as clear using an if/else, it just saves a
little typing. I am not arguing that ?: is not sometimes handy. The
problem, as it used to be with goto, is that people are not always good at
knowing when is appropriate to use and inevitably somebody abuses it.
Many coding standards now prohibit the use of goto. They are justified
because of the problems that it has caused in spite of its benefits. I
personally don't mind goto. goto, even though it is sometimes abused,
at least provides functionality that is not otherwise available. ?: doesn't
really provide anything, other then a little less typing, that if/else does
not.
>> 3. It would seem obvious to me to either change the function declaration so
>> that it uses the same values as p->is_big or modify the struct
>> definition so that it uses the same values expected by the
>> function. Personally, p->is_big should have been an enumeration
>> in the first place.
>
> It should be a boolean, and probably was in the absense of evidence
> to the contrary.
If it is then why not just use it? You are then adding a construct that
didn't need to be there.
>> This isn't a ?: problem, it is a design problem.
>
> Would you say that if I s/(BIG|SMALL)/$1_BUFFER_SIZE/g ?
> If you have so little imagination that you can't imagine those two
> tokens representing sizes, then you're very lacking in imagination.
1. Then you should have made your names more explicit.
2. There still is no necessity for a ?:
|
|
0
|
|
|
|
Reply
|
Tim
|
5/21/2010 10:29:28 PM
|
|
On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
> Many coding standards now prohibit the use of goto. They are justified
> because of the problems that it has caused in spite of its benefits. I
> personally don't mind goto. goto, even though it is sometimes abused,
> at least provides functionality that is not otherwise available. ?: doesn't
> really provide anything, other then a little less typing, that if/else does
> not.
If "a little less typing" means "a large and reasonably complicated
expression not being duplicated", that can be a very big thing.
My rule is this:
If I feel there's a genuine control flow difference involved, in that the
things done are different, I use if/else.
If I feel that there is only a choice of data involved, and the things done
would be the same in both cases, I tend to use ?: instead.
Used that way, it adds clarity to the code, by making it clear to the user
that there isn't a significant shift in functionality, only a value which
may vary under differing circumstances.
-s
--
Copyright 2010, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
|
|
0
|
|
|
|
Reply
|
Seebs
|
5/21/2010 10:35:13 PM
|
|
On 2010-05-21, Seebs <usenet-nospam@seebs.net> wrote:
> On 2010-05-21, Tim Harig <usernet@ilthio.net> wrote:
>> Yes it is idomatic and common; but, in my experience it also contributes to
>> a number of bugs.
>
> I am not convinced of this. Furthrermore, avoiding the standard way of doing
> something creates a lot of bugs too.
Obviously, your spatzing out over a single design decision that is
irrelevan to my main point anyway and which is a non-issue in practice.
>> I am assuming that whatever code creates a printer is wrapped in a
>> function. It should be. Code that accesses a printer should not have to
>> know anything about the printer structure any more then the programmer
>> should know about FILEs.
[SNIP]
> But "whether or not I have a printer" is not anything about the printer
> structure.
1. I already stated that I would personally collect the printers in
collection. There would be no reason to make a pointer to any
given printer unless you were going to actually use that printer.
Since the printer code is not called when there are not any
printers, this pointer would never be created.
2. The same thing applies even if we create the pointer ahead time. Since
the print_dev structure is used as a black box, nothing should be
using if except for the printer handling system. That isn't even
accessed unless there is actually a printer present.
> Let's get more specific. In your implementation, do I ever see a
> (printer_dev *)? If so, what is its value if I don't have a printer?
See 1. If it is statically created in a major scope then it should not
matter; but, I would advocate making it NULL as there is a higher chance
that it will crash the system during debugging if it used.
> If your solution is "never call any code that could possibly have used
> that structure unless there is a printer", we have interesting issues. How
> do I decide whether or not to call the code that could have used that?
Since the printer status is separate from the printer_dev data, you would
use the flag returned from the function that generates the printer_dev
structure if we are dealing with a single device. If you use my idea of
using a collection, then you would simply check the length of the
collection to make sure that is not empty. In this case, there was never
any pointer explicitly defined to have to worry about.
> If I do want to use it, what do I do to tell that code what printer it
> gets? If I pass a pointer to it, what happens if the pointer is a
> null pointer? (And no matter what you think about planning, it WILL be
> a null pointer sooner or later, because That Is What Happens. Code
> defensively!)
If somebody chooses to violate this design decision and tries to pass a
untested pointer to one of the functions that does now allow NULL as part of
its specifications, then they will quickly generate an abort from the
functions assertion during the unit tests that are triggered before they
can check in the code.
>>> You can, of course, do:
>>> fulfillment create_printer(printer_dev **printer);
>>> and declare that, if it doesn't return SUCCESS, it hasn't done anything to
>>> "printer".
>
>> No, then you have the same problem and you are gaining the worst of BOTH
>> implementations.
>
> Why, yes. But you haven't given me any way to get this thing that's like
> a pointer but is never null yet.
My original function was:
fulfillment create_printer(printer_dev* printer);
It does not create a printer, it only writes to the location of the printer
that it is given. It also asserts on receiving a NULL pointer as the user
should have check the return from malloc(). If it returns successful, then
you can, if you like, use the printing infrastructure. If it fails, then
for some reason a proper printer_dev could not be created. The one that
was previously allocated would likely be freed as it is not needed. If the
pointer itself is statically allocated then it should never be used. You
may set it to NULL if you like.
>> I did assume dynamic allocation. The only difference is that the printer
>> handling code no longer does its own allocation. The calling code
>> allocates memory for the print_dev structure and is responsible for
>> releasing it when it is done.
>
> If I might quote something:
>
>> Code that accesses a printer should not have to
>> know anything about the printer structure any more then the programmer
>> should know about FILEs.
>
> Remember that?
>
> You are now requiring the CALLING CODE to do the allocation and release.
> Meaning the calling code now needs to know what size a print_dev structure
> is, and allocate it, and maintain it. That's EXACTLY what you just said
> the calling code shouldn't have to do.
Just because it is a black box to the programmer does not mean that it is a
black box to the compiler. The compiler still knows what size a
struct printer_dev is an will execute sizeof appropriately. This is not an
issue. Obviously, the code must keep track of the printer_dev so that it
can be passed to the proper functions.
The alternative here, if you are using my printer collection method, would
be to make the printer collection global so that it doesn't need to be
explicity passed between functions. (See how curses uses stdscr as a
default window for many functions) I don't have any major issues against
this.
>> No, you have created one status that should be used. The stucture should
>> be unallocated and it is a good idea to change the pointer to NULL so that
>> the program segfaults if it is dereferenced rather then invoke undefined
>> behavior.
>
> First off, dereferecing null pointers *IS* undefined behavior -- and it is
> *not* guaranteed to segfault. Secondly, you're missing my point.
This is true; but, NULL is still more likely to create a crash as any other
value. Since all of the functions that should be accessing it abort on
NULL values, any attempts to pass this pointer after a printer initization
failure cause an abort in the unit tests making immediately clear that
there is a problem.
> You've created a new point of failure.
No, as long as the pointer existed, the point of failure was there.
1. I have limited the opportunity for that failure by preventing any code
that should be accessing this pointer from running whether or not
it is actually able to process the NULL pointer.
2. I have added assertions so that if some code does try to use the printer
handeling code it is quickly identified during the unit tests.
>> Personally, I would have saved all of the available printers to some kind
>> of collection. I would not have used a pointer after the printers were
>> allocated. Then, I would simply pass the pointer to the data structure
>> holding the printers, which may be empty.
>
> And what happens if that allocation fails? We're right back to "you have
> to check whether the pointer is null".
No, then the printer is never added to the collection and the collection is
simply empty.
>>> Right, because at the point where you'd be *using* the printer, you don't
>>> care why. It's only at the point that an attempt was made to *allocate*
>>> a printer that you care why, and the error handling for that belongs
>>> there.
>
>> You do when you attempt to initialize the printer. Did the printer fail
>> because there was no printer, because we do not permissions to access the
>> printer, etc.
>
> There are two possibilities:
>
> 1. These errors occurred during the attempt to create a pointer-to-printer,
> and were logged/diagnosed at that time. We have already logged or diagnosed
> that error, and the fact that we are trying to access an unavailable printer
> is now a separate error which has nothing to do with why there is no
> printer available.
The errors I indicated are relevant when we try to create the printer
object. Different errors may need to be handle differently or at least to
indicate the different reasons why the printer is not available to the
user.
After that, no printer code should be running if we cannot intialize at
least one printer.
> 2. These errors will be generated while interacting with a valid
> pointer-to-printer which is not a null pointer.
Errors involving a real printer are different those pertaining to
initializing the printer driver object. The printer may be out of paper;
but, it does exist.
>> 2. Yes, the NULL pointer is idiomatic and tradional; but, also is a common
>> source of bugs. I don't care about being traditional. I care
>> about creating bugless code.
>
> Which is why you've proposed a system which has several more possible failure
> modes and requires more layers of allocation and bookkeeping, dramatically
> increasing the probability of bugs.
I really have done very little bookkeeping. I have added tests in two
places that prevent the entire printing system from being accessed if it
is not relavant to do so. I have prevented this entire section of code
from being able to cause errors in the event that no printer exists. I
have added assertions to my functions to make sure it is apparent should
they be used improperly. The differences are really minor. All I have
really done is limited the amount of code that could be causing problems.
1. Less code to cause problems == less bugs.
2. More assertion statements means unit tests catch more bugs faster.
3. More information about why the printer is unavailable means more
troubleshooting information to the user.
4. Untangling the status information from the information makes testing for
error conditions more explicit and less error prone.
>> 3. This is one of the coding standards that we use in house. It has been
>> largely effective. The decision only comes in to play when
>> creating the function interfaces. We have not had any problems
>> with people understanding how to use them. We have less problems
>> then we had before we added this to the coding standards.
>
> This does not show that the particular standard you picked is a good one,
> only that paying attention to an issue reduces bugs involving it.
Since it reduces bugs then it has served its purpose.
>> Given the example of a traditional GUI text editor. In my code, when the
>> app starts, it would initialize the printing system. If there are no
>> printers available, no option to print would be available. The only places
>> that need to test whether the printing system is properly established is
>> the dialog code that adds the ability to print to the menu and possibly the
>> wrapper function that is called when the user generates a keystroke
>> designated to print. If the printing system is not initialized, then the
>> menu item is not displayed and the function that wraps the keystroke
>> effectively becomes a noop.
>>
>> Now, with the test in just two places, the system will never even allow the
>> possiblity of calling the printing code unless the printing system is
>> properly initialized.
>
> Yes. I wrote a system very much like this, and in the system very much like
> this I wrote, I do roughly that, only I use the null/non-null pointer to
> determine whether a printer was found, and it works fine.
Yes, you seem to be stuck on that issue. All of my other suggestions will work
by testing the null pointer. We do so because we have found, that do to
its explicit nature, it has a tendency to prevent errors that arise from
improperly sanitizing the mixed data/status stream not because it allows
any of my other suggestions.
>> You could also use the NULL pointer. The operation is equivilant; as long as
>> it is done in the dialog code that allows printing and the keystroke
>> wrapper function the printer handling code need never be accessed.
>
> In other words, none of this has ever been an argument against using null
> pointers as a sentinel to determine that no printer is available?
I have made several independent suggestions.
One of them was to split the channels used for passing information and
those used for status. This includes things like requiring a signed type
so that -1 can be passed as an error condition, using wider fields so
that error or formating information can be passed along with character
information, or passing NULL as an error condition.
My printer allocation function makes it very explicit when an error occurs
without intermingling the information with the data potentially returned.
It also makes it easy to send more information about what actually happened
as opposed to just success or failure by adding new error conditions to the
enumeration returned.
Not doing these things doesn't prevent you from using my other suggestion
to prevent the calling of printer handling code if there is no printer.
That can be done using NULL flagging if you wish. We do this because we
have found that it decreases bugs; but, these are two independent
suggestions. You can certainly use one without the other.
>> 3. Moving the error conditions outside of the communication channel used
>> for transfering data reduces the possiblity that the user will
>> forget to properly check the validity of the data received.
>
> But increases the possibility that the check will be handled incorrectly
> resulting in a garbage or invalid pointer being used, which is a lot harder
> to catch usually than a null pointer is.
Actually, it makes the check explicit rather then requiring that the data
be checked. Since our decoupling *never* creates a pointer (it uses the
pointer that it was given) and does not require the use of a pointer,
any invalid pointer problems must a problem with the calling code.
The return value of the function is the status of the function itself.
It doesn't require any interpretation to know whether the error conditions
could be null or -1 or BLACK_BOX_CONSTANT as are common with so many
function designs. If the function doesn't return SUCCESS, then it had an
error and the data from it should not be used. I cannot force everybody to
check return values; but, if they do, there is not mistaking what is an
error and what is not. Here, every non-void returning function must be
checked. We don't seem to have any problems doing this correctly.
Note that I have recommended changing unused pointers to NULL to help find
any pointers that should not be used but which were.
>> 4. That by separating the data and error status information, you have more
>> flexibility in handling specific errors that may occur.
>
> This, I haven't seen, though. By the point at which you get back the null
> pointer, the error should ALREADY have been handled or diagnosed.
If you have a function like:
printer_dev* create_printer(void);
If you receive a NULL how do you know whether you did not have sufficient
permissions to access the device or whether there was not enough memory to
allocate the printer_dev object? Both error conditions should be handled
separately. Presumably you resort to setting a global variable with
details on error condtions? (ie, errno). We have found that this can be
troublesome. For us, it is much easier to pass the information back
directly:
typedef enum error_state_type {
SUCCESS,
MEM_ERROR,
PERM_ERROR,
PDNE_ERROR //printer does not exist
} error_state;
error_state create_printer(printer_dev* new_priner);
>
>>> I'm using your printer layer. I want to print something. I must deal with
>>> the possibility that there is no printer available. It doesn't matter
>>> whether I deal with this by checking whether a pointer is null or doing
>>> something else; I still have to deal with it.
>
>> You may want to print something; but, you cannot because there is no
>> printer available. You don't need to deal with it, because it has already
>> been delt with far higher up in the codebase. There is no way of getting
>> to where you are that you want to print something. The option to do so has
>> been removed.
>
> That doesn't make any sense. I'm writing a hunk of code. I think it'd be
> fun to, when this code is hit, print something. I can call the printer
> code at this point.
If your code attempts to call the printer, it will hit the assertion and
abort soon after you submit it to the unit tests.
> In short, removing the menu item doesn't remove the API. Yes, you can tell
> people never to call the printer code unless the printer driver has been
> initialized, but you can tell them that in any environment.
But again, with the assertion statements, attempting to do so will case it
to fail the unit tests.
>> We added not using ?: to our coding standards because it created bugs that
>> were clearer to see when they had been converted to if/else syntax using
>> the exact same logic.
>
> This, I'd probably mostly agree with, but I'm not sure I'd always agree
> with it.
If ?: provided any real functionality then there would be an argument to
allow it under out standards. As it is, it adds nothing of real value,
while potentially causing problems. We prefer to err on the safe side.
> There are a whole lot of things of the form:
> printf("%s", x ? x : "<nil>");
> which lose a lot of readability if you don't use ?:.
They don't really loose reability, they just become a little longer:
/* print x if available or print "<nil>" as a substitute */
if (x == NULL)
printf("%s", x);
else
printf("%s", "nil");
Note that I don't make any assumptions about C's true or falsehood values.
The code explicitly states what it is testing for. This is much clearer
for those who may be coming from other languages without the same values.
Note that I have added a comment explaining exactly why a conditional
output was being used. This is something I rarely see done with ?:.
>> Yes, NULL pointers exist. If they are handled improperly, they cause bugs.
>> The longer they are allowed to permiate the code base, the longer that they
>> will be mishandled somewhere. Therefore, we have found it prudent to
>> handle them as soon as possible; even if it violates the long standing
>> idiom. Long standing idioms to not have to account for the bugs that I
>> create using them. I do.
>
> All you're doing is replacing the risk of null pointers with the risk of null
> pointers amplified by the risk that your other piece of data which is supposed
> to tell you whether or not the pointer is valid will be wrong.
Yep, and our bug reports suggest a distinct drop in errors caused by
pointers that were improperly checked against being NULL and other errors
where return values were not fully checked for error conditions.
>> That would *NOT* be my solution. My solution would never call a function
>> outputing a message unless the message was available to begin with. I
>> would call a different function.
>
> This seems like it creates a lot of code duplication in likely error
> handlers. I'd rather have a single error handler which may or may not
> receive a given piece of data, and reacts accordingly, than several different
> error handlers each of which takes different inputs and tries to do otherwise
> the same things.
We have two very in-depth error handlers. One for normal situations and
one that is safe to use for memory conditions because it does not attempt
to allocate more memory. It has the ability to trace strings of errors, it
has canned reactions for most error conditons, it has its own
internationalized canned routines for handling most common error condtions.
Do to our high stability requirements, it has the ability to send important
data and restart requests back to a program which monitors the main process
for errors.
| | | |