Is this type (uintptr_t) ok to use? Like, is it portable? Because from what i read, this type is optional
That is correct. According to §7.22.1.4 of the C23 standard, the data type uintptr_t is optional.
does this mean that in some compiler implementation it can be not implemented?
This means that a compiler is not required to support it, so it is theoretically possible for it to be not implemented in a certain compiler. However, I am unaware of any modern compiler which does not support it.
If a compiler decides not to support uintptr_t on a certain platform, it is probably a very old or exotic platform and there probably is a special technical reason not to support it.
For example, in the 1980s, it was common for CPUs to only support 16-bit arithmetic and only have a general register size of 16 bits. But with 16 bits, you can only address up to 64 KiB of memory. Therefore, in order for such a 16-bit CPUs to be able to address more than 64 KiB of memory, these CPUs had to use a segmented memory model instead of a flat memory model (which modern CPUs use), which means that several 16-bit registers had to be used in order to store an address.
In order to implement the integer data type uintptr_t on such a 16-bit platform with memory addresses larger than 16 bits, the compiler would need to implement an integer data type that is larger than 16 bits, despite such a data type not being directly supported by the CPU. The compiler could for example implement a 32-bit data type in software, by making this data type use two 16-bit CPU registers and by making arithmetic operations on this 32-bit data type internally use several 16-bit arithmetic operations.
On the other hand, the compiler could simply decide not to support such a 32-bit data type at all, and only support up to 16-bit data types (which are directly supported by the CPU). In that case, the compiler would not define the data type uintptr_t, and the compiler would still be complying with the C standard.
This example from the 1980s is maybe not a good example, because the data type uintptr_t was only introduced to the language C in the C99 standard (which is from the year 1999), and this standard also introduced the data type long long int, which is required to have a width of at least 64 bits. Nevertheless, this historical example does demonstrate that there may be technical reasons why a compiler would not want to be required to implement the data type uintptr_t, which is probably why the standards committee decided to make this data type optional.
If so, what type can I use to cast a pointer to a type that is big enough to hold any memory address?
If the compiler decides not to support the data type uintptr_t, then as described above, this probably means that there is a special technical reason why it is not trivial to convert a pointer to an integer. In such situations, it is highly unlikely that you will be able to solve the problem by simply using another data type which the compiler implements. Instead, you will likely have to determine the reason why the data type uintptr_t is not supported on this particular platform, and then act accordingly.
On the other hand, if the only reason you want to use the data type uintptr_t is for determining alignment, then using a smaller data type such as unsigned int may be sufficient. But it is worth noting that even when using the data type uintptr_t, the C standard does not guarantee that an even number represents a memory address with an alignment of 2 (even if this probably is a generally safe assumption). The only guarantee that the standard provides is that uintptr_t is an "unsigned integer type" (which means that arithmetic operations may be performed on this data type) and that converting a void * value to uintptr_t and back will yield a pointer value that compares equal to the original pointer value.
However, as already stated above, all of this is unlikely to be a problem in practice, except on very old or exotic platforms.
The C23 standard introduced the new function memalignment, which allows you to determine at run-time whether a pointer is aligned.
(align & 1) == 0is not a proper test for a power of 2. Consider!(align && !(align & (align - 1)))uintptr_t.(void *)curr_addr + paddingis not valid C. Usechar *.assert((align & 1) == 0 && "aligment must be a power of two");, which seems off. E.g. one is a power of two, but six isn't.