Why -Wsign-conversion compiler warning when using malloc with a struct value? - c

When compiling code accessing a constant from a struct to use in malloc, I get a -Wsign-conversion warning when using gcc -Wconversion sample.c:
sample.c:12:45: warning: implicit conversion changes signedness: 'int' to 'unsigned long' [-Wsign-conversion]
unsigned char *numbers = malloc(s.value * 100 * sizeof(unsigned char));
1 warning generated.
The C code with the warning is this:
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
const int value;
} MyStruct;
int main(int argc, char *argv[])
{
MyStruct s = {5000};
unsigned char *numbers = malloc(s.value * 100 * sizeof(unsigned char));
printf("Address of numbers: %p\n", &numbers);
}
If I don't use a struct, and instead directly provide the value, there is no warning using gcc -Wconversion sample.c:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
unsigned char *numbers = malloc(500 * 100 * sizeof(unsigned char));
printf("Address of numbers: %p\n", &numbers);
}
What is the correct way to use malloc with a struct value when using the -Wconversion flag?

You can change your struct declaration to have a const unsigned int, or just cast into an unsigned int the s.value into the malloc() function, as it requires unsigned arguments (even size_t to be precise).
void *malloc(size_t size);

In this struct
typedef struct
{
const int value;
} MyStruct;
the member value seems to have all words in its declaration wrong
misleading type
misleading name
an odd qualifier (why do you want to have a const-qualified member here?)
Since the "value" is used as a multiplier in call to malloc this would hint that it is not a generic "value" but a size of an array, so perhaps it would better be unsigned - and even size_t altogether; and its name be something like size, length or so.
The warning is because if the value indeed is a signed -1, by multiplying it by 100 you will get -100 but then by multiplying by a sizeof value of type size_t, that would be converted to size_t and as the result the number of bytes allocated would be (SIZE_MAX - 100) * 1 which is hardly what you'd wish for.
Better be explicit and use a declaration that conveys the meaning:
typedef struct
{
size_t size;
} MyStruct;

One problem in the code is that s.value * 100 might overflow INT_MAX, causing undefined behaviour.
gcc continually tweaks exactly which code does and doesn't trigger -Wsign-conversion since it has so many false positives, so you might get different results with different versions.
To fix this you can change to s.value * 100UL (and ditch the sizeof(unsigned char) which is worse than redundant). But think about what would happen if s.valueis very large, you could consider doing a check that the value is not too big (say, underSIZE_MAX/200` at least!) before doing the multiplication.

Related

Resolving munmap warning message

I'm writing a program and have gotten a memory location that I have stored as a unsigned int and the length of the mapping as an unsigned int and I want to unmap this.
My following approach generates the warnings:
warning: passing argument 1 of ‘munmap’ makes pointer from integer without a cast [enabled by default]
/usr/include/i386-linux-gnu/sys/mman.h:77:12: note: expected ‘void *’ but argument is of type ‘unsigned int’
and here is causing me the warning:
//startAddr and addrRange are stored as an unsigned int,
void unmap(mapping_t *maps, const int *curSize){
int i = 0;
for (; i < *curSize; i++){
munmap(maps[i].startAddr, maps[i].addrRange);
}
}
My program also crashes when I hit the munmap, but I am assuming that has to deal with the warning in some way
definition of struct mapping_t as requested:
typedef struct mapping{
unsigned int startAddr;
unsigned int endAddr;
unsigned int addrRange;
} mapping_t;
I'm writing a program and have gotten a memory location that I have stored as a unsigned int
Do not do that. Use void *, char *, or even [u]intptr_t. Do not stuff a pointer into an unsigned int. That is wrong. Pointers are not int values and may not be properly represented by an int, which is why you get a warning. Pointers are allowed to be converted to an int per the C standard - which is why you get a warning instead of an actual error - but there's no guarantee that the conversion back to a pointer value results in the same address.
and the length of the mapping as an unsigned int and I want to unmap this.
Do not do this either. Use size_t:
typedef struct mapping{
void *startAddr;
size_t addrRange;
} mapping_t;
You don't need endAddr as you have the start address and the size. If you need the end address, you need to convert startAddr to a char * to compute the end address.
you cannot use unsigned int for pointers. Use void *.
typedef struct mapping{
void * startAddr;
void * endAddr;
unsigned int addrRange;
}
mapping_t;

Why cast the pointer?

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 *).

code snippet warning: cast to pointer from integer of different size

In the code snippet below, i get this warning - "warning: cast to pointer from integer of different size" in line number 25
which is the 3rd printf i do,
printf("value of a thru struct ptr=%d\n",(unsigned char *)m_arr(ptr_ns_type)[0]);
I dont understand this warning since i do the same thing (except not using macros) in the 2nd printf
printf("value of a thru ptr=%d\n",(unsigned char)*ptr);
for which i dont get any error. could anyone help me in understanding this warning please?
Thanks,
Badri.
#include<stdio.h>
struct ns
{
int i;
unsigned char a[2];
};
#define m_arr(whatever) ((struct ns *)whatever)->a
int main()
{
unsigned char arr[2];
unsigned char brr[2];
struct ns ns_type;
struct ns *ptr_ns_type;
arr[0]=192;
arr[1]=168;
brr[0]=172;
brr[1]=188;
ns_type.i=5;
ns_type.a[0]=brr[0];
ptr_ns_type = &ns_type;
unsigned char *ptr=arr;
printf("value of a=%d\n",arr[0]);
printf("value of a thru ptr=%d\n",(unsigned char)*ptr);
printf("value of a thru struct ptr=%d\n",(unsigned char *)m_arr(ptr_ns_type)[0]);
return 0;
}
You don't need a cast here:
printf("value of a thru ptr=%d\n",(unsigned char)*ptr);
because ptr is a pointer to unsigned char, so *ptr is already an unsigned char.
However, here:
(unsigned char *)m_arr(ptr_ns_type)[0]
which translates to:
(unsigned char *)((struct ns *)ptr_ns_type)->a[0]
you are trying to cast unsigned char to a pointer to an unsigned char. I think what you want is:
((struct ns *)ptr_ns_type)->a[0]
but also note that the (struct ns *) cast is not necessary.
Actually, you aren't doing the same thing at all. Try getting rid of the macro, by doing the text substitution by hand. For example, lets create a variable tmp that is of type unsigned char*:
unsigned char* tmp1 = (unsigned char *) ((struct ns *)ptr_ns_type)->a[0];
You will get the same warning. So the macro has nothing to do with it. Further, the casting is to struct ns * is superfluous and evil since it removes valuable type checking. So we can get rid of it to get clearer code:
unsigned char* tmp1 = (unsigned char *) ptr_ns_type->a[0];
Of course, you still get the completely correct warning, but hopefulll it is now clear what is going on. When you call ptr_ns_type->a[0], you are dereferencing ptr_ns_type to obtain the array a, and using the [] operator to get the value of a[0], which is an unsigned char, and not a pointer to an unsigned char.
If you want to obtain the address of a[0], you can do so by leaving out the [0] operation:
unsigned char* tmp1 = (unsigned char *) ptr_ns_type->a;
I assume this is an exercise to help you undertand the fine points of pointers, arrays, macros and casts. Please learn the following lessons:
1. Macros are evil. Only use them when they increade the clarity of your code.
2. Avoid unecessary casting. It's unsafe.
3. Simplify, simplify. Once we simplified the code in question, the error became immediately obvious.

warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]

I was learning C with a book called "C in Linux" by David Haskins
but there's a problem. When i try to compile this code:
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[], char *env[]) {
printf("Content-type:text/html\n\n<html><body bgcolor=#23abe2>\n");
char value[256] = "";
strncpy(value,(char *) getenv("QUERY_STRING"), 255);
printf("QUERY_STRING:%s<BR>\n", value );
printf("<form>\n");
printf("<input type=\"TEXT\" name=\"ITEM1\"> \n");
printf("<input type=\"TEXT\" name=\"ITEM2\"> \n");
printf("<input type=\"SUBMIT\">");
printf("</form></body></html>\n");
return 0;
}
The terminal shows this warning!
chapter4_1.c: In function ‘main’:
chapter4_1.c:14:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
You forgot to #include <stdlib.h>. This means that getenv() isn't declared anywhere, so it's assumed to return an int by default, which you're casting to char *. On a 64-bit machine, int (32 bits) and char * (64 bits) have different sizes, hence the warning.
As an aside, the cast to char * is not necessary, since getenv() already returns a char *. The cast only serves to mask errors (i.e. without it, the program would have given you a clear error message about passing an int to a char *).

Cast 32 bit int to 64 void * pointer without warning

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.

Resources