printf("%s\n", __FILE__); /* why SIGSEGV? */

V

v4vijayakumar

why the following statement dumps the core(Segmentation fault)?

printf("%s\n", __FILE__);
 
K

Keith Thompson

v4vijayakumar said:
why the following statement dumps the core(Segmentation fault)?

printf("%s\n", __FILE__);

Perhaps because it's not part of a complete C program. (If it were,
presumably you would have shown it to us.)

Or maybe the previous line defines printf as a macro.

Or maybe you closed stdout.

If you want actual answers rather than guesses, show us a complete
program. If this is a quiz rather than an actual problem you're
having, please say so.
 
V

v4vijayakumar

Keith said:
Perhaps because it's not part of a complete C program. (If it were,
presumably you would have shown it to us.)

Or maybe the previous line defines printf as a macro.

Or maybe you closed stdout.

If you want actual answers rather than guesses, show us a complete
program. If this is a quiz rather than an actual problem you're
having, please say so.

sorry. Here is the complete program and sample run.

#cat test.c
#include <stdio.h>

int main()
{
printf("%s\n", __FILE__);
printf("%s\n", __LINE__);
printf("%s\n", __func__);

return 0;
}

#cc test.c
#./a.out
test.c
Segmentation fault (core dumped)
#
 
R

russell kym horsell

Perhaps because it's not part of a complete C program. (If it were,
presumably you would have shown it to us.)

[...]

Must likely the next statement is something like

printf("%s\n",__LINE__);

;-)
 
S

Stephen Sprunk

v4vijayakumar said:
sorry. Here is the complete program and sample run.

#cat test.c
#include <stdio.h>

int main()
{
printf("%s\n", __FILE__);
printf("%s\n", __LINE__);
printf("%s\n", __func__);

return 0;
}

#cc test.c
#./a.out
test.c
Segmentation fault (core dumped)
#

My system doesn't define __func__, so I took that line out. Running that, I
get a segfault just like you. However, a couple seconds of looking tells me
the problem:
gcc -E test.c |tail


int main()
{
printf("%s\n", "test.c");
printf("%s\n", 6);

return 0;
}

__LINE__ is replaced with 6, not "6", so %s is not the correct format
specifier. Using %d works just fine.

I'm sure someone will comment on whether this is required or up to the
implementation.

S

--
Stephen Sprunk "Stupid people surround themselves with smart
CCIE #3723 people. Smart people surround themselves with
K5SSS smart people who disagree with them." --Aaron Sorkin


*** ***
 
S

santosh

v4vijayakumar said:
sorry. Here is the complete program and sample run.

#cat test.c
#include <stdio.h>

int main()

Use int main(void)
{
printf("%s\n", __FILE__);
printf("%s\n", __LINE__);

__LINE__ expands to a decimal integer constant. But you've supplied a
conversion character that expects a pointer to a char. Use:

printf("%d\n", __LINE__);
 
P

pc

v4vijayakumar said:
sorry. Here is the complete program and sample run.

#cat test.c
#include <stdio.h>

int main()
{
printf("%s\n", __FILE__);
printf("%s\n", __LINE__);
printf("%s\n", __func__);

return 0;
}

perhaps you wanted this
int main()
{
printf("%s\n", __FILE__);
printf("%d\n", __LINE__); <<<< note the change
printf("%s\n", __func__);
return 0;
}
 
S

santosh

Stephen said:
My system doesn't define __func__, so I took that line out. Running that, I
get a segfault just like you. However, a couple seconds of looking tells me
the problem:



int main()
{
printf("%s\n", "test.c");
printf("%s\n", 6);

return 0;
}

__LINE__ is replaced with 6, not "6", so %s is not the correct format
specifier. Using %d works just fine.

I'm sure someone will comment on whether this is required or up to the
implementation.

Keith will provide the last word but as far as can tell from my library
refernce, __LINE__ expands to a decimal integer constant. So yes, %d is
needed and %s is the wrong conversion specifier to use.

Additionally, the OP implementation may not be fully C99 compliant in
which case the last of his printf's may also be the cause of the
segmentation fault.
 
R

Richard Bos

v4vijayakumar said:
why the following statement dumps the core(Segmentation fault)?

printf("%s\n", __FILE__);

Because you have made a mistake earlier in the code, probably involving
writing through an invalid pointer. That line in itself is fine.

Richard
 
K

Keith Thompson

santosh said:
Stephen said:
v4vijayakumar said:
sorry. Here is the complete program and sample run.

#cat test.c
#include <stdio.h>

int main()
{
printf("%s\n", __FILE__);
printf("%s\n", __LINE__);
printf("%s\n", __func__);

return 0;
}
[snip]
__LINE__ is replaced with 6, not "6", so %s is not the correct format
specifier. Using %d works just fine.

I'm sure someone will comment on whether this is required or up to the
implementation.

Keith will provide the last word but as far as can tell from my library
refernce, __LINE__ expands to a decimal integer constant. So yes, %d is
needed and %s is the wrong conversion specifier to use.

Additionally, the OP implementation may not be fully C99 compliant in
which case the last of his printf's may also be the cause of the
segmentation fault.

C99 6.10.8:

__FILE__ The presumed name of the current source file (a
character string literal).

__LINE__ The presumed line number (within the current source
file) of the current source line (an integer
constant).

with a footnote:

The presumed source file name and line number can be changed by
the #line directive.

The wording in C90 is a bit different:

__LINE__ The line number of the current source line (a decimal
constant).

__FILE__ The presumed name of the source file (a character string
literal).

A couple of odd points: C99 dropped the requirement that __LINE__ has
to be a *decimal* constant. And neither standard requires the
expansion of __LINE__ to be of type int. Possibly that's meant to
cater to implementations with 16-bit int that allow source files
longer than 32767 lines.

It looks like, strictly speaking, this:
printf("%d\n", __LINE__);
could invoke undefined behavior; to avoid the problem, use this:
printf("%d\n", (int)__LINE__);
or this:
printf("%ld\n", (long)__LINE__);
 
S

santosh

.... snip ...
A couple of odd points: C99 dropped the requirement that __LINE__ has
to be a *decimal* constant. And neither standard requires the
expansion of __LINE__ to be of type int. Possibly that's meant to
cater to implementations with 16-bit int that allow source files
longer than 32767 lines.

It looks like, strictly speaking, this:
printf("%d\n", __LINE__);
could invoke undefined behavior; to avoid the problem, use this:
printf("%d\n", (int)__LINE__);
or this:
printf("%ld\n", (long)__LINE__);

Or:
printf("%lu\n", (unsigned long) __LINE__);
 
J

Jordan Abel

sorry. Here is the complete program and sample run.

#cat test.c
#include <stdio.h>

int main()
{
printf("%s\n", __FILE__);
printf("%s\n", __LINE__);
printf("%s\n", __func__);

return 0;
}

#cc test.c
#./a.out
test.c
Segmentation fault (core dumped)
#

printf("%s\n",__LINE__) is causing the segfault. if it were the __FILE__
line causing the segfault, you likely wouldn't see its output.
 
M

Marc Thrun

Jordan said:
or
#define s(x) #x

printf("%s\n", s(__LINE__));

or even

printf(""s(__LINE__)"\n");

This prints "__LINE__" for me. ITYM something like

#define xs(x) #x
#define s(x) xs(x)

printf("%s\n",s(__LINE__));

printf(s(__LINE__)"\n");

This extra level is needed as otherwise __LINE__ will not be evaluated
before it is stringized resulting in "__LINE__".
 
K

Keith Thompson

santosh said:
... snip ...


Or:
printf("%lu\n", (unsigned long) __LINE__);

Yes, that's an important improvement if the printf statement appears
on line 2147483648 or later of the source file. :cool:}
 
?

=?ISO-8859-1?Q?Alberto_Gim=E9nez?=

Keith said:
A couple of odd points: C99 dropped the requirement that __LINE__ has
to be a *decimal* constant. And neither standard requires the
expansion of __LINE__ to be of type int. Possibly that's meant to
cater to implementations with 16-bit int that allow source files
longer than 32767 lines.

It looks like, strictly speaking, this:
printf("%d\n", __LINE__);
could invoke undefined behavior; to avoid the problem, use this:
printf("%d\n", (int)__LINE__);
or this:
printf("%ld\n", (long)__LINE__);

I don't understand that last point. I tought that parameters passed to
printf that matched a %d conversion in a printf call were implicitly
converted to int, isn't it?

I mean, if you have the following snippet (take all needed headers as
included):

unsigned short int a = 5;
printf("%d\n", a);

printf function doesn't receive an 'int' argument (by making an implicit
conversion to 'a')? Or I'm totally wrong?
 
W

Walter Roberson

I tought that parameters passed to
printf that matched a %d conversion in a printf call were implicitly
converted to int, isn't it?

No. Parameter values *never* have their type converted based upon
the format string: instead it is your responsibility to ensure that
the format string matches the parameter value's type.

I mean, if you have the following snippet (take all needed headers as
included):
unsigned short int a = 5;
printf("%d\n", a);
printf function doesn't receive an 'int' argument (by making an implicit
conversion to 'a')? Or I'm totally wrong?

The unsigned short will undergo "the usual conversions" to
unsigned int just because it appears in a function call in a position
prototyped as ... (three periods). %d is the wrong format string
to print out an unsigned int. You just didn't happen to notice it
because the value wasn't greater than the maximum signed positive
integer.
 
F

Flash Gordon

Alberto said:
I don't understand that last point. I tought that parameters passed to
printf that matched a %d conversion in a printf call were implicitly
converted to int, isn't it?

I doubt that long will ever be implicitly promoted to int, nor will
unsigned long. So as Keith was suggesting if the line number is larger
than 32767 it could be either unsigned in (which won't promote to int),
long, unsigned long or even on C99 unsigned long long. If this is a
concern then:
printf("%lu\n", (unsigned long)__LINE__);
for C89 or
printf("%llu\n", (unsigned long long)__LINE__);
would be better if you want to cope with stupidly long source files. Of
course, I would wonder about source files longer than 32767 lines
anyway, so for any sensible program Keith's suggestion should be fine.
I mean, if you have the following snippet (take all needed headers as
included):

unsigned short int a = 5;
printf("%d\n", a);

printf function doesn't receive an 'int' argument (by making an implicit
conversion to 'a')? Or I'm totally wrong?

You are partially wrong. It could be promoted to unsigned int. This is
especially likely when programming for DSPs where it is common to have
char, short and int as the same size, with CHAR_BIT being large enough
for this to be legal (e.g. you have 16 bit char, short and int on the C
compiler for the TMS320C25).
 
E

Eric Sosman

Alberto Giménez wrote On 05/30/06 12:55,:
I don't understand that last point. I tought that parameters passed to
printf that matched a %d conversion in a printf call were implicitly
converted to int, isn't it?

I mean, if you have the following snippet (take all needed headers as
included):

unsigned short int a = 5;
printf("%d\n", a);

printf function doesn't receive an 'int' argument (by making an implicit
conversion to 'a')? Or I'm totally wrong?

You are not toally wrong, but not totally right.

First, the "default argument promotions" do not depend
on the conversion specifiers found in the printf() format
string. The same promotions are used for all arguments that
match the "..." part of a variadic function's parameter list,
and to all arguments that are passed to functions declared
without prototypes. These promotions are:

1: All "small integer" types (char, short, bit-fields,
enums, ...) are promoted to `int' or to `unsigned int'.
The promtion is to `int' if the `int' type can hold
all possible values of the original type, or to
`unsigned int' otherwise.

2: `float' values are promoted to `double'.

3: All other argument types are passed unchanged.

With these rules in mind, we can now address the issues
you and Keith Thompson have raised.

Keith points out that __LINE__ might produce a constant
whose type is not `int'. For example, it might produce an
`unsigned int' or a `long' or even something more exotic
(the C99 Standard vastly expanded the possible repertoire
of integer types). You know that "%d" requires a matching
`int', but you don't know what __LINE__ produces nor what
it will be promoted to. Keith suggests using a cast to
convert the value of __LINE__ to a `long', and then (now
that you're sure of what dealing with) using "%ld" to
print it.

You raise the issue of what happens when an `unsigned
short' is passed to printf(). This turns out to be a wee
bit tricky, because Rule 1 has two possible outcomes:

1a: If `int' can represent all the possible values an
`unsigned short' might have, `unsigned short'
promotes to `int'.

1b: If there are some `unsigned short' values that are
not representable as `int', `unsigned short' promotes
to `unsigned int'.

The problem is that the C Standard allows the implementation
a lot of leeway in choosing the sizes of its data types, and
different choices lead to different results. On a machine
with (say) 32-bit `int' and 16-bit `unsigned short', Rule 1a
will apply and the promotion will be to `int'. But on a
machine where both `int' and `unsigned short' have 16 bits,
Rule 1b will apply and the promotion will give `unsigned int'.
Therefore, the conversion specifier for `unsigned short' (as
promoted) should be "%d" on some systems, "%u" on others.

The Standard says that using "%d" with `unsigned int'
or "%u" with `int' amounts to a mismatch between the format
string and the argument, and thus yields undefined behavior.
As a practical matter, discrepancies of signedness hardly if
ever make any difference (mismatches of width are another
matter, of course). This is in part because the people who
make C implementations have little incentive to make things
difficult gratuitously; if anything, they find that their C
implementations will be more popular if they are "friendly"
and let the programmer skirt the law in small ways. Still,
for 100% belt-AND-suspenders never-drive-over-55 safety, you
should print an `unsigned short' value with something like

printf ("%u\n", (unsigned int)ushort_value);

... so you're not subject to the implementation's whims
about whether to apply Rule 1a or 1b.
 
D

Dave Thompson

No. Parameter values *never* have their type converted based upon
the format string: instead it is your responsibility to ensure that
the format string matches the parameter value's type.
But they are promoted by standard rules; actually you must ensure that
the format string matches the _promoted_ types. Or vice versa.
The unsigned short will undergo "the usual conversions" to
unsigned int just because it appears in a function call in a position

There are actually three different but closely related concepts:

- the "integer promotions" take integer types of lower rank than int
(and bitfields) to either signed int if it can represent the entire
range of value and otherwise to unsigned int. On many S16I32 systems
unsigned short goes to signed int. These apply to nearly all
computational operators.

- the "default argument promotions" are used for varargs (prototyped
as you say with ellipsis) and for unprototyped aka K&R1 functions;
these apply the integer promotions and also do float->double.

- the "usual arithmetic conversions" are used for most binary
(2-operand) operators, with some exceptions like shift, plus the
ternary conditional operator. They apply the integer promotions to
both operands and then convert both to a common type that is chosen by
rules that boil down to the 'least' type that 'covers' both. This is
sometimes called 'contagion', e.g. 1 + 3.0 is done in (the int 1 is
converted to) floating point, namely double; c + 3.0f where c is
complex is done in (the real float is converted to) complex, etc.
prototyped as ... (three periods). %d is the wrong format string
to print out an unsigned int. You just didn't happen to notice it
because the value wasn't greater than the maximum signed positive
integer.

If it is indeed promoted to unsigned int, yes %d is technically wrong,
but in practice will work if the value is in range as you note.

- David.Thompson1 at worldnet.att.net
 

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,183
Messages
2,570,969
Members
47,524
Latest member
ecomwebdesign

Latest Threads

Top