Typedef

Last updated

typedef is a reserved keyword in the programming languages C, C++, and Objective-C. It is used to create an additional name (alias) for another data type, but does not create a new type, [1] except in the obscure case of a qualified typedef of an array type where the typedef qualifiers are transferred to the array element type. [2] As such, it is often used to simplify the syntax of declaring complex data structures consisting of struct and union types, although it is also commonly used to provide specific descriptive type names for integer data types of varying sizes. [1]

Contents

Syntax

A typedef declaration follows the same syntax as declaring any other C identifier. The keyword typedef itself is a specifier which means that while it typically appears at the start of the declaration, it can also appear after the type specifiers or between two of them. [3] [4]

In the C standard library and in POSIX specifications, the identifier for the typedef definition is often suffixed with _t, such as in size_t and time_t. This is practiced in other coding systems, although POSIX explicitly reserves this practice for POSIX data types.

Examples

This creates the type length as a synonym of the type int:

typedefintlength;

Documentation use

A typedef declaration may be used as documentation by indicating the meaning of a variable within the programming context, e.g., it may include the expression of a unit of measurement or counts. The generic declarations,

intcurrent_speed;inthigh_score;voidcongratulate(intyour_score){if(your_score>high_score){// ...}}

may be expressed by declaring context specific types:

typedefintkm_per_hour;typedefintpoints;// `km_per_hour` is synonymous with `int` here, and thus, the compiler treats// our new variables as integers.km_per_hourcurrent_speed;pointshigh_score;voidcongratulate(pointsyour_score){if(your_score>high_score){// ...}}

Both sections of code execute identically. However, the use of typedef declarations in the second code block makes it clear that the two variables, while representing the same data type int, store different or incompatible data. The definition in congratulate() of your_score indicates to the programmer that current_speed (or any other variable not declared as a points) should not be passed as an argument. This would not be as apparent if both were declared as variables of int datatype. However, the indication is for the programmer only; the C/C++ compiler considers both variables to be of type int and does not flag type mismatch warnings or errors for "wrong" argument types for congratulate(points your_score) in the code snippet below:

voidfoo(){km_per_hourkm100=100;congratulate(km100);}

Declaration simplification

A typedef may be used to simplify the declarations of objects having types with verbose names, such as struct, union, or pointer types. [5] For example,

structMyStruct{intdata1;chardata2;};

defines the data type struct MyStruct. In C, absent a typedef, the full type name must be used to declare variables of that type:

structMyStructa;

A typedef declaration can provide a simpler type name that does not include struct. For example, with the type declaration

typedefstructMyStructnewtype;

the variable declaration can be reduced to:

newtypea;

The structure declaration and typedef may also be combined into a single declaration:

typedefstructMyStruct{intdata1;chardata2;}newtype;

, including when no tag is declared:

typedefstruct{intdata1;chardata2;}newtype;

In C++, unlike in C, tags of class, struct, union, and enum types (such as the "MyStruct" above) automatically can be used by themselves as aliases for the full type names, very much as if there were explicit typedefs declaring so at the point of the type declaration:

structMyStructx;MyStructy;

Indeed, for class, struct, and union types, C++ calls the tag a class name.

The correspondence between this C++ feature and typedef is very strong, extending to the fact that it is possible to shadow the simple type name in a nested scope by declaring it as the identifier of another kind of entity. In such a case, the C-style full type name (an "elaborated type specifier") can still be used to refer to the class or enum type.

In C++, then, MyStruct can be used anywhere that newtype can be used, as long as there are no other declarations of these identifiers. The reverse is not true, however, for C++ requires the class name for some purposes, such as the names of constructor methods.

A notorious example where even C++ needs the struct keyword is the POSIX stat system call that uses a struct of the same name in its arguments:

intstat(constchar*filename,structstat*buf){// ...}

Here both C as well as C++ need the struct keyword in the parameter definition.[ dubious ]

Pointers

The typedef may be used to define a new pointer type.

typedefint*intptr;intptrptr;// Same as:// int *ptr;

intptr is a new alias with the pointer type int *. The definition, intptr ptr;, defines a variable ptr with the type int *. So, ptr is a pointer which can point to a variable of type int.

Using typedef to define a new pointer type may sometimes lead to confusion. For example:

typedefint*intptr;// Both 'cliff' and 'allen' are of type int*.intptrcliff,allen;// 'cliff2' is of type int*, but 'allen2' is of type int**.intptrcliff2,*allen2;// Same as:// intptr cliff2;// intptr *allen2;

Above, intptr cliff, allen; means defining 2 variables with int* type for both. This is because a type defined by typedef is a type, not an expansion. In other words, intptr, which is the int* type, decorates both cliff and allen. For intptr cliff2, *allen2;, the intptr type decorates the cliff2 and *allen2. So, intptr cliff2, *allen2; is equivalent to 2 separate definitions, intptr cliff2; and intptr *allen2. intptr *allen2 means that allen2 is a pointer pointing to a memory with int* type. Shortly, allen2 has the type, int**.

Constant pointers

Again, because typedef defines a type, not an expansion, declarations that use the const qualifier can yield unexpected or unintuitive results. The following example declares a constant pointer to an integer type, not a pointer to a constant integer:

typedefint*intptr;constintptrptr=NULL;// Same as:// int *const ptr = NULL;

Since it is a constant pointer, it must be initialized in the declaration.

Structures and structure pointers

Typedefs can also simplify definitions or declarations for structure pointer types. Consider this:

structNode{intdata;structNode*nextptr;};

Using typedef, the above code can be rewritten like this:

typedefstructNodeNode;structNode{intdata;Node*nextptr;};

In C, one can declare multiple variables of the same type in a single statement, even mixing structure with pointer or non-pointers. However, one would need to prefix an asterisk to each variable to designate it as a pointer. In the following, a programmer might assume that errptr was indeed a Node *, but a typographical error means that errptr is a Node. This can lead to subtle syntax errors.

structNode*startptr,*endptr,*curptr,*prevptr,errptr,*refptr;

By defining the typedef Node *, it is assured that all variables are structure pointer types, or say, that each variable is a pointer type pointing to a structure type.

typedefstructNode*NodePtr;NodePtrstartptr,endptr,curptr,prevptr,errptr,refptr;

Function pointers

intdo_math(floatarg1,intarg2){returnarg2;}intcall_a_func(int(*call_this)(float,int)){intoutput=call_this(5.5,7);returnoutput;}intfinal_result=call_a_func(&do_math);

The preceding code may be rewritten with typedef specifications:

typedefint(*MathFunc)(float,int);intdo_math(floatarg1,intarg2){returnarg2;}intcall_a_func(MathFunccall_this){intoutput=call_this(5.5,7);returnoutput;}intfinal_result=call_a_func(&do_math);

Here, MathFunc is the new alias for the type. A MathFunc is a pointer to a function that returns an integer and takes as arguments a float followed by an integer.

When a function returns a function pointer, it can be even more confusing without typedef. The following is the function prototype of signal(3) from FreeBSD:

void(*signal(intsig,void(*func)(int)))(int);

The function declaration above is cryptic as it does not clearly show what the function accepts as arguments, or the type that it returns. A novice programmer may even assume that the function accepts a single int as its argument and returns nothing, but in reality it also needs a function pointer and returns another function pointer. It can be written more cleanly:

typedefvoid(*sighandler_t)(int);sighandler_tsignal(intsig,sighandler_tfunc);

Arrays

A typedef can also be used to simplify the definition of array types. For example,

typedefchararrType[6];arrTypearr={1,2,3,4,5,6};arrType*pArr;// Same as:// char arr[6] = {1, 2, 3, 4, 5, 6};// char (*pArr)[6];

Here, arrType is the new alias for the char[6] type, which is an array type with 6 elements. For arrType *pArr;, pArr is a pointer pointing to the memory of the char[6] type.

Type casts

A typedef is created using type definition syntax but can be used as if it were created using type cast syntax. (Type casting changes a data type.) For instance, in each line after the first line of:

// `funcptr` is a pointer to a function which takes a `double` and returns an `int`.typedefint(*funcptr)(double);// Valid in both C and C++.funcptrx=(funcptr)NULL;// Only valid in C++.funcptry=funcptr(NULL);funcptrz=static_cast<funcptr>(NULL);

funcptr is used on the left-hand side to declare a variable and is used on the right-hand side to cast a value. Thus, the typedef can be used by programmers who do not wish to figure out how to convert definition syntax to type cast syntax.

Without the typedef, it is generally not possible to use definition syntax and cast syntax interchangeably. For example:

void*p=NULL;// This is legal.int(*x)(double)=(int(*)(double))p;// Left-hand side is not legal.int(*)(double)y=(int(*)(double))p;// Right-hand side is not legal.int(*z)(double)=(int(*p)(double));

Usage in C++

In C++ type names can be complex, and typedef provides a mechanism to assign a simple name to the type.

std::vector<std::pair<std::string,int>>values;for(std::vector<std::pair<std::string,int>>::const_iteratori=values.begin();i!=values.end();++i){std::pair<std::string,int>const&t=*i;// ...}

and

typedefstd::pair<std::string,int>value_t;typedefstd::vector<value_t>values_t;values_tvalues;for(values_t::const_iteratori=values.begin();i!=values.end();++i){value_tconst&t=*i;// ...}

C++11 introduced the possibility to express typedefs with using instead of typedef. For example, the above two typedefs could equivalently be written as

usingvalue_t=std::pair<std::string,int>;usingvalues_t=std::vector<value_t>;

Use with templates

C++03 does not provide templated typedefs. For instance, to have stringpair<T> represent std::pair<std::string, T> for every type T one cannot use:

template<typenameT>typedefstd::pair<std::string,T>stringpair<T>;// Doesn't work

However, if one is willing to accept stringpair<T>::type in lieu of stringpair<T>, then it is possible to achieve the desired result via a typedef within an otherwise unused templated class or struct:

template<typenameT>classstringpair{private:// Prevent instantiation of `stringpair<T>`.stringpair();public:// Make `stringpair<T>::type` represent `std::pair<std::string, T>`.typedefstd::pair<std::string,T>type;};// Declare a variable of type `std::pair<std::string, int>`.stringpair<int>::typemy_pair_of_string_and_int;

In C++11, templated typedefs are added with the following syntax, which requires the using keyword rather than the typedef keyword. (See template aliases.) [6]

template<typenameT>usingstringpair=std::pair<std::string,T>;// Declare a variable of type `std::pair<std::string, int>`.stringpair<int>my_pair_of_string_and_int;

Other languages

In SystemVerilog, typedef behaves exactly the way it does in C and C++. [7]

In many statically typed functional languages, like Haskell, Miranda, OCaml, etc., one can define type synonyms, which are the same as typedefs in C. An example in Haskell:

typePairOfInts=(Int,Int)

This example has defined a type synonym PairOfInts as an integer type.

In Seed7 the definition of a constant type is used to introduce a synonym for a type:

const type: myVector is array integer; 

In Swift, one uses the typealias keyword to create a typedef:

typealiasPairOfInts=(Int,Int)

C# contains a feature which is similar to the typedef or the using syntax of C++. [8] [6]

usingnewType=global::System.Runtime.Interop.Marshal;usingotherType=Enums.MyEnumType;usingStringListMap=System.Collections.Generic.Dictionary<string,System.Collections.Generic.List<string>>;

In D the keyword alias [9] allows to create type or partial type synonyms.

structFoo(T){}aliasFooInt=Foo!int;aliasFun=intdelegate(int);

Usage concerns

Kernighan and Ritchie stated two reasons for using a typedef. [1] First, it provides a means to make a program more portable or easier to maintain. Instead of having to change a type in every appearance throughout the program's source files, only a single typedef statement needs to be changed. size_t and ptrdiff_t in <stdlib.h> are such typedef names. Second, a typedef can make a complex definition or declaration easier to understand.

Some programmers are opposed to the extensive use of typedefs. Most arguments center on the idea that typedefs simply hide the actual data type of a variable. For example, Greg Kroah-Hartman, a Linux kernel hacker and documenter, discourages their use for anything except function prototype declarations. He argues that this practice not only unnecessarily obfuscates code, it can also cause programmers to accidentally misuse large structures thinking them to be simple types. [10]

See also

Related Research Articles

Templates are a feature of the C++ programming language that allows functions and classes to operate with generic types. This allows a function or class declaration to reference via a generic variable another different class without creating full declaration for each of these different classes.

In object-oriented (OO) and functional programming, an immutable object is an object whose state cannot be modified after it is created. This is in contrast to a mutable object, which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change, but the object's state appears unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.

In mathematics and computer science, a higher-order function (HOF) is a function that does at least one of the following:

<span class="mw-page-title-main">C syntax</span> Set of rules defining correctly structured programs

The syntax of the C programming language is the set of rules governing writing of software in C. It is designed to allow for programs that are extremely terse, have a close relationship with the resulting object code, and yet provide relatively high-level data abstraction. C was the first widely successful high-level language for portable operating-system development.

<span class="mw-page-title-main">Pointer (computer programming)</span> Object which stores memory addresses in a computer program

In computer science, a pointer is an object in many programming languages that stores a memory address. This can be that of another value located in computer memory, or in some cases, that of memory-mapped computer hardware. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer. As an analogy, a page number in a book's index could be considered a pointer to the corresponding page; dereferencing such a pointer would be done by flipping to the page with the given page number and reading the text found on that page. The actual format and content of a pointer variable is dependent on the underlying computer architecture.

A function pointer, also called a subroutine pointer or procedure pointer, is a pointer referencing executable code, rather than data. Dereferencing the function pointer yields the referenced function, which can be invoked and passed arguments just as in a normal function call. Such an invocation is also known as an "indirect" call, because the function is being invoked indirectly through a variable instead of directly through a fixed identifier or address.

In the C++ programming language, a reference is a simple reference datatype that is less powerful but safer than the pointer type inherited from C. The name C++ reference may cause confusion, as in computer science a reference is a general concept datatype, with pointers and C++ references being specific reference datatype implementations. The definition of a reference in C++ is such that it does not need to exist. It can be implemented as a new name for an existing object.

In some programming languages, const is a type qualifier that indicates that the data is read-only. While this can be used to declare constants, const in the C family of languages differs from similar constructs in other languages in that it is part of the type, and thus has complicated behavior when combined with pointers, references, composite data types, and type-checking. In other languages, the data is not in a single memory location, but copied at compile time on each use. Languages which use it include C, C++, D, JavaScript, Julia, and Rust.

The computer programming languages C and Pascal have similar times of origin, influences, and purposes. Both were used to design their own compilers early in their lifetimes. The original Pascal definition appeared in 1969 and a first compiler in 1970. The first version of C appeared in 1972.

<span class="mw-page-title-main">C data types</span> Data types supported by the C programming language

In the C programming language, data types constitute the semantics and characteristics of storage of data elements. They are expressed in the language syntax in form of declarations for memory locations or variables. Data types also determine the types of operations or methods of processing of data elements.

A class in C++ is a user-defined type or data structure declared with keyword class that has data and functions as its members whose access is governed by the three access specifiers private, protected or public. By default access to members of a C++ class is private. The private members are not accessible outside the class; they can be accessed only through methods of the class. The public members form an interface to the class and are accessible outside the class.

The C and C++ programming languages are closely related but have many significant differences. C++ began as a fork of an early, pre-standardized C, and was designed to be mostly source-and-link compatible with C compilers of the time. Due to this, development tools for the two languages are often integrated into a single product, with the programmer able to specify C or C++ as their source language.

C++11 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++11 replaced the prior version of the C++ standard, called C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.

In computer programming, an anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function. Anonymous functions are ubiquitous in functional programming languages and other languages with first-class functions, where they fulfil the same role for the function type as literals do for other data types.

Substitution failure is not an error (SFINAE) is a principle in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.

C++ doesn't have:

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

In computer programming, variadic templates are templates that take a variable number of arguments.

C++14 is a version of the ISO/IEC 14882 standard for the C++ programming language. It is intended to be a small extension over C++11, featuring mainly bug fixes and small improvements, and was replaced by C++17. Its approval was announced on August 18, 2014. C++14 was published as ISO/IEC 14882:2014 in December 2014.

<span class="mw-page-title-main">Zig (programming language)</span> A general-purpose programming language, and toolchain to build Zig/C/C++ code

Zig is an imperative, general-purpose, statically typed, compiled system programming language designed by Andrew Kelley. It is intended to be a successor to the C programming language, with the goals of being even smaller and simpler to program in while also offering modern features, new optimizations and a variety of safety mechanisms while not as demanding of runtime safety as seen in other languages. It is distinct from languages like Go, Rust and Carbon, which have similar goals but also target the C++ space.

References

  1. 1 2 3 Kernighan, Brian W.; Ritchie, Dennis M. (1988). The C Programming Language (2nd ed.). Englewood Cliffs, New Jersey.: Prentice Hall. p.  147. ISBN   0-13-110362-8 . Retrieved 18 June 2016. C provides a facility called typedef for creating new data type names. … It must be emphasized that a typedef declaration does not create a new type in any sense; it merely adds a new name for some existing type.
  2. "const type qualifier". cppreference.com. Retrieved 2020-10-20.
  3. "typedef specifier (C++)". cppreference.com. Retrieved 18 June 2016.
  4. "typedef declaration (C)". cppreference.com. Retrieved 18 June 2016.
  5. Deitel, Paul J.; Deitel, H. M. (2007). C how to program (5th ed.). Upper Saddle River, N.J.: Pearson Prentice Hall. ISBN   9780132404167 . Retrieved 12 September 2012. Names for structure types are often defined with typedef to create shorter type names.
  6. 1 2 "Type alias, alias template (since C++11) - cppreference.com". en.cppreference.com. Retrieved 2018-09-25.
  7. Tala, Deepak Kumar. "SystemVerilog Data Types Part-V". www.asic-world.com. ASIC World. Retrieved 25 September 2018.
  8. "Visual Studio 2003 Retired Technical documentation".
  9. "Declarations - D Programming Language". dlang.org. Retrieved 2017-05-28.
  10. Kroah-Hartman, Greg (2002-07-01). "Proper Linux Kernel Coding Style". Linux Journal . Retrieved 2007-09-23. Using a typedef only hides the real type of a variable.