char as string

D

David RF

HI friends, splint doesn't like this:

#include <stdio.h>

#define ctos(x) (char[2]){(char)x, '\0'}

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

printf("%s %s\n", ctos(c), ctos('b'));
return 0;
}

But I can't see the problem

david@debian:~$ splint *.c
Splint 3.1.2 --- 23 Aug 2008

demo.c: (in function main)
demo.c:7:10: Variable c initialized to type char, expects int: 'a'
A character constant is used as an int. Use +charintliteral to allow
character constants to be used as ints. (This is safe since the
actual type
of a char constant is int.)
constraintGeneration.c:2149: at source point
demo.c:11:2: *** Internal Bug at constraintGeneration.c:2149: llassert
failed:
exprNode_isDefined(el) [errno: 25]
*** Please report bug to (e-mail address removed) (via reportbug)
***
(attempting to continue, results may be incorrect)
*** Segmentation Violation
*** Location (not trusted): demo.c:11:2
*** Last code point: transferChecks.c:4415
*** Previous code point: transferChecks.c:4002
*** Please report bug to (e-mail address removed) (via reportbug)
*** A useful bug report should include everything we need to reproduce
the bug.
 
K

Keith Thompson

David RF said:
HI friends, splint doesn't like this:

#include <stdio.h>

#define ctos(x) (char[2]){(char)x, '\0'}

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

printf("%s %s\n", ctos(c), ctos('b'));
return 0;
}

But I can't see the problem

david@debian:~$ splint *.c
Splint 3.1.2 --- 23 Aug 2008

demo.c: (in function main)
demo.c:7:10: Variable c initialized to type char, expects int: 'a'
A character constant is used as an int. Use +charintliteral to allow
character constants to be used as ints. (This is safe since the
actual type
of a char constant is int.)
constraintGeneration.c:2149: at source point
demo.c:11:2: *** Internal Bug at constraintGeneration.c:2149: llassert
failed:
exprNode_isDefined(el) [errno: 25]
*** Please report bug to (e-mail address removed) (via reportbug)
***
(attempting to continue, results may be incorrect)
*** Segmentation Violation
*** Location (not trusted): demo.c:11:2
*** Last code point: transferChecks.c:4415
*** Previous code point: transferChecks.c:4002
*** Please report bug to (e-mail address removed) (via reportbug)
*** A useful bug report should include everything we need to reproduce
the bug.

The message for line 7 is complaining about using 'a' to initialize
an int. This complaint is perhaps overly picky, since 'a' is already
of type int. On the other hand, the message acknowledges this and
tells you how to avoid the message.

The second message obviously indicates a bug in splint. I suggest
you report it; the message itself tells you how.
 
D

david

The second message obviously indicates a bug in splint.  I suggest
you report it; the message itself tells you how.

ops, internal bug, I have not read it correctly, seems the problem
comes with (char[2]){(char)x, '\0'}
¿is this legal?
 
B

Ben Bacarisse

david said:
The second message obviously indicates a bug in splint.  I suggest
you report it; the message itself tells you how.

ops, internal bug, I have not read it correctly, seems the problem
comes with (char[2]){(char)x, '\0'}
¿is this legal?

"Legal" is a funny word for C syntax -- it is a compound literal in C99
(as you intended it to be) and a syntax error in C90. Maybe splint
does not understand C99 (it seems to miss some other C99 features).
 
D

david

"Legal" is a funny word for C syntax -- it is a compound literal in C99
(as you intended it to be) and a syntax error in C90.  Maybe splint
does not understand C99 (it seems to miss some other C99 features).

Thanks Ben!!
Putting extra braces fixes the problem:

#include <stdio.h>

#define ctos(x) ((char[2]){(char)x, '\0'})

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

printf("%s %s\n", ctos(c), ctos('b'));
return 0;
}

david@debian:~$ splint +charintliteral demo.c
Splint 3.1.2 --- 23 Aug 2008

Finished checking --- no warnings
 
B

Ben Bacarisse

david said:
Thanks Ben!!

I don't want to sound ungrateful, but I don't think I helped much.
Putting extra braces fixes the problem:

You discovered that, not me.

BTW, your original syntax is perfectly correct so there is a bug in
splint (if splint claims to understand C99, of course). Splint
miss-parses other C99 constructs so you are skating on thin ice if you
reply on having found a work-around for this one.

<snip>
 
D

david

I don't want to sound ungrateful, but I don't think I helped much.

Saying thank you never hurts ;)
BTW, your original syntax is perfectly correct so there is a bug in
splint (if splint claims to understand C99, of course).  Splint
miss-parses other C99 constructs so you are skating on thin ice if you
reply on having found a work-around for this one.

you're right, here a copy-paste from http://www.splint.org/faq.html
Which compilers does Splint support?

Splint is independent from your compiler. It checks standard C
code, according to the ISO C99 specification. Splint supports most,
but not all, of the C99 extensions to the ANSI C. Splint supports some
of the gcc compiler extensions (if +gnuextensions is used).
 
K

Keith Thompson

Ben Bacarisse said:
BTW, your original syntax is perfectly correct so there is a bug in
splint (if splint claims to understand C99, of course).

Given the posted output, there is a bug in splint whether the
original code is correct or not. Split itself reported that it
had a bug, and the error message referred to the mechanism for
reporting it.
Splint
miss-parses other C99 constructs so you are skating on thin ice if you
reply on having found a work-around for this one.

Agreed.
 
K

Keith Thompson

david said:
Putting extra braces fixes the problem:

#include <stdio.h>

#define ctos(x) ((char[2]){(char)x, '\0'})

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

printf("%s %s\n", ctos(c), ctos('b'));
return 0;
}
[...]

In a function-like macro, each reference to each parameter should be
parenthesized unless there's a specific reason not to do so. So I'd
write the macro definition as:

#define CTOS(x) ((char[2]){(x), '\0'})

(Note that I've used an all-caps name and removed the unnecessary
cast.)
 
B

Ben Bacarisse

david said:
On 25 ago, 15:17, Ben Bacarisse <[email protected]> wrote:

you're right, here a copy-paste from http://www.splint.org/faq.html
Which compilers does Splint support?

Splint is independent from your compiler. It checks standard C
code, according to the ISO C99 specification. Splint supports most,
but not all, of the C99 extensions to the ANSI C. Splint supports some
of the gcc compiler extensions (if +gnuextensions is used).

There is a bug then. I've had a look and I can't see an existing
report. It seems to be only when a compound literal is used as a
function argument. My earlier remark may have been hasty -- C99
support seems to be quite reasonable.
 
B

Ben Bacarisse

Keith Thompson said:
Given the posted output, there is a bug in splint whether the
original code is correct or not. Split itself reported that it
had a bug, and the error message referred to the mechanism for
reporting it.

Very true. What kind of bug depends on the support for C99 but that
there is one is evident from the output alone.

<snip>
 
D

david

In a function-like macro, each reference to each parameter should be
parenthesized unless there's a specific reason not to do so.

Ok, this is a good point, in this case the parameter is isolated, must
be inside parenthesis? excuse my poor english, thanks
 
K

Keith Thompson

david said:
Ok, this is a good point, in this case the parameter is isolated, must
be inside parenthesis? excuse my poor english, thanks

I'm not sure whether it's necessary in this particular case, but C's
operator precedence rules are complex enough that it's best to be
sure. It's much easier to add the parentheses than to prove that
they're not needed, both to yourself and to anyone else who might
read your code.
 
R

Richard Bos

david said:
you're right, here a copy-paste from http://www.splint.org/faq.html
Which compilers does Splint support?

Splint is independent from your compiler. It checks standard C
code, according to the ISO C99 specification. Splint supports most,
but not all, of the C99 extensions to the ANSI C. Splint supports some
^^^^^^^^^^^^^^ ^^^^^^^^^^

If this really is a literal copy-paste, they have at least two more
problems than just with your code, one ideological, one linguistic.

Richard
 
M

Mark

Keith said:
[...]

In a function-like macro, each reference to each parameter should be
parenthesized unless there's a specific reason not to do so. So I'd
write the macro definition as:

#define CTOS(x) ((char[2]){(x), '\0'})


In C99 what does this compound construction do?
 
K

Keith Thompson

Mark said:
Keith said:
[...]

In a function-like macro, each reference to each parameter should be
parenthesized unless there's a specific reason not to do so. So I'd
write the macro definition as:

#define CTOS(x) ((char[2]){(x), '\0'})


In C99 what does this compound construction do?

It's a compound literal. In this case, (char[2]){(x), '\0'} is an
expression of type char[2], i.e., an array of 2 char. (Like any
expression of array type, it will decay to a pointer to the array's
first element in most, but not all, contexts.)

See section 6.5.2.5 of the C99 standard; the latest post-standard
draft is available at
<http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf>.
 
K

Keith Thompson

david said:
"Legal" is a funny word for C syntax -- it is a compound literal in C99
(as you intended it to be) and a syntax error in C90.  Maybe splint
does not understand C99 (it seems to miss some other C99 features).

Thanks Ben!!
Putting extra braces fixes the problem:

#include <stdio.h>

#define ctos(x) ((char[2]){(char)x, '\0'})

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

printf("%s %s\n", ctos(c), ctos('b'));
return 0;
}

Keep in mind that the lifetime of the anonymous object created by a
compound literal depends on the context in which it appearss. In this
case, after macro expansion, both compound literals are within the
body of function, so the objects have automatic storage duration.
That's not a problem here, but it could be in other contexts:

char *build_string(char c)
{
return ctos(c); /* BAD, returns the address of a local object */
}
 
D

david

Keep in mind that the lifetime of the anonymous object created by a
compound literal depends on the context in which it appearss.  In this
case, after macro expansion, both compound literals are within the
body of function, so the objects have automatic storage duration.
That's not a problem here, but it could be in other contexts:

    char *build_string(char c)
    {
        return ctos(c); /* BAD, returns the address of a local object */
    }
Yes, but I see no way to return an allocated string using a similar
method
avoiding malloc and free even if I know his size
Any help is welcomed :)
 
K

Keith Thompson

david said:
Yes, but I see no way to return an allocated string using a similar
method avoiding malloc and free even if I know his size
Any help is welcomed :)

If you can be more specific about what you're trying to do, we can
probably help.

If your requirement is to return a pointer to a string consisting of a
specified character plus a trailing '\0', you could build a single
static array consisting of:

"\0\0\1\0...a\0b\0c\0..."

and return a pointer into it; assuming 8-bit chars, the array is only
512 bytes long.
 
D

David RF

If you can be more specific about what you're trying to do, we can
probably help.

If your requirement is to return a pointer to a string consisting of a
specified character plus a trailing '\0', you could build a single
static array consisting of:

    "\0\0\1\0...a\0b\0c\0..."

and return a pointer into it; assuming 8-bit chars, the array is only
512 bytes long.

Yes!!
Good lesson Keith, is just what I want:

#include <stdio.h>

static char ascii[] = {
0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,
10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,
20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,
30,0,31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,
40,0,41,0,42,0,43,0,44,0,45,0,46,0,47,0,48,0,49,0,
50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,
60,0,61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,
70,0,71,0,72,0,73,0,74,0,75,0,76,0,77,0,78,0,79,0,
80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,
90,0,91,0,92,0,93,0,94,0,95,0,96,0,97,0,98,0,99,0,
100,0,101,0,102,0,103,0,104,0,105,0,106,0,107,0,108,0,109,0,
110,0,111,0,112,0,113,0,114,0,115,0,116,0,117,0,118,0,119,0,
120,0,121,0,122,0,123,0,124,0,125,0,126,0,127,0
};
/* if x is not ascii returns pointer to first member (0) */
#define ctos(x) &ascii[((x) & ~0x7f) == 0 ? (x) * 2 : 0]

static char *dummy(int c);

static char *dummy(int c)
{
return ctos(c);
}

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

printf("%s %s %s %s\n", ctos(c), ctos('b'), dummy('c'), ctos(128));
return 0;
}
 

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,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top