Resolving munmap warning message - c

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;

Related

Passing array in a c function using address operator gives a warning

I was trying to pass an array to a method. I tried following ways:
func2(ARRAY_NAME, length) => WORKS, no warning
func2(&ARRAY_NAME[0], length) => WORKS, no warning
func2(&ARRAY_NAME, length) => WORKS, but WITH WARNING
I dont understand why the last one (#3) gives warning. &ARRAY_NAME works without warnings in memset, memcpy etc. Why its a problem in custom method?
WARNING message:
functionTest.c:35:11: warning: passing argument 1 of ‘func2’ from incompatible pointer type [-Wincompatible-pointer-types]
35 | func2(&temp, ARRAY_SIZE);
| ^~~~~
| |
| unsigned char (*)[200]
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
8 | void func2(unsigned char* buf, int length)
CODE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define ARRAY_SIZE 200
void func2(unsigned char* buf, int length)
{
// Change data of any index
buf[0] = 100;
buf[10] = 200;
}
void func1()
{
unsigned char temp[ARRAY_SIZE];
// Initialize
memset(temp, 0, sizeof(temp));
for(int i = 0; i < ARRAY_SIZE; i++)
{
printf("\t%d", temp[i]);
}
printf("\n-----------------------------------------------\n");
printf("Address : %p\n", &temp);
printf("Address of 0th index : %p\n", &temp[0]);
printf("\n-----------------------------------------------\n");
// Pass array in func2
func2(&temp, ARRAY_SIZE); // WARNING
func2(&temp[0], ARRAY_SIZE); // NO WARNING
for(int i = 0; i < ARRAY_SIZE; i++)
{
printf("\t%d", temp[i]);
}
printf("\n-----------------------------------------------\n");
}
int main()
{
func1();
return 0;
}
As it is clear written in the error message
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
8 | void func2(unsigned char* buf, int length)
the function expects a pointer of the type unsigned char * but the argument expression &temp has the type unsigned char ( * )[200] and there is no implicit conversion from one pointer type to another though values of the pointers are the same: the address of the extent of memory occupied by the array.
As for the functions memset and memcpy then they deal with pointers of the type void *. For example the function memset has the following declaration
void *memset(void *s, int c, size_t n);
And a pointer to object of other type can be implicitly converted to pointer of the type void *.
From the C Standard (6.3.2.3 Pointers)
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.
func2 is declared to have a first parameter of type unsigned char *, so it should be passed a pointer to a type compatible with unsigned char or a pointer to void, which will be automatically converted to unsigned char *.
With func2(temp, ARRAY_SIZE), the array temp is automatically converted to a pointer to its first element. This pointer is an unsigned char *, so it satisfies the requirements.
With func2(&temp[0], ARRAY_SIZE), temp[0] is an unsigned char, so &temp[0] is a pointer to an unsigned char. This satisfies the requirements.
With func2(&temp, ARRAY_SIZE), &temp is a pointer to an array of 200 elements of unsigned char. It points to the same place as &temp[0], bit its type is different. It is a pointer to an array, not a pointer to unsigned char or to a compatible type nor a pointer to void. So it does not satisfy the requirements, and the compiler complains.
Pointers to unsigned char and pointers to arrays are different. If the type of pu is unsigned char * and the type of pa is unsigned char (*)[200] (a pointer to an array of 200 unsigned char), then adding 1 to pu, as in pu + 1, produces a pointer to the next unsigned char after pu, but adding 1 to pa produces a pointer to the next array after pa. In other words, pu + 1 points to the next byte in memory, but pa + 1 points 200 bytes further along in memory.
One purpose of C’s type system is to help avoid errors. When a non-matching type is passed to a function, the programmer may be expected some behavior other than what the language defines. So the compiler issues a diagnostic message.
With memset, its first parameter is declared to be void *. void is an incomplete type; it acts as a placeholder for other types. memset is designed to work on the bytes of any object, so it accepts a pointer to any object type. When any pointer to an object type is passed for a void * parameter, it is automatically converted to void *, without any diagnostic message.

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

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.

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

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.

Trying to Assign Pointer out of a struct to a two-dimensional array

have an assignment where I should use the following struct:
typedef struct natset{
unsigned long min;
unsigned int size;
unsigned char* bits;
}natset_t;
Now I have to assign that pointer to the following two-dimensional array of unsigned chars:
unsigned char bArrayRoll[7][byteNum];
Which I have tried doing like so:
unsigned long min = 20;
unsigned int size = 20;
unsigned char bArrayRoll[7][byteNum];
natset_t newNatset;
newNatset.min = min;
newNatset.size = size;
newNatset.bits = &bArrayRoll;
Variations of that newNatset.bits which I have tried include:
newNatset.bits = bArrayRoll;
newNatset.bits* = &bArrayRoll;
However, the compiler either returns "Assignment from incompatible pointer type" or "Initialization from incompatible pointer type".
How can I assign the bits pointer to this array correctly?
newNatset.bits* = &bArrayRoll;
You are trying to multiply newNatset.bits with the address of bArrayRoll, this is undefined behavior.
newNatset.bits = bArrayRoll;
This one here is trying to assign unsigned char (*)[byteNum] to unsigned char* which is again something about which compiler complained. Types are incompatible clearly.
Correct one would be (correct one as per the compiler)
newNatset.bits = bArrayRoll[0];
wouldn't it have to be: newNatset.bits = bArrayRoll[0][0]?
This is once again a type incompatible assignment. You are assigning unsigned char to unsigned char*. You can do this though,
newNatset.bits = &bArrayRoll[0][0];
Trying to Assign Pointer out of a struct to a two-dimensional array? (The thing you wanted).
Then you need to do this,
unsigned char (*p)[byteNum] = bArrayRoll;
but in the structure you have declared bits to be of type unsigned char* not of type unsigned char (*)[byteNum].
Well if you didn't understand what happened when we assigned in the last case and how did the types match - then here is the key idea.
The 2d array decays into the pointer to the first element - which is a single dimensional array of byteNum unsigned characters. Now we are assigning it to p which is again
unsigned char (*)[byteNum]
^ ^
1 2
\-----------/
3
Pointer to (1) an array of bytenum (2) unsigned characters (3).

Void** as parameter requires cast

I have a function in C that needs to receive a pointer to an array (with an unspecified type).
To do so, I use void**, as I would use void* to receive an array of unspecified elements.
There's a problem unfortunately: the compiler gives a warning (passing argument 1 of 'f' from incompatible pointer type). If I ignore the warning, and try to execute the program, everything works as expected.
The only way to get rid of the warning is to cast whatever I try to pass to the function to void**.
Why does C behaves like that? And is there a better way to solve the warning?
PS: I need to compile using GCC with the flags -std=gnu89 -pedantic -Wall
Example
int f(void** param){ return 1; }
int main(){
int *arr = malloc(sizeof(int) * 20);
int i;
for(i=0; i < 20; i++) arr[i] = i;
f(&arr);
}
The pointer to anything type is void*, and the compiler will not complain about conversions to that type. But void** is not a pointer to anything, it's a pointer to an array of pointers to anything, which is quite different from a pointer to an array of pointers to integers, so the compiler complains.
So, to solve the warning, yes you would need to cast explicitly.
Although void * is the "generic pointer" in C, void ** isn't a "generic pointer to pointer". Instead, it's nothing more than the "specific pointer to void *, the generic pointer".
In your case, a int ** is converted implicitly to a void **, which is not generic. Since a void ** is not guaranteed to be able to hold all pointer variables (thus incompatible to a int **), the compiler raises a warning.
Here is the warning generated by clang:
main.c:7:7: warning: incompatible pointer types passing 'int **' to parameter of
type 'void **' [-Wincompatible-pointer-types]
f(&arr);
^~~~
main.c:1:14: note: passing argument to parameter 'param' here
int f(void** param){ return 1; }
To eliminate this warning, you can have int f(void* param);, and cast param to int ** inside the function. There will be no warning because a void * can be used to store any pointer (Quoted from N1570):
6.3.2.3 Pointers
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.
It seems that you want to modify the address of the data (not the value) inside the function, you can't do that directly with a void * because you can't use arithmetic with void *, but you can pass the size of the first element and a chunk of bytes (char *), suppose you want to change the address of arr to arr + 1 (the second element of the array) inside the function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void f(void *ptr, size_t size)
{
// char *p = *ptr; Wrong, you can't dereference a void * without a cast
char *p = *(char **)ptr; /* pointer to address of ptr */
memmove(p, p + size, size); /* assign the address of ptr + 1 to ptr */
}
int main(void)
{
int *arr = malloc(sizeof(int) * 20);
int i;
for (i = 0; i < 20; i++) arr[i] = i;
f(&arr, sizeof arr[0]);
printf("%d\n", arr[0]);
return 0;
}
Output:
1

Resources