Logo

Navigation
  • Home
  • Services
    • ERP Solutions
    • Implementation Solutions
    • Support and Maintenance Solutions
    • Custom Solutions
    • Upgrade Solutions
    • Training and Mentoring
    • Web Solutions
    • Production Support
    • Architecture Designing
    • Independent Validation and Testing Services
    • Infrastructure Management
  • Expertise
    • Microsoft Development Expertise
    • Mobile Development
    • SQL Server Database and BI
    • SAP BI, SAP Hana, SAP BO
    • Oracle and BI
    • Oracle RAC
  • Technical Training
    • Learn Data Management
      • Business Intelligence
      • Data Mining
      • Data Modeling
      • Data Warehousing
      • Disaster Recovery
    • Learn Concepts
      • Application Development
      • Client Server
      • Cloud Computing Tutorials
      • Cluster Computing
      • CRM Tutorial
      • EDI Tutorials
      • ERP Tutorials
      • NLP
      • OOPS
      • Concepts
      • SOA Tutorial
      • Supply Chain
      • Technology Trends
      • UML
      • Virtualization
      • Web 2.0
    • Learn Java
      • JavaScript Tutorial
      • JSP Tutorials
      • J2EE
    • Learn Microsoft
      • MSAS
      • ASP.NET
      • ASP.NET 2.0
      • C Sharp
      • MS Project Training
      • Silverlight
      • SQL Server 2005
      • VB.NET 2005
    • Learn Networking
      • Networking
      • Wireless
    • Learn Oracle
      • Oracle 10g
      • PL/SQL
      • Oracle 11g Tutorials
      • Oracle 9i
      • Oracle Apps
    • Learn Programming
      • Ajax Tutorial
      • C Language
      • C++ Tutorials
      • CSS Tutorial
      • CSS3 Tutorial
      • JavaScript Tutorial
      • jQuery Tutorial
      • MainFrame
      • PHP Tutorial
      • VBScript Tutorial
      • XML Tutorial
    • Learn Software Testing
      • Software Testing Types
      • SQA
      • Testing
  • Career Training
    • Career Improvement
      • Career Articles
      • Certification Articles
      • Conflict Management
      • Core Skills
      • Decision Making
      • Entrepreneurship
      • Goal Setting
      • Life Skills
      • Performance Development
      • Personal Excellence
      • Personality Development
      • Problem Solving
      • Relationship Management
      • Self Confidence
      • Self Supervision
      • Social Networking
      • Strategic Planning
      • Time Management
    • Education Help
      • Career Tracks
      • Essay Writing
      • Internship Tips
      • Online Education
      • Scholarships
      • Student Loans
    • Managerial Skills
      • Business Communication
      • Business Networking
      • Facilitator Skills
      • Managing Change
      • Marketing Management
      • Meeting Management
      • Process Management
      • Project Management
      • Project Management Life Cycle
      • Project Management Process
      • Project Risk Management
      • Relationship Management
      • Task Management
      • Team Building
      • Virtual Team Management
    • Essential Life Skills
      • Anger Management
      • Anxiety Management
      • Attitude Development
      • Coaching and Mentoring
      • Emotional Intelligence
      • Stress Management
      • Positive Thinking
    • Communication Skills
      • Conversation Skills
      • Cross Culture Competence
      • English Vocabulary
      • Listening Skills
      • Public Speaking Skills
      • Questioning Skills
    • Soft Skills
      • Assertive Skills
      • Influence Skills
      • Leadership Skills
      • Memory Skills
      • People Skills
      • Presentation Skills
    • Finding a Job
      • Etiquette Tips
      • Group Discussions
      • HR Interviews
      • Interview Notes
      • Job Search Tips
      • Resume Tips
      • Sample Resumes
 

C Programming – Expressions

By Exforsys | on March 30, 2006 |
C Language

Expressions in C are basically operators acting on operands. Statements like a = b + 3, ++z and 300 > (8 * k) are all expressions. Strictly speaking, even a single variable or constant can be considered an expression. You have seen several expressions in the previous C tutorial on Operators in which the examples involved expressions.

Precedence and Associativity

When an expression can be interpreted in more than one way, there are rules that govern how the expression gets interpreted by the compiler. Such expressions follow C’s precedence and associativity rules. The precedence of operators determine a rank for the operators. The higher an operator’s precedence, the higher “binding” it has on the operands.

For example, the expression a * b + c can be interpreted as (a * b) + c or a * (b + c), but the first interpretation is the one that is used because the multiplication operator has higher precedence than addition.

Associativity determines the grouping of operations among operators of the same precedence. In the expression a * b / c, since multiplication and division have the same precedence we must use the associativity to determine the grouping. These operators are left associative which means they are grouped left to right as if the expression was (a * b) / c.

The operators’ order of precedence from highest to lowest and their associativity is shown in this table:

Identifier, constant or string literal, parenthesized expression

[] func( arglist ) . -> ++ —

Left associative

++ — & * + – ~ ! sizeof

Right associative

(type-name)

Right associative

* / %

Left associative

+ –

Left associative

<< >>

Left associative

< <= > >=

Left associative

== !=

Left associative

&

Left associative

^

Left associative

|

Left associative

&&

Left associative

||

Left associative

?:

Right associative

= *= /= %= += -= <<= >>= &= ^= |=

Right associative

,

Left associative

The ++ and – on the second row of the table are the postfix increment and decrement operators. The func(arglist) on the second row is a function call. Functions will be covered in detail in another tutorial.

The ++ and – on the third row are the prefix increment and decrement operators. The single + and – on the third row are unary operators used to indicate a positive value or negate a value such as +3 or -a. The & on the third row is the address-of operator; the & on the 10th row is the bitwise AND operator. The (type-name) on the fourth row is an explicit cast which will be covered later in this tutorial.

Example
  1. int a = 9;
  2. int b = 4;
  3. int c = 6;
  4.  
  5. printf( “%dn”, a + b * c );

The output will be 33 because the b * c multiplication has higher precedence.

The next example demonstrates the usage assignment and relational operators. Say func() is a function that returns some value. You want to assign the returned value to a variable and perform some action if the value is 3:

  1. int a;
  2.  
  3. if ( a = func() == 3 )
  4. {
  5. 	/* do something */
  6. }
  7. /* do something with a */

However this will not work because the == has higher precedence than =, so the expression in the if statement is parsed as if (a = (func() == 3)). Therefore a is not assigned to the return value of func(), it will be set to 1 if func() returns 3, or 0 if func() does not return 3. The correct way is to use a parenthesized expression:

if (( a = func()) == 3)
 

This example shows associativity:

int a = 3 + 4 – 2 + 7;
 

This works as you would expect. The expression is parsed as ((3 + 4) – 2) + 7 and 12 is assigned to a. Here is another dealing with associativity:

  1.  #include <stdio.h>
  2.  
  3.  void main()
  4.  {
  5.          int a = 0;
  6.          int b = !++*&a;
  7.          printf( "a = %d, b = %dn", a, b );
  8.  }

This program outputs:

a = 1, b = 0

All the operators on line 6 ( logical not, prefix increment, dereference and address-of) all have the same precedence but they are right associative. So the right operand of the = on line 6 can be written like this: ! ( ++ ( * (&a))).

This takes the address of a, then de-references it to get a again, increments it which stores 1 in a and returns 1, then takes the logical not of 1 which is 0 and assigns that to b.

These examples show that you must pay attention to operator precedence and associativity when writing expressions. Most expressions will work as expected but there are few that can surprise you.

Pay attention to the relational operators which have higher precedence than bitwise, logical and assignment operators. If you are not sure about how an expression will be interpreted then either breaks up the expression into several expressions over multiple statements or fully parenthesize the expression.

Here is a simple example of how to break up or parenthesize an expression. Suppose the original code is like this:

  1. int a;
  2. int c;
  3.  
  4. /* calculate a and c */
  5.  
  6. if ( a & 0x01 == c >> 2 )
  7. {
  8. 	/* do something */
  9. }

This will be interpreted as if (a & (0x01 == (c >> 2))) which is probably not what you wanted. You could use temporary variables like this:

  1. int expr1 = a & 0x01;
  2. int expr2 = c >> 2;
  3. if ( expr1 == expr2 )
  4. {
  5. 	/* do something */
  6. }

The other way is to add parentheses:

  1. if ((a & 0x01) == (c >> 2))
  2. {
  3. 	/* do something */
  4. }

C Expressions – Evaluation

It is important to note that the above section on operator precedence and associativity does not define the order that the operands are evaluated; it only defines how an expression is interpreted.  The operands to most operators may be evaluated in any order.

For example the simple expression a * b + c is interpreted as (a * b) + c according to the precedence rules. But that does not mean a * b is evaluated first. The c variable could be evaluated first, then a * b, which in turn could evaluate either a or b.

When dealing with simple variables this is not a problem. However consider what happens if the operands to an operator have side effects:

  1.  #include <stdio.h>
  2.  
  3.    int a = 0;
  4.  
  5.    int func1()
  6.    {
  7.            a *= 3;
  8.            return a;
  9.    }
  10.  
  11.   int func2()
  12.   {
  13.           a += 3;
  14.           return a;
  15.   }
  16.  
  17.   void main()
  18.   {
  19.           int b = func1() + func2();
  20.  
  21.           printf("a = %d, b = %dn", a, b );
  22.   }

In this example both functions func1() and func2() modify the variable a and return its value. The output of the program depends on which order these functions are called on line 19. If func1() is called first it will assign 0 to a and return 0, then when func2() is called it will assign 3 to a and return 3. This leaves a and b both set to 3.

If func2() is called first, it will set a to 3 and return 3, then when func1() is called it will set a to 9 and return 9. This leaves a set to 9 and b set to 12.

Both outputs would be valid since C does not specify which operand should be evaluated first. C also does not specify in which order arguments to functions are evaluated. If in the previous example we had a function like this:

  1. int func3( int arg1, int arg2 )
  2. {
  3. 	return arg1 + arg2;
  4. }

and we changed line 19 to:

  1.          int b = func3( func1(), func2() );

We still have the same problem. Either func1() or func2() could be called first which will change the arguments passed to func3().

These examples have emphasized the fact that the order of evaluation of operands may not be what you expect. You must be especially careful with expressions that cause side effects. They could cause situations where the program works in one environment but fails in another.

{mospagebreak title=C Expressions – Type Conversions}

C Expressions – Type Conversions

Sometimes when expressions are evaluated the type of an operand is converted. These conversions may happen implicitly or explicitly. Implicit conversion is done automatically. For example when the operands to some operators have different types the smaller operand is converted to the larger operand’s type. You have already seen several examples of implicit type conversion in the tutorial on operators.

One thing to note here is that operators that take integers usually perform what is called “integer promotion” – converting smaller integral types such as char and short into int before carrying out the operation. Let us look at an example of integer promotion:

  1.    #include <stdio.h>
  2.  
  3.    void main()
  4.    {
  5.            char a = 100;
  6.            char b = 28;
  7.            int  c;
  8.            char d;
  9.  
  10.           c = a + b;
  11.           d = a + b;
  12.           printf( "c = %d, d = %dn", c, d );
  13.   }

c = 128, d = -128
 

The addition operator performs implicit integer promotion. On this environment a char is a signed 8 bit value, which has a range of -128 to 127. On line 10, though both a and b are chars they are converted into int before the addition is done, then the result is assigned to c. On line 11 the same thing happens, but the int result of 128 is converted back into a char, which causes the value to “wrap around” to -128 since a positive 128 is not within the range of a char.

C Expressions – Explicit Type Conversion

With explicit type conversion you can tell the compiler to treat a value as a certain type. The way to do explicit type conversion is with the “casting” operator.

The syntax for explicit type conversion is :

(type-name) expression

For example the expression (char *) ptr + 1 will force the compiler to treat ptr as a char pointer (“cast ptr as a char pointer”) and then add one to it. Looking at the operator precedence table above, you can see that the casting operator (on row 4) has higher precedence than the addition operator, so the cast is done first, then the addition is done.

Casting is done when the normal type conversions will not give you the result you want. This example shows the difference:

  1.  #include <stdio.h>
  2.  
  3.    void main()
  4.    {
  5.            int i = 5;
  6.            double d = i / 6;
  7.  
  8.            printf( "d = %fn", d );
  9.  
  10.           d = (double) i / 6;
  11.           printf( "d = %fn", d );
  12.   }

The output is:

d = 0.000000 d = 0.833333
 

On line 6, since i and the constant 6 are both integers an integer division is done which gives 0. On line 10 i is cast to a double, which forces the division to be done using double precision floating point values. This gives the result 0.83.

Another way to get the right result on line 10 would have been to not use the cast and instead change the constant 6 to 6.0:

  1. 10         d = i / 6.0;

Explicit type conversion is also used to change the compiler’s idea of what type a pointer is pointing to. In this next example we use void * which is basically a pointer to any type.

  1.  #include <stdio.h>
  2.  
  3.   typedef enum { INT_ARG, CHAR_ARG, DBL_ARG } ArgType;
  4.  
  5.   void printit( ArgType type, void * data )
  6.   {
  7.            switch( type )
  8.            {
  9.              case INT_ARG:
  10.                   printf("data is %dn", *(int *)data );
  11.                   break;
  12.             case CHAR_ARG:
  13.                   printf("data is %cn", *(char *)data );
  14.                   break;
  15.             case DBL_ARG:
  16.                   printf("data is %fn", *(double *)data );
  17.                   break;
  18.           }
  19.   }
  20.  
  21.   void main()
  22.   {
  23.           int i = 20;
  24.           double d = 78.9571;
  25.           char c = 'A';
  26.  
  27.           printit( CHAR_ARG, &c );
  28.           printit( INT_ARG, &i );
  29.           printit( DBL_ARG, &d );
  30.   }

The output follows:

data is A
data is 20
data is 78.957100
 

This example uses some features of C you probably have not seen before. On line 3 we define a new type called ArgType that can have one of three values: INT_ARG, CHAR_ARG or DBL_ARG. The switch statement on line 7 works basically like multiple if statements, comparing type to each case value and executing the statements after the case if it is a match.

The important part for this tutorial is the code in lines 10,13 and 16. The printit() function takes two arguments. The first argument (type) tells what kind of variable the second argument (data) is pointing to. When the printit() function is called data is a pointer to void, which is basically type-less, so it may point to any different type.

However we cannot dereference a void pointer because the compiler does not know what type it is pointing to. On line 10, if type is an INT_ARG data is cast into a pointer to an int which is then de-referenced to get the integer value. On line 13 if type is a CHAR_ARG data is cast into a pointer to char which is de-referenced to get the char value. Line 16 is similar except data is cast into a pointer to a double.

« « C Programming – Operators
C Programming – Managing Input and Output Operations » »

Author Description

Avatar

Editorial Team at Exforsys is a team of IT Consulting and Training team led by Chandra Vennapoosa.

Free Training

RSSSubscribe 392 Followers
  • Popular
  • Recent
  • C Programming – Constants and Identifiers

    March 2, 2006 - 0 Comment
  • C Programming – Structures and Unions

    May 25, 2006 - 0 Comment
  • C Programming – Data Types : Part 1

    March 7, 2006 - 0 Comment
  • C Programming – Pointers

    May 25, 2006 - 0 Comment
  • C Circular Linked Lists

    June 26, 2011 - 0 Comment
  • C Programming – Dynamic Memory allocation

    May 29, 2006 - 0 Comment
  • C Programming – Operators

    March 30, 2006 - 0 Comment
  • C Programming – Linked Lists

    May 29, 2006 - 0 Comment
  • C Programming – Managing Input and Output Operations

    March 30, 2006 - 0 Comment
  • C Programming – File management in C

    May 31, 2006 - 0 Comment
  • C Programming – Data Types : Part 2

    August 21, 2011 - 0 Comment
  • C Circular Linked Lists

    June 26, 2011 - 0 Comment
  • C Doubly Linked Lists

    June 26, 2011 - 0 Comment
  • TSR in C – An Introduction

    July 11, 2006 - 0 Comment
  • Concept of Pixel in C Graphics

    July 11, 2006 - 0 Comment
  • Call by Value and Call by Reference

    July 6, 2006 - 0 Comment
  • C Language – The Preprocessor

    May 31, 2006 - 0 Comment
  • C Programming – File management in C

    May 31, 2006 - 0 Comment
  • C Programming – Linked Lists

    May 29, 2006 - 0 Comment
  • C Programming – Dynamic Memory allocation

    May 29, 2006 - 0 Comment

Exforsys e-Newsletter

ebook
 

Related Articles

  • C Programming – Data Types : Part 2
  • C Circular Linked Lists
  • C Doubly Linked Lists
  • TSR in C – An Introduction
  • Concept of Pixel in C Graphics

Latest Articles

  • Project Management Techniques
  • Product Development Best Practices
  • Importance of Quality Data Management
  • How to Maximize Quality Assurance
  • Utilizing Effective Quality Assurance Strategies
  • Sitemap
  • Privacy Policy
  • DMCA
  • Trademark Information
  • Contact Us
© 2023. All Rights Reserved.IT Training and Consulting
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish.AcceptReject Read More
Privacy & Cookies Policy

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Non-necessary
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
SAVE & ACCEPT