views:

199

answers:

5

Having this code:

typedef volatile int COUNT;       

COUNT functionOne( COUNT *number );

int  functionTwo( int *number );

I can't get rid of some warnings..

I get this warning 1 at functionOne prototype

[Warning] type qualifiers ignored on function return type

and I get this warning 2, wherever I call functionTwo with a COUNT pointer argument instead of an int pointer

[Warning] cast discards qualifiers from pointer target type

obviously variables/pointers can't be "cast" to volatile/un-volatile.. but every arguments must be specified as volatile too? so how can I use any library function if it's already defined for non-volatile variable?

EDIT: Using gcc -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wextra -Wstrict-prototypes -Wmissing-prototypes

EDIT: After Jukka Suomela advice this is a code sample for warning two

typedef volatile int COUNT;       

static int functionTwo(int *number) {
    return *number + 1;
}

int main(void) {
    COUNT count= 10;
    count = functionTwo(&count);
    return 0;
}
+2  A: 

It's possible that I am way off base here but volatile isn't something normally associated with stack memory region. Therefore I'm not sure if the following prototype really makes much sense.

volatile int functionOne(volatile int number);

I'm not sure how a returned integer can be volatile. What's going to cause the value of EAX to change? The same applies to the integer. Once the value is pushed onto the stack so that it can be passed as a parameter what's going to change its value?

torak
I think nothing will!, but then if I define the function like functionTwo (without volatile inside argument) I get the warningTwo every time I call with a volatile variable if that isn't specified in the prototype..
Hernán Eche
As I understand it, it's not a question of whether something can change a stack variable. Declaring a stack variable as `volatile` forces the compiler to always read / write the value from / to the stack location instead of optimizing the code by caching that stack location's contents in a register.
Praetorian
A: 

It's possible that those who wrote it wanted to be sure that all the operations are atomic, and declared all int variables as volatile (is it a MT application with poor syncronization?), so all the ints from the code are declared as volatile "for consistency".

Or maybe by declaring the function type as volatile they expect to stop the optimizations of the repeated calls for pure functions? An increment of a static variable inside the function would solve it. However, try to guess their original intention, because this just does not make any sense.

ruslik
volatile has nothing to do with atomicity.
Alexandre C.
http://www.netrino.com/node/80
ruslik
@Alexandre you might be wrong, if you do var = var + 1 and var is not volatile, then it can be change in the midle of the operation inside an interruption service routine (for embedded) or a thread, and then if the compiler don't read real var updated value, the operation that could be var = var(Updated) + 1, will lose atomicity doing var = var(NoUpdated) + 1, and the updated value will be lost
Hernán Eche
+2  A: 

If I compile

typedef volatile int COUNT;       

static int functionTwo(int number) {
    return number + 1;
}

int main(void) {
    COUNT count = 10;
    count = functionTwo(count);
    return 0;
}

using

gcc -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith -Wcast-qual \
 -Wextra -Wstrict-prototypes -Wmissing-prototypes foo.c

I don't get any warnings. I tried gcc 4.0, 4.2, 4.3, and 4.4. Your warningTwo sounds like you are passing pointers, not values, and that's another story...

EDIT:

Your latest example should be written like this; again, no warnings:

typedef volatile int COUNT;

static int functionTwo(COUNT *number) { return *number + 1; }

int main(void) { COUNT count = 10; count = functionTwo(&count); return 0; }

EDIT:

If you can't change functionTwo:

typedef volatile int COUNT;

static int functionTwo(int *number) { return *number + 1; }

int main(void) {
    COUNT count= 10;
    int countcopy = count;
    count = functionTwo(&countcopy);
    return 0;
}

Note that any access to a volatile variable is "special". In the first version with functionTwo(COUNT *number), functionTwo knows how to access it properly. In the second version with countcopy, the main function knows how to access it properly when assigning countcopy = copy.

Jukka Suomela
Argument of functionTwo is "int *number", and not "COUNT *number", change that and then you'll get the warnings (Imagine you are dealing with an already common library function that gets *int as argument) but the vars of your code are volatile (+1 for the try and the example)
Hernán Eche
it's ok for an int, but if you have an array, it's not memory efficient to make a copy just to avoid a warning
Hernán Eche
It's not about "avoiding warnings", it's about correctness. :) With an explicit copy, you will have the right semantics (whatever it happens to mean in your implementation), and you know exactly where you have read or write accesses to volatile objects. If you just take a pointer, discard the "volatile" qualifier, and pass it to a library function, all bets are off (undefined behaviour).
Jukka Suomela
@Jukka Suomela, you are right that's about to know when you read/write volatile objects. so remains to be seen if there is some other way, because if there is not other way, in the case of an array, it's a memory waste
Hernán Eche
+2  A: 

I don't understand why you'd want to have the volatile qualifier on a function return type. The variable that you assign the function's return value to should be typed as a volatile instead.

Try making these changes:

typedef int COUNT_TYPE;
typedef volatile COUNT_TYPE COUNT;       

COUNT_TYPE functionOne( COUNT number );

COUNT_TYPE functionTwo( COUNT_TYPE number );

And when calling functionTwo(), explicitly cast the argument:

functionTwo( (COUNT_TYPE)arg );

HTH, Ashish.

Praetorian
Exactly, rvalues can't be volatile. Volatile can only apply to some form of storage (i.e. variables which will be put into registers).
Gianni
+1 @Gianni, rvalues can neither be `const` nor `volatile`. What would that even *mean* if they could be?
Carl Norum
@Carl Imaginary numbers? http://en.wikipedia.org/wiki/Imaginary_number
Gianni
good for warning 1, now have a look at question for warning 2, arguments are pointers
Hernán Eche
I tried your edited sample on Visual Studio 2008; it throws an error saying the conversion from `COUNT *` to `int *` is not possible (I compiled the code as C++, not C). I was able to fix in 2 ways: 1 - change functionTwo() definition to `int functionTwo( COUNT *number )` 2 - cast the argument explicitly (as I listed in my original post) `count = functionTwo( (int *)`
Praetorian
Hernán Eche
What about changing the argument type for `functionTwo()` to `COUNT *` instead of `int *`? Or is that not an option?
Praetorian
it's not an option, it's from a library
Hernán Eche
Does assigning the `volatile` variable to a non-volatile variable also generate the warning? If it doesn't, then you can create a wrapper for `functionTwo()` that has a `COUNT *` argument, dereferences and assigns it to a temporary `int` variable and then calls the library's version of `functionTwo()`. Another option is to temporarily disable that particular warning for the sections where you call `functionTwo()`; assuming gcc allows you to do that.
Praetorian
+1  A: 

The volatile keyword was designed to be applied to objects that represent storage and not to functions. Returning a volatile int from a function does not make much sense. The return value of a function will not be optimized away (with the possible exception of inlined functions, but that's another case altogether...), and no external actor will be modifying it. When a function returns, it passes a copy of the return value to the calling function. A copy of a volatile object is not itself volatile. Therefore, attempting to return a volatile int will result in a copy, casting it down to a non-volatile int, which is what is triggering your compiler messages. Returning a volatile int* might be useful, but not a volatile int.

Passing an object by value into a function makes a copy of the object, thus using a volatile int as a function parameter necessarily involves a conversion that ignores a qualifier. Passing a volatile by address is perfectly reasonable, but not by value.

According to the C spec, the behavior of volatile is completely implementation-dependent, so YMMV.

Are you using volatile in this way to try to defeat some sort of compiler optimization? If so, there is probably a better way to do it.

Edit: Taking into account the updates to your question, it appears that you may be able to approach this in a different way. If you are trying to defeat compiler optimizations, why not take the direct approach and simply tell the compiler not to optimize some things? You can use #pragma GCC optimize or __attribute__((optimize)) to give specific optimization parameters for a function. For example, __attribute__((optimize(0))) should disable all optimizations for a given function. That way, you can keep your data types non-volatile and avoid the type problems you are having. If disabling all optimizations is a bit too much, you can also turn individual optimization options on or off with that attribute/pragma.

Edit: I was able to compile the following code without any warnings or errors:

static int functionTwo(int *number) {
    return *number + 1;
}

typedef union {
                int i;
    volatile    int v;
} fancy_int;

int main(void) {
    fancy_int count;
    count.v = 10;
    count.v = functionTwo(&count.i);
    return 0;
}

This hack"technique" probably has some kind of odd side-effects, so test it thoroughly before production use. It's most likely no different that directly casting the address to a (int*), but it doesn't trigger any warnings.

bta
+1 it's an option, I will try to solve it with optimization, following Jukka Suomela answer, it's needed to make a copy of the object, but I think I should be enough with a cast, for sure gcc makers have a reason to give the warning anyway
Hernán Eche