The Intel Intrinsic headers provide optimized functions that take advantage of the processor’s specialized features. Each intrinsic family has its own header. However, the <immintrin.h> header includes all other supported intrinsic headers.
Some compilers (such as GNU-GCC) support the mode attribute (__attribute__((mode(X)))). The mode attribute and the various machine-mode parameters can be used to create various datatypes. For instance, on an x86-64 system, 128-bit integers and Decimal-Floats can be created using the below code.
typedef signed int __attribute__((mode(TI))) int128_t;
typedef unsigned int __attribute__((mode(TI))) uint128_t;
typedef float __attribute__((__mode__(__SD__))) _Decimal32;
typedef float __attribute__((__mode__(__DD__))) _Decimal64;
typedef float __attribute__((__mode__(__TD__))) _Decimal128;
The below list describes all of the possible parameters used by the mode attribute.
BI – 1 Bit
QI – Quarter Integer; 1 byte
HI – Half Integer; 2 bytes
PSI – Partial Single Integer; 4 bytes; not all bits used
SI – Single Integer; 4 bytes
PDI – Partial Double Integer; 8 bytes; not all bits used
The C programming language (and similar languages) use a coding concept called “pointers”. Pointers point to a memory address. The pointer itself does not contain the data. Rather, the pointer stores the memory address (like the index of a book). Many people may have problems understanding pointers and addressing, so I hope this helps.
A pointer is a programming object that references a memory location. A pointer contains the memory address of a particular part of memory. A pointer is like a page number in the index of a book. The page number is the data stored by pointer. The words on the actual page is like the data in memory.
NOTE: “Dereferencing” is the act of obtaining the data on memory pointed to by the pointer.
A pointer must be of the same datatype as the data to which it points. Also, a pointer must be initialized before it can be used.
To make a pointer that points to an integer in memory, use the below code.
int int_in_mem = 32; // Initialize and declare integer
int* int_ptr; // Declare
int_ptr = &int_in_mem; // Initialize
int* ptr2 = ptr; // Create a second pointer that points to the same data
In the example, “int_in_mem” is an integer in memory. “int_ptr” only stores the memory address of the location of “int_in_mem“. “int_in_mem” has the value “32” while “int_ptr” contains the value the indicates where in memory “int_in_mem” resides. “ptr2” points at the same memory location as “ptr“. This is helpful when a particular memory location must be remembered when the other pointer will be changed to point to another location.
The ampersand (&) means memory location. Therefore, “&int_in_mem” gives the memory address of the data “int_in_mem“. The code &int_ptr would give the address in memory where “int_ptr” is stored. Thus, it is possible to have a pointer that points to a pointer. Also, the ampersand is helpful if code needs to know the literal memory address of a particular variable.
In the example, “int_ptr” is the plain pointer. *int_ptr is the data at that memory location (in this case, “32”). *int_ptr++ reads the value of the memory location and then increments the pointer. This means that the pointer will point to the memory location that comes after the location that is storing the “32”. (*int_ptr)++ will increment the data stored at that location. Thus, “32” will become “33”, but the pointer itself remains unchanged. Below are various pointer notations and their meaning and effects.
Pointer
Memory Address
Data
ptr
Access memory address
Unchanged
*ptr
Unchanged
Access data on memory
*ptr++
Increment address after reading
Unchanged
*(ptr++)
Increment address after reading
Unchanged
(*ptr)++
Unchanged
Increment data after reading
*++ptr
Increment address before reading
Unchanged
*(++ptr)
Increment address before reading
Unchanged
++*ptr
Unchanged
Increment data before reading
++(*ptr)
Unchanged
Increment data before reading
--*ptr
Unchanged
Decrement data before reading
ptr*++
Invalid
Invalid
ptr++*
Invalid
Invalid
Be careful when declaring multiple pointers. For instance, int* ptr_a, ptr_b; is equivalent to int* ptr_a; int ptr_b;.
Arrays notation is a special form of a pointer as seen in the table below.
Array
Pointer Equivalent
array
ptr*
array[1]
*(ptr + 1)
array[2]
*(ptr + 2)
array[1]
*(array + 1)
An array is a collection of elements of data. Arrays are stored on memory and pointers are used to point and retrieve members/elements from the array. For instance, array[0] is a pointer to the first element on the array (computers start counting at zero). In many languages (such as C), strings are arrays of characters.
If using two pointers, the length of an array can be measured. For instance, the below code creates a string (character array) and measures the length of the string.
distance will contain the value “5” since the memory address pointed to by “second_ptr” minus the address “first_ptr” is five. Thus, there are five elements from the first element of the array/string ([0]) to the sixth element ([5]). Remember, computers start counting at zero.
Now for another example.
int int_in_mem = 32; // Initialize and declare integer
int* int_ptr; // Declare
int_ptr = &int_in_mem; // Initialize
int* ptr2 = ptr; // Create a second pointer that points to the same data
int our_num = *ptr2; // Access data at the memory location indicated by ptr2
int num2 = (*int_ptr)++; // Access data at the memory location indicated by ptr2
In the above example, int_in_mem stores “32” while both int_ptr and ptr2 store the location of int_in_mem. our_num contains “32” because the code retrieves the data stored at the pointed memory location. num2 will contain “33” because the code gets the data on memory and then increments the value before storing it in num2.
To change the value of data in memory use the below code which changes “32” to “7”. If the code were int_ptr = 7; instead of *int_ptr = 7;, then int_ptr would point to memory address 7.
int int_in_mem = 32; // Initialize and declare integer
int* int_ptr; // Declare
int_ptr = &int_in_mem; // Initialize
*int_ptr = 7; // Place "7" at the pointed memory address
As for pointers to pointers, the below code, d points to the memory address that stores the “3” placed in a.
int a = 3;
int *b = &a;
int **c = &b;
int ***d = &c;
A NULL pointer (such as int* ptr = 0;) is a pointer that pointers to zero. This means that it does not point to any data.
A function pointer is a pointer to a function. Function pointers allow code to take functions as arguments which may be used to tell a function to use a particular function.
void test_func(int x) { printf("%d\n", x); } // Function
void (*func_ptr)(int); // Declare function pointer
func_ptr = &test_func; // Initialize function pointer
func_ptr(2); // Same as test_func(2)
There are many different type-qualifiers in the C programming language. Here is a brief description of some type qualifiers in C (and similar languages).
Do remember the “Clockwise/Spiral Rule” – Read the type qualifiers backwards
int* – pointer to int
int const* – pointer to const int (const int* == int const*)
int *const – const pointer to int
int const *const – const pointer to const int (const int* const == int const *const)
int ** – pointer to pointer to int
int **const – const pointer to pointer to int
int *const * – pointer to const pointer to int
int const ** – pointer to pointer to const int
int *const *const – const pointer to const pointer to int
volatile int *const – constant pointer to volatile int
void (*signal(int, void (*fp)(int)))(int) – signal is a function passing an int and a pointer to a function passing an int returning nothing (void) returning a pointer to a function passing an int returning nothing (void)
Storage classes (listed below) come before type-qualifiers (also listed below). Only one storage class can be used when declaring a variable. Both storage and type qualifiers come before the datatype.
Storage Classes
auto – Stored in stack during the code-block
extern – Lasts the whole program, block, or compilation unit; globally visible
register – Stored in stack or CPU-register during the code block
static – Lasts the whole program, block, or compilation unit; private in program
typedef – The data specifies a new datatype
__thread – Thread-local-storage; one instance per thread
_Thread_local – Thread-local data
Type-Qualifiers
const – Value does not change; read-only
restrict – For the lifetime of the pointer, the object can only be accessed via the pointer
volatile – Optimizing-compilers must not change
_Atomic – Map a variable to a basic built-in type (depending on the processor) so that reading and writing are guaranteed to happen in a single instruction