There was never any integral type defined that corresponded to value of a segment the same way
uintptr_t corresponds to the value of a pointer defined in any C compiler targeting x86 CPUs that I'm aware of. For that matter I don't know if any 16-bit segmented C compiler for x86 CPUs ever implement the
uintptr_t type. You just had to know if you assigned a 32-bit long integer value to a far pointer then the most significant 16-bits of the value would be used as the segment part of the address and the least significant 16-bit as the offset part.
However there was at least one implementation of pointer-like segment types, by Borland, and of segment-like pointer types, by Microsoft. Borland's implementation used the
_seg keyword to create segment pointer types while Microsoft's implementation used the
_based keyword to create something called based pointers. Microsoft's based pointers were also supported by Watcom's C compiler.
The Borland implementation was the most straightforward. It allows the use of the
_seg keyword just like the
huge keywords in pointer types. For example you could do something like:
unsigned short _seg video_seg = 0xb800;
video_seg[80 * row + column] = character + attribute * 256;
The advantage of using a segment pointer over a far pointer here is that the variable only takes 2 bytes of space rather than 4. Also it should help the fairly primitive Borland compiler generate better code.
The Microsoft implementation used the
_based keyword to based pointers. In theory these were more flexible and not specific in to the x86 segmented architecture. In fact they're still supported by current Microsoft compilers. The basic idea is that you can use the
_based keyword to make a pointer "based" on some other variable. So for example you can do:
void far *video_base = 0xb8000000;
unsigned short __based(video_base) *video_seg = 0;
video_seg[row * 80 + column] = character + attribute * 256;
In practice though Microsoft implementation of based pointers in their 16-bit segmented compilers was pretty buggy and don't really offer an advantage over using far pointers to refer to segments. The were modestly used in combination with the
_segname keyword to specific which segment statically allocated variables should be allocated in. For example:
unsigned char _based(_segname("OFFSCRBUF")) offscren_buffer[320U * 200];
The only other compiler that I know that did anything like this was Watcom's. They took Microsoft's based pointers and added several extensions. In particular they created a
_segment keyword which is almost what you're asking for but much more inconvenient as you need to use it with both based pointers and a special
:> operator. For example:
_segment video_seg = 0xb800;
unsigned short _based(void) *offset = row * 80 + column;
*(video_seg:>offset) = character + attribute * 256;
Note that all the above examples omit casts for brevity. Without the casts these code examples will all generate warnings but generate correct code.