Problem with pointer to array of structures

T

Timo

I am trying to pass a pointer to an array of structures to function,
but
I have some problems. I am using MS Visual C++ 6.0, but I think
this is more of a C-problem than Windows programming specific. Here
is the relevant part of my code.

typedef struct
{
int iControlID;
char controlTxt[64];
} CONTROL_TXT;

void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt[]);

void MyFunc(void)
{
static CONTROL_TXT ctrlTxts[] =
{
{ 1, "text 1" },
{ 2, "text 2" },
{ 3, "text 3" },
{ 0, "" } // marks end of table
};

<clip>

SetControlTxts(hDlg, &ctrlTxts[0]);
}


void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt[])
{
int i, id;
char txt[64];

for (i = 0; &ctrlTxt->iControlID != 0; i++)
{
id = &ctrlTxt->iControlID;
strcpy(txt, &ctrlTxt->controlTxt);
//strcpy(txt, (char*)ctrlTxt->controlTxt);
//SetDlgItemText(hDlg, ctrlTxt->iControlID,
ctrlTxt->controlTxt);
SetDlgItemText(hDlg, id, txt);
}

return;

}

I get some compiler warnings. Function call "SetControlTxts(hDlg,
&ctrlTxts[0]);
":
warning C4047: 'function' : 'struct CONTROL_TXT ** ' differs in levels
of indirection from 'struct CONTROL_TXT *'
warning C4024: 'SetControlTxts' : different types for formal and
actual parameter 2

"id = &ctrlTxt->iControlID;":
warning C4047: '=' : 'int ' differs in levels of indirection from 'int
*'

"strcpy(txt, &ctrlTxt->controlTxt);":
warning C4047: 'function' : 'const char *' differs in levels of
indirection from 'char (*)[64]'
warning C4024: 'strcpy' : different types for formal and actual
parameter 2

Strcpy also causes access violation in Visual C++. However, I can get
valid value of "id", the debugger shows it correctly.

I also tried casting to char*:
strcpy(txt, (char*)&ctrlTxt->controlTxt);
but it also causes access violation.

Any info what am I doing wrong here?

Timo
 
N

nrk

Timo said:
I am trying to pass a pointer to an array of structures to function,
but
I have some problems. I am using MS Visual C++ 6.0, but I think
this is more of a C-problem than Windows programming specific. Here
is the relevant part of my code.

typedef struct
{
int iControlID;
char controlTxt[64];
} CONTROL_TXT;

void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt[]);

The second parameter here is a pointer-to-pointer-to CONTROL_TXT. IOW, this
prototype is equivalent to:
void SetControlTxts(HWND hDlg, CONTROL_TXT **ctrlTxt);

However, what you're passing is essentially a pointer-to CONTROL_TXT (Search
the group archives for Chris Torek's "The" rule to see why this is the
case). So what you really want is either one of:
void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt);
or
void SetControlTxts(HWND hDlg, CONTROL_TXT ctrlTxt[]);
void MyFunc(void)
{
static CONTROL_TXT ctrlTxts[] =
{
{ 1, "text 1" },
{ 2, "text 2" },
{ 3, "text 3" },
{ 0, "" } // marks end of table
};

This declares ctrlTxts to be an array of CONTROL_TXT. When passed as a
parameter to a function, it decays into a pointer to the first element of
the array.
<clip>

SetControlTxts(hDlg, &ctrlTxts[0]);

This is unnecessary. You can simply write:
SetControlTxts(hDlg, ctrlTxts);
to get the same effect.
}


void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt[])

Again, change this definition to match the prototype as shown above.
{
int i, id;
char txt[64];

for (i = 0; &ctrlTxt->iControlID != 0; i++)


-> binds closer than &. So what you're doing is equivalent to:
&(ctrlTxt->iControlID) != 0

Unfortunately, your choice of integer constant here is such that, the
compiler thinks this is fine, even though this is quite clearly not what
you want.

Why not:
for ( i = 0; ctrlTxt.iControlID != 0; i++ )
?
Similar fixes can be applied to other such mistakes.
{
id = &ctrlTxt->iControlID;

See above. However, here your compiler has a legitimate reason to complain.
The rhs is clearly an address and lhs is an int. Hence the noise.
strcpy(txt, &ctrlTxt->controlTxt);

See above.
//strcpy(txt, (char*)ctrlTxt->controlTxt);
//SetDlgItemText(hDlg, ctrlTxt->iControlID,
ctrlTxt->controlTxt);
SetDlgItemText(hDlg, id, txt);
}

return;

}

I get some compiler warnings. Function call "SetControlTxts(hDlg,
&ctrlTxts[0]);
":
warning C4047: 'function' : 'struct CONTROL_TXT ** ' differs in levels
of indirection from 'struct CONTROL_TXT *'
warning C4024: 'SetControlTxts' : different types for formal and
actual parameter 2

"id = &ctrlTxt->iControlID;":
warning C4047: '=' : 'int ' differs in levels of indirection from 'int
*'

"strcpy(txt, &ctrlTxt->controlTxt);":
warning C4047: 'function' : 'const char *' differs in levels of
indirection from 'char (*)[64]'
warning C4024: 'strcpy' : different types for formal and actual
parameter 2


All fine and excellent warnings. It's good that you've set your compiler's
warning levels high enough to catch these mistakes.
Strcpy also causes access violation in Visual C++. However, I can get
valid value of "id", the debugger shows it correctly.

I also tried casting to char*:
strcpy(txt, (char*)&ctrlTxt->controlTxt);
but it also causes access violation.


This tells you that you are not yet strong enough in the language to be
using dubious constructs such as casts. Never ever use a cast to silence
the compiler unless you're absolutely 200% certain that you know what
you're doing. Casts do *not* fix incorrect code, and often prevent you
from getting useful diagnostics from the compiler.

-nrk.
 
P

pete

Timo said:
a pointer to an array of structures
CONTROL_TXT *ctrlTxt[]

That's an array of pointers to structures.

You want:
CONTROL_TXT *ctrlTxt

Make the call like this:
SetControlTxts(hDlg, ctrlTxts);

Access members like this:
ctrlTxt.iControlID
 
T

Timo

pete said:
Timo said:
a pointer to an array of structures
CONTROL_TXT *ctrlTxt[]

That's an array of pointers to structures.

You want:
CONTROL_TXT *ctrlTxt

Make the call like this:
SetControlTxts(hDlg, ctrlTxts);

Access members like this:
ctrlTxt.iControlID


After I had posted my original article, I tried changing my codes to
this:

void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt)
{

CONTROL_TXT *ctrlTxtTmp;

ctrlTxtTmp = ctrlTxt;

for (; ctrlTxtTmp->iControlID != 0; ctrlTxtTmp++)
{
SetDlgItemText(hDlg, ctrlTxtTmp->iControlID,
(char*)&ctrlTxtTmp->controlTxt);
}

return;

}

Works perfectly, no compiler warning. I have been writing with C
several
years, but usually I try to avoid complicated pointer stuff such as
this.

So, when passing array of structures, the function should rather take
pointer to
struct as parameter, not an array of pointers to structs? Seems little
complicated, I have to try to check the rule the previous poster
mentioned.

Timo
 
N

nrk

Timo said:
pete said:
Timo said:
a pointer to an array of structures
CONTROL_TXT *ctrlTxt[]

That's an array of pointers to structures.

You want:
CONTROL_TXT *ctrlTxt

Make the call like this:
SetControlTxts(hDlg, ctrlTxts);

Access members like this:
ctrlTxt.iControlID


After I had posted my original article, I tried changing my codes to
this:

void SetControlTxts(HWND hDlg, CONTROL_TXT *ctrlTxt)
{

CONTROL_TXT *ctrlTxtTmp;

ctrlTxtTmp = ctrlTxt;

for (; ctrlTxtTmp->iControlID != 0; ctrlTxtTmp++)
{
SetDlgItemText(hDlg, ctrlTxtTmp->iControlID,
(char*)&ctrlTxtTmp->controlTxt);
}

return;

}

Works perfectly, no compiler warning. I have been writing with C
several
years, but usually I try to avoid complicated pointer stuff such as
this.

So, when passing array of structures, the function should rather take
pointer to
struct as parameter, not an array of pointers to structs? Seems little
complicated, I have to try to check the rule the previous poster
mentioned.


Read (atleast) this section of the FAQ:
http://www.eskimo.com/~scs/C-faq/s6.html

Chris Torek's "The" rule is explained here:
http://web.torek.net/torek/c/pa.html

You might also find the rest of the FAQ useful:
http://www.eskimo.com/~scs/C-faq/top.html

-nrk.
 
P

pete

Timo wrote:
Works perfectly, no compiler warning. I have been writing with C
several
years, but usually I try to avoid complicated pointer stuff such as
this.

So, when passing array of structures,
the function should rather take pointer to
struct as parameter, not an array of pointers to structs? Seems little
complicated, I have to try to check the rule the previous poster
mentioned.

Whenever you pass *any* array name as an argument,
the name of the array is converted
to a pointer to it's first element.

I prefer
void func(int *array);
over
void func(int array[]);
Even though they mean exactly the same thing,
the true type of the parameter, is a pointer.
 
C

CBFalconer

pete said:
.... snip ...

Whenever you pass *any* array name as an argument, the name of
the array is converted to a pointer to it's first element.

I prefer
void func(int *array);
over
void func(int array[]);

Even though they mean exactly the same thing,
the true type of the parameter, is a pointer.

However func may then be called with a pointer to an array[1] of
int, or with a void*, or with the address of some individual int.
So it makes more sense to make the requisite parameter form
explicit. Thus I would use:

void func(int *i); /* pointer to an individual integer */
void func(int a[]); /* pointer to an actual array, but .. */
void func(int a[], size_t sz); /* an actual array, safer */

of course, arrays may have internal end markers, such as strings,
but such markers do not indicate maximum capacity.

The point of the above is that the user of func has an idea of its
requirements from the prototype alone.
 

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,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top