J.L.Louis J.L.Louis - 1 month ago 9
C Question

Unexplainable side effects of a function on other unrelated ones in C

The following program is supposed to copy the source array's elements into the target arrays using different pointer reference notations in each function:

#include <stdio.h>

void copy_arr(double [], const double [], int);
void copy_ptr(double *, const double *, int);
void copy_ptrs(double *, const double *, double *);
void print_arr(const double *, const double *);

int main(void)
{
double source[5] = { 0.1, 2.2, 4.3, 6.4, 8.5};
double target1[5];
double target2[5];
double target3[5];

copy_arr(target1, source, 5);
copy_ptr(target2, source, 5);

copy_ptrs(target3, source, source + 5);

print_arr(target1, target1 + 5);
print_arr(target2, target2 + 5);
print_arr(target3, target3 + 5);

return 0;
}

void copy_arr(double target[], const double source[], int num)
{
for (int i = 0; i < num; ++i)
target[i] = source[i];
}

void copy_ptr(double *target, const double *source, int num)
{
for (int i = 0; i < num; ++i)
*(target+i) = *(source+i);
}

void copy_ptrs(double *target, const double *source, double *end)
{
for (; target < end; ++target, ++source)
*target = *source;
}

void print_arr(const double *start, const double *end)
{
while ( start < end)
printf("%.1lf, ", *start++);
printf("\n");
}


This produces outputs like:


0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0,
0.1, 2.2, 4.3, 6.4, 8.5,


or garbage in the place of the zeroes.

However when I accidentally changed the copy_ptrs function to:

void copy_ptrs(double *target, const double *source, double *end)
{
for (; *target < *end; ++target, ++source) // notice the asterisks
*target = *source;
}


I get the following output:


0.1 2.2 4.3 6.4 8.5
0.1 2.2 4.3 6.4 8.5
0.0 0.0 0.0 0.0 0.0


Apparently the "mistake" causes the
copy_arr
and
copy_ptr
to function properly, but when I remove the asterisks from the variable names in the for loop of
copy_ptrs
, it causes the two previous functions to malfunction, while it itself works.

To re-iterate: dereferencing the
target
and
end
variables produces the right output for the third function, but 'breaks' the two previous ones; and not doing so breaks its respective function. I think not dereferencing the pointers
start
and
end
is the right way to go, because the
print_arr
does not dereference them, but behaves as expected (and according to my understanding, is the proper way).

How can this change affect code that supposedly runs before it's supposed to be reach. And overall, what's wrong with the program?

I'm using GCC 4.8.5 on Linux.

Answer

The biggest problem with copy_ptrs() is the end condition:

void copy_ptrs(double *target, const double *source, double *end)
{
  for (; target < end; ++target, ++source)
    *target = *source;
}

Note that this is invoked with:

copy_ptrs(target3, source, source + 5);

You should be checking that source is less than end:

void copy_ptrs(double *target, const double *source, double *end)
{
  for (; source < end; ++target, ++source)
    *target = *source;
}

So, you're copying indeterminate amounts of data around the place in the original, trampling outside the bounds of the arrays. A lot depends on how the arrays are laid out by the compiler. It looks like you're (un)fortunate that target3 is laid out at an address lower than source, so incrementing target often enough gets to end before you overwrite something critical and crash.