I'm trying to make an analogue of sscanf with a specifier %p.
I use this:
int res = ahex2num(buf);
*va_arg(ap, void **) = (void *) res;
It works correctly, i actually get the address i pass, like 0x1A but i am facing this error:
warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
In main function:
int main(){
void *a;
readFromStr("0x1A", "%p", &a);
printf("%p", a);
return 0;
}
/*
out: 0x1a
*/
Can I somehow avoid this?
long ahex2num(unsigned char *in){
unsigned char *pin = in;
long out = 0;
while(*pin != 0){
out <<= 4;
out += (*pin < 'A') ? *pin & 0xF : (*pin & 0x7) + 9;
pin++;
}
return out;
}
Apparently pointers, particularly void *, have a different size than int on your system. E.g., pointers may be 64 bits and int may be 32 bits. Implementing %p in a routine like sscanf is a valid reason for converting an integer to void *, but you need to use an integer type that can hold all the bits needed for a pointer. A good type for this may be uintptr_t, declared in <stdint.h>.
You will need to ensure all the code that works with the integers from the scanning, such as ahex2num, can support the necessary width and signedness, including handling potential overflow as desired.
If I had your entire code, I could test it. I assume to remove the warning without using a pragma is as simple as changing your typecast from int to long int.
I solved this problem like this:
long long int res = ahex2num(buf);
Related
void main() {
unsigned int a = 0;
if (a - 10 < 0) {
printf("error!\n");
}
}
we know this comparison won't work, because a-10 will be a big unsigned integer, it can't be smaller than 0.
To avoid this situation, I try this:
void main() {
unsigned int a = 0;
int b = 0;// or const int b = 0;
if (a - 10 < b) {
printf("error!\n");
}
}
this will get warning C4018 using Visual Studio 2022 17.2.4.
However, when I use gcc 4.8.5, there is no warning at all.
Is there a way to avoid coder compare signed number with unsigned variable?
Update:
a more complex situation could be this:
struct s{
unsigned int len;
char *buffer;
} *a;
int not_safe(struct s *ptr){
if(ptr->len - sizeof(struct s) < 0){
return 0;
}
return 1;
}
Programmers may not be aware of such comparisons are wrong. I
hope we can have a safe way to let programmer avoid this.
If you cast the unsigned int operand to int:
if ((int)a - 10 < 0)
Then all of the math with be done using type int.
Or, you can do a little bit of algebra:
if (a < 10)
To avoid the problem completely.
Please study what C formally calls the usual arithmetic conversions Implicit type promotion rules. The TL;DR is that in case you have two integers of the same size but different signedness, the signed one will get converted to unsigned.
You could explicitly cast the unsigned operand to signed. Or you could let the signed operand be a larger type than the unsigned, such as int64_t, in which case the unsigned int (likely 16 or 32 bits) will get converted to int64_t.
But that won't solve your root problem which is this line:
if(ptr->len - sizeof(struct s) < 0)
This is doesn't make any sense to begin with. And casting ptr->len to int64_t might not help either size sizeof returns a size_t, which is guaranteed to be a large unsigned integer type. Simply replace this with:
if(sizeof(struct s) > ptr->len)
Or if you will, change the whole function to one following common best practices:
bool not_safe (const struct s* ptr) {
return sizeof(struct s) > ptr->len;
}
Maybe I confused people with my example. I was trying to understand a part of the code and simplyfied it. Here is part of the original code (simplyfied again... :)) (see original post below).
uint16_t hal_nrf_read_multibyte_reg(uint8_t *pbuf)
{
uint8_t memtype;
memtype = *(uint8_t*)(&pbuf);
if (memtype == 0x00U)
{
uint8_t data *buf = (uint8_t data *)pbuf;
DOTHIS
}
if (memtype == 0x01U)
{
uint8_t xdata *buf = (uint8_t data *)pbuf;
DOTHAT
}
if (memtype == 0xFEU)
{
uint8_t pdata *buf = (uint8_t data *)pbuf;
DOSOMETHING
}
return SOMETHING;
}
void main()
{
uint8_t payload[3];
hal_nrf_read_multibyte_reg(payload);
while(1) { }
}
So I was wondering, why do they cast pbuf which already is of uint8_t. But I think I've got my answer now.
------------ OLD POST -------------
I'm exploring Nordic Semiconductors nRF24LE1.
If I have the following test code.
void tempF(int *test)
{
int varA;
int varB;
int varC;
varA = *(int*)(&test); // The way it is done in the source code
varB = *(&test);
varC = test;
printf("A: %x\n", varA);
printf("B: %x\n", varB);
printf("C: %x\n", varC);
printf("C1: %x\n", test);
if (test == 0x00)
printf("equals 0x00");
}
int main(void) {
int myArray[3];
tempF(myArray);
return 0;
}
The printfs all give the same reply.
What is the reason for doing it "varA-style"? Examples where it is necessary?
If I use the way in varA I don't get the warning "Warning C260: '=': pointer truncation.
Your three samples are all basically converting a pointer into an int. Technically, this requires a cast in your cases B and C, and your compiler ought to warn you about that. For example:
int varC = (int) test;
With the cast, that is completely valid, but without, not. Nevertheless, your compiler probably produces the same code with or without.
In your example code, however, the type of the expression &test is int **. Casting an expression of that type to int * and dereferencing the result, as is done to assign a value to varA, is intended to have the effect of reinterpreting the bytes of test as those of an int, as with a C++ reinterpret_cast. This does not necessarily produce the same value as converting test directly to an int, as is done to assign a value to varC. They are especially prone to differ if the size of a pointer is not the same as the size of an int on the target system, but they are not required to produce the same result even if the sizes are the same.
On the other hand, applying the * operator directly to the result of the & operator has no net effect, so the value computed for varB will reliably be the same as that computed for varC.
The problem is that any pointer type need not be of same size as an int. The compiler truies to warn you about that fact.
Using (int *)(&test) casts the address of test to be a pointer to int.
Dereferencing this yields an int that happily can be assigned to an int variable. It may still be truncated if pointers need more bits than an int can hold, but you convinvced the compiler that you do know what you are doing and it happens by purpose.
Given that your example varibles are actually int:
int varA;
int varB;
int varC;
Without using the GCC compiler versions 4.4.7 or newer and using stdio.h as noted in comments, the code does not compile, the second two of your statements will error out because of illegal types 'int' and 'pointer to int'
varA = *(int*)(&test); // The way it is done in the source code
varB = *(&test);//error
varC = test; //error
If they were int *
int *varA;
int *varB;
int *varC;
Then the first statement: varA = *(int*)(&test); would error out.
The only way the assignment statements will compile is with the variables declared as follows:
int varA;
int *varB;
int *varC;
varA = *(int*)(&test); // The way it is done in the source code
varB = *(&test);
varC = test;
varA = *(int*)(&test); means interpret the bitwise representation of test as an int, then store it in var.
The cast, (int *), indicates that the bitwise representation of test should be interpreted as an int, while the * operator interprets it as an int.
This is identical to memcpy(&varA, &test, sizeof varA);, if sizeof (int) == sizeof (int *).
learning C and now i study simple code snipper that show byte representation of primitive values:
typedef unsigned char *byte_pointer;
void show_bytes(byte_pointer start, int len) {
int i;
for (i = 0; i < len; i++)
printf(" %.2x", start[i]);
printf("\n");
}
void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}
void show_int(int x) {
show_bytes((byte_pointer) &x, sizeof(int));
}
void show_pointer(void *x) {
show_bytes((byte_pointer) &x, sizeof(void *));
}
If i understand correct, &x (an ampersand character) showing address of memory (equal to *x).
So. program routine is showing hexadecimal values of each data type, with int value of bytes like (sizeof(int)).
Im not really understand how its work. First, we typedef pointer of unsigned char, and then use it with other types. What is the meaning of (byte_pointer) &x and why does it work, when we define byte_pointer as value of type unsigned char? I understand that we get address of memory that contain value, but i don't know how exactly it work and WHY it work with char pointer. Could you explain that part?
Thanks.
The code simply takes the address of a random chunk of data and prints the contents byte by byte. The code takes the address of whatever you pass to it, then converts it to a pointer-to-byte (unsigned char). Any pointer type in C can be converted to another pointer type, although in some cases doing so is dangerous practice. In the case of char, it is safe though, you are guaranteed to get a pointer to the lowest addressed byte of the object.
Note that hiding a pointer behind a typedef is bad and dangerous practice. Just forget about that typedef, it adds nothing of value. A better way to write the same code would be:
void show_bytes (const uint8_t* start, int len)
or alternatively
void show_bytes (const void* s, int len)
{
const uint8_t* start = s;
...
byte_pointer is defined to be a pointer to an unsigned char; this is so show_bytes can print out each individual byte (in hexadecimal) of what the address passed to show_bytes points to.
I would have declared start to be a void*, and then cast it inside of show_bytes, making it a) clearer that show_bytes doesn't care what type of thing start points to, and b) avoids the cast in every call.
I have a "generic" linked link in C that takes void * data to store the data in a Node.
insertNode(linkedList * list, void *data);
//Storing/retrieving a string works fine;
char *str="test";
insertNode(list, str);
char *getback=(char *)node->data;
//Storing/retrieving an Int results a cast warning
int num=1;
insertNode(list,(void *)num);
int getback=(int)node->data;
This is because int is 32 bit, but void * is 64 bit on x64 machine. What is the best practice to get rid of this error?
Use intptr_t or uintptr_t. They are integers of the same size as a pointer:
#include <stdint.h>
...
intptr_t num = 1;
insertNode(list, (void *) num);
intptr_t getback = (intptr_t) node->data;
Of course, the maximum value that you can store depends on the system, but you can examine it at compile time via INTPTR_MIN and INTPTR_MAX.
Using a void * member to store an integer value isn't very good practice - apart from anything else, there's no guarantee what range of integers are storable in this way. If you want to store either a pointer or an integer in your structure, then what you should be using is a union:
union data {
void *p;
int i;
};
insertNode(linkedList * list, union data data);
// Storing/retrieving a string:
char *str="test";
union data d;
d.p = str;
insertNode(list, d);
char *getback = node->data.p;
// Storing/retrieving an int:
int num = 1;
union data d;
d.i = num;
insertNode(list, d);
int getback = node->data.i;
If you have a compiler that supports C99 constructs, you can make it a little neater by avoiding the local union variable:
// Insert a string
insertNode(list, (union data){ .p = str});
// Insert an int
insertNode(list, (union data){ .i = num});
You can cast it twice: insertNode( list, (void *)(uint64_t) num);
Or you can use "unsigned long" for num instead of int. At least in my experience, sizeof(unsigned long) == sizeof(void *) on both 32 and 64-bit systems. Using uintptr_t may be more correct; it's been a while since I've read the standards.
Are you sure you're doing what you actually want to be doing? In the first example with the string you are passing the pointer to the string literal to insertNode, while in the second example ((void *)num) you are casting the value of the int to a pointer to void. Did you actually want to do this: insertNode(list,(void *)&num);, i.e., passing the reference to the data like you were doing in the first example?
Also as an aside, char *str="test"; makes str point to a string literal, which is read-only. So you really should be doing const char *str="test";. If you wanted a normal (writable) string then char str[] = "test";.
Apologies if you already knew these things and actually wanted to store an int in a void *, I just can't imagine why you would.
Edit: If you actually want generic storage space then a union would be a safe approach. There is absolutely no guarantee that an int will fit in a void * in standard C, even though in most implementations it probably does.
I am a linguist in charge of a C program, so please excuse me if the answer is obvious. I have the following code:
typedef struct array_s {
(...)
void **value;
} array_t;
typedef array_t *array_pt;
array_pt array_new (int size) {
(...)
array->value = (void **)malloc(size*sizeof(void *));
}
void* array_get (array_pt arr, int i) {
return arr->value[i];
}
int main () {
int a = 1234;
int *ptr = &a;
array_pt array = array_new(1);
array_add(array, ptr);
printf("%i\n", (int)array_get(array, 0));
}
It is supposed to provide me with a multi-purpose array (for storing int and char*, if I understood I can only use void), and I guess there are no problems of allocating/freeing. However, I cannot get to cast it into anything useful (i.e., get back the "original" int/char*), and for what I understood it could be because I am in a 64-bit system and the size of a pointer to void is different from the size of a pointer to int/char* (the program is supposed to be used in both 64 and 32 bit systems). I tried using intptr_t and other alternatives, to no luck.
How can I be sure that the code will accept any data type and work on both 32 and 64 bit systems? Thank you.
EDIT:
Sorry for not adding array_add, here it is:
unsigned int array_add (array_pt array, void *ptr) {
(...) // get the next index
// allocate if needed
array->value = (void **)realloc(array->value, array->size*sizeof(void *));
array->value[index] = p;
}
You need to dereference your pointer:
int* temp = array_get(array, 0);
printf("%i\n", *temp);
However, I strongly recommend avoiding this type of approach. You're basically giving away the small amount of help the compiler in C will normally provide - purposefully trying to make non-typesafe arrays.
You need to decide what is it you are trying to do in this case.
(1) If you want to use your void * array to store int values (actual int forcefully converted to void *), then you should add these int values to the array as follows
int a = 1234;
array_pt array = array_new(1);
array_add(array, (void *) a);
and then get them back from array as follows
int a = (int) array_get(array, 0);
printf ("%d\n", a);
or simply
printf ("%d\n", (int) array_get(array, 0)));
That last part is exactly what you did, but you got the first part wrong.
This is a cast-based approach, which is ugly in many ways, but it has certain practical value, and it will work assuming void * is large enough to hold an int. This is the approach that might depend on the properties of 32- and 64-bit systems.
(2) If you want to use your void * array to store int * values (pointers to int), then you should add these int values to the array as follows
int a = 1234;
array_pt array = array_new(1);
array_add(array, &a);
and then get them back from array as follows
int *pa = array_get(array, 0);
printf ("%d\n", *pa);
or simply
printf ("%d\n", *(int *) array_get(array, 0));
This approach is perfectly safe from any portability problems. It has no 32- or 64-bit issues. A void * pointer is guaranteed to safely hold a int * pointer or any other data pointer.
If that was your intent, then you got the first part right and the last part wrong.
Either this or that. You code appears to be a strange mix of the two, which is why it doesn't work, and which is why it is impossible to figure out from your original message which approach you were trying to use.
intmax_t should be an integer type that is 32 bits on 32bits compilers and 64bits on 64bit compilers. You could use %j in your printf statement to print intmax_t. The size of pointers on one system is always the same - independently of them pointing to int, char or void.
printf("%j\n", (intmax_t)array_get(array, 0));