first program with pointers to structures and realloc....

M

Martin Joergensen

Hi,

I wanted to try something which I think is a very good
exercise... I read in data from the keyboard and store them in a
structure. There's a pointer called "data_pointer" which I use to
keep track on the structures... But it's a bit confusing - my
program won't compile and I don't know what to do about the
warnings/error messages.

c:\documents and settings\dell\Desktop\test\main.c(5) : warning
C4115: 'tablevalues' : named type definition in parentheses

c:\documents and settings\dell\Desktop\test\main.c(41) : warning
C4133: 'function' : incompatible types - from 'tablevalues *' to
'tablevalues *'

c:\documents and settings\dell\Desktop\test\main.c(75) : error
C2065: 'tablevalues' : undeclared identifier

c:\documents and settings\dell\Desktop\test\main.c(80) : warning
C4133: '=' : incompatible types - from 'int *' to 'tablevalues *'

c:\documents and settings\dell\Desktop\test\main.c(84) : error
C2107: illegal index, indirection not allowed


- - - - - - - - - - - - - - - - - - - -
#include <stdlib.h>
#include <stdio.h>


void save_in_memory( struct tablevalues *data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);



/* ************ Main program ************ */

int main()
{

const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory;

double doub_x;
int va1, va2, va3;

typedef struct
{
double double_x;
int var1;
int var2;
int var3;
} tablevalues;

tablevalues *data_pointer;


number_of_sets_in_memory = 0; /* there's nothing stored at
this moment */

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d\n", &doub_x, &va1, &va2, &va3) ==
4)
{
save_in_memory( data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("Enter single double value, followed by 3
integers:\n");
}


exit(EXIT_SUCCESS);
}


/* ************ Function ************ */

void save_in_memory( struct tablevalues *data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* counter */
int *tmp_ptr;

/* update counter of how much is saved currently */
*number_of_sets_in_memory++;

/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* first allocate space for the pointer array */
if ((tmp_ptr = realloc(data_pointer,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(tablevalues) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* ??? possibly I need to copy the old values to the new
array ??? */
data_pointer = tmp_ptr;
}

/* Ok, enough memory available - now: store information in
structure-1 */
data_pointer[number_of_sets_in_memory-1]->double_x =
double_x;
data_pointer[number_of_sets_in_memory-1]->var1 = var1;
data_pointer[number_of_sets_in_memory-1]->var2 = var2;
data_pointer[number_of_sets_in_memory-1]->var3 = var3;
}

- - - - - - - - - - - - - - - - - - - -

If somebody can help me get the program to work, I would be glad.
Then I want to debug it and watch what happens different places
in memory... :)

One other thing on my mind is: Perhaps the "data_pointer"
variable should have been an ** pointer instead of a * pointer. I
actually got the whole program to compile and run using a **
pointer (without warnings/errors), but then I convinced myself
that a *-pointer would be just as good and I modified the whole
program... I would appreciate any comments on this...

Now I can't remember what I did when I got the program to work
using a **-data_pointer... :)


Best regards / Med venlig hilsen
Martin Jørgensen
 
C

Chris Torek

... my program won't compile and I don't know what to do about the
warnings/error messages.

c:\documents and settings\dell\Desktop\test\main.c(5) : warning
C4115: 'tablevalues' : named type definition in parentheses

GCC's complaint is perhaps slightly more useful:

warning: `struct tablevalues' declared inside parameter list
warning: its scope is only this definition or declaration,
which is probably not what you want.
c:\documents and settings\dell\Desktop\test\main.c(41) : warning
C4133: 'function' : incompatible types - from 'tablevalues *' to
'tablevalues *'

This particular complaint is just plain wrong, however. One of
the types is indeed "tablevalues *", but the other is "struct
tablevalues *". The difference is that the second one has the
keyword "struct" in front of it, announcing to the world (and the
human programmer) that this is in fact a type-name. The first
looks like an ordinary variable name; but if it were, it would be
a syntax error, so it must be an identifier defined via a type-alias.

[snippage - code fragment follows]
#include <stdlib.h>
#include <stdio.h>

void save_in_memory( struct tablevalues *data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);

Here is the first problem: "struct tablevalues" appears here in
parentheses. This is a "named type" (as per to your compiler).
But, as GCC says, the scope of this declaration -- that there
exists a type called "struct tablevalues" -- is limited to the
prototype declaration. Once the prototype ends, the type-name
vanishes, and can never be mentioned again.

This is where things get a little tricky. If you write:

void f(void) {
int x;
...
}
void g(void) {
int x;
...
}

we all understand that the x in f() is quite different from the
x in g(). Both are "int x" but they are not the same variable;
if g() calls into f(), both "x"s become active simultaneously.

This occurs because the two "x"s have different scope.

The same thing occurs with type-names. If you create a new
type:

struct zorg;

this type is name-able until its scope terminates. Then it has
gone out of scope and it can never be named again. Thus:

void f(void) {
struct zorg { int i; };
struct zorg a, b, x, y;
...
}
void g(void) {
struct zorg { double d; char *p; };
struct zorg a, b, x, y;
...
}

Not only are "a", "b", "x", and "y" different in f() and g(), so
are the two different "struct zorg"s! They are both named "struct
zorg", but they have different contents and are in fact different
types.

As with variables, it is usually a bad idea to make a type-name do
double duty in inner vs outer scopes. Consider the following code
fragment:

int bang;
void f(void) {
double bang;
...
}

Suppose I now say something beginning with: "when bang is negative".
Which "bang" am I talking about? Obviously I will have to be
more specific.

The same holds for type-names (which we define with the "struct"
keyword, as usual):

struct zorg { int i; };
void g(void) {
struct zorg { double d; char *p; };
...
}

In this case, if I say something about "when the d member of a zorg
is negative", it is clear enough that I mean the zorg in g(), and
not the outer zorg, because only inner-zorg has a "d". But it is
still not a great idea to allow for this kind of confusion.

The problem you have, at this point, is that you are trying to
declare the function save_in_memory(), complete with a prototype,
but the type "tablevalues" has not yet been declared. The solution
is simple: declare the type. Then the inner-scope name in the
prototype will refer to the *same* type that is already declared
in the outer scope. This is just like *not* declaring a new
"struct zorg" inside g(). So now we have:

struct tablevalues; /* This just declares the type. */

void save_in_memory(struct tablevalues *data_pointer,
unsigned int *number_of_sets_in_memory,
unsigned int increase_memory_number,
double double_x, int var1, int var2, int var3);

We proceed onward:
/* ************ Main program ************ */

[this comment is a little redundant :) ]
int main()
{

[better to write "int main(void) { ... }", so as to provide a
prototype along with the definition, but this is OK]
const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory;

[I prefer to write out "unsigned int", but this is also OK]
double doub_x;
int va1, va2, va3;

typedef struct
{
double double_x;
int var1;
int var2;
int var3;
} tablevalues;

Here we have a problem: this creates a new, unnamed type -- we
wrote "struct {" without using a tag -- and then "typedef"s an
alias for that unnamed type. (Remember, typedef *never* defines
a new type; it only makes aliases for existing types.) So this
produces a type-alias named "tablevalues" that is an alias for
a structure with no tag. But in fact, it is obvious to at least
one particular human -- though not to a computer -- that you
wanted to use the *same* structure type here as in your function
save_in_memory().

You could attempt to repair this by doing something like:

typedef struct tablevalues tablevalues;
struct tablevalues { ... contents ... };

(assuming you insist on using the useless "typedef", otherwise
you could omit that line entirely). But this is doomed to fail:
the syntax for "define a new type for me" is to write:

struct tagname_opt { ... contents ... };

so this means "define a *new*, inner-scope type named struct
tablevalues". The typedef-name "tablevalues" will now refer
to the old, outer-scope "struct tablevalues", while the new,
inner-scope "struct tablevalues" is a new and different type,
regardless of the "contents" part.

The only solution is to define (not just declare) the "struct
tablevalues" type *outside* main(). You can do this at any point
before main(), before or after the prototype for save_in_memory(),
as long as you at least *declare* the existence of the type before
defining the prototype.

Again, keep in mind the difference between a "declaration":

I declare that Santa Claus exists!

which does not actually describe anything, just say that it
exists, and a "definition":

He's the guy formerly known as Joe Shlabotnik. He had his
name legally changed last week.

which not only tells you that something exists, but gives you
all the particulars (which may be quite different from what you
were expecting :) ).

So now, with all that in mind, we can rewrite the code up to
this point:

#include ... /* do includes, as needed */

struct tablevalues;
[again this just declares that the type exists, so that
save_in_memory can refer back to it]

void save_in_memory(struct tablevalues *data_pointer,
unsigned int *number_of_sets_in_memory,
unsigned int increase_memory_number,
double double_x, int var1, int var2, int var3);

struct tablevalues {
double double_x;
int var1;
int var2;
int var3;
};
[this actually *defines* the type, so now we can use the .var1
member and so on]

int main(void) {
const unsigned int increase_memory_number = 3;
unsigned int number_of_sets_in_memory;
double doub_x;
int va1, va2, va3;
tablevalues *data_pointer;

Since I hate useless "typedef"s, I will just expand this one out:

struct tablevalues *data_pointer;
number_of_sets_in_memory = 0; /* there's nothing stored at
this moment */

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d\n", &doub_x, &va1, &va2, &va3) == 4)
{

[so far so good]
save_in_memory( data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);

Here we have a problem. On the first call to save_in_memory(),
what is the value stored in "data_pointer"? (Did you initialize
it above?) Moreover, you gave save_in_memory() the address of the
thing named "number_of_sets_in_memory", so that save_in_memory()
can change number_of_sets_in_memory, but you did not give
save_in_memory() the address of "data_pointer", so save_in_memory()
*cannot* change "data_pointer".

There are a number of different solutions to this second problem,
at least two of them perfectly reasonable and quite conventional:
either give save_in_memory() the address of data_pointer, or change
it to return a new value for data_pointer, as desired. (A third
solution is to package up several items -- at least the data pointer
and the count of items, and perhaps also the "increase_memory_number",
in yet another "struct" type and pass a pointer to an instance of
that type.)

The rest of the code I am not going to address too much, except
to note serious bugs and mention a technique I find quite useful:
void save_in_memory( struct tablevalues *data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{

Inside this function, you will refer, quite a few times
(7 times, to be exact) to "*number_of_sets_in_memory". I
find it works better, in such cases, to make a local copy
of *number_of_sets_in_memory, work with the local copy, and
put the final result back "somewhere appropriate" before
returning. ("Somewhere" can be "just at the end", or
"as soon as the new value is known", or whatever is most
convenient.)
unsigned i; /* counter */ ["i" is never used]
int *tmp_ptr;

tmp_ptr should have the same type as data_pointer (and if you change
the function to take a pointer to main()'s data_pointer, you can
use the same technique as for *number_of_sets_in_memory here).
/* update counter of how much is saved currently */
*number_of_sets_in_memory++;

The operators "*" and "++" bind ("have precedence", as some put
it, but "bind" is a better word) in a way other than what you want:
this means:

*(number_of_sets_in_memory)++;

but you want:

(*number_of_sets_in_memory)++;
/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* first allocate space for the pointer array */
if ((tmp_ptr = realloc(data_pointer,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(tablevalues) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* ??? possibly I need to copy the old values to the new
array ??? */
data_pointer = tmp_ptr;

You do not need to copy the values (realloc() has done that), but
you *do* need to inform main(), in some way or another, that its
old copy of "data_pointer" is out of date and it should use the
new one.
}

/* Ok, enough memory available - now: store information in
structure-1 */
data_pointer[number_of_sets_in_memory-1]->double_x =
double_x;
data_pointer[number_of_sets_in_memory-1]->var1 = var1;
data_pointer[number_of_sets_in_memory-1]->var2 = var2;
data_pointer[number_of_sets_in_memory-1]->var3 = var3;

If "data_pointer" is a pointer to the structure, then necessarily
"data_pointer" for some valid index i is an instance of the
structure -- so these should be "data_pointer.field = value".

In this particular case, I might be more likely return the new
data_pointer value from the modified function; but to illustrate
everything I just said, here is a version that takes a pointer to
the "data_pointer" to update, as well as a (separate) pointer to
the "number_of_sets_in_memory" to update:

void save_in_memory(
struct tablevalues **tpp,
unsigned int *nsaved, unsigned int nincrease,
double x, int var1, int var2, int var3
) {
struct tablevalues *tp = *tpp;
int old_n = *nsaved, new_n = old_n + 1;

/* expand the table if needed */
if ((new_n % nincrease) == 0) {
/*
* Note that we have a *copy* of *tpp here. If we trash
* our copy, so what? The original is still in *tpp.
*/
tp = realloc(tp, (old_n + nincrease) * sizeof *tp);
if (tp == NULL) {
/*
* Really should return some failure indication
* rather than just rudely exiting. (As a "low
* level function", we should not be making major
* strategic decisions -- like giving up entirely
* -- at this level: we should just report the
* problem, and let higher level code decide how
* to handle it.) But here save_in_memory() has
* no return value, so no obvious way to indicate
* failure.
*/
fprintf(stderr, "can't allocate memory");
exit(EXIT_FAILURE);
}
*tpp = tp; /* allocation succeeded - update caller's *tpp */
}

/*
* Here old_n is still unchanged, new_n is still old_n+1,
* and either (new_n % nincrease) was nonzero, meaning
* we had room in the table, or the table size has been
* increased successfully, meaning we now have room in
* the table. So just put in the values, and remember
* to update the caller's "number in memory" value too.
*/
tp[old_n].double_x = x;
tp[old_n].var1 = var1;
tp[old_n].var2 = var2;
tp[old_n].var3 = var3;
*nsaved = new_n; /* this can happen anywhere along here */
}

At the moment, the new version is much longer, but only because
of all the comments.
 
F

Flash Gordon

Martin said:
Hi,

I wanted to try something which I think is a very good exercise... I
read in data from the keyboard and store them in a structure. There's a
pointer called "data_pointer" which I use to keep track on the
structures... But it's a bit confusing - my program won't compile and I
don't know what to do about the warnings/error messages.

c:\documents and settings\dell\Desktop\test\main.c(5) : warning C4115:
'tablevalues' : named type definition in parentheses

c:\documents and settings\dell\Desktop\test\main.c(41) : warning C4133:
'function' : incompatible types - from 'tablevalues *' to 'tablevalues *'

c:\documents and settings\dell\Desktop\test\main.c(75) : error C2065:
'tablevalues' : undeclared identifier

c:\documents and settings\dell\Desktop\test\main.c(80) : warning C4133:
'=' : incompatible types - from 'int *' to 'tablevalues *'

c:\documents and settings\dell\Desktop\test\main.c(84) : error C2107:
illegal index, indirection not allowed


- - - - - - - - - - - - - - - - - - - -
#include <stdlib.h>
#include <stdio.h>

void save_in_memory( struct tablevalues *data_pointer,

You haven't yet told the compiler what type struct tablevalues is! In C
you should always declare things before using them.
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);



/* ************ Main program ************ */

int main()
{

const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory;

double doub_x;
int va1, va2, va3;

typedef struct
{
double double_x;
int var1;
int var2;
int var3;
} tablevalues;

Here you declare a local definition of a structure, but it has
absolutely nothing to do with anything outside of main. Also, this does
not define struct tablevalues, it defines an unnamed struct and gives it
an alias that is simply tablevalues.
tablevalues *data_pointer;


number_of_sets_in_memory = 0; /* there's nothing stored at this
moment */

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d\n", &doub_x, &va1, &va2, &va3) == 4)
{
save_in_memory( data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("Enter single double value, followed by 3 integers:\n");
}


exit(EXIT_SUCCESS);
}


/* ************ Function ************ */

void save_in_memory( struct tablevalues *data_pointer,

By here, your typedef has gone out of scope.
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* counter */
int *tmp_ptr;

/* update counter of how much is saved currently */
*number_of_sets_in_memory++;

/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* first allocate space for the pointer array */
if ((tmp_ptr = realloc(data_pointer,

The only data_pointer is a variable local to main, how do you expect
this function to see it?
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(tablevalues) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* ??? possibly I need to copy the old values to the new array
??? */

Read your C text book. It will tell you that realloc is defined as
handling that for you.
data_pointer = tmp_ptr;
}

/* Ok, enough memory available - now: store information in
structure-1 */
data_pointer[number_of_sets_in_memory-1]->double_x = double_x;
data_pointer[number_of_sets_in_memory-1]->var1 = var1;
data_pointer[number_of_sets_in_memory-1]->var2 = var2;
data_pointer[number_of_sets_in_memory-1]->var3 = var3;
}

- - - - - - - - - - - - - - - - - - - -

If somebody can help me get the program to work, I would be glad. Then I
want to debug it and watch what happens different places in memory... :)

One other thing on my mind is: Perhaps the "data_pointer" variable
should have been an ** pointer instead of a * pointer. I actually got
the whole program to compile and run using a ** pointer (without
warnings/errors), but then I convinced myself that a *-pointer would be
just as good and I modified the whole program... I would appreciate any
comments on this...

Now I can't remember what I did when I got the program to work using a
**-data_pointer... :)

Well, I'm sure there must have been more differences than you claim.

You need to read the early chapters of your C text book again. The ones
where it talks about scope, local variables, and defining things before
using them.
--
Flash Gordon, living in interesting times.
Web site - http://home.flash-gordon.me.uk/
comp.lang.c posting guidelines and intro:
http://clc-wiki.net/wiki/Intro_to_clc

Inviato da X-Privat.Org - Registrazione gratuita http://www.x-privat.org/join.php
 
B

Ben Pfaff

Chris Torek said:
This particular complaint is just plain wrong, however. One of
the types is indeed "tablevalues *", but the other is "struct
tablevalues *". The difference is that the second one has the
keyword "struct" in front of it, announcing to the world (and the
human programmer) that this is in fact a type-name. The first
looks like an ordinary variable name; but if it were, it would be
a syntax error, so it must be an identifier defined via a type-alias.

This makes me wonder whether a C++ compiler is being used. I
wouldn't expect a C compiler to confuse typedefs and structs in
its error messages.

(I didn't read the rest of the thread, so I don't know if there's
something else here that rules out that possibility.)
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Chris said:
The problem you have, at this point, is that you are trying to
declare the function save_in_memory(), complete with a prototype,
but the type "tablevalues" has not yet been declared. The solution

Dough! As Homer Simpson would have said it :)

I can easily see that now... You wrote a lot of pretty good comments and
I implemented them. I understood most of it, since it was written very
detailed and thanks a lot for that.

-snip-
[so far so good]

save_in_memory( data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);


Here we have a problem. On the first call to save_in_memory(),
what is the value stored in "data_pointer"? (Did you initialize
it above?) Moreover, you gave save_in_memory() the address of the

I agree it isn't initialized, I wanted it to be initialized inside the
function, when the function finds out that no memory is reserved and it
needs additional memory...

So I now realize that I should use the address-of-operator like
&data_pointer, so it can be initialized from within the function (also
so it can be changed, when it needs more memory)...
thing named "number_of_sets_in_memory", so that save_in_memory()
can change number_of_sets_in_memory, but you did not give
save_in_memory() the address of "data_pointer", so save_in_memory()
*cannot* change "data_pointer".

Exactly... I probably would have discovered this myself, if I at least
could get the program to compile :)
There are a number of different solutions to this second problem,
at least two of them perfectly reasonable and quite conventional:
either give save_in_memory() the address of data_pointer, or change
it to return a new value for data_pointer, as desired. (A third
solution is to package up several items -- at least the data pointer
and the count of items, and perhaps also the "increase_memory_number",
in yet another "struct" type and pass a pointer to an instance of
that type.)

I used the first method. I understand method number 2 too. But number 3
sounds complicated. However, is it possible to give a small example on that?
The rest of the code I am not going to address too much, except
to note serious bugs and mention a technique I find quite useful:




Inside this function, you will refer, quite a few times
(7 times, to be exact) to "*number_of_sets_in_memory". I
find it works better, in such cases, to make a local copy
of *number_of_sets_in_memory, work with the local copy, and
put the final result back "somewhere appropriate" before
returning. ("Somewhere" can be "just at the end", or
"as soon as the new value is known", or whatever is most
convenient.)

Ok, thanks. But why do you prefer to copy it, when you already got it?
Is it because the name is rather long and you prefer using a short
variable name such as "i" for that kind of things?

I hope I won't confuse myself too much, so I kept it until now but I
might change it later - based on your additional comments.
unsigned i; /* counter */

["i" is never used]

Thanks. Damn. I forgot why I wrote that...
tmp_ptr should have the same type as data_pointer (and if you change
the function to take a pointer to main()'s data_pointer, you can
use the same technique as for *number_of_sets_in_memory here).

Thanks. I did that.
>> /* update counter of how much is saved currently */
*number_of_sets_in_memory++;


The operators "*" and "++" bind ("have precedence", as some put
it, but "bind" is a better word) in a way other than what you want:
this means:

*(number_of_sets_in_memory)++;

but you want:

(*number_of_sets_in_memory)++;
Exactly.
/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* first allocate space for the pointer array */
if ((tmp_ptr = realloc(data_pointer,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(tablevalues) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* ??? possibly I need to copy the old values to the new
array ??? */
data_pointer = tmp_ptr;


You do not need to copy the values (realloc() has done that), but
you *do* need to inform main(), in some way or another, that its
old copy of "data_pointer" is out of date and it should use the
new one.
Done.
}

/* Ok, enough memory available - now: store information in
structure-1 */
data_pointer[number_of_sets_in_memory-1]->double_x =
double_x;
data_pointer[number_of_sets_in_memory-1]->var1 = var1;
data_pointer[number_of_sets_in_memory-1]->var2 = var2;
data_pointer[number_of_sets_in_memory-1]->var3 = var3;


If "data_pointer" is a pointer to the structure, then necessarily
"data_pointer" for some valid index i is an instance of the
structure -- so these should be "data_pointer.field = value".


Damn... I couldn't get this part to work... I get "illegal index,
indirection not allowed". Perhaps I'm not allowed to subtract something
inside the index?


In this particular case, I might be more likely return the new
data_pointer value from the modified function; but to illustrate
everything I just said, here is a version that takes a pointer to
the "data_pointer" to update, as well as a (separate) pointer to
the "number_of_sets_in_memory" to update:

void save_in_memory(
struct tablevalues **tpp,
unsigned int *nsaved, unsigned int nincrease,
double x, int var1, int var2, int var3
) {
struct tablevalues *tp = *tpp;
int old_n = *nsaved, new_n = old_n + 1;

/* expand the table if needed */
if ((new_n % nincrease) == 0) {
/*
* Note that we have a *copy* of *tpp here. If we trash
* our copy, so what? The original is still in *tpp.
*/
tp = realloc(tp, (old_n + nincrease) * sizeof *tp);
if (tp == NULL) {
/*
* Really should return some failure indication
* rather than just rudely exiting. (As a "low
* level function", we should not be making major
* strategic decisions -- like giving up entirely
* -- at this level: we should just report the
* problem, and let higher level code decide how
* to handle it.) But here save_in_memory() has
* no return value, so no obvious way to indicate
* failure.
*/
fprintf(stderr, "can't allocate memory");
exit(EXIT_FAILURE);
}
*tpp = tp; /* allocation succeeded - update caller's *tpp */
}

/*
* Here old_n is still unchanged, new_n is still old_n+1,
* and either (new_n % nincrease) was nonzero, meaning
* we had room in the table, or the table size has been
* increased successfully, meaning we now have room in
* the table. So just put in the values, and remember
* to update the caller's "number in memory" value too.
*/
tp[old_n].double_x = x;
tp[old_n].var1 = var1;
tp[old_n].var2 = var2;
tp[old_n].var3 = var3;
*nsaved = new_n; /* this can happen anywhere along here */
}

At the moment, the new version is much longer, but only because
of all the comments.

Wow... You must be a mind-reader, since you knew I got into problems
with the "tp[old_n].var3 = var3;" lines. I eliminated all errors based
on your comments, except these 4 errors.

I copy/pasted your code and now the program works! I'll just have to add
some code that outputs results to screen so I'll probably write have
some comments or questions to the new function you helped me with....

I didn't understood why my function didn't work with the lines
*data_pointer[*number_of_sets_in_memory-1].var1 = var1; etc, but I'll
look at it later and post a new reply - only about this last function
"void save_in_memory()"-stuff...


Best regards / Med venlig hilsen
Martin Jørgensen
 
M

Martin Joergensen

Chris Torek said:
... my program won't compile and I don't know what to do about
the
warnings/error messages.

c:\documents and settings\dell\Desktop\test\main.c(5) : warning
C4115: 'tablevalues' : named type definition in parentheses

GCC's complaint is perhaps slightly more useful:

warning: `struct tablevalues' declared inside parameter list
warning: its scope is only this definition or declaration,
which is probably not what you want.
c:\documents and settings\dell\Desktop\test\main.c(41) :
warning
C4133: 'function' : incompatible types - from 'tablevalues *'
to
'tablevalues *'

This particular complaint is just plain wrong, however. One of
the types is indeed "tablevalues *", but the other is "struct
tablevalues *". The difference is that the second one has the
keyword "struct" in front of it, announcing to the world (and
the
human programmer) that this is in fact a type-name. The first
looks like an ordinary variable name; but if it were, it would
be
a syntax error, so it must be an identifier defined via a
type-alias.

[snippage - code fragment follows]
#include <stdlib.h>
#include <stdio.h>

void save_in_memory( struct tablevalues *data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);

Here is the first problem: "struct tablevalues" appears here in
parentheses. This is a "named type" (as per to your compiler).
But, as GCC says, the scope of this declaration -- that there
exists a type called "struct tablevalues" -- is limited to the
prototype declaration. Once the prototype ends, the type-name
vanishes, and can never be mentioned again.

This is where things get a little tricky. If you write:

void f(void) {
int x;
...
}
void g(void) {
int x;
...
}

we all understand that the x in f() is quite different from the
x in g(). Both are "int x" but they are not the same variable;
if g() calls into f(), both "x"s become active simultaneously.

This occurs because the two "x"s have different scope.

The same thing occurs with type-names. If you create a new
type:

struct zorg;

this type is name-able until its scope terminates. Then it has
gone out of scope and it can never be named again. Thus:

void f(void) {
struct zorg { int i; };
struct zorg a, b, x, y;
...
}
void g(void) {
struct zorg { double d; char *p; };
struct zorg a, b, x, y;
...
}

Not only are "a", "b", "x", and "y" different in f() and g(),
so
are the two different "struct zorg"s! They are both named
"struct
zorg", but they have different contents and are in fact
different
types.

As with variables, it is usually a bad idea to make a type-name
do
double duty in inner vs outer scopes. Consider the following
code
fragment:

int bang;
void f(void) {
double bang;
...
}

Suppose I now say something beginning with: "when bang is
negative".
Which "bang" am I talking about? Obviously I will have to be
more specific.

The same holds for type-names (which we define with the
"struct"
keyword, as usual):

struct zorg { int i; };
void g(void) {
struct zorg { double d; char *p; };
...
}

In this case, if I say something about "when the d member of a
zorg
is negative", it is clear enough that I mean the zorg in g(),
and
not the outer zorg, because only inner-zorg has a "d". But it
is
still not a great idea to allow for this kind of confusion.

The problem you have, at this point, is that you are trying to
declare the function save_in_memory(), complete with a
prototype,
but the type "tablevalues" has not yet been declared. The
solution
is simple: declare the type. Then the inner-scope name in the
prototype will refer to the *same* type that is already
declared
in the outer scope. This is just like *not* declaring a new
"struct zorg" inside g(). So now we have:

struct tablevalues; /* This just declares the type. */

void save_in_memory(struct tablevalues *data_pointer,
unsigned int *number_of_sets_in_memory,
unsigned int increase_memory_number,
double double_x, int var1, int var2, int var3);

We proceed onward:
/* ************ Main program ************ */

[this comment is a little redundant :) ]
int main()
{

[better to write "int main(void) { ... }", so as to provide a
prototype along with the definition, but this is OK]
const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory;

[I prefer to write out "unsigned int", but this is also OK]
double doub_x;
int va1, va2, va3;

typedef struct
{
double double_x;
int var1;
int var2;
int var3;
} tablevalues;

Here we have a problem: this creates a new, unnamed type -- we
wrote "struct {" without using a tag -- and then "typedef"s an
alias for that unnamed type. (Remember, typedef *never*
defines
a new type; it only makes aliases for existing types.) So this
produces a type-alias named "tablevalues" that is an alias for
a structure with no tag. But in fact, it is obvious to at
least
one particular human -- though not to a computer -- that you
wanted to use the *same* structure type here as in your
function
save_in_memory().

You could attempt to repair this by doing something like:

typedef struct tablevalues tablevalues;
struct tablevalues { ... contents ... };

(assuming you insist on using the useless "typedef", otherwise
you could omit that line entirely). But this is doomed to
fail:
the syntax for "define a new type for me" is to write:

struct tagname_opt { ... contents ... };

so this means "define a *new*, inner-scope type named struct
tablevalues". The typedef-name "tablevalues" will now refer
to the old, outer-scope "struct tablevalues", while the new,
inner-scope "struct tablevalues" is a new and different type,
regardless of the "contents" part.

The only solution is to define (not just declare) the "struct
tablevalues" type *outside* main(). You can do this at any
point
before main(), before or after the prototype for
save_in_memory(),
as long as you at least *declare* the existence of the type
before
defining the prototype.

Again, keep in mind the difference between a "declaration":

I declare that Santa Claus exists!

which does not actually describe anything, just say that it
exists, and a "definition":

He's the guy formerly known as Joe Shlabotnik. He had his
name legally changed last week.

which not only tells you that something exists, but gives you
all the particulars (which may be quite different from what you
were expecting :) ).

So now, with all that in mind, we can rewrite the code up to
this point:

#include ... /* do includes, as needed */

struct tablevalues;
[again this just declares that the type exists, so that
save_in_memory can refer back to it]

void save_in_memory(struct tablevalues *data_pointer,
unsigned int *number_of_sets_in_memory,
unsigned int increase_memory_number,
double double_x, int var1, int var2, int var3);

struct tablevalues {
double double_x;
int var1;
int var2;
int var3;
};
[this actually *defines* the type, so now we can use the .var1
member and so on]

int main(void) {
const unsigned int increase_memory_number = 3;
unsigned int number_of_sets_in_memory;
double doub_x;
int va1, va2, va3;
tablevalues *data_pointer;

Since I hate useless "typedef"s, I will just expand this one
out:

struct tablevalues *data_pointer;
number_of_sets_in_memory = 0; /* there's nothing stored at
this moment */

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d\n", &doub_x, &va1, &va2, &va3)
== 4)
{

[so far so good]
save_in_memory( data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);

Here we have a problem. On the first call to save_in_memory(),
what is the value stored in "data_pointer"? (Did you
initialize
it above?) Moreover, you gave save_in_memory() the address of
the
thing named "number_of_sets_in_memory", so that
save_in_memory()
can change number_of_sets_in_memory, but you did not give
save_in_memory() the address of "data_pointer", so
save_in_memory()
*cannot* change "data_pointer".

There are a number of different solutions to this second
problem,
at least two of them perfectly reasonable and quite
conventional:
either give save_in_memory() the address of data_pointer, or
change
it to return a new value for data_pointer, as desired. (A
third
solution is to package up several items -- at least the data
pointer
and the count of items, and perhaps also the
"increase_memory_number",
in yet another "struct" type and pass a pointer to an instance
of
that type.)

The rest of the code I am not going to address too much, except
to note serious bugs and mention a technique I find quite
useful:
void save_in_memory( struct tablevalues *data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{

Inside this function, you will refer, quite a few times
(7 times, to be exact) to "*number_of_sets_in_memory". I
find it works better, in such cases, to make a local copy
of *number_of_sets_in_memory, work with the local copy, and
put the final result back "somewhere appropriate" before
returning. ("Somewhere" can be "just at the end", or
"as soon as the new value is known", or whatever is most
convenient.)
unsigned i; /* counter */ ["i" is never used]
int *tmp_ptr;

tmp_ptr should have the same type as data_pointer (and if you
change
the function to take a pointer to main()'s data_pointer, you
can
use the same technique as for *number_of_sets_in_memory here).
/* update counter of how much is saved currently */
*number_of_sets_in_memory++;

The operators "*" and "++" bind ("have precedence", as some put
it, but "bind" is a better word) in a way other than what you
want:
this means:

*(number_of_sets_in_memory)++;

but you want:

(*number_of_sets_in_memory)++;
/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* first allocate space for the pointer array */
if ((tmp_ptr = realloc(data_pointer,
(*number_of_sets_in_memory +
increase_memory_number)
* sizeof(tablevalues) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* ??? possibly I need to copy the old values to the
new
array ??? */
data_pointer = tmp_ptr;

You do not need to copy the values (realloc() has done that),
but
you *do* need to inform main(), in some way or another, that
its
old copy of "data_pointer" is out of date and it should use the
new one.
}

/* Ok, enough memory available - now: store information in
structure-1 */
data_pointer[number_of_sets_in_memory-1]->double_x =
double_x;
data_pointer[number_of_sets_in_memory-1]->var1 = var1;
data_pointer[number_of_sets_in_memory-1]->var2 = var2;
data_pointer[number_of_sets_in_memory-1]->var3 = var3;

If "data_pointer" is a pointer to the structure, then
necessarily
"data_pointer" for some valid index i is an instance of the
structure -- so these should be "data_pointer.field =
value".

In this particular case, I might be more likely return the new
data_pointer value from the modified function; but to
illustrate
everything I just said, here is a version that takes a pointer
to
the "data_pointer" to update, as well as a (separate) pointer
to
the "number_of_sets_in_memory" to update:

void save_in_memory(
struct tablevalues **tpp,
unsigned int *nsaved, unsigned int nincrease,
double x, int var1, int var2, int var3
) {
struct tablevalues *tp = *tpp;
int old_n = *nsaved, new_n = old_n + 1;

/* expand the table if needed */
if ((new_n % nincrease) == 0) {
/*
* Note that we have a *copy* of *tpp here. If we
trash
* our copy, so what? The original is still in
*tpp.
*/
tp = realloc(tp, (old_n + nincrease) * sizeof *tp);
if (tp == NULL) {
/*
* Really should return some failure indication
* rather than just rudely exiting. (As a "low
* level function", we should not be making
major
* strategic decisions -- like giving up
entirely
* -- at this level: we should just report the
* problem, and let higher level code decide how
* to handle it.) But here save_in_memory() has
* no return value, so no obvious way to
indicate
* failure.
*/
fprintf(stderr, "can't allocate memory");
exit(EXIT_FAILURE);
}
*tpp = tp; /* allocation succeeded - update caller's
*tpp */
}

/*
* Here old_n is still unchanged, new_n is still
old_n+1,
* and either (new_n % nincrease) was nonzero, meaning
* we had room in the table, or the table size has been
* increased successfully, meaning we now have room in
* the table. So just put in the values, and remember
* to update the caller's "number in memory" value too.
*/
tp[old_n].double_x = x;
tp[old_n].var1 = var1;
tp[old_n].var2 = var2;
tp[old_n].var3 = var3;
*nsaved = new_n; /* this can happen anywhere along here
*/
}

At the moment, the new version is much longer, but only because
of all the comments.


#include <stdlib.h>
#include <stdio.h>


/* declaring tablevalue as a structure */
struct tablevalues;

/* declaring a prototype */
void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);

/* defining a data-structure */
struct tablevalues
{
double double_x;
int var1;
int var2;
int var3;
};


/* ************ Main program ************ */

int main(void)
{

const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory, i;

double doub_x;
int va1, va2, va3;

struct tablevalues *data_pointer;

/* there's nothing stored at this moment */
number_of_sets_in_memory = 0;

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d", &doub_x, &va1, &va2, &va3) == 4)
{
save_in_memory( &data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("\nEnter double value, followed by 3
integers:\n");
}

/* print the results to the screen */
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}

exit(EXIT_SUCCESS);
}


/* ************ Function ************ */

void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* index */
struct tablevalues *tmp_ptr;

tmp_ptr = *data_pointer;

/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* allocate space for data structures */
if ( (tmp_ptr = realloc(tmp_ptr,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(**data_pointer) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* success: update pointer to data tables */
*data_pointer = tmp_ptr;
}

/* fill values into the current data structure */
i = *(number_of_sets_in_memory);

/* first two values stored using "->" pointer notation */
data_pointer->double_x = double_x;
data_pointer->var1 = var1;

/* "normal" notation */
(*(data_pointer)).var2 = var2;
(*(data_pointer)).var3 = var3;


/* update counter of how much is saved currently */
(*number_of_sets_in_memory)++;
}
 
M

Martin Joergensen

Chris Torek said:
At the moment, the new version is much longer, but only because
of all the comments.

Damn... I tried your suggestion earlier and it worked.... Then I
made some changes because I wanted to make my own (the original
function) work.... It has compiled a couple of times, but with an
error about uninitialized data_pointer...

Now neither mine or your function works.... I can't spot the
error, but it's probably just a small one.... The complete
program:

- - - - - - - -
#include <stdlib.h>
#include <stdio.h>


/* declaring tablevalue as a structure */
struct tablevalues;

/* declaring a prototype */
void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);

/* defining a data-structure */
struct tablevalues
{
double double_x;
int var1;
int var2;
int var3;
};


/* ************ Main program ************ */

int main(void)
{

const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory, i;

double doub_x;
int va1, va2, va3;

struct tablevalues *data_pointer;

/* there's nothing stored at this moment */
number_of_sets_in_memory = 0;

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d", &doub_x, &va1, &va2, &va3) == 4)
{
save_in_memory( &data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("\nEnter double value, followed by 3
integers:\n");
}

/* print the results to the screen */
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}

exit(EXIT_SUCCESS);
}


/* ************ Function ************ */

void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* index */
struct tablevalues *tmp_ptr;

tmp_ptr = *data_pointer;

/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* allocate space for data structures */
if ( (tmp_ptr = realloc(tmp_ptr,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(**data_pointer) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* success: update pointer to data tables */
*data_pointer = tmp_ptr;
}

/* fill values into the current data structure */
i = *(number_of_sets_in_memory);

/* first two values stored using "->" pointer notation */
data_pointer->double_x = double_x;
data_pointer->var1 = var1;

/* "normal" notation */
(*(data_pointer)).var2 = var2;
(*(data_pointer)).var3 = var3;


/* update counter of how much is saved currently */
(*number_of_sets_in_memory)++;
}
- - - - - -

It's probably just a small error somewhere...


Best regards / Med venlig hilsen
Martin Jørgensen
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Ben said:
This makes me wonder whether a C++ compiler is being used. I
wouldn't expect a C compiler to confuse typedefs and structs in
its error messages.

(I didn't read the rest of the thread, so I don't know if there's
something else here that rules out that possibility.)

I used Microsoft Visual studio 2005, but in "properties" I've chosen
"Compile as C code", so I believe it's microsoft who made a mistake and
not me if it thinks it's C++ code. At least, it *should* know better, if
it thinks its C++...


Best regards / Med venlig hilsen
Martin Jørgensen
 
R

Richard Heathfield

Martin Joergensen said:
.... It has compiled a couple of times, but with an
error about uninitialized data_pointer...

Indeed. That is a significant clue.
Now neither mine or your function works.... I can't spot the
error, but it's probably just a small one....

Small, but perfectly formed! :)
The complete program:

....somewhat redacted to save space...


I hereby name the following line POINT A, for reasons which will become
apparent.
struct tablevalues *data_pointer;

....defines a pointer that can point to a struct tablevalues object (but
which does not currently do so)...
/* there's nothing stored at this moment */

You got that right! :)

save_in_memory( &data_pointer,

Passing the address of a pointer that doesn't point anywhere...

/* ************ Function ************ */

void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* index */
struct tablevalues *tmp_ptr;

tmp_ptr = *data_pointer;

Ping! You just assigned (to tmp_ptr) the value of the object pointed to by
data_pointer. But no such value has been assigned, so the value is
indeterminate. From this point on, the behaviour of the program is
undefined.

The solution is to initialise the pointer when you define it (see above:
POINT A), like this:

struct tablevalues *data_pointer = NULL;

The pointer still doesn't point to an object, but at least it has a
determinate value.
/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* allocate space for data structures */
if ( (tmp_ptr = realloc(tmp_ptr,

This is why you were blowing up. You were handing random junk to realloc,
but realloc requires as its first argument /either/ a null pointer /or/ a
pointer to memory currently allocated via the memory management system
(malloc/calloc/realloc). The fix I indicated above will sort out that
problem for you.
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Richard said:
Martin Joergensen said: -snip-



Ping! You just assigned (to tmp_ptr) the value of the object pointed to by
data_pointer. But no such value has been assigned, so the value is
indeterminate. From this point on, the behaviour of the program is
undefined.

The solution is to initialise the pointer when you define it (see above:
POINT A), like this:

struct tablevalues *data_pointer = NULL;

The pointer still doesn't point to an object, but at least it has a
determinate value.

Ok, I thought it didn't matter which value it had (only the type was
important), since it should be changed anyway.... I guess I was wrong.
This is why you were blowing up. You were handing random junk to realloc,
but realloc requires as its first argument /either/ a null pointer /or/ a
pointer to memory currently allocated via the memory management system
(malloc/calloc/realloc). The fix I indicated above will sort out that
problem for you.

Thanks.... But there's still one small problem left and I can't
understand it... The program works fine the first time, but not the
second time.

So I made some different methods which I thought stored the read-in values:

/* first two values stored using "->" pointer notation */
data_pointer->double_x = double_x;
data_pointer->var1 = var1;

/* "normal" notation */
(*(data_pointer)).var2 = var2;
(*(data_pointer)).var3 = var3;


As long as i=0, it works. When i=1 it doesn't. But I'm pretty sure I
allocated space enough for the first 3 structures:

data_pointer[0]
data_pointer[1]
data_pointer[2]

And when i=3 it'll reserve room for the next 3:

data_pointer[3]
data_pointer[4]
data_pointer[5]

etc...

I tested Chris Torek' suggestion for a save_in_memory()-function and
strangely enough that works even though I think I made the function
almost identically to his when you take an extra level of indirection
into account...

I tried different methods, but no matter what I did the compiler
complained or the problem didn't get solved for i=1. How can that be?


Best regards / Med venlig hilsen
Martin Jørgensen
 
R

Richard Heathfield

Martin Jørgensen said:
Ok, I thought it didn't matter which value it had (only the type was
important), since it should be changed anyway.... I guess I was wrong.

Yes - you were wrong (sorry but there it is) because you *used* the value -
twice, in fact - before you made any attempt to write a value into it.
Thanks.... But there's still one small problem left and I can't
understand it... The program works fine the first time, but not the
second time.

If I get sufficient time later on today I'll take a closer look; in the
meantime, hopefully someone will beat me to it.
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Richard said:
Martin Jørgensen said: -snip-



Yes - you were wrong (sorry but there it is) because you *used* the value -
twice, in fact - before you made any attempt to write a value into it.




If I get sufficient time later on today I'll take a closer look; in the
meantime, hopefully someone will beat me to it.

Ok, thanks. For anyone who didn't read the thread carefully, you don't
have to. You can just jump in and copy/paste the complete program code
posted in message-id <and change:

struct tablevalues *data_pointer = NULL;

No compiler errors/warnings. The program works fine the first time but
not the second. I don't get it...


Best regards / Med venlig hilsen
Martin Jørgensen
 
T

TJW

Martin Jørgensen said:
Thanks.... But there's still one small problem left and I can't understand
it... The program works fine the first time, but not the second time.

So I made some different methods which I thought stored the read-in values:

/* first two values stored using "->" pointer notation */
data_pointer->double_x = double_x;
data_pointer->var1 = var1;

Problem 1: This dereferences the i_th element of data_pointer, which
is not a pointer to your structure. Change this to what you call
"normal notation"
((*data_pointer)).double_x = double_x;
((*data_pointer)).var1 = var1;

/* "normal" notation */
(*(data_pointer)).var2 = var2;
(*(data_pointer)).var3 = var3;

Problem 2: data_pointer is a pointer to a pointer to a
struct. You want to find the i_th element of the thing pointed to
by data_pointer, so you should move the dereference operator inside
the parens as
((*data_pointer)).var2 = var2;
...

Good Luck,
-TJW
 
M

Martin Joergensen

TJW said:
Good Luck,
-TJW

Thanks it works!

Next thing to do is that I'll try to make the program sort the
data (using bubblesort) in order, by looking at the double-value
inside the structure.... After that I consider myself to have
learned enough for now...

I think the best way would be only to sort pointers to the data
instead of moving or copying everything.... But I don't know how
complex this will be.... If I get into too much trouble, I'll
start a new thread for that.

If anyone has any suggestions for that though, I'll still read
any replies to this thread...


Best regards / Med venlig hilsen
Martin Jørgensen
 
M

Martin Joergensen

Martin Joergensen said:
Thanks it works!

Next thing to do is that I'll try to make the program sort the
data (using bubblesort) in order, by looking at the
double-value inside the structure.... After that I consider
myself to have learned enough for now...

Oh, what the hell. I don't think I need to make another thread
for such a small issue as how to dereference my structure
correctly.......

I only need to get rid of this last compiler error I think. Then
I think I'm done, having spent a couple of hours on implementing
this bubble-sort into my code (everything else should work) -
after that I'll play with it in/using my debugger:

error C2088: '[' : illegal for struct

It's the only compiler error I get - but I get it many times. :)

The complete code is shown below. As soon as the compiler error
is gone, I actually think there's a pretty good chance that the
program works "rigth-out-of-the-box" :)

- - - - - - - - - - - - - - - - - - - - - - - -
#include <stdlib.h>
#include <stdio.h>


/* declaring tablevalue as a structure */
struct tablevalues;

/* declaring a prototype */
void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);

/* defining a data-structure */
struct tablevalues
{
double double_x;
int var1;
int var2;
int var3;
};

void bubblesort(struct tablevalues *data_pointer,
int number_of_sets_in_memory);


/* ************ Main program ************ */

int main(void)
{
const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory, i;

double doub_x;
int va1, va2, va3;

struct tablevalues *data_pointer;

data_pointer = NULL;

/* there's nothing stored at this moment */
number_of_sets_in_memory = 0;

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d", &doub_x, &va1, &va2, &va3) == 4)
{
save_in_memory( &data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("\nEnter double value, followed by 3
integers:\n");
}

/* print the results to the screen, UNsorted */

printf("\n\nUnsorted list:\n--------------\n");
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}


/* sort data */
bubblesort(data_pointer, number_of_sets_in_memory);

/* print the results to the screen, SORTED */
printf("\n\nSorted list:\n------------\n");
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}

exit(EXIT_SUCCESS);
}


//////////////////////////////////////////////

void bubblesort(struct tablevalues *data_pointer,
int number_of_sets_in_memory)
{
int i, imax, j;

double save_x;
int save1, save2, save3;

/* this sorts data from smallest to biggest */

do
{
for(j=0, i=0; i<number_of_sets_in_memory-1; i++)
{

/* swap data only if data > data[i+1] */

if( ((*data_pointer)[i+1]).double_x <
((*data_pointer)).double_x )
{
/* save data values at position i */
save_x = ((*data_pointer)).double_x;
save1 = ((*data_pointer)).var1;
save2 = ((*data_pointer)).var2;
save3 = ((*data_pointer)).var3;

/* move data from position i+1 to position i */
((*data_pointer)).double_x =
((*data_pointer)[i+1]).double_x;
((*data_pointer)).var1 =
((*data_pointer)[i+1]).var1;
((*data_pointer)).var2 =
((*data_pointer)[i+1]).var2;
((*data_pointer)).var3 =
((*data_pointer)[i+1]).var3;

/* restore data -> move from position i to i+1 */
((*data_pointer)[i+1]).double_x = save_x;
((*data_pointer)[i+1]).var1 = save1;
((*data_pointer)[i+1]).var2 = save2;
((*data_pointer)[i+1]).var3 = save3;

/* save the index of the last sorted item */
j = i;
}
}
imax = j; /* notice that imax keeps getting smaller as
more and more is sorted */
}
while(imax > 0); /* when imax = 0, everything is sorted */
}

//////////////////////////////////////////////

void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* index */
struct tablevalues *tmp_ptr;

tmp_ptr = *data_pointer;

/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* allocate space for data structures */
if ( (tmp_ptr = realloc(tmp_ptr,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(**data_pointer) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* success: update pointer to data tables */
*data_pointer = tmp_ptr;
}

/* fill values into the current data structure */
i = *(number_of_sets_in_memory);

/* data_pointer is a pointer to at pointer to a struct */
((*data_pointer)).double_x = double_x;
((*data_pointer)).var1 = var1;
((*data_pointer)).var2 = var2;
((*data_pointer)).var3 = var3;

/* update counter of how much is saved currently */
(*number_of_sets_in_memory)++;
}

- - - - - - - - - - - - - - - - - - - - - - - -


Best regards / Med venlig hilsen
Martin Jørgensen
 
B

Barry Schwarz

Oh, what the hell. I don't think I need to make another thread
for such a small issue as how to dereference my structure
correctly.......

I only need to get rid of this last compiler error I think. Then
I think I'm done, having spent a couple of hours on implementing
this bubble-sort into my code (everything else should work) -
after that I'll play with it in/using my debugger:

error C2088: '[' : illegal for struct

It's the only compiler error I get - but I get it many times. :)

The complete code is shown below. As soon as the compiler error
is gone, I actually think there's a pretty good chance that the
program works "rigth-out-of-the-box" :)

- - - - - - - - - - - - - - - - - - - - - - - -
#include <stdlib.h>
#include <stdio.h>


/* declaring tablevalue as a structure */
struct tablevalues;

/* declaring a prototype */
void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3);

/* defining a data-structure */
struct tablevalues
{
double double_x;
int var1;
int var2;
int var3;
};

If you move this above the prototype, you could eliminate the
incomplete structure declaration.
void bubblesort(struct tablevalues *data_pointer,
int number_of_sets_in_memory);

Why is number_of_sets_in_memory an int here but an unsigned int in all
your other functions?
/* ************ Main program ************ */

int main(void)
{
const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory, i;

double doub_x;
int va1, va2, va3;

struct tablevalues *data_pointer;

data_pointer = NULL;

/* there's nothing stored at this moment */
number_of_sets_in_memory = 0;

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d", &doub_x, &va1, &va2, &va3) == 4)
{
save_in_memory( &data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("\nEnter double value, followed by 3
integers:\n");
}

/* print the results to the screen, UNsorted */

printf("\n\nUnsorted list:\n--------------\n");
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}


/* sort data */
bubblesort(data_pointer, number_of_sets_in_memory);

/* print the results to the screen, SORTED */
printf("\n\nSorted list:\n------------\n");
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}

exit(EXIT_SUCCESS);
}


//////////////////////////////////////////////

void bubblesort(struct tablevalues *data_pointer,
int number_of_sets_in_memory)
{
int i, imax, j;

double save_x;
int save1, save2, save3;

/* this sorts data from smallest to biggest */

do
{
for(j=0, i=0; i<number_of_sets_in_memory-1; i++)
{

/* swap data only if data > data[i+1] */

if( ((*data_pointer)[i+1]).double_x <
((*data_pointer)).double_x )


data_pointer is a pointer to struct. Therefore (*data_pointer) is the
actual struct pointed to. You are not allowed to use the subscript
operator ([]) on a struct, only on an array or pointer. Since you
didn't use it in any of the previous code, what did you think was
different here?

It looks like you copied the code from save_in_memory but didn't take
into account that data_pointer is a pointer to pointer to struct there
and only a pointer to struct here.
{
/* save data values at position i */
save_x = ((*data_pointer)).double_x;
save1 = ((*data_pointer)).var1;
save2 = ((*data_pointer)).var2;
save3 = ((*data_pointer)).var3;

/* move data from position i+1 to position i */
((*data_pointer)).double_x =
((*data_pointer)[i+1]).double_x;
((*data_pointer)).var1 =
((*data_pointer)[i+1]).var1;
((*data_pointer)).var2 =
((*data_pointer)[i+1]).var2;
((*data_pointer)).var3 =
((*data_pointer)[i+1]).var3;

/* restore data -> move from position i to i+1 */
((*data_pointer)[i+1]).double_x = save_x;
((*data_pointer)[i+1]).var1 = save1;
((*data_pointer)[i+1]).var2 = save2;
((*data_pointer)[i+1]).var3 = save3;

/* save the index of the last sorted item */
j = i;
}
}
imax = j; /* notice that imax keeps getting smaller as
more and more is sorted */
}
while(imax > 0); /* when imax = 0, everything is sorted */
}

//////////////////////////////////////////////

void save_in_memory( struct tablevalues **data_pointer,
unsigned *number_of_sets_in_memory,
unsigned increase_memory_number,
double double_x, int var1,
int var2, int var3)
{
unsigned i; /* index */
struct tablevalues *tmp_ptr;

tmp_ptr = *data_pointer;

/* need to allocate more memory ? */
if(*number_of_sets_in_memory % increase_memory_number == 0)
{

/* allocate space for data structures */
if ( (tmp_ptr = realloc(tmp_ptr,
(*number_of_sets_in_memory + increase_memory_number)
* sizeof(**data_pointer) )) == NULL) {
printf("Memory failure!\n\n");
exit(EXIT_FAILURE);
}
/* success: update pointer to data tables */
*data_pointer = tmp_ptr;
}

/* fill values into the current data structure */
i = *(number_of_sets_in_memory);

/* data_pointer is a pointer to at pointer to a struct */
((*data_pointer)).double_x = double_x;
((*data_pointer)).var1 = var1;
((*data_pointer)).var2 = var2;
((*data_pointer)).var3 = var3;

/* update counter of how much is saved currently */
(*number_of_sets_in_memory)++;
}



Remove del for email
 
?

=?ISO-8859-1?Q?Martin_J=F8rgensen?=

Barry said:
On Mon, 1 May 2006 22:07:35 +0200, "Martin Joergensen"



If you move this above the prototype, you could eliminate the
incomplete structure declaration.

Oh, yeah. That's prettier and much easier...
Why is number_of_sets_in_memory an int here but an unsigned int in all
your other functions?

Oops... Now it's unsigned everywhere :)
/* ************ Main program ************ */

int main(void)
{
const unsigned increase_memory_number = 3;
unsigned number_of_sets_in_memory, i;

double doub_x;
int va1, va2, va3;

struct tablevalues *data_pointer;

data_pointer = NULL;

/* there's nothing stored at this moment */
number_of_sets_in_memory = 0;

/* get user input - and store the data */
printf("Enter a double value, followed by 3 integers:\n");
while( scanf("%lf %d %d %d", &doub_x, &va1, &va2, &va3) == 4)
{
save_in_memory( &data_pointer,
&number_of_sets_in_memory,
increase_memory_number,
doub_x, va1,
va2, va3);
printf("\nEnter double value, followed by 3
integers:\n");
}

/* print the results to the screen, UNsorted */

printf("\n\nUnsorted list:\n--------------\n");
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}


/* sort data */
bubblesort(data_pointer, number_of_sets_in_memory);

/* print the results to the screen, SORTED */
printf("\n\nSorted list:\n------------\n");
for(i=0; i<number_of_sets_in_memory; i++)
{
printf("double_x = %10.5lf, var1 = %3d, var2 = %3d, var3
= %3d.\n",
data_pointer.double_x, data_pointer.var1,
data_pointer.var2, data_pointer.var3);
}

exit(EXIT_SUCCESS);
}


//////////////////////////////////////////////

void bubblesort(struct tablevalues *data_pointer,
int number_of_sets_in_memory)
{
int i, imax, j;

double save_x;
int save1, save2, save3;

/* this sorts data from smallest to biggest */

do
{
for(j=0, i=0; i<number_of_sets_in_memory-1; i++)
{

/* swap data only if data > data[i+1] */

if( ((*data_pointer)[i+1]).double_x <
((*data_pointer)).double_x )



data_pointer is a pointer to struct. Therefore (*data_pointer) is the
actual struct pointed to. You are not allowed to use the subscript
operator ([]) on a struct, only on an array or pointer. Since you
didn't use it in any of the previous code, what did you think was
different here?


Damn! I was stupid :)

I had changed a lot previously in my program and I thought I called the
sort algorithm in the same way as I called the
save_in_memory-function... It's because I've experimented with and
without calling with the address-of-operator in the memory-function.

I should have seen that myself, but then again it's my first time
working with structures + pointers to structure + realloc. Previously
I've only worked with structures to those simple data-types (and malloc).

Now, I don't think it's that difficult. I've learned a lot from this
code :)
It looks like you copied the code from save_in_memory but didn't take
into account that data_pointer is a pointer to pointer to struct there
and only a pointer to struct here.

Yep, see above. My mistake. It's because this structure notation was a
bit confusing, but I got it now (it looked like C++!) :)

Thanks a lot for the help... I'm really "proud" of the program even
though I got some very qualified help and probably couldn't have
programmed it without a little help here and there when I found it was
most complicated...


Best regards / Med venlig hilsen
Martin Jørgensen
 

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,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top