Той `bool`, який ви використовуєте, не є справжнім _Bool

Для питань, пов'язаних з розробкою вбудованого програмного забезпечення, звертайтеся до мене через LinkedIn. LinkedIn

Початкове розуміння bool

Під час курсу з основ комп'ютерних наук в університеті я познайомився з типом даних bool, і викладач завжди говорив, що як тільки включено правильний заголовний файл, можна його використовувати. Як показано в прикладі нижче, ми також бачимо, що true = 1 і false = 0:

#include   
using namespace std;  

int main()  
{  
 bool b1 = true;  
 bool b2 = false;  
 cout << b1 << " , " << b2;  
 return 0;  
}
> 1 , 0

Крім того, bool також можна використовувати разом з if, як показано в наступному прикладі:

#include   
using namespace std;  

bool isEqual(int x, int y)  
{  
 return (x == y);  
}  

int main()  
{  
 int x = 10;  
 int y = 20;  
 int z = 10;  
 if (isEqual(x, y))  
 cout << "x дорівнює y\n";  
 else  
 cout << "x не дорівнює y\n";  
 if (isEqual(x, z))  
 cout << "x дорівнює z\n";  
 else  
 cout << "x не дорівнює z\n";  
 return 0;  
}
> x не дорівнює y  
> x дорівнює z

Тому до того, як я натрапив на проблему, моє розуміння bool було таким:

  • Він приймає лише 1 і 0
  • bool — це тип даних, подібний до int

Я зрозумів, що bool, який я знаю, це не _Bool

Якось, під час додавання підтримки Suspend/Resume до існуючого драйвера, я включив ``, але несподівано отримав помилку компіляції в Native Kernel:

./include/linux/memcontrol.h:961:9: error: incompatible pointer to   
integer conversion returning 'struct mem_cgroup *' from a function with  
result type 'unsigned char' [-Wint-conversion]

Розглянувши повідомлення про помилку компіляції, я повернувся до вихідного коду, де побачив, що тип повернення був bool, але повернуте значення було вказівником. Я запідозрив, що це проблема неявного перетворення типів.

// член структури task_struct  
struct mem_cgroup *memcg_in_oom;   

// include/linux/memcontrol.h  
static inline bool task_in_memcg_oom(struct task_struct *p)  
{  
 return p->memcg_in_oom;  
}

Після цього я надіслав патч до LKML, пояснивши, що слід уникати використання неявного перетворення типів: [PATCH] mm: avoid implicit type conversion

C99 _Bool

Після того, як я надіслав патч, експерт швидко відповів із таким спостереженням:

Це не має сенсу. Чи є у вас визначення bool (або _Bool) як unsigned char десь? Якщо так, то це помилка, яку потрібно виправити.

Переглянувши специфікацію C99 для типу bool, я зрозумів, що справжній тип даних — це _Bool, а не bool. Тип bool насправді є typedef, визначеним у заголовному файлі для зручності. Специфікація також згадує, що будь-яке значення, яке присвоюється _Bool, буде оцінюватися; якщо значення 0, воно стає 0, в іншому випадку — 1. Це пояснює, чому у Native Code не було проблеми: якщо вказівник дорівнює NULL, він повертає 0; в іншому випадку повертається 1.

6.3.1.2 Тип Boolean

Коли будь-яке скалярне значення перетворюється на _Bool, результат дорівнює 0, якщо значення порівнюється з 0; в іншому випадку результат дорівнює 1.

Перевіривши визначення bool в Native Kernel, я підтвердив, що насправді він типізує _Bool як bool:

// include/linux/types.h  
typedef _Bool bool;

Коли я повернувся до своїх змін у драйвері, я побачив, що один із заголовків явно визначав bool, що призвело до цієї проблеми. Це підтвердило, що це була моя помилка. При зміні чужого драйвера необхідно перевіряти заголовки або стиль коду, який вони використовують, оскільки неуважність може призвести до непередбачених проблем, як у цьому випадку.

#ifndef bool  
#define bool unsigned char  
#endif




Перекладено з: [The bool You Use is Not the True _Bool](https://medium.com/@chao.shun.cheng.tw/the-bool-you-use-is-not-the-true-bool-33703c8501f6)

Leave a Reply

Your email address will not be published. Required fields are marked *