---
# System prepended metadata

title: ByteWise - Breakdown of `BUILD_BUG_ON_ZERO`
tags: [' integer', bytewise, ' fml']

---

---
title: ByteWise - Breakdown of `BUILD_BUG_ON_ZERO`
tags: bytewise, fml, integer
---

# Breakdown of [`BUILD_BUG_ON_ZERO`](https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/tools/include/linux/build_bug.h#L16)

If you are as ignorant as me and think Linux kernel does not have software testing mechanism as what we commonly seen, then you are making a big mistake. In contrast, Linux kernel does it more delicately than what you can imagine. Without further ado, let us get into one of the **compile-time** assertion composed by making use of the rules of the **bit-field**.

The macro seems not straight-forward at the very first sight but after breaking it down, we will see and feel the beauty of it.
:::info
:brain: That is either genius, or a seriously diseased mind.
:::

As it appears, I think we need to know a little about the **bit-field** before going further.
```c
sizeof(struct { int : -!!(e); })
```

## Bit-field

Let us quote from the [Wikipedia](https://en.wikipedia.org/wiki/Bit_field)
> A bit field is a common idiom used in computer programming to compactly store multiple logical values as a short series of bits where each of the single bits can be addressed separately.
[...]
>```c
>struct BoxProps
>{
>  unsigned int  opaque       : 1;
>  unsigned int  fill_color   : 3;
>  unsigned int               : 4; // fill to 8 bits
>  unsigned int  show_border  : 1;
>  unsigned int  border_color : 3;
>  unsigned int  border_style : 2;
>  unsigned char              : 0; // fill to nearest byte (16 bits)
>  unsigned char width        : 4, // Split a byte into 2 fields of 4 bits
>                height       : 4;
>};
>```

Also, as always, let us extract some first-hand information from the C standard.


> #### § 6.7.2.1 
> **4.)** The expression that specifies the width of a **bit-field** shall be an integer constant expression with a nonnegative value that does not exceed the width of an object of the type [...]
>
>
> **4.)** [...] If the value is zero, the declaration shall have no delarator.
>
> **9.)** A member of a structure or union may have any complete object type other than a variably modified type. In addition, a member may be declared to consist of a specified number of bits (including a sign bit, if any). Such a member is called a **bit-field**; its width is preceded by a colon.
>
> **12.)** A **bit-field** declaration with no declarator, but only a colon and a width, indicates an unnamed **bit-field**. As a special case, a **bit-field** structure member with a width of 0 indicates that no further **bit-field** is to be packed into the unit in which the previous **bit-field**, if any, was placed.
>
> **[126]** An unnamed **bit-field** structure member is useful for padding to conform to externally imposed layouts.

## Breakdown

```c
sizeof(struct { int : -!!(e); })
```

`e` is an expression, essentially, we categorize the result of the `e` into zero and non-zero.
1. if `e` is equal to `0`, the `-!!(e)` is `0`
2. if `e` is non-zero, the `-!!(e)` is `-1`

Now it comes to the **bit-field** part

* Before jumping into the logical breakdown, let us see if it is a legal definition. According to **(§ 6.7.2.1 [126])**, the behavior of an unnamed **bit-field** structure member is defined, which as mentioned, it is useful for padding or data alignment.

* Now let us break down its logical behavior
	1. According to **(§ 6.7.2.1 12.)**, a **bit-field** structure member with a width of `0` indicates that no further **bit-field** is to be packed into the unit in which the previous **bit-field**.
	3. According to **(§ 6.7.2.1 4.)**, the width of a **bit-field** shall be an integer constant expression with a **nonnegative** value, so if it is `-1`, the compiler will complain: 
	```sh
	error: negative width in bit-field ‘<anonymous>’
	    3 | #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int : -!!(e); }))
	```

## Use case
```c
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
```

`ARRAY_SIZE` is a straight-forward and convinient macro to calculate the number of element in an array, **BUT** instead of seeing it as a entry-level common sense, its pitfall should be emphasized more frequently.

:::success
:door: Portals
1. [ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array](https://wiki.sei.cmu.edu/confluence/display/c/ARR01-C.+Do+not+apply+the+sizeof+operator+to+a+pointer+when+taking+the+size+of+an+array)
2. [Difference Between Arrays and Pointers](https://www.oreilly.com/library/view/understanding-and-using/9781449344535/ch04.html#DifferencesBetweenArraysAndPointersSection)
:::

Let us see the following code and guess what the result is

```c
#include <stdio.h>
int main()
{       
    int test_arr[10];
    int *test_ptr = test_arr;
    printf("%ld\n", ARRAY_SIZE(test_arr));
    printf("%ld\n", ARRAY_SIZE(test_ptr));                                                            
    return 0;
}
```
:::spoiler **Answer**:
* **64-bit** platform
```bash
10
2
```
* **32-bit** platform
```bash
10
1
```
:::
<br>

The reason of this is the `sizeof`  operator works different for an **array** and a **pointer**. The `sizeof` an array returns the number of bytes allocated to the array while the `sizeof` a pointer return the number of bytes taken for the pointer, *4 bytes for a 32-bit platform* and *8 bytes for a 64-bit platform*.

:::info
:pencil2: **Why `sizeof` operator works differently for array vs pointer?**

Remember. `sizeof()` is a **compile time operator**, it means that any code written within the parentheses of `sizeof()` operator is **not evaluated/executed** [^1]. So a reasonable reasoning is `sizeof()` gets to inference the number of bytes taken by `int arr[10]` but definitely not by `int *arr_ptr`.
:::

In Linux, here is one of the case where the `BUILD_BUG_ON_ZERO` comes to rescue. 

```c
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + __must_be_array(arr)
```

`ARRAY_SIZE` adds a second term, `__must_be_array(arr)` to check if the input argument is an array. 

```c
#define __same_type(_a, _b) \
    __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b))

#define __must_be_array(a) \
    BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
```

1. `__must_be_array(arr)` use `BUILD_BUG_ON_ZERO` to check the `__same_type(...)` expression where leads us to the `__same_type(_a, _b)` macro.
2. `__same_type(_a, _b)` uses the [`__builtin_types_compatible_p`](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html) to check if the `__type_of__` `_a` and `_b` are compatible. It returns `1` if they are compatible; `0` otherwise.

3. The reasoning is
    * if `_a` is an array and `_b` is a pointer, the `_same_type((a), &(a)[0])` is 0, making it a legal expression and return `0` to the `ARRAY_SIZE` macro
    ```c
    sizeof(struct { int : 0 ;}))
    ```
    * if `_a` and `_b` are both pointer, the `_same_type((a), &(a)[0])` is 1, hence the compiler complains about it.
    ```c
    sizeof(struct { int : -1 ;}))
    ```

[^1]: Scaler Topics, *"sizeof() in C"*, https://www.scaler.com/topics/c/size-of-in-c/