Operators in C, Part 2

Share this article

This article is a continuation of the previous article, Operators in C, where we began with Postfix Operators and discussed the Array Subscript Operator and the Function Call Operator. Continuing with postfix operators, the third operator is the dot operator (.) and the arrow operator (->), used to access structure and union members.

3. Structure and Union Members

A structure is a user-defined data type which  is a collection of an ordered group of data objects. Unlike the elements of an array, the data objects within a structure are of different data types. Each data object in a structure/union is called a member of the structure/union. A union is also a user-defined type which is a collection of data objects that have different data types. However , unlike the structure, members of a union share the same storage area, even though the individual members may differ in type. Thus a union permits several different data items to be stored in the same portion of the computer’s memory at different times. for example:
struct student{
               int student_id;
               char[] student_name;
               };
The above structure declaration creates a user-defined data type called student. The structure student has two members: student_id and  student_name. To access the members of a structure we need to first declare a variable of the user-defined data type student, and then we shall use the dot operator (.) to access the individual members of the structure.
struct student student1;
The above statement creates a structure variable student1 of type student. Now to access each member of the structure we will use the dot operator.

The Dot Operator

The dot operator is used to access a structure or union member.
Syntax: structurevariable.membername;
example:
                    student1.student_id=101;
                    student1.student_name="Alice";
The above two statements access the individual members of the structure and assign values to them. A union is also accessed in the same way using the dot operator. A union is declared in the following way:
union  name{ datatype1 member1; datatype2 member2; . . . datatypeN memberN; };
Syntax to declare a variable of a union:
 union unionname  variable;
To access a union member:
   variable.member

The Arrow Operator

The beginning address of a structure/union variable can be stored in a pointer variable and the individual members of the structure/union can then be accessed using the pointer variable and the Arrow Operator -> . For example:
struct student student1;
struct student *stud1;
declares a pointer to the structure student. To store the initial address of the structure variable student1 in the pointer variable *stud1 we will use the address of operator &.
*stud1=&student1;
Now , to access the individual members we can use the pointer *stud1 and the arrow operator in the following way:
stud1->student_id;
stud1->student_name;
The arrow operator is used in a similar way in case of unions. The dot operator and the arrow operator will be discussed in greater detail when we discuss structures and unions in a later article.

4. Postfix Increment and Decrement Operators

The postfix increment and the postfix decrement operators  operate on only one operand which can either be a pointer variable or a variable belonging to the real data types: integer types and the floating point types (int, float, double along with the various type modifiers). The postfix increment operation (operand++) increases the value of the variable by 1 and the postfix decrement operation (operand –) decreases the value of the variable by1. A postfix expression of the form E++  where E is a variable is equivalent to E=E+1. Similarly, a postfix expression of the form E–  is equivalent to E=E-1. Therefore ,
int i=10;
i++;
printf("%d",i); //prints 11
i--;
printf("%d",i); //prints 10
The result of a postfix increment or decrement operation is different when combined with the assignment operator (=). In a  postfix increment/ decrement operation the previous value of the variable being incremented/decremented is first assigned to the variable containing the result of the postfix increment/decrement operation and then the increment/decrement operation is carried out; that is, assignment takes place first and then the increment/decrement. To understand this consider the following example:
#include<stdio.h>

int main()
{
int a, i=10;
a=i++;
printf("a=%d, i=%d", a,i);
a=i--;
printf("a=%d, i=%d", a,i);
return 0;
}
 Output:
a=10, i=11
a=11,i=10
This is because in a postfix increment/decrement operation the value of i is first assigned to a and then updated. Therefore in the first statement, a=i++; a is first assigned the previous value of which is 10 and then  i is updated to 11. In the second statement, a=i--; a is again assigned the previous value of i (i=11 now, due to the previous increment operation) and then i  is decremented to 10.

5. Compound Literals

A postfix expression that consists of a parenthesised data type name followed  by a brace enclosed list of initializers is called a compound literal. The data type can either be a complete type (char, signed and unsigned integer types , floating point types and a pointer type) or an array of unknown size, but not an array of variable length (we will discuss variable length arrays in a later article on arrays). A compound literal initializes an unnamed object. If the data type name specifies an array of unknown size the size is determined by the number of elements in the initializer list. Some examples of compound literals are:

Array of Unknown Size

     (int []){ 2,4,7,6};
The above compound literal is an integer type array of unknown size. The size of the array is determined to be 4 after the initialization is complete.

Creating Structure Objects Using Compound Literals

To create a structure object we initialize each member of the structure through separate initialization statements; such as
     struct student student1;
     student1.student_id=101;
     student2.student_name="Alice";
The same can be accomplished in a more compact form using compound literals in the following way:
(struct student1){.student_id=101,
.student_name="Alice"};
Notice that the dot operator is still being used within the braces.

Pointer to a Structure Used as a Compound Literal

To  initialize a structure member using pointers we declare a pointer to a structure object and then initialize each member in separate initialization statements using the arrow operator; as in:
struct student student1, *stud1;
*stud1=&student1;
stud1->student_id=101;
stud1->student_name="Alice";
To perform the same job using compound literals we do not use the arrow operator. Rather, the same thing can be done in a very compact way using the dot operator and the address of operator (&) as shown below:
&(struct student1){.student_id=101, .student_name="Alice"};

Creating a Constant Compound Literal

A read-only compound literal; that is, a constant compound literal, can be specified by prefixing the const keyword:
(const int[]){1,2,3,4,5};
(const char[]){"Temperature"};
Coding becomes far more compact when compound literals are used. There are some more points related to a compound literal such as passing them to functions, their scope, and so on, which will be discussed later when we have understood functions.

Unary Operators

The unary operators operate on only one operand. There are four unary operators: 1. Prefix increment and decrement operators 2. Address of and indirection operators 3. The sizeof and alignof operators 4. Unary Arithmetic Operators

Prefix Increment and Decrement Operators

The prefix increment and decrement operators operate on only one operand which can either be a pointer variable or a variable belonging to the real data types: integer types and the floating point types (int, float, double along with the various type modifiers). The  prefix increment operation (++ operand) increases the value by 1 and the prefix decrement operation (– operand) decreases the value by 1. A postfix expression of the form ++E  where E is a variable is equivalent to E=E+1. Similarly, a postfix expression of the form –E  is equivalent to E=E-1. It was seen that in case of postfix increment/decrement assignment takes place first and then the increment/decrement. However, in case of a prefix increment or decrement operation the previous value is first incremented/decremented and then the assignment takes place. To understand this consider the following piece of code.
#include<stdio.h>

int main()
{
int i=10,a;
  ++i;
printf("Value of i=%d",i);//prints 11
  --i;
printf("Value of i=%d",i);//prints 10
a=++i;
printf("Value of a=%d",a);//prints 11
printf("Value of i=%d",i);//prints 11
a=--i;
printf("Value of a=%d",a);//prints 10
printf("Value of i=%d",i);//prints 10
return 0;
}
  Here, we can see that the value of i was first updated and then assigned  to a. Hence, a contains the updated value of i and not the previous value as was the case with postfix increment/decrement.

Address of and Indirection Operators

The address of operator & is used to evaluate the address of its operand. The indirection operator * is used to evaluate the value stored at the address given by its operand. The operand must be of pointer type; ie, it can either be an array (which is a self-referential pointer), a pointer to a variable (data pointer) or a pointer to a function (function pointer). Example of a data pointer:
#include<stdio.h>

int main()

{

int val, a=10;

int *p;                       //declares a pointer variable

p=&a;                      //now p stores the address of a

val=*p;                  //now val stores the value of a

printf(" Address of a: %X", p);

printf("Value of a: %d",val);

return 0;

}
  In the above program the statement int *p declares an integer pointer (a pointer that stores the address of an integer variable). To store the address of a in p, the address of operator is applied to  a and the result is stored in p as shown in the instruction p=&a; Now p=&a; Suppose the address of a is 1004 i.e . &a=1004 Since  p stores  the address of a, then p=&a p=1004 Now the value present at the address 1004 is the value stored in a which is 10(a=10). To access the value stored in a using the pointer variable p we need to apply the indirection operator * on p. When the indirection operator is applied on p (*p), it reads the value stored at the address contained in p; ie, the value stored at the address 1004; ie, the value of a. The result of indirection is stored in the integer type variable val  as shown in the statement val =*p;. The indirection operator is also called the value at operator as it gives the value stored at a particular address. To read the address stored in p, the format specifier %X is used in the printf statement as addresses are generally in hexadecimal. The relationship between pointers and arrays has been discussed to some extent while discussing the array subscript operator. Function pointers is an advanced concept that will be discussed in a later article on pointers. The next article will continue with the unary operators and the other operators in C.        

Frequently Asked Questions (FAQs) about Operators in C++

What is the difference between the dot and arrow operators in C++?

The dot (.) and arrow (->) operators are both used to access members of a structure or class in C++. The dot operator is used when the structure or class is accessed directly, while the arrow operator is used when the structure or class is accessed through a pointer. For example, if we have a structure ‘struct student’ with a member ‘name’, and ‘s’ is an object of ‘struct student’, we can access ‘name’ using ‘s.name’. If ‘p’ is a pointer to ‘s’, we can access ‘name’ using ‘p->name’.

Can I overload the member access operators in C++?

Yes, you can overload the member access operators in C++. Overloading these operators allows you to change their behavior for user-defined types. However, it’s important to note that overloading these operators should be done with caution, as it can lead to unexpected results if not done correctly.

What is the purpose of the scope resolution operator in C++?

The scope resolution operator (::) in C++ is used to define a function outside a class or to access a global variable within a scope where a local variable with the same name exists. It helps to specify the context in which an identifier is defined.

How does the increment and decrement operator work in C++?

The increment (++) and decrement (–) operators in C++ are used to increase or decrease the value of a variable by one. They can be used in both postfix (i++) and prefix (++i) forms. In the prefix form, the operation is performed before the value is used, while in the postfix form, the operation is performed after the value is used.

What is the difference between the bitwise and logical operators in C++?

Bitwise operators in C++ operate on the binary representations of integers, while logical operators operate on boolean values. For example, the bitwise AND operator (&) performs a binary AND operation on the bits of two integers, while the logical AND operator (&&) returns true if both of its operands are true.

How does the ternary operator work in C++?

The ternary operator in C++ is a shorthand way of writing an if-else statement. It takes three operands: a condition, a value to be returned if the condition is true, and a value to be returned if the condition is false. For example, ‘a > b ? a : b’ returns ‘a’ if ‘a’ is greater than ‘b’, and ‘b’ otherwise.

What is the purpose of the sizeof operator in C++?

The sizeof operator in C++ is used to get the size (in bytes) of a variable, data type, or object. It can be useful in situations where you need to allocate memory dynamically or when you want to find out how much memory a particular variable or data type uses.

Can I use the assignment operator to copy objects in C++?

Yes, you can use the assignment operator (=) to copy objects in C++. However, it’s important to note that this performs a shallow copy, which means that if the object contains pointers, the pointers are copied but the objects they point to are not. To perform a deep copy, you would need to define a copy constructor or overload the assignment operator.

What is the difference between the equality and relational operators in C++?

The equality operators (== and !=) in C++ are used to check if two values are equal or not equal, respectively. The relational operators (<, >, <=, >=) are used to compare two values. For example, ‘a == b’ checks if ‘a’ is equal to ‘b’, while ‘a < b’ checks if ‘a’ is less than ‘b’.

How does the comma operator work in C++?

The comma operator (,) in C++ is used to link related expressions together. A comma-linked list of expressions is evaluated left-to-right and the value of the rightmost expression is the value of the combined expression. It is commonly used in for loops and with the auto keyword.

Surabhi SaxenaSurabhi Saxena
View Author

My name is Surabhi Saxena, I am a graduate in computer applications. I have been a college topper, and have been awarded during college by the education minister for excellence in academics. I love programming and Linux. I have a blog on Linux at www.myblogonunix.wordpress.com.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week