Confusion with stdarg

M

Mac A. Cody

Hello,

I'm encountering a problem with using stdarg that I cannot
figure out. I'm trying to develop a function for a linux
driver module that takes a variable-length sequence of
u8-type values. Below is the function:

#include <stdarg.h>
..
..
..
63 int sn9c102_write_va_regs(struct sn9c102_device* cam,
u16 index, int len, ...)
64 {
65 int res, i;
66 u8 data[32];
67 va_list args;
68
69 va_start(args, len);
70 for (i = 0; i < len; i++) {
71 data = va_arg (args, u8);
72 }
73 va_end(args);
74 res = sn9c102_write_regs(cam, index, data, len);
75 if (res < 0) {
76 DEBUG(3, "Failed to write registers starting at index "
77 "0x%02X, error %d)", index, res)
78 return -1;
79 }
80 return 0;
81 }

When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'
sonix_pas202b.c:71: warning: `u8' is promoted to `int' when passed
through `...'sonix_pas202b.c:71: warning: (so you should pass `int' not
`u8' to `va_arg')

Why does the warning on line 71 occur? I believe that 'u8'
is typedef'ed somewhere in the linux kernel headers as an
'unsigned char', though I have not verified that yet. Is
there a way of telling 'va_arg' the actual type of 'u8'?

I'm compiling with GCC version 3.2.3 on Slackware Linux 9.1
(kernel 2.4.22).

Thanks,

Mac Cody
 
J

jacob navia

Mac said:
Hello,

I'm encountering a problem with using stdarg that I cannot
figure out. I'm trying to develop a function for a linux
driver module that takes a variable-length sequence of
u8-type values. Below is the function:

#include <stdarg.h>
.
.
.
63 int sn9c102_write_va_regs(struct sn9c102_device* cam,
u16 index, int len, ...)
64 {
65 int res, i;
66 u8 data[32];
67 va_list args;
68
69 va_start(args, len);
70 for (i = 0; i < len; i++) {
71 data = va_arg (args, u8);
72 }
73 va_end(args);
74 res = sn9c102_write_regs(cam, index, data, len);
75 if (res < 0) {
76 DEBUG(3, "Failed to write registers starting at index "
77 "0x%02X, error %d)", index, res)
78 return -1;
79 }
80 return 0;
81 }

When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'
sonix_pas202b.c:71: warning: `u8' is promoted to `int' when passed
through `...'sonix_pas202b.c:71: warning: (so you should pass `int' not
`u8' to `va_arg')

Why does the warning on line 71 occur? I believe that 'u8'
is typedef'ed somewhere in the linux kernel headers as an
'unsigned char', though I have not verified that yet. Is
there a way of telling 'va_arg' the actual type of 'u8'?

I'm compiling with GCC version 3.2.3 on Slackware Linux 9.1
(kernel 2.4.22).

Thanks,

Mac Cody

Since the compiler didn't see the prototype of the function
it will promote u8 to int, and warns you about it.

Always prototype the functions before calling them, specially
when writing an OS!
 
M

Mac A. Cody

jacob said:
Mac said:
Hello,

I'm encountering a problem with using stdarg that I cannot
figure out. I'm trying to develop a function for a linux
driver module that takes a variable-length sequence of
u8-type values. Below is the function:

#include <stdarg.h>
.
.
.
63 int sn9c102_write_va_regs(struct sn9c102_device* cam,
u16 index, int len, ...)
64 {
65 int res, i;
66 u8 data[32];
67 va_list args;
68
69 va_start(args, len);
70 for (i = 0; i < len; i++) {
71 data = va_arg (args, u8);
72 }
73 va_end(args);
74 res = sn9c102_write_regs(cam, index, data, len);
75 if (res < 0) {
76 DEBUG(3, "Failed to write registers starting at index "
77 "0x%02X, error %d)", index, res)
78 return -1;
79 }
80 return 0;
81 }

When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'
sonix_pas202b.c:71: warning: `u8' is promoted to `int' when passed
through `...'sonix_pas202b.c:71: warning: (so you should pass `int'
not `u8' to `va_arg')

Why does the warning on line 71 occur? I believe that 'u8'
is typedef'ed somewhere in the linux kernel headers as an
'unsigned char', though I have not verified that yet. Is
there a way of telling 'va_arg' the actual type of 'u8'?

I'm compiling with GCC version 3.2.3 on Slackware Linux 9.1
(kernel 2.4.22).

Thanks,

Mac Cody

Since the compiler didn't see the prototype of the function
it will promote u8 to int, and warns you about it.

Always prototype the functions before calling them, specially
when writing an OS!


Jacob,

Thanks for the quick response! Since the linux kernel headers
are included (I just didn't show them.) and the compiler does
not otherwise balk at the 'u8' typing (as on line 64 in the
code above), the compiler must be seeing a prototype somewhere
along the way (doesn't it?). Maybe I'm missing something here.

Thanks again,

Mac
 
M

Michael Mair

Mac said:
jacob said:
Mac said:
Hello,

I'm encountering a problem with using stdarg that I cannot
figure out. I'm trying to develop a function for a linux
driver module that takes a variable-length sequence of
u8-type values. Below is the function:

#include <stdarg.h>
.
.
.
63 int sn9c102_write_va_regs(struct sn9c102_device* cam,
u16 index, int len, ...)
64 {
65 int res, i;
66 u8 data[32];
67 va_list args;
68
69 va_start(args, len);
70 for (i = 0; i < len; i++) {
71 data = va_arg (args, u8);
72 }
73 va_end(args);
74 res = sn9c102_write_regs(cam, index, data, len);
75 if (res < 0) {
76 DEBUG(3, "Failed to write registers starting at index "
77 "0x%02X, error %d)", index, res)
78 return -1;
79 }
80 return 0;
81 }

When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'
sonix_pas202b.c:71: warning: `u8' is promoted to `int' when passed
through `...'sonix_pas202b.c:71: warning: (so you should pass `int'
not `u8' to `va_arg')

Why does the warning on line 71 occur? I believe that 'u8'
is typedef'ed somewhere in the linux kernel headers as an
'unsigned char', though I have not verified that yet. Is
there a way of telling 'va_arg' the actual type of 'u8'?

I'm compiling with GCC version 3.2.3 on Slackware Linux 9.1
(kernel 2.4.22).

Thanks,

Mac Cody

Since the compiler didn't see the prototype of the function
it will promote u8 to int, and warns you about it.

Always prototype the functions before calling them, specially
when writing an OS!


Jacob,

Thanks for the quick response! Since the linux kernel headers
are included (I just didn't show them.) and the compiler does
not otherwise balk at the 'u8' typing (as on line 64 in the
code above), the compiler must be seeing a prototype somewhere
along the way (doesn't it?). Maybe I'm missing something here.


Yes. Variable arguments of vararg functions get promoted, i.e. you
cannot pass just a char but will always get an int. That is, you
(necessarily) pass the u8 values as int values -- and have to retrieve
them as such. The same happens to functions without prototypes.
Something else: You should check against a buffer overrun of data
in the loop l70 -- l72.


Cheers
Michael
 
J

Jack Klein

Hello,

I'm encountering a problem with using stdarg that I cannot
figure out. I'm trying to develop a function for a linux
driver module that takes a variable-length sequence of
u8-type values. Below is the function:

#include <stdarg.h>
.
.
.
63 int sn9c102_write_va_regs(struct sn9c102_device* cam,
u16 index, int len, ...)

You can't pass unsigned characters as part of the "..." arguments to
variadic functions. You can't pass any of the character types, or
signed or unsigned short, or floats. The arguments to such functions,
after the fixed ones, undergo the default promotions.

See section 15 on functions with variable argument lists in the FAQ
for this group, link in my signature. Specifically see:

15.10 Why isn't "va_arg(argp, float)" working?

....at: http://www.eskimo.com/~scs/C-faq/q15.10.html
64 {
65 int res, i;
66 u8 data[32];
67 va_list args;
68
69 va_start(args, len);
70 for (i = 0; i < len; i++) {
71 data = va_arg (args, u8);
72 }
73 va_end(args);
74 res = sn9c102_write_regs(cam, index, data, len);
75 if (res < 0) {
76 DEBUG(3, "Failed to write registers starting at index "
77 "0x%02X, error %d)", index, res)
78 return -1;
79 }
80 return 0;
81 }

When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'


Well, obviously you don't have a prototype for this function in scope.
Find the proper header and include it.
sonix_pas202b.c:71: warning: `u8' is promoted to `int' when passed
through `...'sonix_pas202b.c:71: warning: (so you should pass `int' not
`u8' to `va_arg')

Why does the warning on line 71 occur? I believe that 'u8'
is typedef'ed somewhere in the linux kernel headers as an
'unsigned char', though I have not verified that yet. Is
there a way of telling 'va_arg' the actual type of 'u8'?

No, there is no way at all to pass variable arguments to a variadic
function with a type less than int. They will be promoted to int by
the caller, assuming that it has a prototype for the variadic function
in scope. So you have to retrieve them as type int, then you can
assign them to your unsigned char array. There will be no change in
value.
 
R

Richard Bos

jacob navia said:
Mac A. Cody wrote:
#include <stdarg.h>
63 int sn9c102_write_va_regs(struct sn9c102_device* cam,
u16 index, int len, ...)
64 {
66 u8 data[32];
67 va_list args;
71 data = va_arg (args, u8);
74 res = sn9c102_write_regs(cam, index, data, len);
When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'
sonix_pas202b.c:71: warning: `u8' is promoted to `int' when passed
through `...'sonix_pas202b.c:71: warning: (so you should pass `int' not
`u8' to `va_arg')

Since the compiler didn't see the prototype of the function
it will promote u8 to int, and warns you about it.

Bullshit. There _is_ a prototype. The lack of declaration of
sn9c102_write_regs (what a monster of a name, btw!) is completely beside
the point, for line 71. The problem is with the application of va_arg
and sn9c102_write_va_regs, for both of which a good declaration is in
scope.
The real problem is not a _lack_ of prototype, but the fact that the
prototype in question is a variadic one. This means that the arguments
in the variadic part are subject to the Usual Arithmetic Conversions as
much as when there had not been a prototype at all. There's nothing you
can do about that, either. There simply is no typing information in
"...", therefore the UACs kick in, and therefore there is no way a
variadic function can receive anything smaller than an int as one of its
variable arguments.
Always prototype the functions before calling them, specially
when writing an OS!

Always remember that prototyping doesn't protect you from the UACs if
you're using variadic functions.

Richard
 
M

Mac A. Cody

Michael said:
Yes. Variable arguments of vararg functions get promoted, i.e. you
cannot pass just a char but will always get an int. That is, you
(necessarily) pass the u8 values as int values -- and have to retrieve
them as such. The same happens to functions without prototypes.

Okay, I understand now. The webpage I found didn't mention the point
about the promtion of chars to ints.
Something else: You should check against a buffer overrun of data
in the loop l70 -- l72.
Good point. An overrun at that point would not be a nice thing.
Cheers
Michael

Thanks for responding!

Regards,

Mac
 
M

Mac A. Cody

Richard said:
Bullshit. There _is_ a prototype. The lack of declaration of
sn9c102_write_regs (what a monster of a name, btw!) is completely beside

That name wasn't my idea, though sn9c102_write_va_regs isn't much
better! :^/
the point, for line 71. The problem is with the application of va_arg
and sn9c102_write_va_regs, for both of which a good declaration is in
scope.
The real problem is not a _lack_ of prototype, but the fact that the
prototype in question is a variadic one. This means that the arguments
in the variadic part are subject to the Usual Arithmetic Conversions as
much as when there had not been a prototype at all. There's nothing you
can do about that, either. There simply is no typing information in
"...", therefore the UACs kick in, and therefore there is no way a
variadic function can receive anything smaller than an int as one of its
variable arguments.

Thanks for the clarification!

Correct. I just have not placed the function in its final location,
where all of that will clear up.
Always remember that prototyping doesn't protect you from the UACs if
you're using variadic functions.

Richard

Thanks again,

Mac
 
M

Mac A. Cody

Jack said:
You can't pass unsigned characters as part of the "..." arguments to
variadic functions. You can't pass any of the character types, or
signed or unsigned short, or floats. The arguments to such functions,
after the fixed ones, undergo the default promotions.

See section 15 on functions with variable argument lists in the FAQ
for this group, link in my signature. Specifically see:

15.10 Why isn't "va_arg(argp, float)" working?

...at: http://www.eskimo.com/~scs/C-faq/q15.10.html

That reference was very helpful. Thanks!
64 {
65 int res, i;
66 u8 data[32];
67 va_list args;
68
69 va_start(args, len);
70 for (i = 0; i < len; i++) {
71 data = va_arg (args, u8);
72 }
73 va_end(args);
74 res = sn9c102_write_regs(cam, index, data, len);
75 if (res < 0) {
76 DEBUG(3, "Failed to write registers starting at index "
77 "0x%02X, error %d)", index, res)
78 return -1;
79 }
80 return 0;
81 }

When I compile the code, I get the following message:

sonix_pas202b.c: In function `sn9c102_write_va_regs':
sonix_pas202b.c:74: warning: implicit declaration of function
`sn9c102_write_regs'



Well, obviously you don't have a prototype for this function in scope.
Find the proper header and include it.


Obviously true. I didn't think to mention it due to its
obviousness. In my haste, I failed to cut it out of my message.
No, there is no way at all to pass variable arguments to a variadic
function with a type less than int. They will be promoted to int by
the caller, assuming that it has a prototype for the variadic function
in scope. So you have to retrieve them as type int, then you can
assign them to your unsigned char array. There will be no change in
value.

Yes, I see that now. After twenty years of C programming (off and on),
this is the first time I've ever used the stdarg facility. Guess you
can never be too old to learn something about C!

Thanks,

Mac
 
L

Lawrence Kirby

On Mon, 03 Jan 2005 15:31:52 +0000, Richard Bos wrote:

....
The real problem is not a _lack_ of prototype, but the fact that the
prototype in question is a variadic one. This means that the arguments
in the variadic part are subject to the Usual Arithmetic Conversions as
much as when there had not been a prototype at all.

Or rather the default argument promotions.

Lawrence
 

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,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top