why these codes will cause Segmentation fault in linux?

E

eliweiq001

#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if ((!isdigit(c)) && (c != 'a'))
;
return 0;
}

I used ubuntu and gcc.
When this program is running in linux, it will case Segmentation fault
when reach the if sentence.
Also if I chang the && to || , it will effect the same thing.



If I chang "int c;" to "char c;", it won't cause that fault. ( I know
the regular way is to use char)

And it won't cause that fault if I chang " if ((!isdigit(c)) && (c !=
'a')) " to any sentence below:
"if ((c != 'a') && !isdigit(c));" or just "if (!isdigit(c));" or
just "if (c !='a');"
 
I

Ian Collins

#include<stdio.h>
#include<ctype.h>
int main(void)
{
int c;
scanf("%c",&c);
if ((!isdigit(c))&& (c != 'a'))

scanf is expecting the address of a char, you are passing an int.
 
S

Seebs

#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;

You declare here an integer which is uninitialized.
scanf("%c", &c);

You now store to one character out of that integer, leaving the rest
uninitialized.
if ((!isdigit(c)) && (c != 'a'))

You now pass an uninitialized value to isdigit, which has to have a
value which is definitely in the range of unsigned char (or EOF).
When this program is running in linux, it will case Segmentation fault
when reach the if sentence.

That is because your code is wrong.
If I chang "int c;" to "char c;", it won't cause that fault. ( I know
the regular way is to use char)

It's a bit more complicated than that, because of the possibility that
char is signed.

Anyway, your program is wrong. It's wrong in several ways, but the most
significant are that you're lying to scanf about the type of the object
you passed it a pointer to (and the type of the pointer, for that matter),
and that you're using an uninitialized value.

Don't do that.

-s
 
E

eliweiq001

You declare here an integer which is uninitialized.


You now store to one character out of that integer, leaving the rest
uninitialized.


You now pass an uninitialized value to isdigit, which has to have a
value which is definitely in the range of unsigned char (or EOF).


That is because your code is wrong.


It's a bit more complicated than that, because of the possibility that
char is signed.

Anyway, your program is wrong. It's wrong in several ways, but the most
significant are that you're lying to scanf about the type of the object
you passed it a pointer to (and the type of the pointer, for that matter),
and that you're using an uninitialized value.

Don't do that.

-s



But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?


it means :

int c;
scanf("%c", &c);
if (!isdigit(c))
;
return 0;
 
E

eliweiq001

You declare here an integer which is uninitialized.


You now store to one character out of that integer, leaving the rest
uninitialized.


You now pass an uninitialized value to isdigit, which has to have a
value which is definitely in the range of unsigned char (or EOF).


That is because your code is wrong.


It's a bit more complicated than that, because of the possibility that
char is signed.

Anyway, your program is wrong. It's wrong in several ways, but the most
significant are that you're lying to scanf about the type of the object
you passed it a pointer to (and the type of the pointer, for that matter),
and that you're using an uninitialized value.

Don't do that.

-s

But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?

it means :

int c;
scanf("%c", &c);
if (!isdigit(c))
;
return 0;




And it won't cause that fault if I chang " if ((!isdigit(c)) && (c !=
'a')) " to any sentence below:
"if ((c != 'a') && !isdigit(c));" or just "if (!isdigit(c));" or
just "if (c !='a');"

it means:

int c;
scanf("%c", &c);
if ((c != 'a') && !isdigit(c))
;
return 0;

or:

int c;
scanf("%c", &c);
if (!isdigit(c))
;
return 0;

or:

int c;
scanf("%c", &c);
if (c == 'a')
;
return 0;

They all won't cause that fault.
oddness!
 
I

Ian Collins

But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?

If c is declared as char, it it promoted to int. If it is declared as
int and you populate it as char with scanf, you pass garbage to isdigit,
which will invoke the demons of Undefined Behaviour.
 
J

James Dow Allen

But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?

oddness!

One says 'c is uninitialized' but it might be clearer to say
'c has some unknown hard-to-predict value.' For example,
your environmant may have invoked some functions *before*
ever invoking main() and those functions have left their own
scribblings where your 'c' is. Best, of course, is to write
your code so as NOT to depend on those scribblings! :)

I don't know *why* those hard-to-predict scribblings seem
to depend on seemingly irrelevant changes in main(). It
could be(*) as simple as the changed length of main()'s code
leading to changes in the bit values of other addresses.

(* - Probably not, but why worry about it anyway? Frequently
we need to discover why seemingly correct code fails.
Discovering why certainly incorrect code fails is a useless
detour! :)

James Dow Allen
 
I

Ike Naar

But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?

it means :

int c;
scanf("%c", &c);
if (!isdigit(c))
;
return 0;

The behaviour is undefined. Your claim "it won't cause that fault" is
unwarranted. Here's what happened when the program ran on a SPARCstation:

$ ./a.out
a
Segmentation Fault

See? It caused that fault.

With the "%c" format, scanf expects a pointer-to-char as the next
argument. If you supply anything else, you've broken the contract
and things may (or may not) go wrong.
Exactly how things go wrong may depend on many factors, most of
which are system-specific (the way scanf was programmed, properties
of the hardware, state of the system, etcetera) which makes it
impossible to predict what exactly will happen in general.

The most productive way to go is to just play by the rules:
to read a char with scanf, use the "%c" format and supply a
pointer-to-char.
 
E

eliweiq001

The behaviour is undefined. Your claim "it won't cause that fault" is
unwarranted. Here's what happened when the program ran on a SPARCstation:

$ ./a.out
a
Segmentation Fault

See? It caused that fault.

With the "%c" format, scanf expects a pointer-to-char as the next
argument. If you supply anything else, you've broken the contract
and things may (or may not) go wrong.
Exactly how things go wrong may depend on many factors, most of
which are system-specific (the way scanf was programmed, properties
of the hardware, state of the system, etcetera) which makes it
impossible to predict what exactly will happen in general.

The most productive way to go is to just play by the rules:
to read a char with scanf, use the "%c" format and supply a
pointer-to-char.



I am running them in ubuntu:


ubuntu@HP:~/work/C$ ls -l
total 36
-rw-r--r-- 1 ubuntu ubuntu 129 2010-09-25 19:41 a.c
-rwxr-xr-x 1 ubuntu ubuntu 7190 2010-09-25 19:41 a.out
-rw-r--r-- 1 ubuntu ubuntu 115 2010-09-25 19:40 b.c
-rwxr-xr-x 1 ubuntu ubuntu 7190 2010-09-25 19:40 b.out
-rw-r--r-- 1 ubuntu ubuntu 129 2010-09-25 19:44 e.c
-rwxr-xr-x 1 ubuntu ubuntu 7190 2010-09-25 19:45 e.out




ubuntu@HP:~/work/C$ cat a.c
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if (isdigit(c) && (c == 'x'))
;
return 0;
}
ubuntu@HP:~/work/C$ ./a.out
aaaaaaaaaaaaaaaaaaaa
Segmentation fault
ubuntu@HP:~/work/C$



ubuntu@HP:~/work/C$ cat b.c
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if (isdigit(c))
;
return 0;
}
ubuntu@HP:~/work/C$ ./b.out
aaaaaaaaaaaaaaaaaaaaaa
ubuntu@HP:~/work/C$




ubuntu@HP:~/work/C$ cat e.c
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if ((c == 'x') && isdigit(c))
;
return 0;
}
ubuntu@HP:~/work/C$ ./e.out
aaaaaaaaaaaaaaaaaaaa
ubuntu@HP:~/work/C$








You see, in ubuntu, the b.out(from b.c) and e.out(from e.c) won't
cause that fault.
 
R

Rui Maciel

eliweiq001 said:
You see, in ubuntu, the b.out(from b.c) and e.out(from e.c) won't
cause that fault.

You are missing the point. If you rely on a behaviror which is undefined then there is no guarantee
that your code won't blow up in your face. As it has already been shown, that code does trigger
segfaults. You can't disprove that by running three offending versions of the same program without
triggering segfaults. Maybe you simply got lucky and the reference to your int c, when converted to
a pointer, happened to point to a memory location which was accessible when you ran it. Yet, do you
believe that writing this sort of code does anyone any good?


Rui Maciel
 
B

Barry Schwarz

On Sat, 25 Sep 2010 00:49:46 -0700 (PDT), eliweiq001

snip
But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?

One of the worst forms of undefined behavior is to appear to do what
is expected. Now that you know the behavior is undefined, you should
also know that asking questions like why is pointless. No matter what
the program does, the answer is always the same: "It is perfectly
reasonable for undefined behavior to produce this result."
 
S

Seebs

But if I write test if sentence as " if (!isdigit(c)) ; "
it won't cause that fault, why?

Because the compiler optimized the unused test away.

Here's the thing, though. You are thinking about this in a way which
puts you at a high risk of continued problems. You're trying to figure
out why undefined behavior sometimes doesn't crash and sometimes does.

This is not a wise course of action. It is likely to lead to doing
things that are undefined behavior, but which happened not to crash
when you were testing them. Then, when the compiler is upgraded or
you move to a new computer, all your code crashes constantly because
you did it wrong.

If code is wrong, it's just wrong. It does not MATTER whether it
crashes or not -- once you know it's wrong, FIX IT.

-s
 
S

Seebs

I am running them in ubuntu:

That's nice.

Why did you ignore the substance of the post you're responding to?

Your code is wrong. ANYTHING AT ALL is permitted when your code
is wrong. The compiler can punch you in the face. That's okay,
your code is wrong. The compiler can generate a program which
crashes on Ubuntu. That's okay, your code is wrong. The compiler
can generate a program which doesn't seem to do anything. That's
okay, your code is wrong. The compiler can do ABSOLUTELY ANYTHING
IT WANTS. There are no limits, no rules. Your. Code. Is. Wrong.

You're trying to figure out what the rules are. THERE ARE NO RULES.
You see, in ubuntu, the b.out(from b.c) and e.out(from e.c) won't
cause that fault.

On your particular system, with your particular compiler options.
They might on another ubuntu system, or with a different version
of the compiler.

Stop trying to figure out undefined behavior! You were told what is
wrong with your code. Trying to figure out exactly how it results
in crashes some times but not others is pointless.

-s
 
J

Jens Thoms Toerring

I am running them in ubuntu:
ubuntu@HP:~/work/C$ ls -l
total 36
-rw-r--r-- 1 ubuntu ubuntu 129 2010-09-25 19:41 a.c
-rwxr-xr-x 1 ubuntu ubuntu 7190 2010-09-25 19:41 a.out
-rw-r--r-- 1 ubuntu ubuntu 115 2010-09-25 19:40 b.c
-rwxr-xr-x 1 ubuntu ubuntu 7190 2010-09-25 19:40 b.out
-rw-r--r-- 1 ubuntu ubuntu 129 2010-09-25 19:44 e.c
-rwxr-xr-x 1 ubuntu ubuntu 7190 2010-09-25 19:45 e.out
ubuntu@HP:~/work/C$ cat a.c
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if (isdigit(c) && (c == 'x'))
;
return 0;
}
ubuntu@HP:~/work/C$ ./a.out
aaaaaaaaaaaaaaaaaaaa
Segmentation fault
ubuntu@HP:~/work/C$
ubuntu@HP:~/work/C$ cat b.c
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if (isdigit(c))
;
return 0;
}
ubuntu@HP:~/work/C$ ./b.out
aaaaaaaaaaaaaaaaaaaaaa
ubuntu@HP:~/work/C$
ubuntu@HP:~/work/C$ cat e.c
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int c;
scanf("%c", &c);
if ((c == 'x') && isdigit(c))
;
return 0;
}
ubuntu@HP:~/work/C$ ./e.out
aaaaaaaaaaaaaaaaaaaa
ubuntu@HP:~/work/C$
You see, in ubuntu, the b.out(from b.c) and e.out(from e.c) won't
cause that fault.

Wrong. On my Ubuntu system none of the three programs with exactly
the same input results in a segmentation fault. So, no, it's not
related to Ubuntu but to the program being buggy.

Regards, Jens
 
A

August Karlstrom

#include<stdio.h>
#include<ctype.h>
int main(void)
{
int c;
scanf("%c",&c);
if ((!isdigit(c))&& (c != 'a'))
;
return 0;
}

With the code above saved in test.c gcc will tell you this:

$ gcc -Wall test.c
test.c: In function ‘main’:
test.c:6: warning: format ‘%c’ expects type ‘char *’, but argument 2 has
type ‘int *’

/August
 
J

Jorgen Grahn

With the code above saved in test.c gcc will tell you this:

$ gcc -Wall test.c
test.c: In function ?main?:
test.c:6: warning: format ?%c? expects type ?char *?, but argument 2 has
type ?int *?

And 'gcc -Wextra -Wall -pedantic -ansi -O3' would have been even better.

The moral: *always* turn on all relevant compiler warnings!
Don't expect the defaults to be useful!

/Jorgen
 
A

August Karlstrom

And 'gcc -Wextra -Wall -pedantic -ansi -O3' would have been even better.

The moral: *always* turn on all relevant compiler warnings!
Don't expect the defaults to be useful!

The fact that `-Wall' doesn't enable all warnings and that an
optimization flag(!) is needed to get warnings about uninitialized
variables is nothing but laughable.


/August
 
S

Seebs

The fact that `-Wall' doesn't enable all warnings and that an
optimization flag(!) is needed to get warnings about uninitialized
variables is nothing but laughable.

I don't see why. The reason optimization is an option is that it imposes
costs. Some people don't want to pay those costs, and spending a lot of time
doing variable usage analysis, when not optimizing, makes no sense.

While I think -Wall is a little bit anemic, it does make some sense for
"-Wall" to really mean "all reasonable warnings". I just think they've
omitted a few pretty obviously reasonable warnings.

-s
 
K

Keith Thompson

August Karlstrom said:
The fact that `-Wall' doesn't enable all warnings and that an
optimization flag(!) is needed to get warnings about uninitialized
variables is nothing but laughable.

I agree that "-Wall" is mis-named. It's probably for historical
reasons. In any case, the documentation explains what it does and gives
a good overview of which warnings it enables and why.

As for needing an optimization flag, I suppose it does seem
counterintuitive if you haven't thought about what the compiler
does internally.

With optimization disabled, the compiler doesn't perform the
analysis necessary to detect uses of uninitialized variables.
When it sees a reference to x, it just generates code to fetch
the current value of x, without regard to what might have happened
previously. Turning on optimization causes the compiler to gather
much more information about the program, such as which code paths
might reach a given point. With this additional information,
the compiler can do things like this:

int x = 42;
printf("x = %d\n", x); /* compiler generates puts("x = 42"); */

and this:

int x;
printf("x = %d\n", x); /* compiler warns that x is uninitialized */

I suppose the alternative would be for the compiler to *always*
perform the analysis, but not perform the optimizations unless
asked to do so, but that would lose the ability to get much faster
compilation speed by turning off optimization. Since optimization
is so commonly used anyway, I suppose the authors of gcc didn't
think it was worthwhile to provide that option.

Bottom line: Enabling optimization typically gives you better
warnings. It's not immediately obvious, but it's something you
should only have to learn once.
 
J

jacob navia

Le 01/10/10 18:14, Seebs a écrit :
I don't see why.

Try harder.

The reason optimization is an option is that it imposes

Yes.

Some people don't want to pay those costs,

Yes

and spending a lot of time
doing variable usage analysis, when not optimizing, makes no sense.

Yes, but when you want ALL warnings, doing usage analysis is quite
useful. If you want fast compilations then do not use -Wall.

This is just common sense.
While I think -Wall is a little bit anemic, it does make some sense for
"-Wall" to really mean "all reasonable warnings". I just think they've
omitted a few pretty obviously reasonable warnings.

-s

Is it "unreasonable" to expect that using a variable without
initialization generates a warning?

OK. "Reasonable" is everything gcc does because it is GNU.

Sorry, I did not get that...
 

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
473,954
Messages
2,570,114
Members
46,702
Latest member
VernitaGow

Latest Threads

Top