Gillespie
Gillespie

Reputation: 6561

Why can I initialize a string with an int?

Recently a developer at my company committed some code that looked something like this:

char buf[50];
string str;
str = sprintf(buf, "%s", "test");
//proceeds to use str

The thing is, it slipped through CI because the compiler raised no warnings despite -Wall and -Werror being set.

Shouldn't this be an obvious type mismatch? You can't assign an integer to an std::string type without std::to_string...

I took a look at the list of string assignments but I can't tell which one is being triggered in this case? Is it using one of these?

c-string (2) string& operator= (const char* s);
character (3) string& operator= (char c);

I'm guessing the latter, but that still seems like a compiler fail since sprintf clearly returns int not char.

Is there a warning we could have enabled that could have saved us in this case not covered by -Wall?

Edit:

A related thread I found: https://stackoverflow.com/a/39285668/2516916

Upvotes: 3

Views: 593

Answers (2)

Lucas Ramos
Lucas Ramos

Reputation: 348

I made some tests and here is all my results. Compiling this program:

#include <iostream>    
#include <string>

using namespace std;

int main ()
{
    string str;
    str = 1000;

    cout << str << "\n";
    return 0;
}

With the command:

g++ -o test test.cpp

you get the following warning:

teste.cpp: In function ‘int main()’:
teste.cpp:10:11: warning: overflow in implicit constant conversion [-Woverflow]
str = 1000;

if instead of 1000 you use a number from 0 to 255 then you do not get a warning. Apparently if the number is within the range of a char the compiler is trying to transform that number into a char and the string type accepts that.

But now if you do this:

#include <iostream>    
#include <string>

using namespace std;
int number(){
     return 100;
}

int main ()
{
    int a = 100;
    string str;
    str = a;

    cout << str << "\n";

    str = number();
    cout << str << "\n";

    return 0;
}

You do not get any warning and as output you get:

d
d

If instead of 100 you use 10000 in my case i got only two blank lines as result. So after that analysis i believe that what is happening is that even if what the variable str is trying to receive is a int The compiler is trying to make your life easier by converting everything to you, but unfortunatly in this case it made the exact opposite.

After some research here cpp-flags i finally found a combination that would have saved you:

g++ -o test test.cpp -Wconversion -Werror

with -Wconversion you turn those implicit conversions made by the compiler into warnings and with -Werror you turn every warning into an error.

and then in the program above you would get:

test.cpp: In function ‘int main()’:
test.cpp:13:11: error: conversion to ‘char’ from ‘int’ may alter its value [- 
Werror=conversion]
   str = a;
       ^
test.cpp:17:17: error: conversion to ‘char’ from ‘int’ may alter its value [- 
Werror=conversion]
   str = number();

Upvotes: 1

Thomas Caissard
Thomas Caissard

Reputation: 866

The return value of snprintf is an int which can be implicitely casted to a char therefore str = sprintf(buf, "%s", "test"); calls the character (3) string& operator= (char c); assignement operator.

The usual way of preventing that is to add the explicit keyword, but in your case since std::string is in the library you can't really do anything.

One way of detecting that is using UBSAN which has an option for implicit casts.

Hope that helps!

Upvotes: 3

Related Questions