How do I get rid of the following sign-conversion warning? - c

I am getting the following warning whenever the function initSetArray() is called :
error: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Werror=sign-conversion]
setarray = (set*)malloc(sizeof(set) * number_of_sets);
The function initSetArray simply initializes the setarray.
void initSetArray(set *setarray, int number_of_sets, int number_of_blocks)
{
setarray = (set*)malloc(sizeof(set) * number_of_sets);
}
I have defined two structures which are used in the helper function defined above:
typedef struct{
uint64_t tag; // For identifying the block
uint8_t valid; // Valid bit for the block
uint8_t dirty; // Dirty bit for the block
} block;
typedef struct{
uint64_t index; // For identifying the set
block* way;
} set;
I cannot exactly find out which variable is of type "long unsigned int". What can I do to resolve this issue?

In this statement
setarray = (set*)malloc(sizeof(set) * number_of_sets);
the variable number_of_sets is an integer (int), and because it is used in an expression with sizeof (size_t), the value is converted to match.
size_t is usually an unsigned long. If you don't like the warning, this would fix it:
setarray = (set*)malloc(sizeof(set) * (size_t) number_of_sets);

The malloc function takes a size_t argument, and size_t is defined for your build as unsigned long int (as it frequently is). In your call:
setarray = (set*)malloc(sizeof(set) * number_of_sets);
you are multiplying such a size_t value (the sizeof operator gives a size_t) by a (signed) int variable - hence the warning.
To avoid this, either explicitly cast number_of_sets to a size_t, like this:
setarray = (set*)malloc(sizeof(set) * (size_t)number_of_sets);
Or, better, change the type of that argument to a size_t:
void initSetArray(set *setarray, size_t number_of_sets, size_t number_of_blocks)
{
setarray = (set*)malloc(sizeof(set) * number_of_sets);
}
Generally, when using variables that represent the 'count' or 'size' of objects, an unsigned int is prefereable (unless you can really have a negative count or size).

The warning is produced under very strict warning settings because the value number_of_sets is converted implicitly from int to the unsigned type unsigned long int, which may have a counter-intuitive value for negative values of number_of_sets.
To silence this warning, you can either:
change the prototype of initSetArray to fix the type of the arguments: number_of_sets and numer_of_blocks should probably be unsigned values with type size_t anyway:
void initSetArray(set *setarray, size_t number_of_sets, size_t number_of_blocks)
alternately, you can add an explicit conversion using the cast operator:
void initSetArray(set *setarray, int number_of_sets, int number_of_blocks) {
setarray = (set*)malloc(sizeof(set) * (size_t)number_of_sets);
}
Note however that setting the value of argument setarray has no effect on the caller's variable that is used as an argument to initSetArray. You should either return the pointer or take a pointer to a pointer argument.

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

What is the meaning of (unsigned char *)&variable_name?

what is the meaning of (unsigned char*)&ch in the following function call ?
HAL_UART_Transmit(&UartHandle, (unsigned char *)&ch, 1, 0xFFFF);
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, unsigned char *pData, int Size, long Timeout)
{
/*...........Function Body ........*/
}
&ch is an address of some variable, type is unknown given this code. (unsigned int*)&ch is simply casting the result of this expression to a pointer to int.
It takes the address of the variable ch using the address-of operator &. The resulting address is then converted (cast) to the type unsigned int *, i.e. a pointer to an unsigned int. This only makes sense if
The type of ch was not unsigned int to begin with
The size of ch is at least as large as the size of unsigned int
The called function accepts an unsigned int * argument
Since we can see that the type of the argument is in fact uint8_t *, this is very likely a bug. The cast should either be removed (if ch is of type uint8_t already, which it should be) or changed to uint8_t *. Also, the function's parameter should be const, a transmit function shouln't change its argument.
Let's say you have a function that takes an argument of type unsigned int *.
You have an object of type int and you want to pass a pointer to this object the function.
int ch = 42;
Then &ch get you an int * to the int object. You cannot pass &ch directly to your function as &ch is of type int * but the function wants a unsigned int *, so you convert it to unsigned int * with a cast:
(unsigned int *) &ch

C cast void * to type_t

I have following code from an object-oriented programming C book:
{
struct Set { int count; };
struct Set * set = malloc(sizeof(struct Set));
void * p = set;
const size_t size = * (const size_t *) p;
}
I cant understand how and why last line works. size_t size is dereferenced value of pointer of type size_t. pointer of type type_t is cast from void* p.
What is happening when I cast void* to type_t*, I could not find any information in the book or online tutorials. Can someone explain it to me or refer me to a good tutorial?
So what happens here is the following: You have a pointer to a structure (p) and you cast it to a const size_t * pointer, and use the value resulted. Supposedly the value should be the same as the value of p->count however do not really count on this. According to Can I trust sizeof(size_t) <= sizeof(unsigned long int) is always true? int and size_t must not have the same size, so you well might end up with accessing memory which is not yours.
Here, the void * p is being casted to const size_t * type and used as the initializer for the const size_t size variable. The value in the address [of type const size_t ]held by p is being used.
When you cast void* p to size_t* p you are telling the compiler that p is pointing to a value of type size_t
In fact p is pointing to a a Set structure, which happens to contain a single int. The code is assuming that the type size_t is the same size as int, which the standard seems to suggest it will be
However size_t is unsigned, so if the value in the int is negative it will not be read correctly. Currently the data in the Set structure is uninitialised, so the result will be random

Writing a generic function in C, how to handle strings

I have a function that takes a void** argument and an integer that indicates its datatype
void foo (void** values, int datatype)
Inside the function, depending on the datatype, I malloc it this way:
if (datatype == 1)
*values = (int*) malloc (5 * sizeof(int));
else if (datatype == 2)
*values = (float*) malloc (5 * sizeof(float));
All is good upto now. However, when character strings come into the picture, things get complicated. The void** would need to be void***, since I will need to do something like this:
*values = (char**) malloc (5 * sizeof(char*));
for(i=0;i<5;i++)
(*values)[i] = (char*) malloc (10);
..
strncpy( (*values)[0], "hello", 5);
How should such a situation be handled?
Can I pass a char*** to the function that expects a void** but cast it correctly inside it?
void foo (void** values, int datatype) {
if(datatype == 3) {
char*** tmp_vals = (char***) values;
*tmp_vals = (char**) malloc (5 * sizeof(char*));
...
(*tmp_vals)[i] = (char*) malloc (10 * sizeof(char));
strncpy ( (*tmp_vals)[i], "hello", 5);
}
So I just cast the void** into a char***. I tried this and ignoring the warnings, it worked fine.
But is this safe? Is there a more graceful alternative?
How should such a situation be handled? Can I pass a char*** to the function that expects a void** but cast it correctly inside it?
No, that's technically Undefined Behavior. It may appear to work on your computer, but it may fail on some future computer that implements different pointer types with different representations, which is allowed by the C language standard.
If your function expects a void**, then you better pass it a void**. Any pointer type can be implicitly converted to void*, but that only works at the top level: char* can be converted to void*, and char** can be implicitly converted to void* (because char** is "pointer to char*"), but char** cannot be converted to void**, and likewise char*** also cannot be converted to void**.
The proper way to call this function is to pass it a proper void**, then cast the resulting void* pointer back to its original type:
void foo(void **values, int datatype)
{
if(datatype == 3)
{
char ***str_values = ...;
*values = str_values; // Implicit cast from char*** to void*
}
else
...
}
...
void *values;
foo(&values, 2);
char ***real_values = (char ***)values;
Assuming that *values was actually pointed to a char***, then this cast is valid and does not have any Undefined Behavior in any of the code paths.
A void * is just a pointer to an unspecified type; it could be a pointer to an int, or a char, or a char *, or a char **, or anything you wanted, as long as you ensure that when you dereference, you treat it as the appropriate type (or one which the original type could safely be interpreted as).
Thus, a void ** is just a pointer to a void *, which could be a pointer to any type you want such as a char *. So yes, if you are allocating arrays of some types of objects, and in one case those objects are char *, then you could use a void ** to refer to them, giving you something that could be referred to as a char ***.
It's generally uncommon to see this construction directly, because usually you attach some type or length information to the array, rather than having a char *** you have a struct typed_object **foo or something of the sort where struct typed_object has a type tag and the pointer, and you cast the pointer you extract from those elements to the appropriate types, or you have a struct typed_array *foo which is a struct that contains a type and an array.
A couple of notes on style. For one, doing this kind of thing can make your code hard to read. Be very careful to structure it and document it clearly so that people (including yourself) can figure out what's going on. Also, don't cast the result of malloc; the void * automatically promotes to the type its assigned to, and casting the result of malloc can lead to subtle bugs if you forget to include <stdlib.h> or your update the type declaration but forget to update the cast. See this question for more info.
And it's generally a good habit to attach the * in a declaration to the variable name, not the type name, as that's how it actually parses. The following declares one char and one char *, but if you write it the way you've been writing them, you might expect it to declare two char *:
char *foo, bar;
Or written the other way:
char* foo, bar;
You don't need to (and probably shouldn't) use a void ** at all - just use a regular void *. Per C11 6.3.2.3.1, "a pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer." A pointer variable, including a pointer to another pointer, is an object. void ** is not "a pointer to void". You can convert freely and safely to and from void *, but you're not guaranteed to be able to convert safely to and from void **.
So you can just do:
void foo (void* values, int datatype) {
if ( datatype == 1 ) {
int ** pnvalues = values;
*pnvalues = malloc(5 * sizeof int);
/* Rest of function */
}
and so on, and then call it similar to:
int * new_int_array;
foo(&new_int_array, 1);
&new_int_array is of type int **, which will get implicitly converted to void * by foo(), and foo() will convert it back to type int ** and dereference it to indirectly modify new_int_array to point to the new memory it has dynamically allocated.
For a pointer to an dynamic array of strings:
void foo (void* values, int datatype) {
/* Deal with previous datatypes */
} else if ( datatype == 3 ) {
char *** psvalues = values;
*psvalues = malloc(5 * sizeof char *);
*psvalues[0] = malloc(5);
/* Rest of function */
}
and so on, and call it:
char ** new_string_array;
foo(&new_string_array, 3);
Similarly, &new_string_array is type char ***, again gets implicitly converted to void *, and foo() converts it back and indirectly makes new_string_array point to the newly allocated blocks of memory.
There is a builtin mechanism to do this already with the added bonus that it allows a variable number of arguments. It is commonly seen in this format yourfunc(char * format_string,...)
/*_Just for reference_ the functions required for variable arguments can be defined as:
#define va_list char*
#define va_arg(ap,type) (*(type *)(((ap)+=(((sizeof(type))+(sizeof(int)-1)) \
& (~(sizeof(int)-1))))-(((sizeof(type))+ \
(sizeof(int)-1)) & (~(sizeof(int)-1)))))
#define va_end(ap) (void) 0
#define va_start(ap,arg) (void)((ap)=(((char *)&(arg))+(((sizeof(arg))+ \
(sizeof(int)-1)) & (~(sizeof(int)-1)))))
*/
So here is a basic example that you could use with a format string and variable number of args
#define INT '0'
#define DOUBLE '1'
#define STRING '2'
void yourfunc(char *fmt_string, ...){
va_list args;
va_start (args, fmt_string);
while(*fmt_string){
switch(*fmt_string++){
case INT: some_intfxn(va_arg(ap, int));
case DOUBLE: some_doublefxn(va_arg(ap, double));
case STRING: some_stringfxn(va_arg(ap, char *));
/* extend this as you like using pointers and casting to your type */
default: handlfailfunc();
}
}
va_end (args);
}
So you can run it as: yourfunc("0122",42,3.14159,"hello","world");
or since you only wanted 1 to begin with yourfunc("1",2.17); It doesn't get much more generic than that. You could even set up multiple integer types to tell it to run a different set of functions on that particular integer. If the format_string is too tedious, then you can just as easily use int datatype in its place, but you would be limited to 1 arg (technically you could use bit ops to OR datatype | num_args but I digress)
Here is the one type one value form:
#define INT '0'
#define DOUBLE '1'
#define STRING '2'
void yourfunc(datatype, ...){ /*leaving "..." for future while on datatype(s)*/
va_list args;
va_start (args, datatype);
switch(datatype){
case INT: some_intfxn(va_arg(ap, int));
case DOUBLE: some_doublefxn(va_arg(ap, double));
case STRING: some_stringfxn(va_arg(ap, char *));
/* extend this as you like using pointers and casting to your type */
default: handlfailfunc();
}
va_end (args);
}
With some tricks, you can do it. See example:
int sizes[] = { 0, sizeof(int), sizeof(float), sizeof(char *) }
void *foo(datatype) {
void *rc = (void*)malloc(5 * sizes[datatype]);
switch(datatype) {
case 1: {
int *p_int = (int*)rc;
for(int i = 0; i < 5; i++)
p_int[i] = 1;
} break;
case 3: {
char **p_ch = (char**)rc;
for(int i = 0; i < 5; i++)
p_ch[i] = strdup("hello");
} break;
} // switch
return rc;
} // foo
In the caller, just cast returned value to appropriate pointer, and work with it.

Resources