I Keep Switching Back...

P

Programmer Dude

Kamilche said:
You know what? I AM fallible and imperfect! I DON'T get things
right the first time! ...

True for us all.
But what if you get it wrong, and the program KEEPS RUNNING, as
in the example I posted? That's quite a bit larger mess than I
had in C.

Are you under the impression that all bugs in a C program cause
noticable symptoms immediately? Surely you know better.
Since C++ doesn't let you rely on the constructor calling order
for global objects, a typical programmer response is to switch
to pointers, so you can control the ordering.

First, a typical C++ programmer's response is probably different.
(Ways of dealing with this are OT here; contact me if you really
want to know.)

Second, global objects are not generally a Good Thing.

Third, if you must have globals and pointers,...
And you know what? I FORGOT to instantiate the global pointer
before use.

....you need--as in any program--to do it right.
 
D

Default User

Kamilche said:
Well, I'll admit, the book I was using, 'Teach yourself C++ in 21
days', left much to be desired. They didn't cover many basic things,
not even the use of the string library.

So you didn't have a book. What would be your opinion of someone
lambasting C when all they was that sort of book?

Try again with Accelerated C++, or The C++ Programming Language.



Brian Rodenborn
 
M

Malcolm

Niklas Norrthon said:
The caller is responsible for initializing the objects, calling member
functions for NULL objects is just as much undefined behavior as
the "C" constructs:
Are you sure? The following C program is defined

#include <stdio.h>

typedef struct
{
int bar;
}FOO;

void foo(FOO *f);

int main(void)
{
foo(0);
return 0;
}

void foo(FOO *f)
{
if(f == NULL)
printf("NULL pointer\n");
}
 
C

Chris Torek

"Niklas Norrthon" <[email protected]> wrote in message
[[email protected]], probably --
(e-mail address removed)'s software seems to have
omitted the message ID]
[undefined-behavior C stuff snipped]

Are you sure? The following C program is defined ...

[this time, I snipped the C code]

He was referring to C++ code. In C++, given a class or struct
type T, one can -- just as in C -- do:

T *p = NULL;

Unlike C, however, C++ has a convenient (if slightly gimmicky) bit
of syntax to say "using object p's type, locate a type-T-specific
function and call it, passing p's value as a secret, hidden argument".
That syntax is:

p->func()

(which is of course in conflict with the same syntax that has a
quite different meaning in C; but hold that thought).

As Niklas Norrthon and numerous others have noted, the effect in
C++ of making such a call is defined only when p is a valid value
of type T, and is not NULL. In practice, real C++ compilers will
resolve the call to the T-specific function "T::func()" at compile
time if and only if T::func is not marked "virtual". So -- defined
behavior or not -- non-"virtual" T::func()s get called even if p
is NULL. When T::func() is marked "virtual", however, the link
between source syntax and actual called function is deferred until
runtime, and looked up at that point, so that if p is NULL, the
call tends to fail.

(I outlined the actual mechanisms, and a way to achieve them in
"raw" C, earlier.)

Note that if we do this, in C:

struct S {
void (*func)(void);
};

void f(struct S *p) {
p->func();
}

we have just used the exact same syntax that C++ uses for "member
function" calls to do something quite different. So when you are
reading C++ code and see:

p->func();

you can never be sure what it means without first inspecting the
data members of the class or struct type that was used to declare
"p". The following (rather wretched) C++ code illustrates the
situation:

struct S1 {
void (*func)(void);
};
struct S2 {
int x;
void func() { x = 42; }
};
struct S3 {
virtual void func();
};

void f(struct S1 *p1, struct S2 *p2, struct S3 *p3) {
p1->func(); // Literally calls p1->func(); does not pass p1.
p2->func(); // Just sets p2->x.
p3->func(); // Calls a "virtual" function, which may vary if p3
// is actually part of a derived type. Passes p3.
}

In f(), the first call works just as in C, calling through p1->func.
The second call sets p2->x to 42, and the third call indirects
through p3's "vtable", which is much like the indirect call for
p1, but using compiler magic rather than an explicit indirect.
You must look up each of the three "struct"s to see which of the
three possible behaviors is being invoked. (This is, of course,
no worse than deciding which overloaded function or operator is
called, in the general case. The concept itself is sensible enough
too: the idea is that you are not supposed to care -- nor have to
care -- which of the various mechanisms is involved. My own
experience has been that you *do* have to care, though, and that
C++ code often[%] lacks sufficient "local" clues to tell you which
one you are getting. Perhaps better compile-time tools would help:
one could click on a "+" operator, for instance, and immedately be
told "this invokes the `add single-precision complex number to
matrix holding string data' function", or whatever. Of course this
does not work for runtime bindings, unless the compiler can somehow
prove that a given variable has some fixed datatype.)

[% "Often" here really means "somewhat rarely, but too often to
just ignore" :) ]
 
D

Default User

Chris said:
As Niklas Norrthon and numerous others have noted, the effect in
C++ of making such a call is defined only when p is a valid value
of type T, and is not NULL. In practice, real C++ compilers will
resolve the call to the T-specific function "T::func()" at compile
time if and only if T::func is not marked "virtual". So -- defined
behavior or not -- non-"virtual" T::func()s get called even if p
is NULL. When T::func() is marked "virtual", however, the link
between source syntax and actual called function is deferred until
runtime, and looked up at that point, so that if p is NULL, the
call tends to fail.


All of which reinforces what people have been trying to tell Kamilche,
comparing the reaction of different implementations to undefined
behavior is silly. Also, relying on some sort of consistent handling of
undefined behavior as a programming practice is downright foolish.

The newbie chant, "Why didn't it crash like it's supposed to!"




Brian Rodenborn
 
R

Randy Howard

The newbie chant, "Why didn't it crash like it's supposed to!"

Or the much more insidious "Hey, it didn't crash like you said it
would, so it must be perfectly ok. I thought you were a C expert?"


--
Randy Howard
(remove the obvious bits from my address to reply.)
"Most of the drivers nowadays are a bit like Eddie Irvine, who if
he was half as fast as he thought he was, would be moderate."
-- Sir Stirling Moss
 
D

Dan Pop

In said:
All of which reinforces what people have been trying to tell Kamilche,
comparing the reaction of different implementations to undefined
behavior is silly. Also, relying on some sort of consistent handling of
undefined behavior as a programming practice is downright foolish.

I don't think anyone advocated that. However, we do make mistakes. And
it's a lot more helpful for a null pointer to cause a program crash when
dereferenced than to silently let the program continue as if nothing bad
has happened. OTOH, there is the issue of run-time overhead involved in
detecting the indirection through a null pointer (we don't want our
correct program to take a performance hit because of it, either).

Ideally, an implementation would have two modes: a debug mode, where as
many checks as (reasonably) possible are done at run time and a production
mode, where no check is performed at all, the program being assumed to be
correct. All we actually have is assert(), to implement the two modes in
our own code, but we're too lazy to use it as often as necessary
(and a fully assert-instrumented code doesn't look very well, either).

Dan
 
D

Default User

Dan said:
I don't think anyone advocated that. However, we do make mistakes. And
it's a lot more helpful for a null pointer to cause a program crash when
dereferenced than to silently let the program continue as if nothing bad
has happened. OTOH, there is the issue of run-time overhead involved in
detecting the indirection through a null pointer (we don't want our
correct program to take a performance hit because of it, either).

Well, that's an implementation decision. In spite of what some people on
CLC think, the designers and implementors of C++ aren't buffoons. If
they decided that it was best to allow a certain behavior with null
pointers, there is quite probably a good reason for it. Frankly, and
I've been working in C++ for some time, I was unaware of that particular
feature. That was one hell of a description by Chris Torek.
Ideally, an implementation would have two modes: a debug mode, where as
many checks as (reasonably) possible are done at run time and a production
mode, where no check is performed at all, the program being assumed to be
correct. All we actually have is assert(), to implement the two modes in
our own code, but we're too lazy to use it as often as necessary
(and a fully assert-instrumented code doesn't look very well, either).

Sure. It's an important programming requirement (like I need to tell
you) to consider the behavior of the program under development when
presented with invalid pointers. About the only thing you can reasonably
check is for null pointers. Using assert is a pretty good way to weed
out the design errors that can result in a null. You hope that your
testing does find all the ways that such a thing can happen. That's were
good unit testing and integration comes into play.


There are perfectly valid reasons for prefering one programming language
over another. I just thought that Kamilche's reason for abandoning C++
amounted to a frivilous one, and that she didn't really make that much
effort to learn C++.




Brian Rodenborn
 
G

goose

It doesn't hurt to engage your brain when trying to find out.

it wont hurt for you to do the same :)

look at what I was responding to above ... (i've not
snipped anything in this post).

the whole point of my response was that Kamliche's statement
"In C, the above code crashes the app" was just plain wrong.

bearing in mind the code she was referring to was
char *s=NULL;

*s++;
[LManickum@lee] Fri Jul 11 12:04:27 [1 bg] /tmp
17 ok cat ./foo.c
#include <stdio.h> /* for NULL */

You also need it for printf().
int main (void) {
char *s=NULL;
*s++;
printf ("no crash\n");
return 0;
}
[LManickum@lee] Fri Jul 11 12:04:32 [1 bg] /tmp
18 ok gcc -ansi -Wall -pedantic foo.c -o foo
foo.c: In function `main':
foo.c:5: warning: value computed is not used

The compiler has clearly told you that you created an idiotic test
program which need not perform the illegal operation at all, because
its result is discarded.


thats the point. it need not have performed any illegal operation
at all, which is why I posted it. Kamilche was under the impression
that it *would* *crash*.


goose,
hand
 
D

Dan Pop

In said:
it wont hurt for you to do the same :)

Unlike you, I did!
look at what I was responding to above ... (i've not
snipped anything in this post).

It was a schematic example, not supposed to be taken ad litteram.
But you completely missed this aspect, for the above mentioned reason
(that is, unless you're a genuine idiot ;-)

Dan
 
G

goose

Unlike you, I did!


It was a schematic example, not supposed to be taken ad litteram.
But you completely missed this aspect, for the above mentioned reason
(that is, unless you're a genuine idiot ;-)

ah, its /clearer/ now ...
("ad litteram" == "literally" ???)

goose,
not the *genuine* article, just a wannabe idiot :)
 
D

Dan Pop

In said:
("ad litteram" == "literally" ???)

"ad litteram" (Latin expression adopted by English) is less ambiguous
than "literally":

Main Entry: literally
Function: adverb
Date: 1533
1 : in a literal sense or manner : ACTUALLY <took the remark literally>
<was literally insane>

2 : in effect : VIRTUALLY <will literally turn the world upside down
to combat cruelty or injustice -- Norman Cousins>

usage: Since some people take sense 2 to be the opposite of sense 1,
it has been frequently criticized as a misuse. Instead, the use is
pure hyperbole intended to gain emphasis, but it often appears in
contexts where no additional emphasis is necessary.

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,075
Messages
2,570,562
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top