Make your own free website on Tripod.com
C Ripped Apart :: Section 2
Author: Mike Ware
Website: [warebiz] :: "The Programmer's Domain" - http://warebiz.tripod.com
Email: warebiz@yahoo.com
Copyright © 2003 Michael Shawn Ware, All Rights Reserved.


You are here --> [Section 2 :: C Basics]

"Jump To Articles"
    --> Input / Output Conversion Modifiers
    --> Operators & Operator Precedence
    --> Logical Expressions
    --> Type Conversion (Implicit/Explicit)
    --> Character Strings


Input / Output Conversion Modifiers

Output Conversion Modifiers
When using the printf( ) function, conversion specifiers are used to instruct the compiler of which type of variable needs to be displayed in a specific region of the output stream. Input conversion modifiers are then used to define the conversion specification in more detail through the use of formatting flags, field widths, and other detailed output formations. These conversion modifers are placed between a percent ( % ) symbol and a conversion specifier (character symbol). The following is a description of input modifiers proposed by ANSIC C standards:

Modifier Description
flag Defines a formatting flag for the output stream. The five possible flag options are ( -, +, space, #, 0 ). See further in article for additional flag option details.
digit Defines a minimum field width for the output stream. Truncation does not occur when attempting to print a value larger than the specified field width; the field width will be expanded for such a case.
.digit Defines precision for the output stream. Specifies the number of integers to be printed to the right of decimal for e, E, and f conversion specifications. Specifies the maximum number of significant integers to be printed for g and G conversion specifications. Specifies the maximum number of characters to be printed for s conversion specifications. Specifies the maximum number of integers to appear for integer conversions.
h Defines a short int or unsigned short int value when using an integer conversion specification.
l Defines a long int or unsiged long int value when using an integer conversion specification.
L Defines a long double value when using a floating point conversion specification.
* Allows for the program to decide a field width by passing a variable or expression value argument indicating the field width as well as the actual value to be printed.


The following is a detailed description of the possible formatting flag options:

Flag Description
- Defines the field to be left-justified. Justification is right by default.
+ Defines signed values to be printed with a plus ( + ) sign if positive and with a minus ( - ) sign if negative.
space Specifies a space to be attached to signed quantities when no plus ( + ) sign is used.
# Defines an alternative conversion specification form of output. Makes all floating point values be printed with a decimal point even if no values trail the point. Also prevents trailing zeros to be truncated for both g and G specifications.
0 Defines a field width's empty region to be filled with zeros rather than spaces.


The following complete program demonstrates the use of most of the input modifiers described above:


///////////////////// COMPILED USING DEV C++/C V.4 COMPILER /////////////////////////////

// PURPOSE: Demonstrates the use of output conversion modifiers
//
// PROGRAMMER: Mike Ware
// DATE LAST UPDATED: 6/23/02

#include <stdlib.h>
#include <stdio.h>

int main()
{
      int fieldWidth;
      int int1 = 8976;
      float float1 = 4453.231;
      float float2 = 987.5667;

      printf("I%10dI\n", int1);
      printf("I%-10dI\n", int1);
      printf("%.2f\n\n", float1);
      printf("Enter a field width for next number: ");
      scanf("%d", &fieldWidth);
      printf("\n\nI%*.1fI", fieldWidth, float2);
      printf("\n\n");

      system("PAUSE");
      return EXIT_SUCCESS;
}

//////////////////////////////////////////////////////////////////////////////////////////


Input Conversion Modifiers
When using the scanf( ) function, conversion specifiers are used to format the data to be converted that was inputted by the user during program execution. There is also a set of input conversion modifers used to define the input stream in more detail. Just like the output modifiers, these modifiers are placed between a percent ( % ) symbol and conversion character. The following is a description of two common input modifiers proposed by ANSI C standards:

Modifier Description
* Defines suppressed assignment. Causes scanf( ) to ignore the corresponding input.
integer Defines a maximum field input stream width. Input will actually stop when a first white space character is reached or when the field has reached a maximum, whichever occurs first.


The following complete program demonstrates the use of the two input modifiers described above:


////////////////////////// COMPILED USING DEV C++/C V.4 COMPILER //////////////

// PURPOSE: Demonstrates the use of input conversion modifiers
//
// PROGRAMMER: Mike Ware
// DATE LAST UPDATED: 6/23/02

#include <stdlib.h>
#include <stdio.h>

int main()
{
      int thirdScore, quizScore;

      printf("Enter three test scores (each separated by a space): ");
      scanf("%*d %*d %d", &thirdScore);

      printf("Enter quiz score: ");
      scanf("%3d", &quizScore);

      printf("\n\nThe final test score is: %d\n", thirdScore);
      printf("The quiz score is: %d\n\n", quizScore);

      system("PAUSE");
      return EXIT_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////


Operators & Operator Precedence

Because programming languages such as C solve problems by directly manipulating data, there must be operators to carry out necessary data operations for a particular algorithm. C has many operators and the following will describe assignment operators, extended and shorthand assignment operators, arithmetic operators, and miscellaneous "built-in" operators.

NOTE: An expression always has a value and consists of at least one operand and perhaps more than one operand combined with an operator. An operand is what the operator will act on. A statement is a command or instruction (executable statement) carried out by the CPU (central processing unit) of a computer; a program is ultimately a series of statements structured in a way to achieve a goal or solve a problem.

Assignment Operator
The assignment operator ( = ) is used for assigning a value to a particular variable. The form is as follows:

    variable = value;

where variable is considered to be an "lvalue" and value is a constant, expression, value, or variable. An "lvalue" is a name or expression that identifies an object; in other words, an "lvalue" represents a storage location that is capable of storing some type of value. During assignment, variable is considered to be the operand since the assignment operator ( = ) will "copy" the contents of value and store the result in the "lvalue" variable. When using the assignment operator ( = ) , the expression will always be evaluated from right to left (right associative).

Arithmetic Operators
For arithmetic expressions, the fundamental operators are as follows:

    +,- (unary and binary forms: positive/negative; addition/subtraction)
    * (multiplication)
    / (division)
    % (modulus or MOD) ---> returns the integer remainder of a division (defined for integers only)

NOTE: / will result in an integer value if both operands are integers; otherwise, it will result in an floating point value.
NOTE: % is defined for integers only.

Examples:

        int x = 25;
        int y = 7;
        printf("%d\n", x / y);        ---> 3
        printf("%d\n", x % y);        ---> 4
        printf("%d\n", y % x);        ---> 7

NOTE: the only possible values for an expression of the form x % y are: 0,1,2,...,y - 1

Consider the following examples:

Example 1: Suppose a line is 76 inches long. Convert this line in dimensions of feet and inches.

    feet = 76 / 12;
    inches = 76 % 12

Example 2: Determine if a given integer is odd or even.

    number % 2 == 0
    if true ---> number is even
    if false ---> number is odd

Example 3: Determine if a given number is divisble by 3.

    number % 3 == 0
    if true ---> yes
    if false ---> no

NOTE: == is a relational operator which is discussed in later articles [see Logical Expressions]

Hopefully you can see how division and modulus can be used for solving problems. The following are a few common errors that many beginning programmers seem to encounter.

Common Errors:

1) x = 29 % 4.8;
ERROR: The compiler will display something along the lines of "invalid use of floating-point". Modulus ( % ) is defined only for integers.

2) p + 7 = x + 3;
ERROR: The compiler will display something along the lines of "Lvalue required", which means the compiler must find a memory location when assigning a value to a variable. In C and C++, = is an assignment operator; it does not imply equality.

Extended Assignment
The extended assignment involves using the assignment operator ( = ) and is used to assign a value to multiple variables. For example, consider:

    int x = y = i = a = 15;

The above statement would assign all variables in the statement the value of 15. Remember when using the assignment op, the expression is always evaluated from right to left; in this case, a is assigned 15, i is assigned the value of a (15), y is assigned the value of i (15), and x is assigned the value of y (15). Extended assignments are always evaluated from right to left (right associative).

Shorthand Assignment Operators
C also has shorthand assignment operators or sometimes called compound assignment operators. In general,

    n += expression; --> implies n = n + (expression)
    n -= expression; --> implies n = n - (expression)
    n *= expression; --> implies n = n * (expression)
    n /= expression; --> implies n = n / (expression)
    n %= expression; --> implies n = n % (expression)

Misc. "Built-In" Operators
These are "built-in" operators used for misc. purposes.

    sizeof( argument ) --> returns the size in bytes of argument, which can be a variable or data type.

The increment operator, ++, adds 1 to a variable. The decrement operator, --, does the opposite and subtracts 1 from a variable.

Example:

    n = n + 1 is equivalent to n++ and ++n
    n = n - 1 is equivalent to n-- and --n

n++ and n-- are called post-increments, which mean they are performed after everything else in a statement (including assignment). ++n and --n are called pre-increments, which mean they are performed before anything else in the statement.

Example:

    int x = 7;
    int y = 4;
    printf("%d\n", x++ * --y);        --> 21
    printf("%d %d\n", x, y);         --> 8    3

n = n + 1    -->    n++    -->    ++n
n = n - 1    -->    n--    -->    --n

Operator precedence (determines which expressions are evaluated before others in a statement) and operator associativity are both very important to understand. The best way to understand these concepts is to examine a few examples.

Operator Precedence
General Overview:

    ( ) --> parentheses (high precedence)
    unary +, -
    *, /, %
    binary +, - (low precedence)

NOTE: To override the default precedence in a statement, use parentheses ( ) to group expressions that need to be evaluated before others.

Operator Associativity

Associativity may be left associative (left to right) or right associative (right to left). All arithmetic ops are left associative; assignment ops (+=, -=, *=, /=, %=, =) are right associative.

Let's examine some examples.

Example 1:

    int a = 6, b = 5, c = 9;
    printf("%d\n", a + b * c);        --> 6 + ( 5 * 9 ) = 51
    printf("%d\n", a * b % c);        --> ( 6 * 5 ) % 9 = 3

Example 2:

    int a = b = c = d = 6;    --> intializes a, b, c, and d to a value of 6

Example 3:

    x = y = 5;
    a = 8;
    b = 5
    c = a * b / 3 + a / x;
    printf("%d", c);                --> ( 8 * 5 ) / 3 + ( 8 / 5 ) = 13 + 1 = 14

    c = 9 + a % b;
    printf("%d", c);        --> 12

    c = 10 / 3 * ( 2 + b )
    printf("%d", c);            --> 10 / 3 * ( 7 ) = 3 * 7 = 21;

Example 4:

    17 / 2 / 3 - 125 % 5        --> ( ( 17 / 2 ) / 3 ) - ( 125 % 5 ) = 2

    2.5 * 6 + 3 / 2.0        --> 15.0 + 1.5 = 16.5
    2.5 * ( 6 + 3 ) / 2.0        --> 22.5 / 2.0 = 11.25

Now that we have covered the precedence of operators and operator associativity, we can now move on and talk about logical expressions. Read on for more...

Logical Expressions

Logical expressions, sometimes called boolean expressions, are expressions that return a value of true or false. A ( 1, or more generally any nonzero value ) is always associated with a true expression; a ( 0 ) is always associated with a false expression. There will be many times when you as a programmer will have to write code to handle certain (true/ false) situations. If an expression is true, then you will issue some command or execute a sequence of instructions. If an expression is false, then you will issue some other command or instruction. A relational expression is a special type of logical expression in which some type of comparison is performed. The relational operators are >, <, <=, >=, ==, and !=.

>>>>>>>>>
DEFINITION: logical expression - an expression that returns a value of either true ( 1 ) or false ( 0 ).
<<<<<<<<<

>>>>>>>>>
DEFINITION: relational expression - a special type of logical expression in which some type of comparison is performed.
<<<<<<<<<

    Relational Operators
    > : greater than
    < : less than
    >= : greater than or equal to
    <= : less than or equal to
    == : is equal to
    != : is not equal to

One of the biggest problems many beginning programmers have is distinguishing between ( = ) and ( == ). Experienced programmers can even run into problems with these two operators because it is very easy to mistype them when programming. ( = ) is an operator used for assignment purposes; it does not imply equality. ( == ) is a relational operator used to compare two values to see if they are equal; "is equal to". Be careful not to use ( = ) in relational expressions when ( == ) is needed.

Examples of relational expressions (also logical expressions):

    1- a < 2 * b - 7
    2- c != -1
    3- b > c + 4 * 7

A relational expression is always a logical expression. Logical expressions are either relational expressions or a combination of multiple relational expressions joined together by the logical operators: &&, ||, and !.

    Logical Operators
    && : logical and
    || : logical or
    ! : logical not

Example of a logical expression (not a relational expression):

(a < b) || ( b < c)

If a = 5, b = 3, and c = 10, the result of this expression is 1 (true).

A quick way to tell if an expression is logical but not relational is if one of the logical operators is being used.

Common Errors:

1) Using = (assignment operator) when == is needed.

2) Using an expression such as:

    midTerm || final == 'A'     which needs to be changed to    midTerm == 'A' || final == 'A'

3) Using an expression such as:

    a < b < c     which needs to be changed to    a < b && b < c

4) Using && when || is needed or vice versa:

Suppose a variable x is being used and x = -1 and x = -99 have special meaning:

    if (x != -1 || x != -99)     is wrong and needs to be changed to    if (x != -1 && x != -99)

In general, the operators have precedence (highest to lowest):

    A - arithmetic
    R - relational
    L - logical

NOTE: The only exception is logical not ( ! ), which is evaluated before arithmetic operators.

Specifically, the overall precedence for operators in this tutorial is:

    1 : !, +, - (unary)
    2 : *, /, %
    3 : +, - (binary)
    4 : <, <=, >, >=
    5 : ==, !=
    6 : &&
    7 : ||

Practical Use Example:

Suppose an insurance company offers a discount on automobile insurance for students who have no prior accidents and a GPA of 3.0 or above. What expression could be used to ask this?

    numAccidents == 0 && gpa >= 3.0

Most compilers also have a capability called short-circuit evaluation when evaluating logical expressions. This makes the evaluation of logical operations much faster and increases program efficiency. If a compound logical expression involves one expression that is false and an AND ( && ) operation is used, the entire expression must be false. If one expression is true and OR ( || ) is involved, the entire expression must be true.

Type Conversion (Implicit/Explicit)

When executing assignment statements and performing calculations, data types may be temporarily or permanently converted to other data types through something called type conversion. Implicit conversions take place without the programmer specifically asking for it. Conversions requested by the programmer are called explicit conversions. Implicit conversions cause trouble because the programmer can be unaware of what is actually happening during execution of the program until the code is tested thoroughly.

Implicit Conversion

>>>>>>>>>
DEFINITION: implicit conversion - type conversion that happens without the programmer specifically asking for it
<<<<<<<<<

It's pretty clear that programmers can run into problems because of implicit conversions. However, if you fully understand data types and how they are defined for certain arithmetic operators, you should be able to avoid them. For instance, if y and z are float, and a is int, when the statement z = a + y is executed, the value of a will be stored in a temporary memory location as float for use in the rest of the statement. Then, the addition is done (in float form) and finally, the result stored in z. During this statement, a was implicitly converted into float during the execution of the statement.

Another form of implicit conversion may take place during assignment, but not necessarily from a smaller type to a larger type.

Example:

    int a;
    float b = 3.5;
    float c = 5.0;
    int d = 2;
    a = b * c / d;
    printf("%d", a);        --> 8

What conversion took place in the above example? d was implicitly converted to 2.0 during b * c / d. The result of that arithmetic is 8.75, but a was declared int, so a was implicitly converted to 8 during the assignment statement.

Explicit Conversion

>>>>>>>>>
DEFINITION: explicit conversion - type conversion requested by the programmer; sometimes called typecasting
<<<<<<<<<

While programmers are unaware of implicit conversions, they are completely in control of explicit conversions. Explicit conversions, or typecasting, can come in handy as you will see in later problems. Typecasting can be easily accomplished and it has a easy to use form which is: ( type ) variable where type is whatever data type you choose and variable is the variable you are converting or casting. It simply takes the value of variable and converts it into the type specified.

Example 1:

    double d;
    int k;
    d = 12.78;
    k = ( int )d;        --> k = 12
    d = ( double)k;        --> d = 12.0

Example 2:

    int m = 10, n = 4, r;
    double q;
    q = m / n;        --> q = 2.0
    r = ( double )m / n;        --> 2

    r = m / n;        --> 2
    q = ( double)m / n        --> 2.5

Let's take a break from expressions and cover how C handles strings. Read on for more...

Character Strings

Think of a string as a "group" of characters. A string also relates to an array because a string can be thought of as an array of characters. Let's first refresh ourselves on how to declare single character variables in C++, and then discuss how to declare and initialize strings.

If you have a program that asks the user if he/she is married, you will most likely store the value of this prompt in a variable declared as char because there is only one answer of two possible values: Y (yes) or N (no). A variable declared as char allocates one character byte for the variable. In this case, Y would be stored or N would be stored. If the user were to enter more than one character at the prompt, such as YES, only Y would be stored in the variable.

While a variable, such as

    char x;

allocates one character for x, a variable declared as

    char x[21];

allocates practically 20 characters for x and room for a null terminator ( '\0' ).

As shown above, char x[21] is a string variable. You can store a value for x that is up to 20 characters in length. All strings in C++ end with a null terminator which denotes the end of the string. Whenever you try to search strings for certain values or manipulate strings, keep in mind that all strings have a null terminator as the last value. If your program asks the user to enter his/ her first name, you could declare the string as char firstName[16].

You can initialize a string as follows:

    variable[max num of chars + 1] = "value";

    Ex: firstName[8] = "Michael";

You can also neglect to specify the size of the character array during initialization. The compiler will then determine its size and handle its internal address appropriately. For example, consider:

    firstName[ ] = "John William Babbage, Jr";

Reading Strings
Reading a value as a string requires the use of one of the following "built-in" functions: scanf( ), gets( ), or fgets( ). scanf( ) will extract data characters from the input stream until the first whitespace character is reached; for more information, [see Basic C Statements : Input / Output (section 1)]. gets( ) extracts data characters until the NEWLINE character ( '\n' ) is reached. fgets( ) is defined for file input streams. Study the following program examples to see how these functions are able to read strings:


////////////////////////////////////////// COMPILED USING DEV C++/C V.4 COMPILER /////////////////////////////////////////

// using scanf function
// try inputting values with internal whitespace characters

#include <stdio.h>
#include <stdlib.h>

const int NAMELEN = 20;

int main()
{
      char name[NAMELEN];

      printf("Enter your name: ");
      scanf("%s", name);

      printf("\n\nHello, %s. Your wish is my command.\n\n", name);

      system("PAUSE");
      return EXIT_SUCCESS;
}


// using gets function
// try inputting values with internal whitespace characters

#include <stdio.h>
#include <stdlib.h>

const int NAMELEN = 20;

int main()
{
      char name[NAMELEN];

      printf("Enter your name: ");
      gets(name);

      printf("\n\nHello, %s. Your wish is my command.\n\n", name);

      system("PAUSE");
      return EXIT_SUCCESS;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Writing Strings
You can write data as a string using puts( ), printf( ), or fputs( ). puts( ) will output characters until the NULL terminater ( '\0' ) is reached. printf( ) provides much more flexibility when writing strings to an output stream; for more information, [see Basic C Statements : Input / Output (section 1)]. fputs( ) is defined for file output streams.

String Manipulation
Assignment statements for strings are different compared to assignment statements for integers and floating-point variables. You can assign a value to a string using the strcpy member function:

    FORM: strcpy (destination, source);
    EX: strcpy (nameOfLang, "C++");

where source is a group of characters enclosed in quotations or a variable/constant, and destination is the variable that you want the value of source to be stored in.

For example, if you have a string variable named firstName and a string variable named newFirst, you could assign the contents of firstName to newFirst by using the following statement:

    strcpy (newFirst, firstName); // defined in string.h

A key point to remember when assigning strings to a new location is to be sure that the new location is defined as having a capacity large enough to handle the source string.

Let's look at some examples of how strings can be used in programs.

Example 1:

Write a section of code that will calculate and display (to screen) the number of vowels contained in a string identified as mixedString that was given a value earlier in a program:


#include <stdlib.h>
#include <stdio.h>

int main()
{
      char mixedString[] = "ABCDEFGIJKLMNOPQRSTUVWXYZ";
      int count = 0;
      int k = 0;

      while ( mixedString[k] != '\0' ) // '\0' is the null terminator
      {
            if ( mixedString[k] == 'A' ||
                 mixedString[k] == 'E' ||
                 mixedString[k] == 'I' ||
                 mixedString[k] == 'O' ||
                 mixedString[k] == 'U' || )
            count++;
            k++;
      }

      printf("The number of vowels contained in [ %s ] is: %d\n\n", mixedString, count);

      system("PAUSE");
      return EXIT_SUCCESS;
}

Example 2:

Write a section of code that will turn all of the commas in a string identified as string1 into asterisks:


#include <stdlib.h>
#include <stdio.h>

int main()
{
      char string1[] = "W,A,R,E,B,I,Z";
      int k = 0;

      while ( string1[k] != '\0' )
      {
            if ( string1[k] == ',' )
                 string1[k] = '*';
            k++;
      }

      puts(string1);
      printf("\n\n");

      system("PAUSE");
      return EXIT_SUCCESS;
}

The following are descriptions of useful "built-in" string manipulation functions (defined in string.h):

strlen( ) --> Returns the length in numerical value of a specified string.

strcat(char* string1, char* string2) --> Cancatenates string1 and string2; only value of string1 will change.

strcmp(char* string1, char* string2) --> Compares string1 and string2 and returns a value of -1 (meaning string1 comes before string2 in ASCII representation), a value of 0 (meaning string1 is identical to string2), or a value of 1 (meaning string1 comes after string2 in the ASCII representation). These values are defined as the standard and are used by most compilers.

Study the following simple program to see the capabilities of these string manipulation functions:


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
      char string1[41] = "Hello, there.";
      char string2[] = "What's up?";
      char string3[] = "Good evening";

      printf("The length of [ %s ] is: %d\n\n", string1, strlen(string1));
      strcat(string1, string2);
      puts(string1);
      printf("\nWhen comparing [ %s ] and [ %s ], a value of %d is evaluated.\n\n", string2, string3, strcmp(string2, string3));

      system("PAUSE");
      return EXIT_SUCCESS;
}

Move on to next set of topics: Section 3 - Controlling Execution With Control Structures

Back to Top