3nondatur
3nondatur

Reputation: 475

Where is the implicit conversion that changes signedness?

I am having trouble with the following code:

#include <stdio.h>
#include <stdlib.h>

void someFunction(int number);

int main()
{
    printf("Hello world!\n");
    return 0;
}

void someFunction(int number)
{
  char* string = (char*) malloc((number+1)*sizeof(char));
}

When I run it on my computer with the Code::Blocks IDE and the GNU GCC compiler it works without any problems. But when I use it on another machine (also with Code:Blocks and LLVM Clang Compiler compiler) I get the error

implicit conversion changes signedness: 'int' to 'unsigned long' [-Werror,-Wsign-conversion]

This error refers to the line

char* string = (char*) malloc((number+1)*sizeof(char));

If I get the error message correctly there is some "int" that is implicitly converted to "unsigned long", but I do not see where this happens.

Could you please explain this to me?

Upvotes: 1

Views: 748

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 311088

It is a warning considered by the compiler as an error due to the compiler options -Werror and -Wsign-conversion.

Within the expression

(number+1)*sizeof(char)

used in the call of malloc the operand sizeof( char ) has unsigned integer type size_t that is an alias for the type unsigned long. On the other hand, the operand number+1 has the signed type int. The rank of the type size_t is higher than the rank of the type int. It means that due to the usual arithmetic conversions the operand of the type int will be implicitly converted to the type size_t and thus can lose its sign.

To resolve the problem just declare the function parameter like

void someFunction(size_t number);

There is no sense to pass a signed value to the function that allocates memory. And moreover the parameter of the function malloc has the type size_t.

To make it clear then consider the following simple program.

#include <stdio.h>

int main( void )
{
    int number = -1;

    printf( "( size_t )number = %zu\n", ( size_t )number );
}

Its output might look like

( size_t )number = 4294967295

As you can see the negative variable number of the type int converted to the unsigned integer type size_t becomes a very big unsigned integer.

Upvotes: 3

ChrisB
ChrisB

Reputation: 3724

The signature of the malloc function is

void* malloc(size_t size);

size_t is an unsigned integer type, whereas int is signed. You are passing the value (number+1)*sizeof(char) to this function.

clang basically asks you: what is gonna happen in this function if the value of number passed into it is negative?

To get rid of this, either explicitly cast number to a size_t to tell the compiler that you know it's not gonna be negative, or change the type of number to be size_t in the first place.

Also note that this is only an error because clang was called with the -Werror flag (treats all warnings as errors). Otherwise this would just be a warning, meaning that the program would still compile, but the compiler will inform you about a potential issues. That is not to say that -Werror is a bad idea though, in fact it is recommended practice unless you are doing rapid prototyping.

Note: in this specific case, the conversion actually already happens during the multiplication. My clang gives the following output:

main.c:14:40: error: implicit conversion changes signedness: 'int' to 'unsigned long' [-Werror,-Wsign-conversion]
  char* string = (char*) malloc((number+1)*sizeof(char));
                                 ~~~~~~^~ ~

If you look closely, it underlines number+1 (operand of type int) and * (the operator). The type of sizeof(char) is size_t (here shown as the equivalent unsigned long). So to perform the multiplication, clang already has to convert the types.

Upvotes: 4

Related Questions