By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,781 Members | 1,134 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,781 IT Pros & Developers. It's quick & easy.

Strange behaviour

P: n/a
Hi all,

I've been testing out a small function and surprisingly it does not
work okay. Here's the full code listing:
#include "stdlib.h"
#include "stdio.h"

char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf(t);
free(t);

return 0;
}

char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);

char* str = malloc(4);

str[0] = '%';
str[1] = buff[0];
str[2] = buff[1];
str[3] = 0x0;

return str;
}

The program does not print anything - neither in bash nor in gdb.
Although I do remember getting it running on XP with migw(currently I'm
testing on OS X).

Thanks for your help,
gamehack

Apr 25 '06 #1
Share this Question
Share on Google+
31 Replies


P: n/a
gamehack wrote:
Hi all,

I've been testing out a small function and surprisingly it does not
work okay. Here's the full code listing:
#include "stdlib.h"
#include "stdio.h"

char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf(t);


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

Otherwise you don't flush stdout.

--
Ian Collins.
Apr 25 '06 #2

P: n/a
gamehack wrote:
Hi all,

I've been testing out a small function and surprisingly it does not
work okay. Here's the full code listing:
#include "stdlib.h"
#include "stdio.h"

To ensure that you are using standard headers and not ones in your
working directory, use
#include <stdio.h>
#include <stdlib.h>
char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf(t);
free(t);

return 0;
}
All good so far.
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);
To simply copy a string, use strcpy(char *dest, char *source);
I'm not sure that you used the correct printf argument (%X). char* str = malloc(4);

str[0] = '%';
str[1] = buff[0];
str[2] = buff[1];
str[3] = 0x0;
This is okay, but the intermediate variable buff is unneccessary, unless
you need to validate the input. return str;
}

The program does not print anything - neither in bash nor in gdb.
Although I do remember getting it running on XP with migw(currently I'm
testing on OS X).


Try inserting my modifications, and see if that fixes it.
Apr 25 '06 #3

P: n/a
Ian Collins wrote:
gamehack wrote:
Hi all,

I've been testing out a small function and surprisingly it does not
work okay. Here's the full code listing:
#include "stdlib.h"
#include "stdio.h"

char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf(t);


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

Otherwise you don't flush stdout.


Disregard my last post; it appears that

printf (t);

should be

printf ("%s", t);
Apr 25 '06 #4

P: n/a
Andrew Poelstra wrote:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);

To simply copy a string, use strcpy(char *dest, char *source);
I'm not sure that you used the correct printf argument (%X).

The OP wants the value in hex, not a copy. It would have been clearer
if the function had a sensible name!
char* str = malloc(4);

str[0] = '%';
str[1] = buff[0];
str[2] = buff[1];
str[3] = 0x0;

This is okay, but the intermediate variable buff is unneccessary, unless
you need to validate the input.

See above.

--
Ian Collins.
Apr 25 '06 #5

P: n/a
gamehack ha scritto:
The program does not print anything - neither in bash nor in gdb.
Although I do remember getting it running on XP with migw(currently I'm
testing on OS X).


this works:

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

char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf("%s",t);
free(t);

return 0;
}

char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
char* str = malloc(4);

sprintf(buff, "%X", byte);

str[0] = '%';
str[1] = buff[0];
str[2] = buff[1];
str[3] = 0x0;

return str;
}
Apr 25 '06 #6

P: n/a
Ian Collins wrote:
gamehack wrote:
Hi all,

I've been testing out a small function and surprisingly it does not
work okay. Here's the full code listing:
#include "stdlib.h"
#include "stdio.h"

char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf(t);

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

Otherwise you don't flush stdout.

Right fix, wrong explanation, sorry.

The first argument to printf is a format string.

--
Ian Collins.
Apr 25 '06 #7

P: n/a
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);


Undefined behaviour -- %X expects an unsigned int, but you
pass it a char. Also, you cause a buffer overflow if byte has
any value other than 0 - 99 (ie. values greater than 99, and
values less than 0). In the case of negative bytes, the
buffer overflow will be particularly bad, as -1 will output
FFFFFFFF if you are on IA32 architecture.

Either use snprintf, or this:

char buff[50];
sprintf(buff, "%X", (unsigned int)byte);

where '50' is larger than any int will be in the foreseeable future.

Apr 26 '06 #8

P: n/a
Old Wolf wrote:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);


Undefined behaviour -- %X expects an unsigned int, but you
pass it a char. Also, you cause a buffer overflow if byte has
any value other than 0 - 99 (ie. values greater than 99, and
values less than 0).
In the case of negative bytes, the
buffer overflow will be particularly bad, as -1 will output
FFFFFFFF if you are on IA32 architecture.

Either use snprintf, or this:

char buff[50];
sprintf(buff, "%X", (unsigned int)byte);

where '50' is larger than any int will be in the foreseeable future.


Let's assume that we have an unsigned char (which is a false assumption
in most cases).

A 3-element array can take a 2-digit string, which in this case will be
a hex number. All the values that fit into uchar (0x00-0xFF) fit this
criteria.

It is true, however, that negative numbers will expand very uncleanly.
Apr 26 '06 #9

P: n/a
Andrew Poelstra wrote:
Old Wolf wrote:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);


Undefined behaviour -- %X expects an unsigned int, but you
pass it a char. Also, you cause a buffer overflow if byte has
any value other than 0 - 99 (ie. values greater than 99, and
values less than 0).
In the case of negative bytes, the
buffer overflow will be particularly bad, as -1 will output
FFFFFFFF if you are on IA32 architecture.

Either use snprintf, or this:

char buff[50];
sprintf(buff, "%X", (unsigned int)byte);

where '50' is larger than any int will be in the foreseeable future.


Let's assume that we have an unsigned char (which is a false assumption
in most cases).

A 3-element array can take a 2-digit string, which in this case will be
a hex number. All the values that fit into uchar (0x00-0xFF) fit this
criteria.

It is true, however, that negative numbers will expand very uncleanly.


Note: no standard-ASCII character will exceed 0x7F, and therefore the
negative number problem is practically nonexistant. Of course, in theory
it is possible that this function will be passed bad data, and so
precautions should be taken (in this case, simply using an unsigned char
will eliminate all issues, because anything bigger than 255 will be
wrapped).
Apr 26 '06 #10

P: n/a
In article <11**********************@i39g2000cwa.googlegroups .com> "Old Wolf" <ol*****@inspire.net.nz> writes:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);
Undefined behaviour -- %X expects an unsigned int, but you
pass it a char.


Not so. An int is passed. You can not pass chars to a variadic function.
However, changing "char byte" above to "unsigned char byte" above would be
an improvement.
pass it a char. Also, you cause a buffer overflow if byte has
any value other than 0 - 99 (ie. values greater than 99, and
values less than 0).
I have no idea where you base that "99" on. There will be problems if
it is larger than 255 or smaller than 0.
Either use snprintf, or this:

char buff[50];
sprintf(buff, "%X", (unsigned int)byte);

where '50' is larger than any int will be in the foreseeable future.


This is the wrong solution. Consider what happens if byte == -1.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Apr 26 '06 #11

P: n/a
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);
Undefined behaviour -- %X expects an unsigned int, but you
pass it a char.


Not so. An int is passed.


C99 7.19.6.1#9 says:

If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.

3.3 defines "argument" as:

expression in the comma-separated list bounded by the
parentheses in a function call expression ...

6.5.2.2#6 defines the default argument promotions:

the integer promotions are performed on each argument,
and arguments that have type float are promoted to double.

The latter two quotes support the hypothesis that the "argument"
is the expression before the default promotions occur (as
opposed to the result of the default argument promotions).

In the above code, the argument is the expression:

byte

and it undoubtedly has type char, thus falling foul of 7.19.6.1#9 .

pass it a char. Also, you cause a buffer overflow if byte has
any value other than 0 - 99 (ie. values greater than 99, and
values less than 0).
I have no idea where you base that "99" on. There will be
problems if it is larger than 255 or smaller than 0.


Sorry, I was thinking of printing in decimal, for some reason.
Either use snprintf, or this:

char buff[50];
sprintf(buff, "%X", (unsigned int)byte);

where '50' is larger than any int will be in the foreseeable future.


This is the wrong solution. Consider what happens if byte == -1.


Then there will be no buffer overflow, unless the unsigned int
has more than 4*49 = 196 bits, which I don't foresee happening.

(I'm not speculating on what the intent of the code is -- just
making sure it doesn't cause a buffer overflow. The OP clearly
did not consider the negative-char case anyway, so he is
welcome to start considering it now).

Apr 26 '06 #12

P: n/a
Old Wolf wrote:
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);
Undefined behaviour -- %X expects an unsigned int, but you
pass it a char.

Not so. An int is passed.


C99 7.19.6.1#9 says:

If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.

3.3 defines "argument" as:

expression in the comma-separated list bounded by the
parentheses in a function call expression ...

6.5.2.2#6 defines the default argument promotions:

the integer promotions are performed on each argument,
and arguments that have type float are promoted to double.

The latter two quotes support the hypothesis that the "argument"
is the expression before the default promotions occur (as
opposed to the result of the default argument promotions).

In the above code, the argument is the expression:

byte

and it undoubtedly has type char, thus falling foul of 7.19.6.1#9 .

No, a char is a type of int. Therefore, it will be promoted to an
unsigned long int, or whatever it expects.
Apr 26 '06 #13

P: n/a
Old Wolf wrote:
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:
gamehack wrote:

char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);

Undefined behaviour -- %X expects an unsigned int, but you
pass it a char.


Not so. An int is passed.

C99 7.19.6.1#9 says:

If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.

But char is of the correct type, it will be promoted to int.

extern void fn( int );

char m;
short n;
int o;
fn( m );
fn( n );
fn( o );

Are all correct.

--
Ian Collins.
Apr 26 '06 #14

P: n/a
Andrew Poelstra <ap*******@shaw.ca> writes:
Old Wolf wrote:
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:
gamehack wrote:
> char* escaped_byte_cstr_ref(char byte)
> {
> char buff[3];
> sprintf(buff, "%X", byte);
Undefined behaviour -- %X expects an unsigned int, but you
pass it a char.
Not so. An int is passed.

C99 7.19.6.1#9 says:
If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.
3.3 defines "argument" as:
expression in the comma-separated list bounded by the
parentheses in a function call expression ...
6.5.2.2#6 defines the default argument promotions:
the integer promotions are performed on each argument,
and arguments that have type float are promoted to double.
The latter two quotes support the hypothesis that the "argument"
is the expression before the default promotions occur (as
opposed to the result of the default argument promotions).
In the above code, the argument is the expression:
byte
and it undoubtedly has type char, thus falling foul of 7.19.6.1#9 .

No, a char is a type of int. Therefore, it will be promoted to an
unsigned long int, or whatever it expects.


Wrong, and wrong.

char is an integer type; int is another integer type. A char is not
"a type of int". (Are you thinking of the fact that character
literals are of type int?)

In the following:

void foo(unsigned long x);
...
char c;
foo(c);

c is converted to type unsigned long. This kind of conversion does
not take place for variadic functions like sprintf. In that case,
only the default argument promotions are performed; char is promoted
to int, regardless of the type specified by the format string. If the
argument is of type char, and the format string specifies a type other
than int, the behavior is undefined.

(If CHAR_MAX==UINT_MAX, char is promoted to unsigned int rather than
int; this can happen only if plain char is unsigned and
sizeof(int)==1, which can happen only if CHAR_BIT>=16.)

(Passing a signed int when an unsigned int is expected, or vice versa,
is likely to work as long as the value is within the range of both
types; the standard almost guarantees this, but doesn't *quite* say
so.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Apr 26 '06 #15

P: n/a

gamehack wrote:
Hi all,

I've been testing out a small function and surprisingly it does not
work okay. Here's the full code listing:
#include "stdlib.h"
#include "stdio.h"

char* escaped_byte_cstr_ref(char byte);

int main (int argc, const char * argv[])
{
char* t = escaped_byte_cstr_ref('!');
printf(t);
free(t);

return 0;
}

char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);

char* str = malloc(4);

str[0] = '%';
str[1] = buff[0];
str[2] = buff[1];
str[3] = 0x0;

return str;
}

The program does not print anything - neither in bash nor in gdb.
Although I do remember getting it running on XP with migw(currently I'm
testing on OS X).

Thanks for your help,
gamehack


Thanks a lot everyone. I did a really stupid mistake.

Kind regards,
gamehack

Apr 26 '06 #16

P: n/a
Ian Collins wrote:
Old Wolf wrote:
C99 7.19.6.1#9 says:

If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.

But char is of the correct type, it will be promoted to int.

extern void fn( int );


The section I quoted is talking about arguments to variadic
functions that match the '...' bit.

Apr 26 '06 #17

P: n/a
In article <11*********************@i39g2000cwa.googlegroups. com> "Old Wolf" <ol*****@inspire.net.nz> writes:
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:
gamehack wrote:
char* escaped_byte_cstr_ref(char byte)
{
char buff[3];
sprintf(buff, "%X", byte);

Undefined behaviour -- %X expects an unsigned int, but you
pass it a char.
Not so. An int is passed.


C99 7.19.6.1#9 says:
If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.


So according to your reasoning the following:
char c = 'a';
printf("%c\n", c);
is undefined behaviour. (%c expects an int argument, see that section #8.)
3.3 defines "argument" as:
expression in the comma-separated list bounded by the
parentheses in a function call expression ...

6.5.2.2#6 defines the default argument promotions:
the integer promotions are performed on each argument,
and arguments that have type float are promoted to double.

The latter two quotes support the hypothesis that the "argument"
is the expression before the default promotions occur (as
opposed to the result of the default argument promotions).


The argument is, but the type of the argument is not.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Apr 27 '06 #18

P: n/a
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:

C99 7.19.6.1#9 says:
If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.


So according to your reasoning the following:
char c = 'a';
printf("%c\n", c);
is undefined behaviour. (%c expects an int argument, see that section #8.)


It looks that way. Section 7 says:
hh Specifies that a following d, i, o, u, x, or X conversion
specifier applies to a signed char or unsigned char
argument

h Specifies that a following d, i, o, u, x, or X conversion
specifier applies to a short int or unsigned short int
argument

By your reasoning these modifiers could never be used, as
the argument is always an int (assuming we are talking
about systems where chars and shorts promote to int).

I think that it is inconsistent to read "argument" as the pre-
promotion argument in some clauses, and to read it as the
post-promotion argument in other clauses of the same
section.

In fact, those section 7 entries have a suffix clarifying that
they are talking about the pre-promotion argument. Perhaps
a DR is in order for section 8 to clarify that your code
should not cause UB.

The latter two quotes support the hypothesis that the "argument"
is the expression before the default promotions occur (as
opposed to the result of the default argument promotions).


The argument is, but the type of the argument is not.


In your above code, the argument is 'a' of type char. After the
default argument promotions, the argument is 'a' of type int.
Right?

Apr 27 '06 #19

P: n/a
Old Wolf wrote:

Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:

C99 7.19.6.1#9 says:
If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.


So according to your reasoning the following:
char c = 'a';
printf("%c\n", c);
is undefined behaviour.
(%c expects an int argument, see that section #8.)


It looks that way.


C99
6.3.1 Arithmetic operands
6.3.1.1 Boolean, characters, and integers

2 The following may be used in an expression
wherever an int or unsigned int may be used:

An object or expression with an integer type
whose integer conversion rank is less than
the rank of int and unsigned int.

--
pete
Apr 27 '06 #20

P: n/a
pete wrote:
C99
6.3.1 Arithmetic operands
6.3.1.1 Boolean, characters, and integers

2 The following may be used in an expression
wherever an int or unsigned int may be used:

- An object or expression with an integer type
whose integer conversion rank is less than
the rank of int and unsigned int.


OK; so passing the signed char to %X is well-defined
as well then. Is it guaranteed to produce the same
result as casting the signed char to unsigned int first?

Apr 27 '06 #21

P: n/a
In article <11**********************@g10g2000cwb.googlegroups .com> "Old Wolf" <ol*****@inspire.net.nz> writes:
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:

C99 7.19.6.1#9 says:
If any argument is not the correct type for the corresponding
conversion specification, the behaviour is undefined.
So according to your reasoning the following:
char c = 'a';
printf("%c\n", c);
is undefined behaviour. (%c expects an int argument, see that section #8.)


It looks that way. Section 7 says:
hh Specifies that a following d, i, o, u, x, or X conversion
specifier applies to a signed char or unsigned char
argument


You conveniently snipped: "(the argument will have been promoted according
to the integer promotions, but its value shall be converted to
signed char or unsigned char before printing)". So here the standard is
clearly talking about the type before promotion.
By your reasoning these modifiers could never be used, as
the argument is always an int (assuming we are talking
about systems where chars and shorts promote to int).
No, see what you snipped.
I think that it is inconsistent to read "argument" as the pre-
promotion argument in some clauses, and to read it as the
post-promotion argument in other clauses of the same
section.
It is clearly identified here that the talk is about the type before
promotion.
In fact, those section 7 entries have a suffix clarifying that
they are talking about the pre-promotion argument. Perhaps
a DR is in order for section 8 to clarify that your code
should not cause UB.


I think that that is not needed. At least it is clear to me that there
is no UB.
The latter two quotes support the hypothesis that the "argument"
is the expression before the default promotions occur (as
opposed to the result of the default argument promotions).


The argument is, but the type of the argument is not.


In your above code, the argument is 'a' of type char. After the
default argument promotions, the argument is 'a' of type int.
Right?


No. 'a' is of type int. c is of type char. c contains the value
of 'a' converted to type char. After the promotion it is of type
int, and hopefully identical to the original. (The latter is not
guaranteed. For instance, if char is signed char, the following:
char c = '\240';
printf("%X %X\n", c, '\240');
can produce differing output.)

However, there is something else here. X requires that the argument
is of type unsigned int. But the arguments supplied are of type int.
I think there is definitely a problem if c is of type char (with
char being signed) and also negative.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Apr 28 '06 #22

P: n/a
In article <11**********************@u72g2000cwu.googlegroups .com> "Old Wolf" <ol*****@inspire.net.nz> writes:
pete wrote:
C99
6.3.1 Arithmetic operands
6.3.1.1 Boolean, characters, and integers

2 The following may be used in an expression
wherever an int or unsigned int may be used:

- An object or expression with an integer type
whose integer conversion rank is less than
the rank of int and unsigned int.


OK; so passing the signed char to %X is well-defined
as well then. Is it guaranteed to produce the same
result as casting the signed char to unsigned int first?


No, it is not well-defined. Passing a signed char passes an int.
%X expects an unsigned int, and at that stage no conversion takes
place. See 6.5.2.2#6, where there is talk about compatible types
in function calls, if the two are not compatible it is still
well-defined if one type is signed integer, the other unsigned
integer and the value representable for both. In the case of %X
this means that if a signed char is passed, it is only well-defined
if the value is >= 0. So in:
signed char c = '\240';
printf("%X %X\n", c, '\240');
the conversion of the first is undefined, the conversion of the
second is well-defined (assuming 8-bit chars).
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
Apr 28 '06 #23

P: n/a
Dik T. Winter wrote:
"Old Wolf" <ol*****@inspire.net.nz> writes:
pete wrote:
C99
6.3.1 Arithmetic operands
6.3.1.1 Boolean, characters, and integers

2 The following may be used in an expression
wherever an int or unsigned int may be used:

- An object or expression with an integer type
whose integer conversion rank is less than
the rank of int and unsigned int.


OK; so passing the signed char to %X is well-defined
as well then.


No, it is not well-defined. Passing a signed char passes an int.
%X expects an unsigned int, and at that stage no conversion takes
place. See 6.5.2.2#6, where there is talk about compatible types
in function calls, if the two are not compatible it is still
well-defined if one type is signed integer, the other unsigned
integer and the value representable for both. In the case of %X
this means that if a signed char is passed, it is only well-defined
if the value is >= 0.


Ok, that's what 6.5.2.2#6 says. At first this appears to
conflict with 6.3.1.1 which says that a signed char can be
used wherever an unsigned int can be used.

I was reading 6.3.1.1's "may be used" as "may be used,
with well-defined behaviour". But I think this is not correct:
although it says a signed char may be used, other constraints
may still apply that render it undefined.

For example:

char ch = -2;
isalpha(ch);

? isalpha's argument is a place where an int or unsigned int
may be used, but this causes undefined behaviour because
it violates the requirement that the argument to isalpha be
in the range 0 ... UCHAR_MAX.

If you agree so far, then it follows that 6.3.1.1 actually
doesn't have any bearing on the issue we were discussing.
My original position was that the rules in 7.19.6 (fprintf)
imply that passing the char to %X caused UB, and we
have just established that other rules can 'trump' 6.3.1.1.

If I can continue replying to your other post here instead of
having 2 sub-threads: it looks like the resolution is to say
that 7.19.6#7 is talking about the pre-promotion argument,
but #8 ("The int parameter to %d...") is talking about the
post-promotion argument. This is clearly the intent,
although it was not clear to me at first that the wording
expressed this intent accurately.

Have we resolved the case of:
char c = 0x20;
printf("%X\n", c);
?

6.5.2.2#6 says:
If the function is defined with a type that does not include
a prototype, and the types of the arguments after promotion
are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following
cases:
-- one promoted type is a signed integer type, the other
promoted type is the corresponding unsigned integer type,
and the value is representable in both types;

-- both types are pointers to qualified or unqualified versions
of a character type orvoid.

But the fprintf call is not a function defined with a type that does
not include a prototype. Even if we ignore that clause, and
assume that everything mentioned in 6.5.2.2#6 is part of
"default argument promotions", then how can you compare
the post-promotion argument type with the "type of the
parameter" ? The parameter is "..." which has no type.

If we assume that the definition of fprintf also provides
definitions of parameters to which 6.5.2.2#6 can be applied,
that would solve the problem.

A corollary of this would be that you can pass (char *)
and (unsigned char *) pointers as parameters to %p,
without having to cast them to (void *) first.

Apr 28 '06 #24

P: n/a
In article <11********************@v46g2000cwv.googlegroups.c om> "Old Wolf" <ol*****@inspire.net.nz> writes:
Dik T. Winter wrote: ....
OK; so passing the signed char to %X is well-defined
as well then.


No, it is not well-defined. Passing a signed char passes an int.
%X expects an unsigned int, and at that stage no conversion takes
place. See 6.5.2.2#6, where there is talk about compatible types
in function calls, if the two are not compatible it is still
well-defined if one type is signed integer, the other unsigned
integer and the value representable for both. In the case of %X
this means that if a signed char is passed, it is only well-defined
if the value is >= 0.


Ok, that's what 6.5.2.2#6 says. At first this appears to
conflict with 6.3.1.1 which says that a signed char can be
used wherever an unsigned int can be used.


But that is about operands in expression, so it does not apply here.
? isalpha's argument is a place where an int or unsigned int
may be used, but this causes undefined behaviour because
it violates the requirement that the argument to isalpha be
in the range 0 ... UCHAR_MAX.
But range violations are again something different.
My original position was that the rules in 7.19.6 (fprintf)
imply that passing the char to %X caused UB, and we
have just established that other rules can 'trump' 6.3.1.1.
Yup. I think that if 'char' is signed that it may result in UB, but
when it is unsigned that it does not.

.... Have we resolved the case of:
char c = 0x20;
printf("%X\n", c);
?

6.5.2.2#6 says:
If the function is defined with a type that does not include
a prototype, and the types of the arguments after promotion
are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following
cases:
-- one promoted type is a signed integer type, the other
promoted type is the corresponding unsigned integer type,
and the value is representable in both types;

-- both types are pointers to qualified or unqualified versions
of a character type orvoid.

But the fprintf call is not a function defined with a type that does
not include a prototype.
Yes, I think 6.5.2.2#6 is sloppy. It does not say anything about compatible
types in calls to variadic functions (where the expected type is also not
known to the compiler). I think the compatibility rules are intended to
also apply in this case, but that is not mentioned. The intent is that
if in the representation (and size) of a specific value the two types are
not different, they are compatible.
If we assume that the definition of fprintf also provides
definitions of parameters to which 6.5.2.2#6 can be applied,
that would solve the problem.


6.5.2.2#6 should be augmented so that the clause also applies to parameters
of variadic functions and the type that is expected at that place.
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
May 2 '06 #25

P: n/a
In article <Iy********@cwi.nl> "Dik T. Winter" <Di********@cwi.nl> writes:

Time for a cross-post to comp.std.c. For those in comp.std.c, the
following came up, is this program valid or does it show undefined
behaviour?

/* provides a prototype for printf */
#include <stdio.h>

int main(void) {
char c = 'a';

printf("%X\n", c);
return 0;
}

According to 6.3.1.1, in the call 'c' is promoted to int (under assumptions
that are commonly satisfied). According to 7.19.6.1#8, %X expects an
unsigned int. And there is nothing in either 6.5.2.2#6 or 6.5.2.2#7 that
tells me the two are compatible in this case (6.5.2.2#6 talks only about
compatibility if there is no prototype, but there is one). So according
to 7.19.6.1 the behaviour is undefined. My conclusion was:
6.5.2.2#6 should be augmented so that the clause also applies to parameters
of variadic functions and the type that is expected at that place.


Where is my reasoning wrong?
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
May 2 '06 #26

P: n/a
Dik T. Winter wrote:
In article <Iy********@cwi.nl> "Dik T. Winter" <Di********@cwi.nl> writes:

Time for a cross-post to comp.std.c. For those in comp.std.c, the
following came up, is this program valid or does it show undefined
behaviour?

/* provides a prototype for printf */
#include <stdio.h>

int main(void) {
char c = 'a';

printf("%X\n", c);
return 0;
}

According to 6.3.1.1, in the call 'c' is promoted to int (under assumptions
that are commonly satisfied). According to 7.19.6.1#8, %X expects an
unsigned int. And there is nothing in either 6.5.2.2#6 or 6.5.2.2#7 that
tells me the two are compatible in this case (6.5.2.2#6 talks only about
compatibility if there is no prototype, but there is one). So according
to 7.19.6.1 the behaviour is undefined. My conclusion was:
> 6.5.2.2#6 should be augmented so that the clause also applies to parameters
> of variadic functions and the type that is expected at that place.


Where is my reasoning wrong?


In the above example c is guaranteed to be positive. Since the
representation of a valid positive int value must also be a valid
representation for the same value for an unsigned int, the behavior is
well-defined. If c was negative, the behavior would be undefined.

Robert Gamble

May 2 '06 #27

P: n/a
In article <11**********************@i40g2000cwc.googlegroups .com>,
Robert Gamble <rg*******@gmail.com> writes
Dik T. Winter wrote:
In article <Iy********@cwi.nl> "Dik T. Winter" <Di********@cwi.nl> writes:

Time for a cross-post to comp.std.c. For those in comp.std.c, the
following came up, is this program valid or does it show undefined
behaviour?

/* provides a prototype for printf */
#include <stdio.h>

int main(void) {
char c = 'a';

printf("%X\n", c);
return 0;
}

According to 6.3.1.1, in the call 'c' is promoted to int (under assumptions
that are commonly satisfied). According to 7.19.6.1#8, %X expects an
unsigned int. And there is nothing in either 6.5.2.2#6 or 6.5.2.2#7 that
tells me the two are compatible in this case (6.5.2.2#6 talks only about
compatibility if there is no prototype, but there is one). So according
to 7.19.6.1 the behaviour is undefined. My conclusion was:
> 6.5.2.2#6 should be augmented so that the clause also applies to
>parameters
> of variadic functions and the type that is expected at that place.


Where is my reasoning wrong?


In the above example c is guaranteed to be positive. Since the
representation of a valid positive int value must also be a valid
representation for the same value for an unsigned int, the behavior is
well-defined. If c was negative, the behavior would be undefined.

Robert Gamble

And, I think that is unreasonable even though it seems to be a correct
reading of the Standard. The requirements of the relationship between
unsigned and signed integer types results in essentially requiring that
a signed and unsigned integer type of the same rank will have identical
bit patterns with the exception of one bit that is used as a sign bit in
the signed version.

Of course what the signed bit pattern represents as an unsigned value
will depend on which binary representation is used for negative values
and possibly for bizarre representations which bit is the sign bit but I
think that is as far as it goes.

Is there any conforming way in which a signed integer value can be a
trap value for an unsigned but otherwise equivalent integer type? If not
we should, IMO, remove this undefined behaviour from the standard.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
May 2 '06 #28

P: n/a
In article <11**********************@i40g2000cwc.googlegroups .com> "Robert Gamble" <rg*******@gmail.com> writes:
Dik T. Winter wrote:
In article <Iy********@cwi.nl> "Dik T. Winter" <Di********@cwi.nl> writes:

Time for a cross-post to comp.std.c. For those in comp.std.c, the
following came up, is this program valid or does it show undefined
behaviour?

/* provides a prototype for printf */
#include <stdio.h>

int main(void) {
char c = 'a';

printf("%X\n", c);
return 0;
}

According to 6.3.1.1, in the call 'c' is promoted to int (under assumptions
that are commonly satisfied). According to 7.19.6.1#8, %X expects an
unsigned int. And there is nothing in either 6.5.2.2#6 or 6.5.2.2#7 that
tells me the two are compatible in this case (6.5.2.2#6 talks only about
compatibility if there is no prototype, but there is one). So according
to 7.19.6.1 the behaviour is undefined. My conclusion was:
> 6.5.2.2#6 should be augmented so that the clause also applies to parameters
> of variadic functions and the type that is expected at that place.


Where is my reasoning wrong?


In the above example c is guaranteed to be positive. Since the
representation of a valid positive int value must also be a valid
representation for the same value for an unsigned int, the behavior is
well-defined. If c was negative, the behavior would be undefined.


But where do you find it is well-defined? According to 7.19.6.1 it is
undefined (the two types int and unsigned int do not match).
--
dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
May 2 '06 #29

P: n/a
"Dik T. Winter" <Di********@cwi.nl> wrote in message
news:Iy********@cwi.nl...
But where do you find it is well-defined? According to 7.19.6.1 it is
undefined (the two types int and unsigned int do not match).


You won't find it in any normative text that directly applies to printf(),
but a footnote explains that that was the intent. Also, it has been argued
here that the promises that the standard makes about va_arg() automatically
apply to printf(), but not everybody was convinced.

http://groups.google.com/group/comp....eae947?rnum=57
May 2 '06 #30

P: n/a
Francis Glassborow <fr*****@robinton.demon.co.uk> writes:
Is there any conforming way in which a signed integer value can be a trap
value for an unsigned but otherwise equivalent integer type? If not we
should, IMO, remove this undefined behaviour from the standard.


A possibility is that the padding bits are kept to 0 for unsigned
types, when they follow the sign for unsigned types. It would be a
way to implement unsigned type when the machine has only a signed one
(AFAIR, Knuth's MIX would be one such machine; I wonder if it has ever
be the case for true binary machine, that seem to me more a
characteristic of decimal machine and MIX is defined in a way that it
could be either binary or decimal).

Yours,

--
Jean-Marc
May 2 '06 #31

P: n/a
On 27 Apr 2006 21:33:20 -0700, "Old Wolf" <ol*****@inspire.net.nz>
wrote:
Dik T. Winter wrote: <snip: signed char for printf %X which expects unsigned int> Ok, that's what 6.5.2.2#6 says. At first this appears to
conflict with 6.3.1.1 which says that a signed char can be
used wherever an unsigned int can be used.
6.5.2.2 doesn't apply here; it's about nonprototyped = oldstyle calls.
All variadic routines including printf must be declared prototyped.
The (not coincidentally) identical semantics are specified for
_user-written_ variadic arguments in 7.15.1.1. There isn't any
explicit statement that the variadic standard-library routines *printf
and *scanf must actually use va_arg et al, and indeed the standard
clearly allows that the standard library routines aren't written in C
at all, but it seems to have been intended that they are "as-if" in C
and particularly "as-if" using va_arg, and the more specific rules in
7.19.6 and 7.24.2 are visibly consistent with this.
I was reading 6.3.1.1's "may be used" as "may be used,
with well-defined behaviour". But I think this is not correct:
although it says a signed char may be used, other constraints
may still apply that render it undefined.
I think the confusion is that all of 6.3 is about conversions. "may be
used" should be read as "can and will be converted implicitly (without
a cast) if and when conversions are done".

<snip> If I can continue replying to your other post here instead of
having 2 sub-threads: it looks like the resolution is to say
that 7.19.6#7 is talking about the pre-promotion argument,
but #8 ("The int parameter to %d...") is talking about the
post-promotion argument. This is clearly the intent,
although it was not clear to me at first that the wording
expressed this intent accurately.
You mean 7.19.6.1 for fprintf. p7 is really talking about both, for
the value cases (i.e. not %n); e.g. h expects [unsigned] short
argument "[which] will have been promoted ... but shall be converted".
Clearly this means the argument is actually passed as a signed or
unsigned int according to the rules in 6.3.1.1, but is presumed to
have been promoted from a short [u]value and is converted back to that
[u]short value for formatting.

<snip> But the fprintf call is not a function defined with a type that does
not include a prototype. Even if we ignore that clause, and
Actually we don't know that. The _definition_ is inside the library
and up to the implementation. It is quite likely to be written in C
and use a prototype with an ellipsis and va_arg et al, but this is not
required. What we do know is that it is called through a declaration,
provided by <stdio.h>, that must be a prototype, so 6.5.2.2p6 does not
apply, but as above 7.15.1.1 arguably does.
assume that everything mentioned in 6.5.2.2#6 is part of
"default argument promotions", then how can you compare
The first sentence are the default argument promotions. The two items
at the end of the paragraph are exceptions to the compatability rules,
and the same two items are exceptions in 7.15.1.1.
the post-promotion argument type with the "type of the
parameter" ? The parameter is "..." which has no type.
Right. For varargs, at least user-written varargs, in 7.15.1.1 what
must match modulo the exceptions is the promoted type of the actual
argument and the 'expected' type used to extract it.
If we assume that the definition of fprintf also provides
definitions of parameters to which 6.5.2.2#6 can be applied,
that would solve the problem.

A corollary of this would be that you can pass (char *)
and (unsigned char *) pointers as parameters to %p,
without having to cast them to (void *) first.


/*plain*/ char*, signed char*, and unsigned char*, which are (all)
formally incompatible, but all within the exception in 7.15.1.1 --
assuming it applies -- yes. Although personally I would still cast
just to be clear and consistent and robust against maintenance.

- David.Thompson1 at worldnet.att.net
May 4 '06 #32

This discussion thread is closed

Replies have been disabled for this discussion.