A C entity is a value, object, reference, function, enumerator, type, class member, bit-field, structured binding, namespace, template, template specialization, or parameter pack. An entity can be of one or more types. There are two categories of C types: fundamental and compound types. A scalar is arithmetic or a pointer object type. Fundamental types are scalars, while the rest of the entity types are compound types.

The memory of a computer is a series of cells. Each cell has the size of one byte, it is normally the space occupied by a Western European character. The size of an object is given in bytes. This article gives a summary of C types. You should already have basic knowledge of C , in order to understand this article.

Article Content

– Fundamental Types

– Ways of Constructing Compound Types

– Arrays

– Enumeration

– Class

– Union

– References

– Functions

– Other Compound Types

– Conclusion

Fundamental Types

Fundamental types are scalar types.

bool

A Boolean type or bool type has a value of true or false for 1 or 0. True or false occupies one byte.

char, unsigned char, and signed char

A char is typically for one Western European character. It typically occupies one byte. There is also an unsigned and signed char, which is each an eight-bit integer. Unsigned chars do not involve negative values, while signed chars involve negative values. The kind of value a char holds depends on the compiler and may just be an unsigned char. These three types of chars are called, narrow character types, and each occupies one byte.

Integer

There are five unsigned standard integer types and five signed standard integer types. The five unsigned integer types are: “unsigned char”, “unsigned short int”, “unsigned int”, “unsigned long int”, and “unsigned long long int”. The five corresponding signed integer types are: “signed char”, “short int”, “int”, “long int”, and “long long int”.

“unsigned char” is the same type as the narrow character types (see above). “signed char” is the other type of the narrow character types (see above).

With the g compiler, “unsigned char” or “signed char” occupies one byte; “unsigned short int” or “short int” occupies two bytes; “unsigned int” or “int” occupies four bytes; “unsigned long int” or “long int” occupies 8 bytes; “unsigned long long int” or “long long int” still occupies 8 bytes (as of now).

char16_t, char32_t, wchar_t

When dealing with Western European characters, the char type is enough in many situations. However, when dealing with Chinese and other Eastern languages, char16_t, or char32_t, or wchar_t is needed. With the g compiler, char16_t occupies two bytes; char32_t occupies four bytes and wchar_t also occupies four bytes.

The bool, the char, the char16_t, the char32_t, the wchar_t, the signed, and the unsigned integer types, form another set, called integral (integer) types.

At this point in the article, two collective types have been mentioned: narrow character types and integral types.

Floating Point Types

Assume that the numbers 457,000 and 457,230 are the same reading, measured by two different measuring instruments. 457,230 is more precise than 457,000 because the value is more detailed (involves smaller places: 200 plus 30). A floating-point number is a number with a fractional (decimal) part. Though numbers in the computer are a sequence of bits, some floating-point numbers are more precise than the others.

Some measuring instruments take measurements in minimum steps, say 10 units. Such an instrument would have the following readings: 10, 20, 30, 40, . . .100, 110, 130, 140, . . . 200, 210, 220, 230, 240, and so on. Though numbers in the computer are a sequence of bits, floating-point numbers range in some minimum steps (much smaller than 10 units).

C has three floating-point types, which are: float, double, and long double. For any compiler, double must have the precision that is higher than that of float or at least that of float; long double must have the precision that is higher than that of double or at least that of double.

There is a third collective name: arithmetic type. This is the name for integral and floating-point types. Note that this is also the name for all the scalar types, as explained so far.

With the g compiler, the number of bytes for a float is four; the number of bytes for a double is eight; the number of bytes for a long double is sixteen.

void Type

With the g compiler, the size of the void type is one byte. The byte officially has no bits, meaning its location has empty content.

Ways of Constructing Compound Types

Compound types are non-fundamental types. This means that compound types are non-scalar types. This section explains the basics of compound types.

Arrays

The following code segment shows an array of ints and an array of chars:

     int arrInt[] = {1, 2, 3, 4, 5};


     char arrCha[] = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’};

    cout << arrInt[2] <<‘ ‘ <<arrCha[2] <<n

The output is: 3 c.

Enumeration

An enumeration is a type, with named constants. Consider the following code segment:

enum {a=3, b, c};


cout << b <<n;

The output is: 4. The first line of the code segment is an enumeration, and a, b, or c is an enumerator.

Class

A class is a generalized unit from which many objects of the same generalized unit can be created (instantiated). The following program shows a class and two objects, instantiated from it. Such an object is different from a scalar object.

#include


using namespace std;

class TheCla


    {


        public:


        int num = 5;


        int fn()


            {


                return num;


            }


    };

int main()

{


    TheCla obj1;


    TheCla obj2;

    cout << obj1.num << ‘ ‘ << obj2.num <<n;

    return 0;

}

The output is: 5 5. The name of the class is TheCla, and the names of the two objects are obj1 and obj2. Note the semicolon just after the description (definition) of the class. Note how the two objects were instantiated in the main() function.

Note: num is a data member and fn is a member function.

Union

struct

A struct is like an array but instead of having index/value pairs, it has name/value pairs. The names may be written in any order. The following program shows a struct and its use:

#include


using namespace std;

struct TheCla


    {


        int num = 5;


        float flt = 2.3;


        char ch = ‘a’;


    } obj1, obj2;

int main()

{


    cout << obj2.num <<“, “<< obj2.flt <<“, “<< obj2.ch <<n;

    return 0;

}

The output is:

5, 2.3, a

The name of the struct is TheCla. obj1 and obj2 are two different objects of the struct.

Union

The following program shows a union and its use:

#include


using namespace std;

union TheCla


    {


        int num;


        float flt = 2.3;


        char ch;


    } obj1, obj2;

int main()

{


    cout << obj2.flt <<n;

    return 0;

}

The output is: 2.3. The union is similar to a struct. The main difference between a struct and a union is that, for a struct, only one member can have a value (initialized) at any one time. In the above program, the member, flt has a value of 2.3. Each of the other members, num or ch, can only have a value next if the value for flt is abandoned.

References

A reference is a synonym for an identifier. The following code segment shows how to obtain a reference to an identifier:

int id = 5;


    int& ref1 = id;


    int& ref2 = id;


    cout << id << ‘ ‘ << ref1 << ‘ ‘ << ref2 <<n;

The output is: 5 5 5. ref1 and ref2 are synonyms to id.

lvalue Reference and rvalue Reference

The above references are lvalue references. The following code shows rvalue reference:

int&& ref = 5;


cout << ref <<n;

The output is: 5. This reference is created without identifying any location in memory. In order to achieve this, double & is needed, i.e., &&.

Pointer

A pointer is not really a C entity. However, it provides a better scheme for dealing with references. The following code shows how a pointer can be created:

int ptdId = 5;

int ptdId = 5;


    int *ptrId;


    ptrId = &ptdId;

    cout << *ptrId <<n;

The output is: 5. Note the difference in name between ptdId and ptdId. ptdId is the pointed object and ptrId is the pointer object. &ptdId returns the address of the pointed object that is assigned to ptrId. To return the value of the pointed object, use *ptrId.

Functions

Basic Function and its Call

The following code shows a basic function definition and its call:

#include


using namespace std;

int fn(int num)


    {


        cout<<“seen”<<n;


        return num;


    }

int main()

{


    int ret = fn(5);

    cout << ret <<n;

    return 0;

}

The output is

function definition

5

The function call is fn(5). The name of the function is fn.

Reference and Pointer to a Function

&fn return the address in memory of the function whose name is fn. The following statement declares a pointer to a function:

Here, func is the name of the pointer to the function. The first pair of parentheses differentiate this function pointer from a scalar object pointer. func can be made to hold the address of a function identified by fn, as follows:

The following program puts the function reference and pointer into action:

#include


using namespace std;

int fn(int num)


    {


        /* some statements */


        return num;


    }

int main()

{


    int (*func)(int);


    func = &fn;


    int ret = func(5);

    cout << ret <<n;

    return 0;

}

The output is: 5. Note that both fn and func each have the int parameter in the declaration.

Other Compound Types

The above basic compound types are compound in themselves. They are also used to construct elaborated compound types.

typedef

The typedef reserved word is used to replace a sequence of types with one name (for the sequence). The following code segment illustrates this:

typedef unsigned long int IduIL;

IduIL myInt = 555555555555555555;


cout << myInt <<n;

The output is 555555555555555555. In the code, IduIL has become a type that stands for “unsigned long int”.

Structured Binding

Structured binding is a feature that makes it possible for names to be given to subobjects. The following code illustrates this for the array:

int arr[3] = {1, 2, 3};

auto [x, y, z](arr);


cout << x <<‘ ‘<< y <<‘ ‘<< z <<n;

The output is 1 2 3. So, the values: 1, 2, 3 have been given the names, x, y, z. Note the use and position of the reserved word, auto. Also, note the use of the square brackets.

Bit-Field

The memory is a sequence of cells. Each cell takes a byte. Also, each byte consists of eight bits. A group of bits, not necessarily eight bits, can be set and changed. Such a group is called a bit-field. These groups would lie next to one another. If the groups will not make up a type, say 16 bits for a short int, padding bits are added. The following code illustrates this with the struct:

struct Date


        {


            unsigned short wkDay : 3;   //3 bits


            unsigned short monDay : 6;   //6 bits


            unsigned short mon : 5;    //5 bits


            unsigned short yr : 8;    //8 bits for 2 digit year


        } dte;

    dte.wkDay = 1; dte.monDay = 2; dte.mon = 2; dte.yr = 21;

    cout << dte.mon <<‘/’<< dte.monDay <<‘/’<< dte.yr <<n;

The output is: 2/2/21. The total number of bits for wkDay, MonDay, and mon is 3 6 5 = 14. So, two padding bits would be added to make up 16 bits for the short integer of 2 bytes (16 bits). The next 8 bits begin the next short int, which is then filled up with 8 padding bits.

Note: Avoid using bit-fields; use it only for research.

Namespace

A namespace is a set of names, which should not conflict with the same names of other sets of names. The following program illustrates the use of the same names from two different namespaces, applied in the main() function’s namespace:

#include


using namespace std;

namespace NS1


    {


        int myInt = 8;


        float flt;


    }

namespace NS2


    {


        int myInt = 9;


        float flt;


    }

int main()

{


    cout << NS1::myInt << n;


    cout << NS2::myInt << n;


    NS1::flt = 2.5;


    NS2::flt = 4.8;


    cout << NS1::flt << n;


    cout << NS2::flt << n;

    return 0;

}

The output is:

9

8

2.5

4.8

There are two conflicting same int names and two conflicting same float names in the code.

Template and Template Specialization

The template scheme allows the use of a placeholder for different possible scalar types. Specialization is choosing a particular scalar type. The following code illustrates this for a function:

#include


using namespace std;

    template void func (T cha, U no)


        {


            cout << “I need bread for “ << cha << no << ‘.’ << n;


        }

int main()

{


    func(‘$’, 3);

    return 0;

}

The output is:

“I need bread for $3.”

Template Parameter Pack

Compilers are still to fully implement this feature – see later.

Conclusion

C types exist in two categories: fundamental types and compound types. Fundamental types are scalar types. Basic compound types are arrays, enumerations, classes, unions, references, pointers, and functions. These basic compound types are used to construct elaborated compound types, which are typedef, structured bindings, bit-fields, namespace, and template features.

Chrys

About the author

<img alt="Chrysanthus Forcha" data-lazy-src="https://kirelos.com/wp-content/uploads/2021/01/echo/Chrysanthus-150×150.jpeg6003c2d63153c.jpg" height="112" src="data:image/svg xml,” width=”112″>

Chrysanthus Forcha

Discoverer of mathematics Integration from First Principles and related series. Master’s Degree in Technical Education, specializing in Electronics and Computer Software. BSc Electronics. I also have knowledge and experience at the Master’s level in Computing and Telecommunications. Out of 20,000 writers, I was the 37th best writer at devarticles.com. I have been working in these fields for more than 10 years.