Introduction to C#

The C programming language#

  • C, Deniss Ritchie, Bell Labs, 1972; overcomes B’s typeless representation; used for developing UNIX.

  • Early 80’s - traditional C

  • 1990 – the ANSI-C standard (course standard)

  • C++ and Java

Why C ?#

  • C is compact: few reserved words, terse commands, powerful set of operators.

  • C is efficient: direct memory management, some operators directly modify machine registers, manipulation of memory addresses.

  • C is modular and typed.

  • C is the native language of UNIX.

Basis of C++ (also Java and Python libraries).

C vs. Java#

Feature

C

Java

Origin

C is a procedural language developed in the 1970s.

Java was developed in the 1990s, inspired by C.

Paradigm

Procedural (not object-oriented). No classes or interfaces.

Object-oriented. Built around classes and objects.

Primitive Types

Similar to Java but no built-in boolean or string types.

Includes boolean and String as primitive and class types.

Memory Management

Manual memory management via pointers (e.g., malloc, free).

Automatic memory management with garbage collection.

Garbage Collection

No garbage collection. Developers manage memory manually.

Automatic garbage collection.

C Topics#

  • Lexical Elements (Ch. 2, self)

  • Fundamental Data Types (Ch. 3, self)

  • Flow of Control (Ch. 4, self)

  • Functions (Ch. 5); Runtime Environment

  • Arrays, Pointers, and Strings (Ch. 6); Dynamic Matrix Allocation (Ch. 12.6)

  • Structures (Ch. 9)

  • Linked Lists (Ch. 10)

  • Preprocessor (Ch. 8)

  • Input/Output and Files (Ch. 11)

Today’s class: Basic C#

#include <stdio.h>

int main(void)
{
  printf("hello, world\n");
}

In Python

print("hello, world")
# @title
%%writefile hello.c
#include <stdio.h>

int main(void)
{
  printf("hello, world\n");
}
  Cell In[1], line 5
    int main(void)
        ^
SyntaxError: invalid syntax
# @title
!gcc hello.c -o hello
./hello
               +-----------+               +---------+               +--------+
   -------->   | hello.c   |   -------->   | hello.o |   -------->   | hello  |
     edit      +-----------+    compile    +---------+    Linking    +--------+

                Keywords       Preprocessor         
                Identifiers    Lexical analysis     
                Constants      Syntax analysis      
                Operators      Semantic analysis    
                Punctuation    

Lexical Elements (chapter 2)#

Kewords#

Reserved words with a strict meaning C does a lot with relatively few keywords.

Control Flow

Data Types

Storage Classes

Type Qualifiers

Other Keywords

if

int

auto

const

sizeof

else

char

static

volatile

typedef

switch

float

extern

goto

case

double

register

continue

default

void

break

for

short

return

while

long

do

enum

struct

union

signed

unsigned

Identifiers#

A token composed of a sequence of letters

tax = price * tax_rate
  • First character cannot be a digit!!

  • Case sensitive

  • Cannot be a keyword.

  • Choose names that are meaningful (e.g., cse_5a is not a good example!!)

Comments#

Arbitrary strings placed between the delimiters /* and */.

/*
 * A comment can be written in this fashion
 * to set it off from the surrounding code.
 */

For single line comments (do not follow the ANSI standard):

// This is a comment in c++

Fundamental data types (Chapter 3 )#

Integral (signed or unsigned)

  • char

  • short

  • int

  • long

Floating Point Types

  • float

  • double

Declaration is Important for:

  • Memory allocation

  • Interpreting the number correctly within expressions.

Integral Types#

  • char

  • signed char

  • unsigned char

  • short

  • int

  • long

  • unsigned short

  • unsigned int

  • unsigned long

Floating Types#

  • float

  • double

  • long double

    // Signed integer (can hold negative values)
    int signed_int = -10;
    printf("Signed int: %d\n", signed_int);

    // Unsigned integer (cannot hold negative values)
    unsigned int unsigned_int = 10;
    printf("Unsigned int: %u\n", unsigned_int);

    // Floating-point example
    float floating_point = -3.123;
    printf("Floating-point: %.3f\n", floating_point);
# @title
%%writefile tmp.c

# include <stdio.h>

int main() {
    // Signed integer (can hold negative values)
    int signed_int = -10;
    printf("Signed int: %d\n", signed_int);

    // Unsigned integer (cannot hold negative values)
    unsigned int unsigned_int = 10;
    printf("Unsigned int: %u\n", unsigned_int);

    // Floating-point example
    float floating_point = -3.123;
    printf("Floating-point: %.3f\n", floating_point);

    return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Key Differences#

  • Signed int (int):

    • Can store negative, zero, and positive values.

    • Range (on most systems): -2,147,483,648 to 2,147,483,647.

  • Unsigned int (unsigned int):

    • Can only store zero and positive values.

    • Range: 0 to 4,294,967,295.

  • Floating-point types (float, double):

    • Always support negative, positive, and zero values by default.

    • No distinction between signed and unsigned.

Int Representation#

Binary Value Representation

Binary Value

Value

Type

0000 0000

0

unsigned char

1111 1111

255

unsigned char

0111 1111

127

unsigned char

1000 0000

128

unsigned char

Two’s Complement for Signed Values

Binary Value

Value

Type

svvv vvvv

signed char

1111 1111

-1

signed char

1000 0000

-128

signed char

Signed Integer

Binary Value

Type

svvv vvvv vvvv vvvv vvvv vvvv vvvv vvvv

signed int

Two’s Complement for Signed Values#

The most significant bit (MSB) in two’s complement indicates the sign:

  • 0: The number is positive.

  • 1: The number is negative.

In two’s complement:

  • To get the negative of a number, you invert all the bits (change 0s to 1s and 1s to 0s) and add 1 to the result.

  • The range of representable values is asymmetric: For an 8-bit number, the range is from -128 to 127.

Converting Two’s Complement Back to a Signed Integer/char If the Number is Negative (MSB is 1):

  • Invert all the bits (flip 0s to 1s and 1s to 0s).

  • Add 1 to the inverted number.

  • The result is the magnitude of the negative number.

Example:

Converting 1111 1011 (Two’s Complement of -5) Back to -5

  1. Step 1: Check the sign bit:

    • MSB is 1, so the number is negative.

  2. Step 2: Invert the bits:

    • Original: 1111 1011

    • Inverted: 0000 0100

  3. Step 3: Add 1 to the inverted bits:

    • 0000 0100 + 1 = 0000 0101 (which is 5 in decimal).

  4. Step 4: Apply the sign:

    • Since the MSB was 1, the original number is -5.

Alert: integer overflow (or wrap-around)#

    signed char x = 127;
    
    printf("Initial value: %d\n", x);
    
    // Add 1 to 127
    x = x + 1;
    
    // Print the value after adding 1
    printf("After adding 1: %d\n", x);
# @title
%%writefile tmp.c
#include <stdio.h>

int main()
{
   signed char x = 127;

   // Print the initial value
   printf("Initial value: %d\n", x);

   // Add 1 to 127, which should wrap around to -128
   x = x + 1;

   // Print the value after adding 1
   printf("After adding 1: %d\n", x);

   return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Floating-Point Representation in C#

In floating-point representation, a number is expressed in scientific notation, either in decimal or binary format.

Decimal Representation:

\[ \text{number} = \text{int}.\text{frac} \times 10^{\text{exp}} \]

Example: $\(123.45 = 1.2345 \times 10^2\)$

Binary Representation:

\[ \text{number} = 1.\text{frac} \times 2^{\text{exp}} \]

Example: $\(0.625 = 1.01 \times 2^{-1}\)$

Floating-point numbers in C are represented using the IEEE 754 standard, which defines how numbers are stored in memory. This representation is used for both float (single precision) and double (double precision).

Format

Single Precision (float): 32 bits

s eeeeeeee fffffffffffffffffffffff

  • s: Sign bit (1 bit) - 0 for positive, 1 for negative.

  • e: Exponent (8 bits) - Biased exponent (bias of 127).

  • f: Fraction (23 bits) - Mantissa, normalized to 1.f.

Value Calculation#

The value of a floating-point number is calculated using the formula:

\[ \text{Value} = (-1)^{\text{sign}} \times 1.\text{fraction} \times 2^{\text{exponent} - \text{bias}} \]
  • For float, the bias is 127.

Example

  • For 0.5 in float:

  • Binary: $\(0.5 = 1.0 \times 2^{-1}\)$

  • IEEE 754 Representation: 0 01111110 00000000000000000000000

Code#

Here’s a short code snippet to print the binary representation of a float:


 float f = 0.5;
 printf("Binary representation of float (0.5): ");
 print_binary_float(f);

# @title
%%writefile tmp.c
# include <stdio.h>
# include <stdint.h>
# include <string.h>

void print_binary_float(float num) {
 uint32_t bits;
 memcpy(&bits, &num, sizeof(bits));
 for (int i = 31; i >= 0; i--) {
     printf("%d", (bits >> i) & 1);
     if (i == 31 || i == 23) printf(" ");  // Separate sign, exponent, and mantissa
 }
 printf("\n");
}

int main() {
 float f = 0.5;
 double d = 0.5;
 printf("Binary representation of float (0.5): ");
 print_binary_float(f);

 return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Double Precision (double): 64 bits#

s eeeeeeeeeee ffffffffffffffffffffffffffffffffffffffffffffff

  • s: Sign bit (1 bit).

  • e: Exponent (11 bits) - Biased exponent (bias of 1023).

  • f: Fraction (52 bits) - Mantissa, normalized to 1.f.

  • For 0.5 in double:

  • Binary: $\(0.5 = 1.0 \times 2^{-1}\)$

  • IEEE 754 Representation: 0 01111111100 0000000000000000000000000000000000000000000000000000

Size of Data Types in C#

sizeof(char) == 1
sizeof(short) <= sizeof(int) <= sizeof(long)
sizeof(signed) == sizeof(unsigned) == sizeof(int)
sizeof(float) <= sizeof(double) <= sizeof(long double)

on “nova”:

  • char: 1 byte

  • short: 2 bytes

  • int: 4 bytes

  • long: 8 bytes

  • unsigned: 4 bytes

  • float: 4 bytes

  • double: 8 bytes

  • long double: 16 bytes

    printf("Size of char: %zu bytes\n", sizeof(char));

# @title
%%writefile tmp.c
#include <stdio.h>

int main() {
    printf("Size of char: %zu bytes\n", sizeof(char));
    return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Constants in C#

Constants are fixed values that do not change during program execution. They can be categorized by their types as follows:

Type

Examples

char

‘a’, ‘x’, ‘0’, ‘&’, ‘\n’

int

1, 0, -54, 4234567

long

0l, 123l, –7766l

unsigned

0u, 23u, 3000000000u

float

1.2f, .2f, 1.f, 3.14159f

double

1.0, -3.1412, 1.2e-2

long double

1.0l, –2.4433l

ASCII Character Constants#

Lowercase Letters

‘a’

‘b’

‘c’

‘z’

Value

97

98

99

122


Digits

‘0’

‘1’

‘2’

‘9’

Value

48

49

50

57


Special Characters

Name of Character

Written in C

Integer Value

Alert

'\a'

7

Backslash

'\\'

92

Horizontal Tab

'\t'

9

Newline

'\n'

10

Null Character

'\0'

0

Quote

'\"'

39

Ampersand

'&'

38

Asterisk

'*'

42

Plus

'+'

43

Example 1

char c = 'a';
printf("%c", c);       // prints ?
printf("%d", c);       // prints ?
printf("%c%c%c", c, c+1, c+2); // prints ?
# @title
%%writefile tmp.c
#include <stdio.h>
int main()
{
   char c = 'a';
   printf("%c\n", c);                   // Prints the character 'a'
   printf("%d\n", c);                   // Prints the integer value of 'a' (97)
   printf("%c%c%c\n", c, c + 1, c + 2); // Prints 'abc'
}
# @title
!gcc tmp.c -o tmp
!./tmp

Example 2

‘A’

‘B’

‘C’

‘Z’

Value

65

66

67

90

char c = 0;
int i = 0;

// printed ?
for (i = 'a'; i <= 'z'; ++i) {
    printf("%c", i);
}

// printed ?
for (c = 65; c <= 90; ++c) {
    printf("%c", c);
}

// printed ?
for (c = '0'; c <= '9'; ++c) {
    printf("%d", c);
}
# @title
%%writefile tmp.c
#include <stdio.h>

int main()
{
   char c = 0;
   int i = 0;

   // abc ... z is printed
   for (i = 'a'; i <= 'z'; ++i)
   {
      printf("%c", i);
   }
   printf("\n");
   // ABC ... Z is printed
   for (c = 65; c <= 90; ++c)
   {
      printf("%c", c);
   }
   printf("\n");
   // 48 49 50 ... 57 is printed
   for (c = '0'; c <= '9'; ++c)
   {
      printf("%d", c);
   }

   return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Decimal, Hexadecimal, and Octal Representation in C#

    printf("%d  %x  %o\n", 19, 19, 19);       // Output ?
    printf("%d  %x  %o\n", 0x1c, 0x1c, 0x1c); // Output ?
    printf("%d  %x  %o\n", 017, 017, 017);    // Output ?
    printf("%d\n", 11 + 0x11 + 011);          // Output ?
    printf("%x\n", 2097151);                  // Output ?
    printf("%d\n", 0x1FfFFf);                 // Output ?

# @title
%%writefile tmp.c
# include <stdio.h>

int main(void)
{
    printf("%d  %x  %o\n", 19, 19, 19);       // Output: 19  13  23
    printf("%d  %x  %o\n", 0x1c, 0x1c, 0x1c); // Output: 28  1c  34
    printf("%d  %x  %o\n", 017, 017, 017);    // Output: 15  f   17
    printf("%d\n", 11 + 0x11 + 011);          // Output: 37
    printf("%x\n", 2097151);                  // Output: 1fffff
    printf("%d\n", 0x1FfFFf);                 // Output: 2097151
    return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

The usual arithmetic conversion (promotion)#

The usual arithmetic conversion (promotion) in C follows a set of rules to convert operands to a common type before performing operations. Here’s the hierarchy:

Type Hierarchy (bottom to top)

long double

double

float

unsigned long

long

unsigned int

int

  1. If either operand is long double: → other operand is converted to long double

  2. If either operand is double: → other operand is converted to double

  3. If either operand is float: → other operand is converted to float

  4. Integral promotions:

    • char, short → int

    • If either operand is unsigned: → other operand is converted to unsigned

Example:

int + float = float

char + int = int

long + unsigned int = unsigned long

Here are some expressions and their resulting types based on the above rules:

char c;			short s;		int  i;		unsigned  u;
unsigned long  ul;		float  f;  		double  d;  	long double  ld;

Expression

Resulting Type

c - s / i

int

u * 7 - i

unsigned

u * 2.0 - i

double

f * 7 - i

float

c + 3

int

7 * s * ul

unsigned long

c + 5.0

double

ld + c

long double

d + s

double

u - ul

unsigned long

2 * i / l

long

u - l

system dependent

Explanation of Examples#

  • c - s / i: Both s and i are typically int, so the result is an int.

  • u * 7 - i: u is unsigned, and i is int. The result is unsigned due to the presence of unsigned.

  • u * 2.0 - i: The multiplication with a double promotes the result to double, so the final type is double.

  • f * 7 - i: f is float, and since there’s multiplication involved, the result is promoted to float.

  • c + 3: Here, both c and 3 are compatible as int, so the result remains int.

  • 7 * s * ul: The presence of unsigned long makes the whole expression evaluate to unsigned long.

  • c + 5.0: The presence of a double (5.0) causes the result to be promoted to double.

  • ld + c: Here, ld is long double, which promotes c to long double.

  • d + s: Since d is double, it promotes s to double, and the result is double.

  • u - ul: This operation involves unsigned, resulting in unsigned long.

  • 2 * i / l: The division by l (a long) results in a long type.

  • u - l: This is system dependent because the conversion rules can vary based on the specific system and compiler.

Cast#

double d = 3.3;
int i = 0;
unsigned ui = 0;

i = (int)d; /* i==3*/
d = (double)i / 2; /* d==1.5*/
ui = (unsigned)i;
# @title
%%writefile tmp.c
#include <stdio.h>

int main()
{
    double d = 3.3;
    int i = 0;
    unsigned ui = 0;

    i = (int)d;        /* i==3*/
    d = (double)i / 2; /* d==1.5*/
    ui = (unsigned)i;
    printf("%d\n", i);
    printf("%f\n", d);
    printf("%d\n", ui);
    printf("%d\n", i / 2);
    return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Boolean in C#

Numeric values are used instead: 0 ⇔ false, ≠0 ⇔true

int flag = 0;      // initialize flag
flag = 1;          // set flag
if(flag) {         // check flag
    // do something
}

Flow of Control (Chapter 4)#

Operators and Associativity#

This table shows C operators and their associativity (the order in which operations are performed when multiple operators of the same precedence appear).

Operator Type

Associativity

Example

Unary (+, -, ++, –, !)

Right to left

!++x

Multiplicative (*, /, %)

Left to right

a * b / c

Additive (+, -)

Left to right

a + b - c

Relational (<, <=, >, >=)

Left to right

a < b <= c

Equality (==, !=)

Left to right

a == b != c

Logical AND (&&)

Left to right

a && b && c

Logical OR (||)

Left to right

a || b || c

Assignment (=, +=, -=, *=, /=, etc.)

Right to left

a = b = c

// Right to left: unary operators
!!x        // evaluated as !(!(x))

// Left to right: arithmetic
a + b - c  // evaluated as (a + b) - c

// Right to left: assignment
a = b = 5  // evaluated as a = (b = 5)

Increment Operators#

int a = 0, b = 0, c = 0;
a = ++c;
b = c++;
printf( %d %d %d\n, a, b, c++ ); // prints ?

Note:

++x  // Pre-increment: increment first, then use value
x++  // Post-increment: use value first, then increment
# @title
%%writefile tmp.c
#include <stdio.h>

int main()
{
    int a = 0, b = 0, c = 0;
    a = ++c;
    b = c++;
    printf("% d % d % d\n", a, b, c++); // 1 1 2 is printed
}
# @title
!gcc tmp.c -o tmp
!./tmp

Declarations and Initializations 1#

int main(void){
	char	c = 'w';
	int	i = 1, j = 2, k = -7;
	double  x = 7e+33, y = 0.001;
	...

Expression

Equivalent Expression

Value

‘a’ + 1 < c

(‘a’ + 1) < c

1

-i - 5 * j >= k + 1

((-i) - (5 * j)) >= (k + 1)

0

3 < j < 5

(3 < j) < 5

1

x-3.333 <= x + y

(x - 3.333) <= (x + y)

1

x < x + y

x < (x + y)

0

Declarations and Initializations 2#

int  i = 1, j = 2, k = 3;

Expression

Equivalent Expression

Value

i == j

j == i

0

i != j

j != i

1

i + j + k == -2 * -k

((i + j) + k) == ((-2) * (-k))

1

Declarations and Initializations 3#

    char c = 'A';
    int i = 7, j = 7;
    double x = 0.0, y = 2.3;

Expression

Equivalent Expression

Value

!c

!c

0

!(i - j)

!(i - j)

1

!i - j

(!i) - j

-7

!!(x + y)

!(!((x + y)))

1

!x * !!y

(!x) * (!(!y))

1

Declarations and Initializations 4#

    char c = 'B';
    int i = 3, j = 3, k = 3;
    double x = 0.0, y = 2.3;

Expression

Equivalent Expression

Value

i && j && k

(i && j) && k

1

x || i && j - 3

x || (i && (j - 3))

0

i < j && x < y

(i < j) && (x < y)

0

i < j || x < y

(i < j) || (x < y)

1

‘A’ <= c && c <= ‘Z’

(‘A’ <= c) && (c <= ‘Z’)

1

c - 1 == ‘A’ || c + 1 == ‘Z’

((c - 1) == ‘A’) || ((c + 1) == ‘Z’)

1

The if Statement#

An if / else statement:

    if (a == 1)
        printf( * **\n);
    else
    {
        printf( “## #\n);
        other commands
    }

An if statement, not followed by an else statement:

    if (a == 1)
        printf( * **\n);

    printf( another command\n);

If the body of an if…else statement has only one statement, you do not need to use brackets {}.

Indentation#

Indentation does not influence the else statement

if (a == 1)
   if (b == 2)
      printf("***\n");
   else
      printf("###\n");

is equivalent to

if (a == 1)
   if (b == 2)
      printf("***\n");
else
      printf("###\n");

Dangling else#

Dangling else: What about the else???

if (a == 1)
   if (b == 2)
      printf("***\n");
   else
      printf("###\n");

NOT equivalent to

if (a == 1) {
   if (b == 2)
      printf("***\n");
}
else
      printf("###\n");

# @title
%%writefile tmp.c
#include <stdio.h>

int main() {
    int x = 5, y = -10;

    if (x > 0)
        if (y > 0)
            printf("Both x and y are positive\n");
    else
        printf("x is not positive\n");

    return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

Best Practice#

Best way to use use brackets.

    if (number1 >= number2) {
      if (number1 == number2) {
        printf("Result: %d = %d",number1,number2);
      }
      else {
        printf("Result: %d > %d", number1, number2);
      }
    }
    else {
        printf("Result: %d < %d",number1, number2);
    }

The while Statement#

Loop until i is equal to, or bigger than n.

while (i++ < n)
    factorial *= i;

Loop until the EOF character is encountered.

while ( ( c = getchar() ) != EOF )
{
	if ( c >= a && c <= z )
		++lowercase_letter_cnt;
	++total_cnt;
}

The for Statement#

Here we sum the numbers from 1 to 10:

i = 1;
sum = 0;
for (; i <= 10; ++i)
    sum += i;
i = 1;
sum = 0;
for (; i <= 10;)
    sum += i++;
sum = 0
for (i = 1; i <= 10; ++i)
    sum += i;

Here we have an infinite loop!!!

i = 1;
sum = 0;
for ( ; ; )
{
	sum += i++;
	printf("%d\n", sum);
}

The loop stops when the condition (middle statement) evaluates to 0.

The comma Operator#

  • Usually used in “for loops” - allows for multiple initializations and multiple processing of indices.

  • Has the lowest precedence of all the operators in C

  • left-to-right Associativity.

  • The expression exp1,exp2 as a whole has the value and type of the right operand (i.e. exp2)

Example:

    int i, j, k = 3;
    double x = 3.3;

Expression

Equivalent Expression

Value

i = 1, j = 2, ++k + 1

((i = 1), (j = 2)), ((++k) + 1)

5

k != 1, ++x * 2.0 + 1

(k != 1), (((++x) * 2.0) + 1)

9.6

Example - The for Statement#

for ( sum = 0, i = 1; i <=n; ++i )
   sum += i;

Short-circuit Evaluation (&&,||)#

Short-circuit evaluation is an optimization technique used in logical operations where the second operand is only evaluated if necessary.

Behavior

  • &&: If the first operand is false, the second is not evaluated

  • ||: If the first operand is true, the second is not evaluated

if ( (i != 0) && ( 1/i < ....)){
                // 1/i not evaluated if i==0

...
}

Benefits

  1. Improved efficiency

  2. Avoids potential errors (e.g., division by zero)

  3. Enables conditional execution of code

Best Practices

  • Use parentheses for clarity in complex expressions

  • Be cautious with expressions that have side effects

The do-while Statement#

do {
	printf( Input a positive integer:  );
	scanf( %d, &n );
	if (error = (n <= 0) )
	     printf( “\nERROR; Do it again!\n\n );
} while (error);

The break Command#

    while (1)
    {
        while (1)
        {
            scanf( %f, &x);

            if (x < 0.0) /* Exit the loop if x is negative */

                break;

            printf( % f\n, sqrt(x));
        }

        /*break jumps to here!!*/
    }

The continue Command#

    for (i = 0; i < TOTAL; ++i)
    {
        c = getchar();
        if (c >= `0 ' && c <= `9')
            continue; /* Jump to the end of the current iteration to ignore an uninteresting case */
        .
        .
        .
    }

Switch Statement#

The cases are constant integral expressions with unique values, indicating labels to jump to (according to the value of c)

    switch (c) /* value to be evaluated (integral expression) */
    {
    case 'a':
        ++a_cnt;
        break;
    case 'b':
        ++b_cnt;
        break;
    case 'c':
    case 'C':
        ++C_cnt;
        break;
    default:
        ++other_cnt;
    }
# @title
%%writefile tmp.c
#include <stdio.h>

int main() {
    char grade = 'B';

    switch (grade) {
        case 'A':
        case 'B':
        case 'C':
            printf("You passed!\n");
            break;
        case 'D':
        case 'F':
            printf("You failed.\n");
            break;
        default:
            printf("Invalid grade.\n");
    }

    return 0;
}
# @title
!gcc tmp.c -o tmp
!./tmp

The conditional operator exp1?exp2:exp3#

    x = exp1 ? exp2 : exp3;  

is Equivalent to:

    if (exp1)
        x = exp2;
    else
        x = exp3;

Example:

    char a = 'a', b = 'b'; /* ‘a‘ has a decimal value of 97 */
    int i = 1, j = 2;
    double x = 7.07;

Expression

Equivalent Expression

Value

Type

i == j ? a - 1 : b + 1

(i == j) ? (a - 1) : (b + 1)

99

int

j % 3 == 0 ? i + 4 : x

((j % 3) == 0) ? (i + 4) : x

7.07

double

j % 3 ? i + 4 : x

(j % 3) ? (i + 4) : x

5.0

double

The type is determined by both exp2 and exp3, irrespective of which is evaluated!!!

A quick intro to IO (Chapter 11)#

Printf#

int printf(<control string>, <arg2>, <arg2>,,<argn>)

Example

     printf(The bill is %d\n, x); /* x == 100 */
     The bill is 100
     printf(My name is %s and I am %d years old\n, John, 30);
     My name is John and I am 30 years old
  • The % symbol introduces conversion specification

  • Returns the number of characters printed

Printf Conversion Characters#

Conversion Character

How the Corresponding Argument is Printed

c

as a character

d, i

as a decimal integer

u

as an unsigned decimal integer

o

as an unsigned octal integer

x, X

as an unsigned hexadecimal integer

f

as a floating-point number; example: 7.123000e+00

E

as a floating-point number; example: 7.123000E+00

g

as a floating-point number; example: 7.123000

G

in the E-format or f-format, whichever is shorter

s

as a string

p

the corresponding argument is a pointer to void; its value is printed as a hexadecimal number

n

the corresponding argument is a pointer to an integer into which the number of characters printed so far is stored

%

with the format %% a single % is written to the output stream; there is no corresponding argument to be converted

Format Specifiers and How They are Printed#

char c = A, s[] = Blue moon!;

Format

Printed

Remarks

%c

"A"

field width 1 by default

%2c

" A"

field width 2, right adjusted

%-3c

"A  "

field width 3, left adjusted

%s

"Blue moon!"

field width 10 by default

%3s

"Blue moon!"

more space is needed

%.6s

"Blue m"

precision 6

%-11.8s

"Blue moo   "

precision 8, left adjusted

int i = 123;
double x = 0.123456789;

Format

Printed

Remarks

%d

"123"

field width 3 by default

%05d

"00123"

padded with zeros

%7o

"    173"

right adjusted, octal

%-9x

"7b       "

left adjusted, hexadecimal

%-9#x

"0x7b     "

left adjusted, hexadecimal

%10.5f

"   0.12346"

field width 10, precision 5

%-12.5e

"1.23457e-01"

left adjusted, e-format

Scanf#

int scanf(<control string>, <arg2>,<arg2>,,<argn>)

Example:

     int a, b;
     scanf(%d %d, &a, &b);
     The text 100 200 will assign 100 to a and 200 to b
  • Coverts non-white-space characters (unless using “%c”)

  • Returns the number of successful conversions

# @title
%%writefile tmp.c
#include <stdio.h>

int main()
{
    int a, b;
    printf("Enter two numbers: ");
    scanf("%d%d", &a, &b);
    printf("The sum is: %d\n", a + b);
}
# @title
!gcc tmp.c -o tmp
!./tmp