What is wrong with this code?

B

Bill Reid

#define MAX_VALUES 64

typedef struct {
unsigned value_1;
double value_2;
double value_3;
double value_4;
} VALUES;

typedef struct {
unsigned num_values;
unsigned tot_value_1;
double tot_value_2;
double tot_value_3;
double tot_value_4;
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

VALUES_STRUCT values_struct=
{3,0,0.0,0.0,0.0,{{1,1.0,1.0,1.0},{1,1.0,1.0,1.0},{1,1.0,1.0,1.0}}};

double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;
total+=*double_ptr;
inc_ptr+=inc_size;
number_num++;
}

return total;
}

void add_value_4(void) {

values_struct.tot_value_4=add_double_numbers
(&values_struct.values[0].value_4,values_struct.num_values,
sizeof(VALUES));

printf("\nThe total is %f",values_struct.tot_value_4);
}

int main(void) {

add_value_4();

return 0;
}
 
W

Walter Roberson

Bill Reid said:
#define MAX_VALUES 64

You fail to include <stdio.h>, which would affect the interpretation
of printf(), possibly causing garbage values to be printed out.

Other than that: what is the difference between the output you
see and the output you expect?
 
D

Default User

Bill Reid wrote:


Why do you think there's something wrong with it? Did it fail to
compile? Did it get different results than you expected?




Brian
 
B

Bill Reid

Walter Roberson said:
You fail to include <stdio.h>, which would affect the interpretation
of printf(), possibly causing garbage values to be printed out.
Uh, yeah, for me on this compiler, just a warning, I think. I haven't
actually compiled the actual code I posted, just similar stuff...
Other than that: what is the difference between the output you
see and the output you expect?

I get exactly what I expect. Honestly, I was looking for some
type of criticism of the general approach for accessing structure
elements in add_double_numbers(), that's all.
There are some ideas so wrong that only a very intelligent person
could believe in them. -- George Orwell

That is precisely the point, that is precisely the point. Here, let's
try again...what is wrong with this code?

#include <stdio.h> /* !!!!!!!!!!!! */

#define MAX_VALUES 64

typedef struct {
unsigned value_1;
double value_2;
double value_3;
double value_4;
} VALUES;

typedef struct {
unsigned num_values;
unsigned tot_value_1;
double tot_value_2;
double tot_value_3;
double tot_value_4;
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

VALUES_STRUCT values_struct=
{3,0,0.0,0.0,0.0,{{1,1.0,1.0,1.0},{1,1.0,1.0,1.0},{1,1.0,1.0,1.0}}};

double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;
total+=*double_ptr;
inc_ptr+=inc_size;
number_num++;
}

return total;
}

void add_value_4(void) {

values_struct.tot_value_4=add_double_numbers
(&values_struct.values[0].value_4,values_struct.num_values,
sizeof(VALUES));

printf("\nThe total is %f",values_struct.tot_value_4);
}

int main(void) {

add_value_4();

return 0;
}
 
R

Richard Heathfield

Bill Reid said:
Uh, yeah, for me on this compiler, just a warning, I think.

No such thing. It's not "just a warning". It's a warning.
I haven't
actually compiled the actual code I posted, just similar stuff...

....removing the last vestiges of a point from this entire thread.
 
C

Chris Torek

I get exactly what I expect. Honestly, I was looking for some
type of criticism of the general approach for accessing structure
elements in add_double_numbers(), that's all.

[where the add_double_numbers routine is]
double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;
total+=*double_ptr;
inc_ptr+=inc_size;
number_num++;
}

return total;
}

Oh ... in that case, as far as I can tell, the code is valid. It
does seem "slightly dangerous" (in the sense that it would be easy
for a caller to provide improper arguments, and then have everything
go kaboom). Also, while we tend to discourage casts in comp.lang.c,
I would probably go ahead and use them here, and make a few other
minor changes:

/*
* Iterate over some kind of array, which may be an array of structures
* or array of arrays, in which there is at least one "double" element.
* Add up the n "double" elements that are spaced apart by
* "inc_size" bytes, given a pointer to the first such "double".
*/
double add_double_numbers(double *first, size_t n, size_t inc_size) {
unsigned char *p; /* will point to our various "double"s */
double total = 0.0;
size_t i;

p = (unsigned char *)first;
for (i = 0; i < n; i++) {
total += *(double *)p;
p += inc_size;
}
return total;
}

The loop could be compressed to:

for (p = (unsigned char *)first, i = 0; i < n; p += inc_size, i++)
total += *(double *)p;

for those who prefer to see all the loop invariants in the "for"
expressions.

The reason for taking a "double *" is to force the caller to
pass the address of the first "double" element in the array of
structures, in case the caller wants to do something like:

struct S {
int a;
double x;
char *b;
FILE *c;
/* etc */
};
struct S arr[SIZE];
double result;
...
result = add_double_numbers(<args here to total up "arr">);

which in this case should be:

result = add_double_numbers(&arr[0].x, SIZE, sizeof arr[0]);

and *not*:

result = add_double_numbers(&arr[0], SIZE, sizeof arr[0]); /*WRONG*/

(Note that you could actually make the latter work by passing not
only a "byte spacing from double to double", i.e., inc_size, but
also an "offset from base to first double" element. There is
no real need, in this case: we can just include the offset in
the first argument. Some might like the symmetry of &arr[0] and
sizeof arr[0], though, in which case, add one more argument,
offsetof(arr[0], x), and change the first formal parameter to
"void *base" and make the other obvious required changes.)
 
K

Keith Thompson

Bill Reid said:
Uh, yeah, for me on this compiler, just a warning, I think.

If you use functions declared in <stdio.h>, the "#include <stdio.h>"
directive is not optional. Your compiler might not do you the favor
of rejecting your program, but you need to add it anyway.

(There are some cases where the "#include" isn't actually mandatory as
far as the language standard is concerned, but there is no reason at
all not to add it.)
I haven't
actually compiled the actual code I posted, just similar stuff...
[...]

Well, if it's not worth your time to compile your code before posting
it, it's certainly not worth my time to look at it. Let us know when
you have a serious question.
I get exactly what I expect. Honestly, I was looking for some
type of criticism of the general approach for accessing structure
elements in add_double_numbers(), that's all.

Then it would have been useful to say so. Asking "What is wrong with
this code?" (and that only in the subject header; it needs to be in
the body of your article) isn't particularly helpful.

We're glad to answer questions, but you need to take the time to ask
them.
 
R

Richard Tobin

Bill Reid said:
typedef struct {
unsigned value_1;
double value_2;
double value_3;
double value_4;
} VALUES;

typedef struct {
unsigned num_values;
unsigned tot_value_1;
double tot_value_2;
double tot_value_3;
double tot_value_4;
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

To avoid replicating the fields here, you could do

typedef struct {
unsigned num_values;
VALUES total[MAX_VALUES];
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

and refer to v.total.value1 instead of v.tot_value_1, etc.
double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;
total+=*double_ptr;
inc_ptr+=inc_size;
number_num++;
}

return total;
}

I would prefer something like:

double add_double_numbers(double *base, size_t count, size_t stride)
{
unsigned char *byte_ptr = (unsigned char *)base;
unsigned i;
double total=0.0;

for(i=0; i<count; i++)
{
total += *(double *)byte_ptr;
byte_ptr += stride;
}

return total;
}

Since the function only works for doubles, you gain nothing by making
the argument void *; use double * to emphasise its meaning.

size_t is the right type for object counts and sizes (cf calloc()).

I prefer my variable names; others may disagree. ("Stride" is a
standard term for this sort of distance between successive elements.)

There's no need to use both void and char pointers; converting a
correctly aligned pointer to and from a char pointer is guaranteed to
work. "Char" would be just as correct as "unsigned char" but the
latter is perhaps better for generic byte pointers.

Using a cast in the accumulating expression instead of an extra
variable and assignment might not be to everyone's taste, but reducing
the vertical size of a function aids readability in my opinion.

When a loop is over a sequence of integers, a for loop is clearer than
a while loop.

-- Richard
 
R

Richard Tobin

double add_double_numbers(double *base, size_t count, size_t stride)
{
unsigned char *byte_ptr = (unsigned char *)base;
unsigned i;

That should have been "size_t i;".

-- Richard
 
B

Bill Reid

Chris Torek said:
I get exactly what I expect. Honestly, I was looking for some
type of criticism of the general approach for accessing structure
elements in add_double_numbers(), that's all.

[where the add_double_numbers routine is]
double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;
total+=*double_ptr;
inc_ptr+=inc_size;
number_num++;
}

return total;
}

Oh ... in that case, as far as I can tell, the code is valid. It
does seem "slightly dangerous" (in the sense that it would be easy
for a caller to provide improper arguments, and then have everything
go kaboom).

Yes, but I tend to use stuff like this in my REALLY fundamental
"deep" libraries, and usually the only stuff that touches it are essentially
slightly higher-level more specific-purpose libraries. I'm trading off
a little "danger" for shrinking my total code-base by eliminating as
much redundant (except for the function signature) simplistic functionality
as possible...
Also, while we tend to discourage casts in comp.lang.c,
I would probably go ahead and use them here, and make a few other
minor changes:
OK, THIS is what I'm looking for, an ACTUAL discussion of
real-world practical "standard C" here...
/*
* Iterate over some kind of array, which may be an array of structures
* or array of arrays, in which there is at least one "double" element.
* Add up the n "double" elements that are spaced apart by
* "inc_size" bytes, given a pointer to the first such "double".
*/

Yup, that's it in a nutshell, except I use this basic approach for
integers, long doubles, chars, whatever (the classic and most
straightforward
is how I generate menu item lists for either GUI or "console" applications
from an array of structures that describe the routine parameters, function
pointers, etc., which also include a menu item text string somewhere
in the mix, by calling a single function signature "make_menu_list()" for
any
type of identifiable class of routines).
double add_double_numbers(double *first, size_t n, size_t inc_size) {
unsigned char *p; /* will point to our various "double"s */
double total = 0.0;
size_t i;

p = (unsigned char *)first;
for (i = 0; i < n; i++) {
total += *(double *)p;
p += inc_size;
}
return total;
}
Let me substitute that in my little test library and see what happens...
The loop could be compressed to:

for (p = (unsigned char *)first, i = 0; i < n; p += inc_size, i++)
total += *(double *)p;

for those who prefer to see all the loop invariants in the "for"
expressions.
A lot of times, that's me, but in many of the actual cases, I'm doing
SIGNIFICANTLY more processing in the loop, so it ain't going down
to two lines in those cases...
The reason for taking a "double *" is to force the caller to
pass the address of the first "double" element in the array of
structures, in case the caller wants to do something like:

struct S {
int a;
double x;
char *b;
FILE *c;
/* etc */
};
struct S arr[SIZE];
double result;
...
result = add_double_numbers(<args here to total up "arr">);

which in this case should be:

result = add_double_numbers(&arr[0].x, SIZE, sizeof arr[0]);

and *not*:

result = add_double_numbers(&arr[0], SIZE, sizeof arr[0]); /*WRONG*/

(Note that you could actually make the latter work by passing not
only a "byte spacing from double to double", i.e., inc_size, but
also an "offset from base to first double" element. There is
no real need, in this case: we can just include the offset in
the first argument. Some might like the symmetry of &arr[0] and
sizeof arr[0], though, in which case, add one more argument,
offsetof(arr[0], x), and change the first formal parameter to
"void *base" and make the other obvious required changes.)

OK, let me play around with this stuff a little, thanks...
 
B

Bill Reid

Richard Tobin said:
Bill Reid said:
typedef struct {
unsigned value_1;
double value_2;
double value_3;
double value_4;
} VALUES;

typedef struct {
unsigned num_values;
unsigned tot_value_1;
double tot_value_2;
double tot_value_3;
double tot_value_4;
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

To avoid replicating the fields here, you could do

typedef struct {
unsigned num_values;
VALUES total[MAX_VALUES];
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

and refer to v.total.value1 instead of v.tot_value_1, etc.
double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;
total+=*double_ptr;
inc_ptr+=inc_size;
number_num++;
}

return total;
}

I would prefer something like:

double add_double_numbers(double *base, size_t count, size_t stride)
{
unsigned char *byte_ptr = (unsigned char *)base;
unsigned i;
double total=0.0;

for(i=0; i<count; i++)
{
total += *(double *)byte_ptr;
byte_ptr += stride;
}

return total;
}

Since the function only works for doubles, you gain nothing by making
the argument void *; use double * to emphasise its meaning.
OK, that's a good point, my problem is that I'm not that clever at
writing "C" code, so let me try this out and see how it works...
size_t is the right type for object counts and sizes (cf calloc()).
Yes, although in most all cases I don't think I'll be exceeding
an unsigned...
I prefer my variable names; others may disagree. ("Stride" is a
standard term for this sort of distance between successive elements.)
Never really heard of it...
There's no need to use both void and char pointers; converting a
correctly aligned pointer to and from a char pointer is guaranteed to
work. "Char" would be just as correct as "unsigned char" but the
latter is perhaps better for generic byte pointers.
Seems like I get some warnings when I get a little too frisky with
pointer assignments and casts, and I don't use code that has warnings...let
me see what happens...
Using a cast in the accumulating expression instead of an extra
variable and assignment might not be to everyone's taste, but reducing
the vertical size of a function aids readability in my opinion.

When a loop is over a sequence of integers, a for loop is clearer than
a while loop.
Yeah, more straightforward to maintain and code in the first place,
too...there is no particular logic as to why I used a "while" loop in the
test code I posted...
 
B

Barry Schwarz

Uh, yeah, for me on this compiler, just a warning, I think. I haven't
actually compiled the actual code I posted, just similar stuff...

No, it is a diagnostic. The fact that your compiler calls it a
warning is irrelevant. The error causes undefined behavior.
I get exactly what I expect. Honestly, I was looking for some
type of criticism of the general approach for accessing structure
elements in add_double_numbers(), that's all.

If you haven't compiled the code, then how do you get what you expect?
That is precisely the point, that is precisely the point. Here, let's
try again...what is wrong with this code?

It suffers from attempting to implement a really poor design. You go
through ridiculous contortions whose only apparent purpose is to avoid
using a pointer to struct.

A little horizontal white space would make your code a lot more
readable.
#include <stdio.h> /* !!!!!!!!!!!! */

#define MAX_VALUES 64

typedef struct {
unsigned value_1;
double value_2;
double value_3;
double value_4;
} VALUES;

typedef struct {
unsigned num_values;
unsigned tot_value_1;
double tot_value_2;
double tot_value_3;
double tot_value_4;
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

VALUES_STRUCT values_struct=
{3,0,0.0,0.0,0.0,{{1,1.0,1.0,1.0},{1,1.0,1.0,1.0},{1,1.0,1.0,1.0}}};

The use of a global struct is another unnecessary design flaw.
double add_double_numbers
(void *number,unsigned num_numbers,unsigned inc_size) {

If you change the first parameter to VALUES *struct_ptr, you can
eliminate the third parameter ..
void *void_ptr=NULL;
char *inc_ptr=number;
double *double_ptr=NULL;

and all three of these ...
unsigned number_num=0;
double total=0.0;

while(number_num<num_numbers) {
void_ptr=inc_ptr;
double_ptr=void_ptr;

and both of these ...
total+=*double_ptr;

and change the second expression to struct_ptr->value_4
inc_ptr+=inc_size;

and replace this with a simple
struct_ptr++;
number_num++;
}

The common idiom for this loop would be
for (number_num = 0;
number_num < num_numbers;
number_num++, struct_ptr++){
and delete the last two lines of "manual increments."
return total;
}

void add_value_4(void) {

values_struct.tot_value_4=add_double_numbers
(&values_struct.values[0].value_4,values_struct.num_values,
sizeof(VALUES));

The argument list would then be
(values_struct.values, values_struct.num_values)
printf("\nThe total is %f",values_struct.tot_value_4);

Since your value is a double, why not use %lf and avail yourself of
the extra precision.
}

int main(void) {

add_value_4();

return 0;
}

---


Remove del for email
 
R

Richard Tobin

Barry Schwarz said:
It suffers from attempting to implement a really poor design. You go
through ridiculous contortions whose only apparent purpose is to avoid
using a pointer to struct.

I think you've missed the point. It's intended to be usable with
different structures and different members of the structures. Of
course, a comment explaining that would be a good idea!

-- Richard
 
C

Chris Torek

Never really heard of it...

It is a compiler-geek term, found in "array descriptors" in languages
that have such things (the common keywords here are "base",
"bound(s)", "offset", and "stride"). I almost used it, but decided
to stick with the original name, when I switched to "i" and "n".
 
E

Ernie Wright

Chris said:
It is a compiler-geek term, found in "array descriptors" in languages
that have such things

It's also pretty common in computer graphics, where it denotes, for
example, the distance in bytes between the start of two successive rows
of an image. Because of alignment, interleaving, bitplaning, and other
things, this distance often differs from the one that might be inferred
from the image width and number of bits per pixel.

- Ernie http://home.comcast.net/~erniew
 
R

Richard Heathfield

Ernie Wright said:
Chris said:
["Stride"] is a compiler-geek term, found in "array descriptors"
in languages that have such things

It's also pretty common in computer graphics, where it denotes,
for example, the distance in bytes between the start of two
successive rows of an image. Because of alignment, interleaving,
bitplaning, and other things, this distance often differs from
the one that might be inferred from the image width and number
of bits per pixel.

To give a slightly different example of "stride", VGA gives you eight
(or possibly sixteen?) pages of 80x25 video memory, each of which
occupies 2000 bytes (80 x 25 x 2), but nevertheless starts on a
2048-byte boundary. I think this was possibly more for convenience than
for speed; 2048-byte boundaries are easy to remember, ending as they do
in either 0x...000 or 0x...800.

To the terminally insane, this meant you had a whole bunch of 48-byte
blocks where you could squirrel away stuff that wouldn't fit anywhere
else... </blush>
 
D

Don Bruder

Richard Heathfield said:
Ernie Wright said:
Chris said:
["Stride"] is a compiler-geek term, found in "array descriptors"
in languages that have such things

It's also pretty common in computer graphics, where it denotes,
for example, the distance in bytes between the start of two
successive rows of an image. Because of alignment, interleaving,
bitplaning, and other things, this distance often differs from
the one that might be inferred from the image width and number
of bits per pixel.

To give a slightly different example of "stride", VGA gives you eight
(or possibly sixteen?) pages of 80x25 video memory, each of which
occupies 2000 bytes (80 x 25 x 2), but nevertheless starts on a
2048-byte boundary. I think this was possibly more for convenience than
for speed; 2048-byte boundaries are easy to remember, ending as they do
in either 0x...000 or 0x...800.

To the terminally insane, this meant you had a whole bunch of 48-byte
blocks where you could squirrel away stuff that wouldn't fit anywhere
else... </blush>

Why does this remind me of the old Apple II's "screen holes"? :)
 
B

Bill Reid

Chris Torek said:
Oh ... in that case, as far as I can tell, the code is valid. It
does seem "slightly dangerous" (in the sense that it would be easy
for a caller to provide improper arguments, and then have everything
go kaboom). Also, while we tend to discourage casts in comp.lang.c,
I would probably go ahead and use them here, and make a few other
minor changes:

/*
* Iterate over some kind of array, which may be an array of structures
* or array of arrays, in which there is at least one "double" element.
* Add up the n "double" elements that are spaced apart by
* "inc_size" bytes, given a pointer to the first such "double".
*/
double add_double_numbers(double *first, size_t n, size_t inc_size) {
unsigned char *p; /* will point to our various "double"s */
double total = 0.0;
size_t i;

p = (unsigned char *)first;
for (i = 0; i < n; i++) {
total += *(double *)p;
p += inc_size;
}
return total;
}
OK, I've tested this and it is a perfect "drop-in" fit for any of my
fundamental libraries that implement this type of structure element
access. It's got to be a little quicker because it eliminates the unneeded
pointer assignments (considering these libraries perform at least several
hundred billion loops a day, that's got to be worth a few seconds), and
should be more "type-safe" as well for ongoing development.

So thanks again because that's EXACTLY what I was looking for;
my original design was just the result of my general inability to handle
pointers very adroitly...
 
B

Bill Reid

Richard Tobin said:
To avoid replicating the fields here, you could do

typedef struct {
unsigned num_values;
VALUES total[MAX_VALUES];
VALUES values[MAX_VALUES];
} VALUES_STRUCT;

and refer to v.total.value1 instead of v.tot_value_1, etc.
Yeah, for the purposes of this "toy" code, but of course I really
WASN'T trying to add 1.0+1.0+1.0, despite what some people
seem to think! An actual structure I use might consist of several
dozen "results" or "descriptive" variables along with pointers to
several malloc'd arrays of 50-100 thousand structures containing
related values of floats, doubles, long doubles, ints...
I would prefer something like:

double add_double_numbers(double *base, size_t count, size_t stride)
{
unsigned char *byte_ptr = (unsigned char *)base;
unsigned i;
double total=0.0;

for(i=0; i<count; i++)
{
total += *(double *)byte_ptr;
byte_ptr += stride;
}

return total;
}

Since the function only works for doubles, you gain nothing by making
the argument void *; use double * to emphasise its meaning.
Yup, this is almost identical to what Chris Torek came up with,
works like a champ. Thanks.
size_t is the right type for object counts and sizes (cf calloc()).
Yes, "technically", which of course what this is all about...
I prefer my variable names; others may disagree.

I tend to prefer nice big preferably unabbreviated English words that
I can understand immediately, but that's not really a "C" convention, now
is it?
There's no need to use both void and char pointers; converting a
correctly aligned pointer to and from a char pointer is guaranteed to
work.

Yeah, this was a confusion on my part...I didn't really know that,
at least in a useful sense...
 
B

Bill Reid

Barry Schwarz said:
No, it is a diagnostic. The fact that your compiler calls it a
warning is irrelevant. The error causes undefined behavior.


If you haven't compiled the code, then how do you get what you expect?
Ummmm, let's think about this...maybe because I actually compiled
and linked two separate "translation units" with "extern" declarations
and typedefs in a what's called a "header file"? Maybe I just cut and
pasted that code into what appears to be a single source code file,
and just forgot to paste in the include stdio.h define? Naaahhhh,
never happen...
It suffers from attempting to implement a really poor design. You go
through ridiculous contortions whose only apparent purpose is to avoid
using a pointer to struct.
To repeat myself:

"That is precisely the point, that is precisely the point."

Hey, why not take it a step further; I'm actually going through the
ridiculous contortions to avoid using a pointer to an ARRAY. After
all, why couldn't I just declare four separate arrays, with four
separate variables for the number of elements in the arrays?
What the hell is the matter with me, anyway?
A little horizontal white space would make your code a lot more
readable.

Another egregious personality flaw, perhaps requiring "professional"
help...
The use of a global struct is another unnecessary design flaw.
Well, you didn't actually make me laugh here, but you did make
me smile...
If you change the first parameter to VALUES *struct_ptr, you can
eliminate the third parameter ..
Actually, thinking about it even more, I know realize that I could
have just done it like this:

double number_4_1=1.0;
double number_4_2=1.0;
double number_4_3=1.0;

double add_double_numbers_4(void) {
return (number_4_1+number_4_2+number_4_3);
}

OK, call off the hunt, we have a winner!!!
and all three of these ...
Yeah, I've got you beat, but you helped!
and both of these ...
Mine's much better, but again, you helped!
and change the second expression to struct_ptr->value_4


and replace this with a simple
struct_ptr++;
Well, now I FINALLY know how to increment through an array of
structs...
Since your value is a double, why not use %lf and avail yourself of
the extra precision.
Oh, I guess I just thought it was overkill for adding 1.0+1.0+1.0...but
you never know, tomorrow, I might try to add 2.0+2.0+2.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

No members online now.

Forum statistics

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

Latest Threads

Top