|
|
View previous topic :: View next topic |
Author |
Message |
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
Pointer function arguments |
Posted: Thu Jan 31, 2013 3:11 pm |
|
|
Hello everyone, I am once again frustrated with another oddity of the CCS compiler (PCH 4.14), and perhaps someone can enlighten me as to why CCS considers this behavior acceptable.
Basically, it appears that if you define a function that takes a pointer value, you can pass ANYTHING in for that argument, and the compiler does not complain. Take a look at the code below, which DOES compile. I am annoyed that the compiler allows me to make mistakes like this, because I have to waste time debugging a mistake the compiler should not have allowed me to make in the first place! Thoughts?
Code: |
typedef unsigned int8 uint8;
typedef unsigned int16 uint16;
typedef unsigned int32 uint32;
void PrintStr(FakeType* Str) // WHY DOES THIS COMPILE (FakeType is NOT defined)
{
// FakeType* FakePtr; // doesn't compile with this though
int i = 0;
while (Str[i] != '\0')
{
putc(Str[i]);
i++;
}
}
void main() {
char String[20];
uint32 Grr;
while (TRUE) {
sprintf(String, "AAAAAAAA %u\r\n", (uint8)1);
PrintStr(String); // WHY DOES THIS COMPILE??
output_high(PIN_B6);
delay_ms(500);
sprintf(String, "AAAAAAAA %u\r\n", (uint8)0);
PrintStr(Grr); // WHY DOES THIS COMPILE???
PrintStr(&Grr); // WHY DOES THIS COMPILE???
output_low(PIN_B6);
delay_ms(500);
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19516
|
|
Posted: Thu Jan 31, 2013 4:16 pm |
|
|
That is standard C......
C is a weakly typed language. It allows _any_ type to be passed for any required value. It automatically casts if a conversion is required. This is actually a very powerful 'feature' of C, but comes at the cost that _you_ as a programmer need to be checking all types.
Many 'modern' C's will warn, but this is not part of the language. Some third party C syntax checking programs can also be setup to give such warnings, and used on CCS code.
This is one reason why it is worth using variable names that 'code' what they are. So 'ptr_str' for example, gives you a 'clue' that the variable is a pointer to a string.
This is not CCS's fault, but a feature of C.
Best Wishes |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Thu Jan 31, 2013 5:30 pm |
|
|
I'm sorry to be blunt, especially since you seem to know your stuff, but in this case you're wrong. I've been programming in C and C++ for 25 years and I can tell you with some certainty that that code should not compile. First of all, my function is taking a pointer to a type that is not even defined. Secondly, the compiler should require every argument that is passed to that function match the specified type. C _does_ enforce typing, even with pointers. You should have to use an explicit cast to pass something to a function if it doesn't match the defined argument (unless there is an implicit cast that the compiler can apply).
Let me put it another way: why does the void* type even exist, if you can just assign any address to an int*? So in the CCS compiler, it would seem that every use of int* is equivalant to using void*. And you're saying that is C standard behavior?
Here is another example that I think should not compile successfully, but does.
Code: |
void PrintStr(char* str) { ... }
char String[20];
float* spam;
uint32 Grr;
spam = String;
spam = Grr;
spam = &PrintStr;
sprintf(spam, "LED = %u\r\n", (uint8)1);
|
I really don't think that the last four lines should compile. I understand that there are legitimate situations where you might want to access String as a float* rather than a char*, however, an explicit cast should be required. And look! I assigned the address of a function to a float* variable. That gets no error from the compiler??? Give me a break.
Also, if I should be passing a pointer to a function and I forget to put the ampersand in front of my argument, the compiler should tell me. Seriously, why even bother with function declarations if you can just pass whatever you want regardless?
Finally, if you really look at the code in my OP (and maybe you didn't), ask yourself, how does the compiler know what to do when I dereference Str with Str[i]? Didn't I supposedly pass that pointer in as a FakeType, which isn't even defined? How big do you suppose FakeType is? |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Thu Jan 31, 2013 8:26 pm |
|
|
I don't have access to other C standards at this moment and I am fighting a kid, but according to the most recent ISO C standard, the pointer conversions are allowed but the results can be undefined if they are not properly aligned. This is listed in section 6.3.2.3 parts 5, 6, and 7.
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Thu Jan 31, 2013 8:55 pm |
|
|
Quote: | I don't have access to other C standards at this moment and I am fighting a kid, but according to the most recent ISO C standard, the pointer conversions are allowed but the results can be undefined if they are not properly aligned. This is listed in section 6.3.2.3 parts 5, 6, and 7. |
Good try, especially with the kid, and thanks. I think the section that I'd like to point out is 6.5.4 part 3: "Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast." Exactly. You MUST explicitly cast a pointer of one type in order to assign it to a variable of another type (except for void*). Did you guys read my code? It just seems so absurd that it compiles that I'm confused why I'm having trouble convincing you. In fact I feel like there must be some weird compiler switch that I inadvertently flipped or something. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
Re: Pointer function arguments |
Posted: Fri Feb 01, 2013 2:37 am |
|
|
1) We are not CCS. We are users of CCS compilers and IDE. We are helping other users. If you have a complaint about any CCS product take it to CCS.
2) If you want to be protected from your mistakes don't use C. Use a more modern language that provides greater inherent protection. Pointers, regardless of whether they are correctly implemented according to whatever standard, are a common source of trouble for even very experienced programmers. Stuff happens. That's what debuggers are for. Live with it. " I have to waste time debugging a mistake the compiler should not have allowed me to make in the first place!" No, that's an abdication of responsibility. You have wasted your time debugging your own mistake. You cannot reasonably blame CCS for your mistake.
3) If you think CCS C is so poor then don't use it. We are not telling you to use it. Its your choice.
4) Thank you for alerting us to yet another issue with CCS C. We'll bear it in mind when we are helping others in future, though it has to be admitted, its on a pretty long list.
RF Developer
PS: CCS C versions have three digit minor parts. So 4.14 is not a valid and therefore recognisable version. Is it 4.140 by any chance? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19516
|
|
Posted: Fri Feb 01, 2013 3:10 am |
|
|
Seriously, read K&R.
CCS is compliant with what it specified.
_However_ as you say the checking is required in ANSI. Though CCS claims some ANSI compliance, this is still at best partial, and does not include such checking.
However what you post is fully compliant with the original K&R C, which is what CCS generally follows. It is worth reading the original K&R paragraph on this:
"In practice there would be a strong tendency to omit declarations:
Code: |
strsave(s) //save string s somewhere
{
char *p;
if ((p=alloc(strlen[s]+1}} != NULL)
strcpy(p,s);
return (p);
}
|
This will work on many machines since the default type for functions and arguments is int, and int and pointer can usually be safely assigned back and forth. Nevertheless this kind of code is inherently risky, for it depends on details of implementation and machine architecture which may not hold for the particular compiler you use. It's wiser ot be complete in all declarations (The program lint will warn of such constructions in case they creep in inadvertently.)
"
Note that this is K&R, saying explicitly that C itself will not complain about mixing types like this, and _you_ have to use a third party tool (in this case suggesting lint), to catch such constructions. Obviously the 'riskiness' they talk about particularly applies with CCS, where for the PIC16, and 18, the default integer type is only an int8...
The whole point about programs like lint, was to catch this type of 'fluff', since otherwise a little carelessness, could lead to major implications. However it was accepted that from the compiler's point of view such incorrect typing, was completely legal.
Best Wishes |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Fri Feb 01, 2013 2:23 pm |
|
|
Okay, I accept the fact that the CCS compiler complies with some esoteric "pre-C" standard, and that I just have to deal with it and stop my whining. But surely my confusion is at least a little justified since I have found absolutely no mention of this particular quirk anywhere. And according to CCS, they have implemented function overloading http://www.ccsinfo.com/content.php?page=compiler-features#overload, but I think we've proven that that doesn't work either: http://www.ccsinfo.com/forum/viewtopic.php?t=49734.
Anyway, thanks everyone for your responses... at least there is now a thread in the forum that mentions it. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19516
|
|
Posted: Fri Feb 01, 2013 3:29 pm |
|
|
K&R, _wrote_ C. Hardly 'esoteric'...
Not 'pre C'.
Their 'The C programming language' _is_ the definition of C. This predates things like ANSI, etc..
Overloading does work, _but_ what happens is that the target is only guaranteed to be called if the type is an exact match. If not, the first function that can accept the variable in any form will be used.
Best Wishes |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Fri Feb 01, 2013 6:39 pm |
|
|
Okay, maybe esoteric isn't the right word. How about obsolete? I mean, the ANSI C standard was published in 1989. That makes the K&R about 24 years obsolete. How long has the CCS compiler been around? Not that long, I assume. So I guess what really bugs me is WHY? Why diverge from the ANSI standard unless there is a very specific reason to do so (which sometimes there is when doing embedded solutions, I admit, but I just can't imagine a viable reason for diverging from enforcing types). It may be obvious that I only just started using this compiler, and I've had to work my way through several quirks, but I've been able to understand the reasoning behind most. This one is still blowing my mind. I find it completely unacceptable. Well, okay, let's rephrase to "ridiculous" rather than unacceptable, since I'm still going to use the compiler -- I just like to whine a lot. Oh, and as for the function overloading, it doesn't seem to work even with exact matches unless it is a built-in type (i.e. if you overload with a struct*, it no workie). |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Fri Feb 01, 2013 7:11 pm |
|
|
I haven't tested in the most recent version of the compiler, but in the 4.130's somewhere I was using function overloading a lot. Mind you, I was using reference operators instead of pointers because they tend to lean towards inlining functions (I use it to emulate a "class" type organization) which I like on the bigger chips (64k and higher).
Stuff like this "should" work though I haven't tested it recently:
Code: |
typedef struct{
unsigned int16 param1;
unsigned int16 param2;
unsigned int16 param3;
} class_type1;
typedef struct{
unsigned int16 param1;
unsigned int16 param2;
unsigned int16 param3;
} class_type2;
void setParam1(class_type1 &o, unsigned int16 val){ o.param1=val;}
void setParam1(class_type2 &o, unsigned int16 val){ o.param1=val;}
|
Mind you that is just typed off the cuff. I didn't check for compilability, but just wanted to show what I typically did with function overloading. I also use it a lot for things like eeprom drivers where I may have one function that only writes a byte: void eepromWrite(addr_type addr, int8 val) vs one that writes multiple bytees void eepromWrite(addr_type addr, int8 *vals, int16 len)
Those situations of overloading have worked for me in the past, but I haven't worked on anything recently to test them with the newer compiler versions.
EDIT: also note, I primarily work with PIC24 and dsPIC33 products, so I haven't ever really tested on PIC18's, PIC16s, etc. and there may be differences there. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|