Thursday, December 28, 2023

Narrowing type conversions in C/C++

 Hi, my friends and just everyone! :)

You shouldn't think that I forgot about my blog. After more then 6 years I decided to continue writing articles. And I'm glad to present my new article about strange behavior of C++ compilers. However, the reason maybe more interesting. E.g. in C++ standards.

So today I would like to talk about implicit type conversions and particular about implicit narrowing conversion of types.

Me and my friends were sure that if we can lose data the compiler has to show error or at least show warning.
Actually, look at this:

1
2
3
4
5
6
7
8
#include <cstdint>

void f(uint16_t) {}

int main() {
    uint32_t x = 0xFFFFFFFF;
    f(x);
}

I think you are sure that gcc or clang compilers have to show warning or error? :) But no!
$ rm -f a.out; g++ main1.cpp && ./a.out
$ rm -f a.out; clang++ main1.cpp && ./a.out
$
As we can see there are no any warnings or errors. Hmmm. Ok! Maybe we can use special warnings level? No problem!
$ rm -f a.out; g++ -Wall -Wextra -Wpedantic main1.cpp && ./a.out
$ rm -f a.out; clang++ -Wall -Wextra -Wpedantic main1.cpp && ./a.out
$
So, we don't see any warnings or errors. And any warnings flags couldn't help. In that case we can write the program which can demonstrate big problems with that behavior.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <cstdint>

void Bob(uint16_t x) {
    std::cout << "I'm Bob. My salary is $"
              << x
              << std::endl;
}

struct Snob {
    Snob(uint16_t x) {
        std::cout << "I'm Snob and my salary is $"
                  << x
                  << std::endl;
    }
};

int main() {
    uint32_t x = 0xFFFF0001; // 4294901761

    std::cout << "I'm the boss and I would like to give money to Bob and Snob. "
              << "Guys! Take your salary! "
              << "It is $"
              << x
              << std::endl;
    
    Bob(x);

    Snob snob(x);
}

The result (gcc):
$ rm -f a.out; g++ -std=c++17 -Wall -Wextra -Wpedantic main2.cpp && ./a.out
I'm the boss and I would like to give money to Bob and Snob. Guys! Take your salary!
It is $4294901761
I'm Bob. My salary is $1
I'm Snob and my salary is $1
$
The result (clang):
$ rm -f a.out; clang++ -std=c++17 -Wall -Wextra -Wpedantic main2.cpp && ./a.out
I'm the boss and I would like to give money to Bob and Snob. Guys! Take your salary!
It is $4294901761
I'm Bob. My salary is $1
I'm Snob and my salary is $1
$

OK? Do you understand what problems can be? Currently, I have no idea about reasons. I have not found the explanation of this behavior yet. So if you know I would be happy if you could explain to me.

Have a nice time! :)
Best regards,
Vasiliy V. Bodrov aka Bodro,
the 28-th of December, 2023.

No comments:

Post a Comment

How to reverse singly linked list in C (trivial way)

There is a beautiful morning. It's snowing. And I decided to write one more article. Today it will be just simple article where I would...