Learning C and having some issues with pointers/arrays.
Using MPLAB X and a PIC24FJ128GB204 as target device, but I don't really think it matters for this question.
The answer might be obvious, but without much background in C (yet), it is difficult to find a similar question that I understand enough to draw conclusions.
I have written an I2C library with the following function:
int I2C1_Write(char DeviceAddress, unsigned char SubAddress, unsigned char *Payload, char ByteCnt){
int PayloadByte = 0;
int ReturnValue = 0;
char SlaveWriteAddr;
// send start
if(I2C1_Start() < 0){
I2C1_Bus_SetDirty;
return I2C1_Err_CommunicationFail;
}
// address slave
SlaveWriteAddr = (DeviceAddress << 1) & ~0x1; // shift left, AND with 0xFE to keep bit0 clear
ReturnValue = I2C1_WriteSingleByte(SlaveWriteAddr);
switch (ReturnValue){
case I2C1_OK:
break;
case I2C1_Err_NAK:
I2C1_Stop();
I2C1_Bus_SetDirty;
return I2C1_Err_BadAddress;
default:
I2C1_Stop();
I2C1_Bus_SetDirty;
return I2C1_Err_CommunicationFail;
}
// part deleted for brevity
// and finally payload
for(PayloadByte = 0; PayloadByte < ByteCnt; PayloadByte++){
// send byte one by one
if(I2C1_WriteSingleByte(Payload[PayloadByte]) != I2C1_ACK){
I2C1_Stop();
I2C1_Bus_SetDirty;
return I2C1_Err_CommunicationFail;
}
}
return I2C1_OK;
}
I want to call this function from another one, using a predefined const:
const unsigned char CMD_SingleShot[3] = {2, 0x2C, 0x06};
This has the length of the command as first byte, then the command bytes.
The calling function is:
int SHT31_GetData(unsigned char MeasurementData[]){
// address device and start measurement
if(I2C1_Write(SHT31_Address,
0xFF,
CMD_SingleShot[1], // this is where the error message is given
CMD_SingleShot[0])
< 1){
return -1;
}
//code omitted for brevity
return 1;
}
The error message:
../Sensirion_SHT31.c:40:17: warning: passing argument 3 of 'I2C1_Write' makes pointer from integer without a cast
../I2C1.h:66:5: note: expected 'unsigned char *' but argument is of type 'unsigned char'
The problem is clearly
(unsigned char)CMD_SingleShot[1] where I try to give a pointer to the second byte of the unsigned char array.
I have tried:
reading up on pointers and arrays and trying to understand
searching for similar functions
given up on understanding and trying random thing hoping the error messages would lead me to the correct way of doing this. Things like:
CMD_SingleShot[1]
&CMD_SingleShot[1]
(unsigned char)CMD_SingleShot + 1
this just gave other error messages.
My questions:
given the I2C1_Write function as is (expecting a unsigned char *) (for instance, if that was not my code and I couldn't change that), how would I pass the pointer to the second byte in the cont unsigned char array? My understanding is that an array is a pointer, so
since this is my code, is there a better way to do this altogether?
First, don't do casting unless you know better than the compiler what is going on. Which, in your case, you don't. This is nothing to be ashamed of.
Doing &CMD_SingleShot[1] is a step in the right direction. The problem is that CMD_SingleShot[1] is of type const unsigned char and therefore taking the address of that gives you a pointer of type const unsigned char *. This cannot be passed to the Payload parameter since it expects a pointer of unsigned char *. Luckily you don't modify whatever Payload points to, so there is no reason for that to be non-const. Just change the definition of Payload to const unsigned char * and the compiler should be happy.
And by the way, in c, &Foo[n] is the same as Foo + n. Whatever you write is a matter of taste.
Edit:
Sometimes you don't have access to the library source code, and because of bad library design you are forced to cast. In that case, it is up to you to get things right. The compiler will happily shoot you in the foot if you ask it to.
In your case, the correct cast would be (unsigned char *)&CMD_SingleShot[1] and NOT (unsigned char *)CMD_SingleShot[1]. The first case interprets a pointer of one type as a pointer of different type. The second case interprets an unsigned character as a pointer, which is very bad.
Passing the address of the second byte of your command is done with either
&CMD_SingleShot[1]
or
CMD_SingleShot+1
But then you will run into an invalid conversion error since your command is defined as const unsigned char and then &CMD_SingleShot[1] is of type const unsigned char* but your function expects unsigned char*.
What you can do is either change the argument of your function:
int I2C1_Write(char DeviceAddress, unsigned char SubAddress, const unsigned char *Payload, char ByteCnt)
or cast your passing argument:
I2C1_Write(SHT31_Address, 0xFF, (unsigned char*)&CMD_SingleShot[1], CMD_SingleShot[0])
In the latter case be aware that casting away const'ness might result in undefined behaviour when changing it afterwards.
The function call is mostly correct, but since the 3rd parameter of the function is a pointer, you must pass an address to an array accordingly, not a single character. Thus &CMD_SingleShot[1] rather than CMD_SingleShot[1].
if(I2C1_Write(SHT31_Address,
0xFF,
&CMD_SingleShot[1],
CMD_SingleShot[0])
< 1)
However when you do this, you claim that you get "discards qualifiers from pointer target" which is a remark about const correctness - apparently CMD_SingleShot is const (since it's a flash variable or some such?).
That compiler error in turn simply means that the function is incorrectly designed - an I2C write function should clearly not modify the data, just send it. So the most correct fix is to change the function to take const unsigned char *Payload. Study const correctness - if a function does not modify data passed to it by a pointer, then that pointer should be declared as const type*, "pointer to read-only data of type".
If it wouldn't be possible to change the function because you are stuck with an API written by someone else, then you'd have to copy the data into a read/write buffer before passing it to the function. "Casting away" const is almost never correct (though most often works in practice, but without guarantees).
Other concerns:
When programming C in general, and embedded C in particular, you should use stdint.h instead of the default types of C, that are problematic since they have variable sizes.
Never use char (without unsigned) for anything else but actual strings. It has implementation-defined signedness and is generally dangerous - never use it for storing raw data.
When programming an 8 bit MCU, always use uint8_t/int8_t when you know in advance that the variable won't hold any larger values than 8 bits. There are plenty of cases where the compiler simply can't optimize 16 bit values down to 8 bit.
Never use signed or potentially signed operands to the bitwise operators. Code such as (DeviceAddress << 1) & ~0x1 is wildly dangerous. Not only is DeviceAddress potentially signed and could end up negative, it gets implicitly promoted to int. Similarly, 0x1 is of type int and so on 2's complement PIC ~0x1 actually boils down to -2 which is not what you want.
Instead try to u suffix all integer constants and study Implicit type promotion rules.
Related
I wasn't exactly exactly sure what title I should use for this question, but I'll try to clarify through an explanation:
So the long and short of it is that I want a functions argument to accept both an array AND an integer for the same argument. Not too difficult, just use a pointer right? So I have this:
#define BIT0 (unsigned char){0b00000001}
void foo (unsigned char *ptr_bar)
{
printf("%x", *ptr_bar);
}
void main (void)
{
foo(&BIT0);
}
Works all fine and dandy for a regular value, but in my case I need to be able to inverse the value, so I figured this would work:
#define BIT0 (unsigned char){0b00000001}
void foo (unsigned char *ptr_bar)
{
printf("%x", *ptr_bar);
}
void main (void)
{
foo(&~BIT0);
}
Just invert the value with a bitwise NOT (~) when passing it. No biggie right? Well I get the error
error: lvalue required as unary '&' operand
If I rewrite it as:
#define BIT0 (unsigned char){0b00000001}
void foo (unsigned char *ptr_bar)
{
printf("%x", *ptr_bar);
}
void main (void)
{
foo(&(unsigned char){~BIT0});
}
First off, this is a super ugly solution, and I really don't want to have to use it in my code. It doesn't feel intuitive at all.
Second, It works as I wanted it to work initially, but what I believe that this is telling me is that the '~' operator is promoting the unsigned char to an unsigned int. So I have to cast it back to an unsigned char for the function to be able to accept the address. But the error message doesn't really match up with that which is confusing me.
What's going on here? How do I work around this to get the behavior that I want?
The reason for the error is that some operators (like unary *) return a so-called modifiable lvalue, meaning a reference to an object that you can change. Most operators don't do this however and ~ is such an operator. All you get from it is a temporary read-only value. It does indeed also promote the operand to signed int, which is often problematic but not the reason for the error.
As for how to solve the problem, it's in how you use these macros. If BIT0 is a compound literal with bit 0 set, then for the sake of consistently one would probably create a similar macro:
#define BITS1_7 (unsigned char){0xFE}
(Note that binary literals aren't standard C.)
But I wouldn't do that either. The root of the problem is overengineering and insisting on using compound literals in strange ways. Instead apply the KISS principle. If you have a function accepting either a single byte or an array, then the most readable way to write the program would be:
#define BIT0 (1u << 0) // industry de facto standard way to write a bit mask
...
uint8_t tmp = BIT0;
foo(&tmp);
alternatively
uint8_t tmp = ~BIT0;
This code can be read and understood by all C programmers.
I have medium knowledge working with pointers. Some of the syntax styles baffles me.
Like:
*(uint8 *) (a) = (b)
typecasting the reference when parsing it in a function. The function is prototyped as func(unsigned char * a); but it is used as func((unsigned short *) &b); .... As far I understand they were casting a expected char type pointer into a short.
Can anyone help me understand what these statements exactly mean in C?
To understand what definitions mean in C take a look at the right-left rule, it's very simple and explains a lot http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html. What it basically means is that you start out with the name and move right building up the definition then move left only when you can't move right. This might sound weird but let's take an example with number 1.
*(uint8 *) (a) = (b)
So step 1 what is a? Start with the name of the variable which in this case is a. Then look to see what is defined to the right of that, here it is nothing, so move to the part immediately to the right, which is (uint8 ). Which means that a is a pointer to a uint8. Ok now move right from the uint8 and you get back to a so that means you can't move right anymore, so go right then which gets you *, which is the dereferencing a. So for this line the uint8 stored at the address a is set to the value of b.
Ok for the second part I think the func((unsigned short *) &b) is where they begin defining the function body? If that is case then that means for the function body the passed in parameter is used as a pointer to an unsigned short, but if the function is called like this I think the compiler wouldn't be happy because of the type mismatch since the function is expecting an unsigned char * not an unsigned short *.
The first one simply casts (a) into a pointer to an unsigned 8-bit integer, then writes the result of evaluating (b) to the resulting address (as an 8-bit number).
The second one isn't valid, it shouldn't compile. Perhaps you mis-remember, it's a bit sketchy-looking.
I am working on some embedded device which has SDK. It has a method like:
MessageBox(u8*, u8*); // u8 is typedefed unsigned char when I checked
But I have seen in their examples calling code like:
MessageBox("hi","hello");
passing char pointer without cast. Can this be well defined? I am asking because I ran some tool over the code, and it was complaining about above mismatch:
messageBox("Status", "Error calculating \rhash");
diy.c 89 Error 64: Type mismatch (arg. no. 1) (ptrs to signed/unsigned)
diy.c 89 Error 64: Type mismatch (arg. no. 2) (ptrs to signed/unsigned)
Sometimes I get different opinions on this answer and this confuses me even more. So to sum up, by using their API the way described above, is this problem? Will it crash the program?
And also it would be nice to hear what is the correct way then to pass string to SDK methods expecting unsigned char* without causing constraint violation?
It is a constraint violation, so technically it is not well defined, but in practice, it is not a problem. Yet you should cast these arguments to silence these warnings. An alternative to littering your code with ugly casts is to define an inline function:
static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; }
And use that function wherever you need to pass strings to the APIs that (mistakenly) take unsigned char * arguments:
messageBox(ucstr("hi"), ucstr("hello"));
This way you will not get warnings while keeping some type safety.
Also note that messageBox should take const char * arguments. This SDK uses questionable conventions.
The problem comes down to it being implementation-defined whether char is unsigned or signed.
Compilers for which there is no error will be those for which char is actually unsigned. Some of those (notably the ones that are actually C++ compilers, where char and unsigned char are distinct types) will issue a warning. With these compilers, converting the pointer to unsigned char * will be safe.
Compilers which report an error will be those for which char is actually signed. If the compiler (or host) uses an ASCII or similar character set, and the characters in the string are printable, then converting the string to unsigned char * (or, better, to const unsigned char * which avoids dropping constness from string literals) is technically safe. However, those conversions are potentially unsafe for implementations that use different character sets OR for strings that contain non-printable characters (e.g. values of type signed char that are negative, and values of unsigned char greater than 127). I say potentially unsafe, because what happens depends on what the called function does - for example does it check the values of individual characters? does it check the individual bits of individual characters in the string? The latter is, if the called function is well designed, one reason it will accept a pointer to unsigned char *.
What you need to do therefore comes down to what you can assume about the target machine, and its char and unsigned char types - and what the function is doing with its argument. The most general approach (in the sense that it works for all character sets, and regardless of whether char is signed or unsigned) is to create a helper function which copies the array of char to a different array of unsigned char. The working of that helper function will depend on how (and if) you need to handle the conversion of signed char values with values that are negative.
I'm trying to implement some code in an Atmel datasheet. I have directly copied it from the datasheet below.
(unsigned int) (*IAP_Function)(unsigned long);
void main (void)
{
unsigned long FlashSectorNum = 200; //
unsigned long flash_cmd = 0;
unsigned long flash_status = 0;
unsigned long EFCIndex = 0; // 0:EEFC0, 1: EEFC1
/* Initialize the function pointer (retrieve function address from NMI vecto*/
IAP_Function = ((unsigned long) (*)(unsigned long))0x00800008;
/* Send your data to the sector here */
/* build the command to send to EEFC */
flash_cmd = (0x5A << 24) | (FlashSectorNum << 8) | AT91C_MC_FCMD_EWP;
/* Call the IAP function with appropriate command */
flash_status = IAP_Function (EFCIndex, flash_cmd);
}
This enables me to call a function in ROM to program the flash that runs the code in the micro.
I'm using the gcc for ARM Cortex and get an error for the first line:
expected identifier or ‘(’ before ‘unsigned’
Now, I just changed that to:
unsigned int (*IAP_Function)(unsigned long);
As it looked wrong (not sure why the return value from a function should be in brwackets) and that error went away.
However, this line:
IAP_Function = ((unsigned long) (*)(unsigned long))0x00800008;
Throws up the error:
expected expression before ‘)’ token
With the cursor being by the ) next to the *.
Unsurprisingly, I also get the error:
too many arguments to function ‘IAP_Function’
For the line:
flash_status = IAP_Function (EFCIndex, flash_cmd);
I suspect the datasheet has been poorly written in terms of that last error and, indeed, the first error.
However, the line:
IAP_Function = ((unsigned long) (*)(unsigned long))0x00800008;
I do not understand. I believe, it is trying to get the contents of 0x00800008, and use that as the pointer to the function. If that is correct, how should I write that line?
Also, are my other assumptions correct in terms of ignoring the brackets around unsigned int in the first line for the value returned from the function? Also, I assume I should just change that first line to be:
unsigned int (*IAP_Function)(unsigned long, unsigned long);
As the function call needs two values?
Many thanks :)
Line IAP_Function = ((unsigned long) (*)(unsigned long))0x00800008; casts integer to a function pointer address and assigns it to IAP_Function. Cast is required, because any self-respecting compiler should throw warning if you try to assign integer to pointer without a cast.
But as you noted return type doesn't match and for some reason there are parenthesis around it. So proper cast would look like:
IAP_Function = (unsigned int (*)(unsigned long))0x00800008;
And if you need another parameter, do add that also. Datasheet code examples are often lies, so do read carefully what manual says about the function parameters.
You should consider declaring typedef of function pointer to make sense out of this madness:
typedef unsigned (*FuncType)(unsigned long, unsigned long);
FuncType IAP_Function;
...
IAP_Function = (FuncType)0x00800008; // Nice and simple
Actually, IAP_Function = ((unsigned long) (*)(unsigned long))0x00800008; doesn't uses content of 0x00800008 as a pointer to function. It uses 0x00800008 as the address itself.
In your definition you declared IAP_Function as a function that returns unsigned int, however, when you're casting you cast to function that returns unsigned long.
Also, function takes just one argument and you're passing two. You should either change your cast and function declaration or pass only one argument.
To match the call at the bottom, the function pointer declaration at the top should be
unsigned long (*IAP_Function)(unsigned long, unsigned long);
However, the correct thing to do depends on the function that is assigned to the pointer, which you have not described.
It is out of the primary topic, but I report my result, as I found first this page for my original problem with the IAP_Function() in question.
After some tries I successfully used the IAP_Function() (a clear GPNVM Bit operation) with SAM3A8C, in the following way. The address in not 0x00800008, but 0x00100008 (I found this tip elsewhere), and IAP_Function() takes only one long argument, as described in chapter 20.5.2 of the Atmel manual. I don't know if the 0x00800008 address works similarly, but with two arguments (with EFCIndex), or these are mistypings in the manual.
I have a structure to represent strings in memory looking like this:
typedef struct {
size_t l;
char *s;
} str_t;
I believe using size_t makes sense for specifying the length of a char string. I'd also like to print this string using printf("%.*s\n", str.l, str.s). However, the * precision expects an int argument, not size_t. I haven't been able to find anything relevant about this. Is there someway to use this structure correctly, without a cast to int in the printf() call?
printf("%.*s\n", (int)str.l, str.s)
// ^^^^^ use a type cast
Edit
OK, I didn't read the question properly. You don't want to use a type cast, but I think, in this case: tough.
Either that or simply use fwrite
fwrite(str.s, str.l, 1, stdout);
printf("\n");
You could do a macro
#define STR2(STR) (int const){ (STR).l }, (char const*const){ (STR).s }
and then use this as printf("%.*s\n", STR2(str)).
Beware that this evaluates STR twice, so be carefull with side effects, but you probably knew that already.
Edit:
I am using compound initializers such that these are implicit conversions. If things go wrong there are more chances that the compiler will warn you than with an explicit cast.
E.g if STR has a field .l that is a pointer and you'd only put a cast to int, all compilers would happily convert that pointer to int. Similar for the .s field this really has to correspond to a char* or something compatible, otherwise you'd see a warning or error.
There is no guarantee that the size_t is an int, or that it can be represented within an int. It's just part of C's legacy in not defining the exact size of an int, coupled with concerns that size_t's implementation might need to be leveraged to address large memory areas (ones that have more than MAX_INT values in them).
The most common error concerning size_t is to assume that it is equivalent to unsigned int. Such old bugs were common, and from personal experience it makes porting from a 32 bit to a 64 bit architecture a pain, as you need to undo this assumption.
At best, you can use a cast. If you really want to get rid of the cast, you could alternatively discard the use of size_t.