First:
You're using a dynamic allocation for the tmp buffer in the initializeUint128 and it should be freed after the left member assignation. A better way here is to use a buffer on the stack with char tmp[17]; as suggested by @chux - Reinstate Monica.
Second:
As you're using fixed type integers uint64_t from C99, i guess your C compiler is C99 compliant.
Since C99, there's a generic support for 128bits integer through __int128 and unsigned __int128, even if the target CPU doesn't have native 128bits integer support.
Beware that this support does not extend to the standard C library. As an exemple, printf("%032X\n", (unsigned __int128)bigint); will only print the LSB (or MSB, depending on the Endianness of your target system) part of bigint. Thus you have to care about the Endianness in your case if you want to decompose the bigint, as for the case of the 0x000000000000001A0000000000102243 value below.
To this day, most C compiler can't compute a 128bits value directly (like unsigned __int128 value = 0x1A0000000000102244;) as there is not literal for it in the actual standard, you'll get a warning / error at compile time. But, for the 128bits initialization, note in the code below how this value is computed for the second_value variable. It could be a more appropriate way in your case than a string initialization.
To make it more clear, an example with your code :
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
struct uint128 {
uint64_t left;
uint64_t right;
};
union uint128u { struct uint128 source; unsigned __int128 big; };
typedef union uint128u uint128_t;
void initializeUint128(const char *input, struct uint128 *output) {
output->right = strtoull(input+16, NULL, 16);
char tmp[17]; // char *tmp = alloca(17); see above why stack is more adequate here
memcpy(tmp, input, 16);
tmp[16] = '\0';
output->left = strtoull(tmp, NULL, 16);
}
int main(int nbargs, char *args[]) {
// Initialization through string in your structure uint128
struct uint128 value;
initializeUint128("000000000000001A0000000000102241", &value);
printf("L:%016X R:%016X\n", value.left, value.right);
// Initialization through string on your structure uint128 mirrored with __int128
uint128_t bigvalue;
initializeUint128("000000000000001A0000000000102242", &bigvalue.source);
printf("L:%016X R:%016X\n", bigvalue.source.left, bigvalue.source.right);
printf("The (wrong) whole with no int size specifier : %032X\n", bigvalue.big);
printf("The (wrong) whole with ll : %032llX\n", bigvalue.big);
// Initialization through shift uint64 + uint64 of __int128
unsigned __int128 second_value = 0x1A;
second_value = second_value<<64;
second_value+=0x102243;
uint128_t big2;
big2.big=second_value;
printf("L:%016X R:%016X\n", big2.source.left, big2.source.right);
printf("The (wrong) whole second value : %032llX\n", second_value);
}
will print on a x86_64 (Little_Endian) :
L:000000000000001A R:0000000000102241
L:000000000000001A R:0000000000102242
The (wrong) whole with no int size specifier : 0000000000000000000000000000001A
The (wrong) whole with ll : 0000000000000000000000000000001A
L:0000000000102243 R:000000000000001A
The (wrong) whole second value : 00000000000000000000000000102243