strange behavior

M

Marlene Stebbins

Something very strange is going on here. I don't know if it's a C
problem or an implementation problem. The program reads data from
a file and loads it into two arrays. When xy, x, y, *xlist and
*ylist are ints or floats there is no apparent problem. If these
variables are doubles, the program crashes. Furthermore, the
crashes occur only when xlist and ylist are free()ed. When the
above variables are doubles and the calls to free() are
eliminated, the program doesn't crash, but the output is weird.

If there is anyone out there with enough time on his hands to
compile this code and play with it, I would appreciate getting
your opinion. Don't forget to change the format specifiers in the
calls to fscanf() when changing types. Here are data for the
input file:

0 12 5 12 8 9 11 3 10 -2 5 -8 0 -10 -7 -1 -7 12

/* Read xy data from a file.
Load x&y values into respective arrays.
Works with ints, floats, but not doubles???
*/
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
double xy, x, y;
double *xlist, *ylist;
int xct, yct, xycount, idx;
FILE *data;

if((data = fopen("vertices.txt", "r")) == NULL)
{
fprintf(stderr, "can't open data file\n");
exit(EXIT_FAILURE);
}
xycount = 0;
while(fscanf(data, "%lf") == 1)
xycount++;
if(xycount%2 != 0)
{
fprintf(stderr, "unequal number of x,y values\n");
exit(EXIT_FAILURE);
}
xlist = calloc(xycount/2, sizeof(xlist));
ylist = calloc(xycount/2, sizeof(ylist));
rewind(data);
xct = 0;
yct = 0;
while(fscanf(data, "%lf %lf", &x, &y) == 2)
{
xlist[xct++] = x;
ylist[yct++] = y;
}
fclose(data);

for(idx = 0; idx < xycount/2; ++idx)
printf("%4.f", xlist[idx]);
putchar('\n');
for(idx = 0; idx < xycount/2; ++idx)
printf("%4.f", ylist[idx]);
putchar('\n');

free(xlist);
free(ylist);

return 0;
}
 
C

Christopher Benson-Manica

Marlene Stebbins said:
while(fscanf(data, "%lf") == 1)
^
Where is the float you're reading going? If this is your actual code,
no wonder it crashes. If you want to ignore some input, use

fscanf( data, "%*lf" ); /* legal */

I have no idea whether that's the root of the behavior you describe,
but it's definitely a problem.
 
N

Neil Cerutti

Something very strange is going on here.>

xlist = calloc(xycount/2, sizeof(xlist));
ylist = calloc(xycount/2, sizeof(ylist));

You want

xlist = calloc(xycount/2, sizeof *xlist);
ylist = calloc(xycount/2, sizeof *ylist);

The reason that other types of lists work is a coincedence of,
e.g., sizeof xlist and sizeof *xlist.
 
M

Marlene Stebbins

Christopher said:
^
Where is the float you're reading going? If this is your actual code,
no wonder it crashes. If you want to ignore some input, use

fscanf( data, "%*lf" ); /* legal */

I have no idea whether that's the root of the behavior you describe,
but it's definitely a problem.

OK, thanks. Doesn't affect the strange behavior though.

Marlene
 
K

Kevin Bagust

xlist = calloc(xycount/2, sizeof(xlist));
ylist = calloc(xycount/2, sizeof(ylist));

These two lines should be:
xlist = calloc( xycount/2, sizeof( *xlist ));
ylist = calloc( xycount/2, sizeof( *ylist ));

Because without dereferencing xlist or ylist you are getting enough
memory for pointers to the type, rather that for the type it self.

From your description I would guess that the size of a pointer is larger
or equal to the size of an int or a float. Where as the size of a pointer
is smaller than the size of a double, so with double you were over
writing other memory and so getting strange results and crashes.

Kevin.
 
R

Ralf Damaschke

^
Where is the float you're reading going? If this is your
actual code, no wonder it crashes. If you want to ignore some
input, use

fscanf( data, "%*lf" ); /* legal */

But useless when trying to test the number of input items
assigned as in the OP's program.

Better:
while (fscanf(data, "%lf", &xy) == 1)
I have no idea whether that's the root of the behavior you
describe, but it's definitely a problem.

Later on in program there is another problem:

We do not need space for some pointers but for doubles, i.e.
sizeof(*xlist).

Ralf
 
M

Marlene Stebbins

Neil said:
You want

xlist = calloc(xycount/2, sizeof *xlist);
ylist = calloc(xycount/2, sizeof *ylist);

The reason that other types of lists work is a coincedence of,
e.g., sizeof xlist and sizeof *xlist.

Nice try. Still crashing after this change. :-(
 
M

Marlene Stebbins

Ralf said:
But useless when trying to test the number of input items
assigned as in the OP's program.

Better:
while (fscanf(data, "%lf", &xy) == 1)
>
Later on in program there is another problem:



We do not need space for some pointers but for doubles, i.e.
sizeof(*xlist).

Thank you Ralf. These two changes fix the problem. Actually, I had

while (fscanf(data, "%lf", &xy) == 1)

originally, and changed it, thinking it inefficient. Live and learn.

Marlene
 
D

Darrell Grainger

Something very strange is going on here. I don't know if it's a C
problem or an implementation problem. The program reads data from
a file and loads it into two arrays. When xy, x, y, *xlist and
*ylist are ints or floats there is no apparent problem. If these
variables are doubles, the program crashes. Furthermore, the
crashes occur only when xlist and ylist are free()ed. When the
above variables are doubles and the calls to free() are
eliminated, the program doesn't crash, but the output is weird.

If there is anyone out there with enough time on his hands to
compile this code and play with it, I would appreciate getting
your opinion. Don't forget to change the format specifiers in the
calls to fscanf() when changing types. Here are data for the
input file:

0 12 5 12 8 9 11 3 10 -2 5 -8 0 -10 -7 -1 -7 12

/* Read xy data from a file.
Load x&y values into respective arrays.
Works with ints, floats, but not doubles???
*/
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
double xy, x, y;
double *xlist, *ylist;
int xct, yct, xycount, idx;
FILE *data;

if((data = fopen("vertices.txt", "r")) == NULL)
{
fprintf(stderr, "can't open data file\n");
exit(EXIT_FAILURE);
}
xycount = 0;
while(fscanf(data, "%lf") == 1)
xycount++;

Where are you storing the data that fscanf is reading it? This could cause
a program to crash. I'd use:

while(fscanf(data, "%lf", &xy) == 1)
xycount++;
if(xycount%2 != 0)
{
fprintf(stderr, "unequal number of x,y values\n");
exit(EXIT_FAILURE);
}
xlist = calloc(xycount/2, sizeof(xlist));
ylist = calloc(xycount/2, sizeof(ylist));

Obviously the /2 is okay. You want to store half the list in xlist and the
other half in ylist. The problem here is that sizeof(xlist) is the size of
a pointer to double. You want the size of a double or sizeof(*xlist). Same
thing for ylist.
rewind(data);
xct = 0;
yct = 0;
while(fscanf(data, "%lf %lf", &x, &y) == 2)
{
xlist[xct++] = x;
ylist[yct++] = y;
}
fclose(data);

for(idx = 0; idx < xycount/2; ++idx)
printf("%4.f", xlist[idx]);
putchar('\n');
for(idx = 0; idx < xycount/2; ++idx)
printf("%4.f", ylist[idx]);
putchar('\n');

Just as a side note, a good compiler will do this for you but I'm in the
habit of taking things like xycount/2 and replacing them with another
variable. In a for loop, like above, this is a chance division will occur
on each iteration of the loop. That could have a notble impact.
 
M

Martin Ambuhl

Marlene said:
Something very strange is going on here. I don't know if it's a C
problem or an implementation problem. The program reads data from a file
and loads it into two arrays. When xy, x, y, *xlist and *ylist are ints
or floats there is no apparent problem.

The problem is obvious, but may not be apparent to you. Your program
relies on sizeof(pointer-to-T) being equal to sizeof(T). You were
unlucky to have not seen this before because, it seems, your
implementation does have
sizeof(*int) == sizeof(int)
and
sizeof(*float) == sizeof(float)
but
sizeof(*double) != sizeof(double)
I'll bet
sizeof(*char) != sizeof(char)
and probably that
!(sizeof(*short) == sizeof(short) && sizeof(*long) == sizeof(long))

If these variables are doubles,
the program crashes. [...]
double *xlist, *ylist; [...]
while(fscanf(data, "%lf") == 1)

Could it be because the above has insufficient arguments for the format?
xlist = calloc(xycount/2, sizeof(xlist));
ylist = calloc(xycount/2, sizeof(ylist));

Or because the above makes no sense?

With these three lines corrected, the program below at least seems to
work properly:


/* Read xy data from a file.
Load x&y values into respective arrays.
Works with ints, floats, but not doubles???
*/
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
double xy, x, y;
double *xlist, *ylist;
int xct, yct, xycount, idx;
FILE *data;

if ((data = fopen("vertices.txt", "r")) == NULL) {
fprintf(stderr, "can't open data file\n");
exit(EXIT_FAILURE);
}
xycount = 0;
while (fscanf(data, "%lf", &xy) == 1)
xycount++;
if (xycount % 2 != 0) {
fprintf(stderr, "unequal number of x,y values\n");
exit(EXIT_FAILURE);
}
if (!(xlist = malloc(xycount / 2 * sizeof *xlist))) {
fprintf(stderr, "allocation failed for xlist, giving up\n");
exit(EXIT_FAILURE);
}
if (!(ylist = malloc(xycount / 2 * sizeof *ylist))) {
fprintf(stderr, "allocation failed for ylist, giving up\n");
free(xlist);
exit(EXIT_FAILURE);
}
rewind(data);
xct = 0;
yct = 0;
while (fscanf(data, "%lf %lf", &x, &y) == 2) {
xlist[xct++] = x;
ylist[yct++] = y;
}
fclose(data);

for (idx = 0; idx < xycount / 2; ++idx)
printf("%4.f", xlist[idx]);
putchar('\n');
for (idx = 0; idx < xycount / 2; ++idx)
printf("%4.f", ylist[idx]);
putchar('\n');

free(xlist);
free(ylist);

return 0;
}
 
M

Marlene Stebbins

Martin said:
The problem is obvious, but may not be apparent to you. Your program
relies on sizeof(pointer-to-T) being equal to sizeof(T).

Here is a subtle issue that I was unaware of. It's never cropped
up in any C instruction or book that I have seen (or I missed it
if it did). I've learned something important.

Thanks,
Marlene
 
K

Keith Thompson

Marlene Stebbins said:
Here is a subtle issue that I was unaware of. It's never cropped up in
any C instruction or book that I have seen (or I missed it if it
did). I've learned something important.

It's pretty obvious once you think about it. It's common (though not
required) for all pointers to be the same size, typically 32 or 64
bits. Imagine that type T is some large struct, several kilobytes in
size. No matter how big or small T is, a pointer-to-T is still going
to be only 32 or 64 bits (or whatever the size of a pointer is on your
platform).
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top