delirious program, episode II

A

Army1987

The following program compiles without errors or warnings with
gcc -ansi -pedantic and works properly.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *f(char *str1, char *str2)
{
char *res = malloc(strlen(str1)+strlen(str2)+1);
return res ? strcat(strcpy(res, str1), str2) : (exit(1), NULL);
}

int main(void)
{
char a[] = "\nHey man look at me rockin' now\nI'm on the ";
char b[] = "Danny and Lisa";
char c[] = "\nThey take me away from";
char *d = f(f(a, "radio"), f(a, "video"));
char *e = f(d, f(" with ", b));
char *h = f(c, f("\nThe strangest places\nSweet ", b));
char *j = f(e, f(f(h, c), f(h, c)));
return puts(f(f(f(f(d, j), f(j, e)), h), f(d, e)) + 1);
/* No, I don't want to free the memory. If an OS doesn't free it itself, */
/* it doesn't deserve to have it 'till reboot */
}

Instead this one compiles (and does work under Windows if I compile it with
lcc-win32), but under Linux it prints "Segmentation fault (core dumped)" and
returns. Why? I can't see any substantial difference.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *f(char *str1, char *str2)
{
char *res;
return strcat(strcpy(res=malloc(strlen(str1)+strlen(str2)+1) ? res :
(exit(1), NULL), str1), str2);
}

int main(void)
{
char a[] = "\nHey man look at me rockin' now\nI'm on the ";
char b[] = "Danny and Lisa";
char c[] = "\nThey take me away from";
char *d = f(f(a, "radio"), f(a, "video"));
char *e = f(d, f(" with ", b));
char *h = f(c, f("\nThe strangest places\nSweet ", b));
char *j = f(e, f(f(h, c), f(h, c)));
return puts(f(f(f(f(d, j), f(j, e)), h), f(d, e)) + 1);
}

--
#include <stdio.h>
main()
{
printf("\n\n--\nArmy1987");
}
 
A

Alan Curry

The following program compiles without errors or warnings with
gcc -ansi -pedantic and works properly. [...]
char *res = malloc(strlen(str1)+strlen(str2)+1);
return res ? strcat(strcpy(res, str1), str2) : (exit(1), NULL); [...]
Instead this one compiles (and does work under Windows if I compile it with
lcc-win32), but under Linux it prints "Segmentation fault (core dumped)" and
returns. Why? I can't see any substantial difference.

char *res;
return strcat(strcpy(res=malloc(strlen(str1)+strlen(str2)+1) ? res :
(exit(1), NULL), str1), str2);

Assignment has lower precedence than the ternary operator, so you did the
equivalent of this:

res=(
malloc(strlen(str1)+strlen(str2)+1) ? res : (exit(1), NULL)
);
return strcat(strcpy(res,str1),str2);

or more verbosely:

if(malloc(strlen(str1)+strlen(str2)+1))
res=res;
else {
exit(1);
res=NULL;
}
return strcat(strcpy(res,str1),str2);

gcc -Wall warns about the uninitialized use of res.

The big ugly statement you were looking for is:
return strcat(strcpy((res=malloc(strlen(str1)+strlen(str2)+1)) ? res :
(exit(1), NULL), str1), str2);
 
A

Army1987

return puts(f(f(f(f(d, j), f(j, e)), h), f(d, e)) + 1);
That's what happens when you trust your textbook. It claims that puts()
returns zero if it succeeds and a negative value if it fails. Now I've
looked up the Standard, it just requires the success return value to be
nonnegative, but requires the failure value to be EOF.
No problem, I'll just replace it with
return puts(f(f(f(f(d, j), f(j, e)), h), f(d, e)) + 1) < 0;

Of course this can't be what causes the problem, as it is the same in both
programs, and anyway I can't see how the return value of puts() and
therefore of main() can affect that.
 
U

user923005

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *f(char *str1, char *str2)
{
char *res;
return strcat(strcpy(res=malloc(strlen(str1)+strlen(str2)+1) ? res :
(exit(1), NULL), str1), str2);
}


int main(void)
{
char a[] = "\nHey man look at me rockin' now\nI'm on the ";
char b[] = "Danny and Lisa";
char c[] = "\nThey take me away from";
char *d = f(f(a, "radio"), f(a, "video"));
char *e = f(d, f(" with ", b));
char *h = f(c, f("\nThe strangest places\nSweet ", b));
char *j = f(e, f(f(h, c), f(h, c)));
return puts(f(f(f(f(d, j), f(j, e)), h), f(d, e)) + 1);
}
/*
SPLINT output:
Splint 3.1.1 --- 12 Mar 2007

foo.c: (in function f)
foo.c(8,7): Argument to exit has implementation defined behavior: 1
The argument to exit should be 0, EXIT_SUCCESS or EXIT_FAILURE (Use -
exitarg
to inhibit warning)
foo.c(8,11): Second clause of comma expression is unreachable: NULL
This code will never be reached on any possible execution. (Use -
unreachable
to inhibit warning)
foo.c(7,65): Variable res used before definition
An rvalue is used that may not be initialized to a value on some
execution
path. (Use -usedef to inhibit warning)
foo.c: (in function main)
foo.c(17,14): New fresh storage (type char *) passed as implicitly
temp (not
released): f(a, "radio")
A memory leak has been detected. Storage allocated locally is not
released
before the last reference to it is lost. (Use -mustfreefresh to
inhibit
warning)
foo.c(17,29): New fresh storage (type char *) passed as implicitly
temp (not
released): f(a, "video")
foo.c(18,17): New fresh storage (type char *) passed as implicitly
temp (not
released): f(" with ", b)
foo.c(19,17): New fresh storage (type char *) passed as implicitly
temp (not
released): f("\nThe strangest places\nSweet ", b)
foo.c(20,19): New fresh storage (type char *) passed as implicitly
temp (not
released): f(h, c)
foo.c(20,28): New fresh storage (type char *) passed as implicitly
temp (not
released): f(h, c)
foo.c(20,17): New fresh storage (type char *) passed as implicitly
temp (not
released): f(f(h, c), f(h, c))
foo.c(21,20): New fresh storage (type char *) passed as implicitly
temp (not
released): f(d, j)
foo.c(21,29): New fresh storage (type char *) passed as implicitly
temp (not
released): f(j, e)
foo.c(21,18): New fresh storage (type char *) passed as implicitly
temp (not
released): f(f(d, j), f(j, e))
foo.c(21,16): New fresh storage (type char *) passed as implicitly
temp (not
released): f(f(f(d, j), f(j, e)), h)
foo.c(21,43): New fresh storage (type char *) passed as implicitly
temp (not
released): f(d, e)
foo.c(21,57): Fresh storage d not released before return
foo.c(17,44): Fresh storage d created
foo.c(21,57): Fresh storage e not released before return
foo.c(18,33): Fresh storage e created
foo.c(21,57): Fresh storage h not released before return
foo.c(19,57): Fresh storage h created
foo.c(21,57): Fresh storage j not released before return
foo.c(20,38): Fresh storage j created
foo.c(4,7): Function exported but not used outside foo: f
A declaration is exported, but not used outside this module.
Declaration can
use static qualifier. (Use -exportlocal to inhibit warning)
foo.c(9,1): Definition of f

Finished checking --- 20 code warnings

PC-Lint output:
--- Module: foo.c (C)
_
(exit(1), NULL), str1), str2);
foo.c(8) : Warning 530: Symbol 'res' (line 6) not initialized --- Eff.
C++ 3rd
Ed. item 4
foo.c(6) : Info 830: Location cited in prior message
_
(exit(1), NULL), str1), str2);
foo.c(8) : Warning 668: Possibly passing a null pointer to function
'strcpy(char *, const char *)', arg. no. 1 [Reference: file foo.c:
line 8]
foo.c(8) : Info 831: Reference cited in prior message
_
}
foo.c(9) : Note 952: Parameter 'str1' (line 4) could be declared const
--- Eff.
C++ 3rd Ed. item 3
foo.c(4) : Info 830: Location cited in prior message
_
}
foo.c(9) : Info 818: Pointer parameter 'str1' (line 4) could be
declared as
pointing to const --- Eff. C++ 3rd Ed. item 3
foo.c(4) : Info 830: Location cited in prior message
_
}
foo.c(9) : Note 952: Parameter 'str2' (line 4) could be declared const
--- Eff.
C++ 3rd Ed. item 3
foo.c(4) : Info 830: Location cited in prior message
_
}
foo.c(9) : Info 818: Pointer parameter 'str2' (line 4) could be
declared as
pointing to const --- Eff. C++ 3rd Ed. item 3
foo.c(4) : Info 830: Location cited in prior message
_
char *d = f(f(a, "radio"), f(a, "video"));
foo.c(17) : Info 1776: Converting a string literal to char * is not
const safe
(arg. no. 2)
foo.c(17) : Info 1776: Converting a string literal to char * is not
const safe
(arg. no. 2)
_
char *e = f(d, f(" with ", b));
foo.c(18) : Info 1776: Converting a string literal to char * is not
const safe
(arg. no. 1)
_
char *h = f(c, f("\nThe strangest places\nSweet ", b));
foo.c(19) : Info 1776: Converting a string literal to char * is not
const safe
(arg. no. 1)
_
}
foo.c(22) : Note 953: Variable 'd' (line 17) could be declared as
const ---
Eff. C++ 3rd Ed. item 3
foo.c(17) : Info 830: Location cited in prior message
_
}
foo.c(22) : Note 953: Variable 'e' (line 18) could be declared as
const ---
Eff. C++ 3rd Ed. item 3
foo.c(18) : Info 830: Location cited in prior message
_
}
foo.c(22) : Note 953: Variable 'h' (line 19) could be declared as
const ---
Eff. C++ 3rd Ed. item 3
foo.c(19) : Info 830: Location cited in prior message
_
}
foo.c(22) : Note 953: Variable 'j' (line 20) could be declared as
const ---
Eff. C++ 3rd Ed. item 3
foo.c(20) : Info 830: Location cited in prior message

*/
 
A

Army1987

foo.c: (in function f)
foo.c(8,7): Argument to exit has implementation defined behavior: 1
The argument to exit should be 0, EXIT_SUCCESS or EXIT_FAILURE (Use -
exitarg
to inhibit warning)
OK, exit(EXIT_FAILURE) then...
foo.c(8,11): Second clause of comma expression is unreachable: NULL
This code will never be reached on any possible execution. (Use -
unreachable
to inhibit warning)
Who cares? I want the programm to halt if the malloc() fails. I used the
comma just because I'm not allowed to cast void to char*, and anyway I could
use (char *)anything instead of NULL...
foo.c(7,65): Variable res used before definition
An rvalue is used that may not be initialized to a value on some
execution
path. (Use -usedef to inhibit warning)
Ay, there's the rub...
foo.c(4,7): Function exported but not used outside foo: f
A declaration is exported, but not used outside this module.
Declaration can
use static qualifier. (Use -exportlocal to inhibit warning)
foo.c(9,1): Definition of f
What does this exactly mean?
foo.c(8) : Warning 668: Possibly passing a null pointer to function
If that function survived exit().
foo.c(9) : Note 952: Parameter 'str1' (line 4) could be declared const
I'll try that...
char *d = f(f(a, "radio"), f(a, "video"));
foo.c(17) : Info 1776: Converting a string literal to char * is not
const safe
What does this exactly mean?
 
A

Army1987

[trimmed:]
(res = malloc(length)) ? res : (exit(1), NULL);


Is there a Python-like "or" operator for which
malloc(length) or (exit(1), NULL);

returns the first member if it's true and the second if the first is false?
C's || operator apparently always returns (int)1 or 0...
In other words, I need a version of funct1(a)?funct1(a):funct2(b) in which
funct1(a) is evaluated once, so I could rewrite the above without having to
declare the variable char *res.

(Yes, I know they've invented the if statement, but I don't want to use it
here...)
 
O

Old Wolf

return strcat(strcpy(res=malloc(strlen(str1)+strlen(str2)+1) ? res :
(exit(1), NULL), str1), str2);

There's a code structure in C that is like the ternary operator
but allow you to write more readable code. The keyword involved
is 'if' (not sure if you've come across it before). The above
code, written with this 'if', would look like this:

if ( malloc(strlen(strl) + strlen(str2) + 1) )
res = res;
else
res = ( exit(1), NULL );

strcpy( res, str1 );
strcat( res, str2 );
return res;

I'm sure you can now see what the problem was.
--
#include <stdio.h>
main()
{
printf("\n\n--\nArmy1987");
}

This is not a correct program in C90, nor in C99.
 
K

Keith Thompson

Army1987 said:
The following program compiles without errors or warnings with
gcc -ansi -pedantic and works properly. [...]
char *f(char *str1, char *str2)
{
char *res = malloc(strlen(str1)+strlen(str2)+1);
return res ? strcat(strcpy(res, str1), str2) : (exit(1), NULL);
}
[...]

A style question: is there something wrong with using an "if"
statement? You want to call exit() if malloc() fails. To force the
exit() call into the return statement, you combine it in a comma
expression with NULL, enclose that in parentheses, and enclose *that*
in a "?:" expression. It makes my eyes hurt. Trsnss s nt lwys a
vrtu.

Here's how I might write it:

char *f(char *str1, char *str2)
{
char *res = malloc(strlen(str1) + strlen(str2) + 1);
if (res == NULL) {
exit(EXIT_FAILURE);
}
else {
strcpy(res, str1);
strcat(res, str2);
return res;
}
}

Non-obvious things I've done:

1. I've changed "exit(1)" to a portable "exit(EXIT_FAILURE)".

2. I've added blanks around operators to make it more readable.

3. I've made the check on the result of malloc() more explicit.

4. I've split the strcpy() and strcat() calls into separate statements.

Some more things I'd probably change if I took the time:

1. I wouldn't name the function "f".

2. I'd save the result of strlen(str1) and use it to precompute where
to start copying str2 into the string pointed to by res. strcat()
has to scan for the terminating '\0' character, but I've already
found it.

3. I might have the function return NULL if malloc() fails, so the
caller can decide what to do about the error (*unless* I'm positive
that any program that might possibly use this function has a policy
of aborting immmediately on a malloc() error).

4. I'd make the function static unless I specifically wanted it to be
callable from outside that source file.
 
O

Old Wolf

What does this exactly mean?

It means that in the object file for this C file, the symbol 'f'
will appear, so that other modules can call it. However, you did
not actually call it from any other module. So the lint is
suggesting that perhaps you should declare 'f' as private to this
C file (the way to do that is to put 'static' at the start of the
function declaration).
What does this exactly mean?

Your 'f' function is declared as accepting a pointer to modifiable
characters. You pass in a pointer to string literal. Modifying a
string literal causes undefined behaviour. The solution is to declare
'f' as taking pointers to non-modifiable characters.
 
K

Keith Thompson

Army1987 said:
[trimmed:]
(res = malloc(length)) ? res : (exit(1), NULL);


Is there a Python-like "or" operator for which
malloc(length) or (exit(1), NULL);

returns the first member if it's true and the second if the first is false?
C's || operator apparently always returns (int)1 or 0...
In other words, I need a version of funct1(a)?funct1(a):funct2(b) in which
funct1(a) is evaluated once, so I could rewrite the above without having to
declare the variable char *res.

(Yes, I know they've invented the if statement, but I don't want to use it
here...)

Please don't snip attribution lines.

The if statement is exactly the right solution for what you're trying
to do. *Why* don't you want to use it?
 
A

Army1987

Please don't snip attribution lines.
The if statement is exactly the right solution for what you're trying
to do. *Why* don't you want to use it?

I want to use as few characters as possible.
(A version I have on my computer has no whitespace at all except within
literals and to prevent consecutive alphanumerical tokens running together,
and some of the most often recurrent character sequences such as =f(f( are
replaced with one-letter identifiers which I #define'd at the beginning.
I'm trying to make it fit in four 80-characters lines (excluding
preprocessor directives), but it's still about twenty characters too long.
 
K

Keith Thompson

I asked you not to snip attribution lines. I wrote the text above
starting with "The if statement is ...". There should be a line above
from your headers that you're using Microsoft Outlook Express; as far
as I know, it will insert an attribution line for you automatically.
Please don't delete it; knowing who wrote what makes it easier to
follow the discussion.
I want to use as few characters as possible.
(A version I have on my computer has no whitespace at all except within
literals and to prevent consecutive alphanumerical tokens running together,
and some of the most often recurrent character sequences such as =f(f( are
replaced with one-letter identifiers which I #define'd at the beginning.
I'm trying to make it fit in four 80-characters lines (excluding
preprocessor directives), but it's still about twenty characters too long.

Ok. I hope you're aware that that's a very silly thing to want to do.
You're writing code that's difficult to read and difficult to
maintain, with no real benefit. Now if you're doing this just as a
fun exercise, or if you're working on a submission to the IOCCC (I've
submitted to it myself), that's fine. But it would be nice to let us
know what you're trying to do so we can decide whether it's worth our
time helping you do it. (Maybe that's what you meant by "delirious
program", but that wasn't at all clear.) I'd like to know whether
my answer should be:

Just use an if statement; that's what it's for. And add some
whitespace so your code is more legible.

or:

Here's another way you can make your code shorter and *completely*
illegible.

or just to ignore it.
 
A

Army1987

Keith Thompson said:
Ok. I hope you're aware that that's a very silly thing to want to do.
Yes, I know that.
You're writing code that's difficult to read and difficult to
maintain, with no real benefit. Now if you're doing this just as a
fun exercise, or if you're working on a submission to the IOCCC (I've
submitted to it myself), that's fine. But it would be nice to let us
I won't submit *this* program because I don't think it is a good idea to
submit a program which just outputs copyrighted lyrics.
"Submitting a program to IOCCC" is in my list of things to do before dying,
but not now; yet, I must start somewhere, mustn't I?
Yes, maybe "fun exercise" are the right words. That text is very redundant,
so I wanted to try and write a program which is shorter than the output
itself. I've done that. The version with correct whitespace (the one which I
posted on 18 March in another thread, with the corrected malloc, the
statement if (res == NULL) exit(EXIT_FAILURE); added, and spaces around
operators and after commas) is 730 bytes; the output is 1073 bytes. (Yes,
maybe this counts as cheating, as the compiled file is few kilobytes...) But
I wanted to go further.

Also, I wanted to test how far the fact that there are functions which have
both side effects and a return value (lack of
http://en.wikipedia.org/wiki/Command-query_separation) can be taken, even in
a language which is said th be imperative paradigm.
Here's another way you can make your code shorter and *completely*
illegible.
Good idea... HHOS ;-)
 

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

Similar Threads


Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top