Programming in Python: Dictionaries

Dictionaries are a fundamental data structure in Python, providing a way to store key-value pairs. Unlike lists, which are indexed numerically, dictionaries use keys to access values. Keys can be numbers, strings, or even tuples (if they are immutable). Dictionaries are unordered collections in Python versions before 3.7; starting from Python 3.7, dictionaries maintain insertion order.

Creating a Dictionary

An empty dictionary can be created using {} or the dict() constructor:

# Creating an empty dictionary
dic = {}

# Creating a dictionary with key-value pairs
dic = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}

Accessing and Modifying Dictionary Elements

You can access values using their keys, add new key-value pairs, and delete keys:

# Accessing values
print(dic['one'])  # Output: 1

# Adding a new key-value pair
dic['six'] = 6
print(dic)  # Output: {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6}

# Deleting a key-value pair
del dic['five']
print(dic)  # Output: {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'six': 6}

Checking for Key Existence

To check if a key exists in a dictionary, use the in keyword:

print('one' in dic)  # Output: True
print('seven' in dic)  # Output: False

Retrieving Keys and Values

Python provides methods to get all keys, values, or key-value pairs:

# Getting all keys
print(list(dic.keys()))  # Output: ['one', 'two', 'three', 'four', 'six']

# Getting all values
print(list(dic.values()))  # Output: [1, 2, 3, 4, 6]

# Getting key-value pairs
print(list(dic.items()))  # Output: [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('six', 6)]

Creating a Dictionary from a List of Tuples

A dictionary can be created from a list of tuples, where each tuple contains a key-value pair:

a = [('aass', 23), ('iiii', 56), ('dftj', 38)]
dic_from_list = dict(a)
print(dic_from_list)  # Output: {'aass': 23, 'iiii': 56, 'dftj': 38}

Using dict() with Keyword Arguments

You can also create a dictionary using keyword arguments:

d = dict(jhjkhk=433, jkhjkhk=3434, iuijmkm=344343)
print(d)  # Output: {'jhjkhk': 433, 'jkhjkhk': 3434, 'iuijmkm': 344343}

Dictionary Methods

Here are some useful dictionary methods:

# Removing a key and returning its value
value = dic.pop('one', 'Key not found')
print(value)  # Output: 1
print(dic)  # Output: {'two': 2, 'three': 3, 'four': 4, 'six': 6}

# Merging dictionaries
other_dic = {'seven': 7, 'eight': 8}
dic.update(other_dic)
print(dic)  # Output: {'two': 2, 'three': 3, 'four': 4, 'six': 6, 'seven': 7, 'eight': 8}

Python 3 Enhancements for Dictionaries

  • Dictionary Comprehensions: You can create dictionaries using comprehensions: squares = {x: x**2 for x in range(5)} print(squares) # Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
  • The defaultdict from collections module: Provides a default value for missing keys: from collections import defaultdict dd = defaultdict(int) # Default value of int is 0 print(dd['missing_key']) # Output: 0
  • The Counter from collections module: Used to count occurrences in an iterable: from collections import Counter counter = Counter("banana") print(counter) # Output: {'b': 1, 'a': 3, 'n': 2}

Dictionaries are one of the most powerful and versatile data structures in Python. Understanding their operations and enhancements helps in writing more efficient and readable code.

Programming in Python: Sets

Introduction to Sets

A set is a built-in data type in Python that represents an unordered collection of unique elements. Sets are useful when dealing with collections of items where duplicates are not allowed, and set operations (like union, intersection, and difference) can be performed efficiently.

Creating a Set

We can create a set from a list or any iterable using the set() function:

list1 = ['apple', 'mango', 'pineapple', 'mango', 'apple', 'orange']
fruit = set(list1)
print(fruit)

Output:

{'orange', 'mango', 'apple', 'pineapple'}

As seen above, duplicate values ('mango' and 'apple') are automatically removed.

Checking Membership

You can check if an element is present in a set using the in keyword:

print('mango' in fruit)  # True
print('grape' in fruit)  # False

Set Operations

Python sets support various operations such as union, intersection, difference, and symmetric difference.

Example: Set Operations on Strings

We can also create sets from strings, which treat each character as a unique element.

a = 'abhilash'
b = 'abhijith'

set_a = set(a)
set_b = set(b)

print("Set A:", set_a)
print("Set B:", set_b)

Output:

Set A: {'a', 'b', 'i', 'h', 'l', 's'}
Set B: {'a', 'b', 'i', 'h', 'j', 't'}

Difference (-)

Elements in set_a but not in set_b:

print(set_a - set_b)  # {'s', 'l'}

Elements in set_b but not in set_a:

print(set_b - set_a)  # {'j', 't'}

Union (|)

Combines all elements from both sets:

print(set_a | set_b)  # {'a', 'b', 'i', 'h', 'j', 'l', 's', 't'}

Intersection (&)

Common elements in both sets:

print(set_a & set_b)  # {'a', 'b', 'i', 'h'}

Symmetric Difference (^)

Elements unique to each set (not in both):

print(set_a ^ set_b)  # {'s', 'l', 'j', 't'}

Additional Set Methods

Python provides several built-in methods for working with sets:

# Adding elements
a_set = {1, 2, 3}
a_set.add(4)
print(a_set)  # {1, 2, 3, 4}

# Removing elements
a_set.remove(2)
print(a_set)  # {1, 3, 4}

# Discarding elements (won't raise an error if element is absent)
a_set.discard(5)

# Clearing a set
a_set.clear()
print(a_set)  # set()

When to Use Sets

  • Removing duplicate values from a list.
  • Checking for membership (in is faster in sets than in lists).
  • Performing mathematical set operations like union, intersection, and difference.

Conclusion

Sets in Python provide an efficient way to handle collections of unique elements and perform operations like union, intersection, and difference. By understanding these fundamental operations, you can use sets to write cleaner and more efficient Python code.

Programming in Python: Tuples and Sequences

Tuples in Python are immutable sequences, meaning their elements cannot be changed after assignment. They are typically used for grouping related data.

Creating Tuples

A tuple consists of elements enclosed in parentheses, although parentheses are optional. An empty tuple is represented as ().

# Creating tuples
empty_tuple = ()
print(empty_tuple)  # Output: ()

single_element_tuple = ('one',)  # Note the comma!
print(single_element_tuple)  # Output: ('one',)

multi_element_tuple = ('one', 'two', 'three')
print(multi_element_tuple)  # Output: ('one', 'two', 'three')

Why the Comma in a Single-Element Tuple?

If you don’t include a trailing comma, Python will not recognize it as a tuple:

not_a_tuple = ('one')
print(type(not_a_tuple))  # Output: <class 'str'>

single_element_tuple = ('one',)
print(type(single_element_tuple))  # Output: <class 'tuple'>

Tuple Packing and Unpacking

Tuple packing refers to grouping multiple values into a tuple, while unpacking extracts those values into variables.

# Packing values into a tuple
t = 1223, 5676, 'one', 'two'  # Parentheses are optional here
print(t)  # Output: (1223, 5676, 'one', 'two')

# Unpacking the tuple
a, b, c, d = t
print(a)  # Output: 1223
print(d)  # Output: 'two'

Extended Unpacking (Python 3.0+)

Python allows using * to capture multiple elements during unpacking:

t = (1, 2, 3, 4, 5)

first, *middle, last = t
print(first)   # Output: 1
print(middle)  # Output: [2, 3, 4]
print(last)    # Output: 5

Immutable Nature of Tuples

Tuples are immutable, meaning elements cannot be changed after assignment:

t = (1, 2, 3)
t[0] = 10  # TypeError: 'tuple' object does not support item assignment

However, if a tuple contains mutable elements (like lists), those elements can still be modified:

t = (1, [2, 3], 4)
t[1].append(5)  # Modifying the list inside the tuple
print(t)  # Output: (1, [2, 3, 5], 4)

When to Use Tuples?

  • When you need an immutable sequence of elements.
  • When returning multiple values from a function.
  • As dictionary keys (since lists are not hashable).
  • For performance optimization since tuples are slightly faster than lists.

Conclusion

Tuples are a fundamental data structure in Python, providing an immutable sequence type. With tuple packing and unpacking, they allow for convenient assignment and handling of multiple values. While lists are more flexible, tuples serve an important role where immutability is needed.

Programming in Python: Lists

Lists in Python are one of the most versatile and widely used data structures. Unlike other data types, lists allow for extensive operations such as modification, slicing, nesting, and more. Lists are defined using square brackets [] and can contain elements of different data types, including numbers, strings, tuples, and even other lists (nested lists).

Creating and Accessing Lists

You can create a list with a mix of different types of elements:

list1 = [1, 2, 3, 'how', 'are', 'you']
print(list1)

Output:

[1, 2, 3, 'how', 'are', 'you']

You can access individual elements using their index:

print(list1[3])

Output:

'how'

Slicing Lists

Python allows you to extract parts of a list using slicing:

print(list1[3:])  # Elements from index 3 to the end
print(list1[:2])  # Elements from start up to (but not including) index 2

Output:

['how', 'are', 'you']
[1, 2]

Modifying Lists

Lists in Python are mutable, meaning their contents can be changed:

list1[:2] = 'and'  # Assigning a string to a slice
print(list1)

Output:

['a', 'n', 'd', 3, 'how', 'are', 'you']

Here, Python treats the string as an iterable and replaces the first two elements with its characters. To correctly replace multiple elements, use a list:

list1[:2] = ['and']
print(list1)

Output:

['and', 3, 'how', 'are', 'you']

Adding Elements from Another List

You can insert elements from one list into another at a specific position:

list1 = [12, 34, 56, 's', 'y']
list2 = [22, '22']
list1[3:3] = list2  # Inserts list2 at index 3 without replacing elements
print(list1)

Output:

[12, 34, 56, 22, '22', 's', 'y']

Nested Lists

Lists can also contain other lists:

list3 = [33, list2, '33']
print(list3)

Output:

[33, [22, '22'], '33']

You can access elements inside a nested list using multiple indices:

print(list3[1][1])  # Accessing '22' from the nested list
print(list3[1][0])  # Accessing 22 from the nested list

Output:

'22'
22

Removing Elements: The del Statement

The del statement removes elements from a list using their index. It can also delete the entire list.

list4 = [10, 20, 30, 40, 50]
del list4[2]  # Removes the element at index 2
print(list4)

Output:

[10, 20, 40, 50]

To remove an entire list:

del list4

Now, list4 no longer exists.

Additional List Methods

Python provides several built-in methods to work with lists:

  • append(item): Adds an item to the end of the list.
  • insert(index, item): Inserts an item at a specific index.
  • remove(item): Removes the first occurrence of an item.
  • pop(index): Removes and returns the element at the specified index (default is last item).
  • sort(): Sorts the list in place.
  • reverse(): Reverses the order of elements in the list.

Example:

nums = [5, 2, 9, 1]
nums.append(7)
nums.sort()
print(nums)

Output:

[1, 2, 5, 7, 9]

Conclusion

Python lists offer powerful functionalities, making them essential in everyday programming. With their ability to store heterogeneous elements, support slicing, allow modifications, and provide built-in methods, lists remain one of the most useful data structures in Python.

Would you like to explore more advanced list operations, such as list comprehensions or functional programming with lists? Let us know in the comments!

Programming in Python: Strings

Strings are a fundamental data type in Python and can be manipulated in various ways. Strings are enclosed within either single ('), double ("), or triple (''' or “””) quotes. Triple quotes allow for multi-line strings.

Assigning and Printing Strings

We can assign a string to a variable and print it using the print function.

# Assigning a string
greeting = 'Hello, World!'

# Printing a string
print(greeting)

Output:

Hello, World!

String Concatenation

Strings can be concatenated using the + operator:

print("Hello" + " World")

Output:

Hello World

String Slicing

Python allows slicing strings using indices. The slice notation follows the format string[start:end], where start is inclusive, and end is exclusive.

string1 = 'hello world'

print(string1[0:1])  # 'h'
print(string1[0:4])  # 'hell'
print(string1[2:4])  # 'll'
print(string1[:4])   # 'hell'
print(string1[3:])   # 'lo world'

Attempting to modify a string using slicing will result in an error because strings in Python are immutable:

string1[:0] = 'Hai'  # TypeError: 'str' object does not support item assignment

However, we can create a new string using concatenation:

print('Hai' + string1[:])  # 'Haihello world'

Using Negative Indices

Negative indices allow accessing elements from the end of the string.

print(string1[-1])    # 'd'
print(string1[-4:-1]) # 'orl'
print(string1[-4:-2]) # 'or'

If the slicing range is incorrect, it may return an empty string:

print(string1[-4:0])   # ''
print(string1[-1:-4])  # ''
print(string1[-11:-1]) # 'hello worl'

Additional String Methods

Python provides several useful methods for string manipulation:

s = " Python Programming "

print(s.lower())       # ' python programming '
print(s.upper())       # ' PYTHON PROGRAMMING '
print(s.strip())       # 'Python Programming' (removes leading/trailing spaces)
print(s.replace("Python", "Ruby"))  # ' Ruby Programming '
print(s.split())       # ['Python', 'Programming'] (splits by whitespace)

String Formatting

Python supports f-strings (introduced in Python 3.6) for formatting strings:

name = "Alice"
age = 30

print(f"My name is {name} and I am {age} years old.")

Output:

My name is Alice and I am 30 years old.

Conclusion

Python provides powerful ways to manipulate strings using slicing, concatenation, and various built-in methods. Understanding these techniques helps in efficient string handling in real-world applications.

Programming in Python: Arithmetic Operations

Python is a versatile and widely-used programming language that supports various programming paradigms, including functional, procedural, and object-oriented programming. One of its fundamental features is its ability to perform arithmetic operations effortlessly.

Basic Arithmetic Operations

Python provides built-in support for standard arithmetic operations. These can be performed interactively using the Python shell (REPL).

>>> 3 + 5  # Addition
8

>>> 10 - 4  # Subtraction
6

>>> 10 * 4  # Multiplication
40

>>> 10 / 4  # Division (returns a float)
2.5

>>> 10 // 4  # Floor division (integer result)
2

>>> 10 % 4  # Modulus (remainder)
2

>>> 2 ** 3  # Exponentiation (power)
8

Assigning Values to Multiple Variables

Python allows assigning a single value to multiple variables simultaneously:

>>> x = y = z = 0
>>> print(x, y, z)
0 0 0

You can also assign different values to multiple variables in a single line:

>>> a, b, c = 5, 10, 15
>>> print(a, b, c)
5 10 15

Working with Complex Numbers

Python natively supports complex numbers. Imaginary numbers are written with a suffix j or J. The real and imaginary parts of a complex number can be accessed using the .real and .imag attributes:

>>> z = 3 + 4j
>>> z.real
3.0

>>> z.imag
4.0

>>> -1j * 1j  # Multiplying imaginary numbers
(1+0j)

Using the Last Evaluated Result (_)

In interactive mode (Python shell), the last printed value is automatically assigned to the special variable _:

>>> a = 678
>>> a * 23
15594

>>> print(_)  # Accessing the last result
15594

>>> _ / 22  # Using the last result in another calculation
708.0

This feature is useful when performing quick calculations without explicitly storing intermediate values in variables.

Additional Considerations

  • Python supports floating-point arithmetic, but be aware of precision issues due to how floating-point numbers are stored.
  • The decimal and fractions modules provide more precise control over numerical calculations.
  • Python 3 introduced // for floor division, ensuring integer division results are consistent.

Conclusion

Python makes arithmetic operations simple and intuitive. Its support for complex numbers, multiple assignments, and interactive features like _ provide an efficient programming experience. Whether you’re working on basic calculations or advanced numerical computations, Python’s arithmetic capabilities are powerful and easy to use.

Programming in Unix Environment: Using the Shell

Command Line Structure

In a Unix shell, commands are typically executed by pressing Enter. However, there are different ways to structure and control the execution of commands:

  • Command Terminators:
    • A command usually ends with a newline but can also be terminated with a semicolon (;).
    • Parentheses () can be used to group commands and execute them in a subshell.
    • The ampersand (&) allows a command to run in the background, letting the user continue with other tasks.
  • Redirection and Piping:
    • The pipe (|) allows passing the output of one command as input to another.
    • The tee command captures output from a pipeline and writes it both to a file and to the standard output.

Examples:

# Grouping commands using parentheses and piping output to wc (word count)
$ (date ; who) | wc

# Capturing output in a file and continuing the pipeline
$ (who ; date) | tee output.txt | wc

# Running a long-running command in the background
$ long-running-command &

# Sleeping for 5 seconds before executing date
$ sleep 5 ; date

# Running a command in the background while executing another immediately
$ (sleep 5 ; date) & who

In the last example, who executes immediately, while (sleep 5 ; date) & waits for 5 seconds before printing the current date in the background.

Metacharacters in the Shell

Metacharacters have special meanings in Unix shells. To use them literally, enclose them in single quotes (').

Example:

$ echo '**'  # Prints ** instead of interpreting * as a wildcard

Other common metacharacters include:

  • * (Wildcard for multiple characters)
  • ? (Wildcard for a single character)
  • {} (Brace expansion)
  • [] (Character class matching)
  • $ (Variable substitution)
  • > and < (Redirection operators)
  • | (Pipe operator)

Escaping Metacharacters

If you need to use a metacharacter without its special meaning, escape it using a backslash (\) or enclose it in single quotes:

$ echo \$HOME    # Prints the string "$HOME" instead of expanding it to the home directory
$ echo 'Hello > World'  # Prints "Hello > World" instead of treating '>' as a redirection operator

Additional Notes

  • The nohup command allows a process to continue running after the user logs out.
  • Job control commands like fg, bg, and jobs help manage background processes.
  • Using &> redirects both standard output and standard error to a file.

Example of nohup:

$ nohup long-running-command &  # Keeps running even if the session is closed

UNIX File System: An Overview

A file in UNIX is essentially a collection of data, whether it be text, binary, or structured information. The UNIX file system provides various commands for examining and managing files. This post covers some fundamental commands used to inspect and navigate files efficiently.

Viewing File Content with od

The od (octal dump) command displays a file’s contents in different formats, which is useful for inspecting non-text files. Some commonly used options include:

  • -c: Interprets bytes as characters.
  • -b: Prints bytes as octal numbers.
  • No option: Dumps the file in 16-bit words (default).

Example:

$ od -c example.txt   # Show file content as characters
$ od -b example.txt   # Show file content in octal format
$ od example.txt      # Default output (16-bit words)

Identifying File Types with file

The file command determines a file’s type by inspecting its contents rather than relying on extensions.

Example:

$ file example.txt
example.txt: ASCII text
$ file script.sh
script.sh: Bourne-Again shell script, UTF-8 Unicode text
$ file binary_file
binary_file: ELF 64-bit LSB executable, x86-64

Checking Disk Usage with du

The du (disk usage) command reports the disk space used by files and directories.

  • du: Shows disk usage of directories.
  • du -a: Includes files along with directories.
  • du -h: Displays sizes in a human-readable format.
  • du -a | grep filename: Filters output for a specific file.

Example:

$ du             # Show disk usage of directories
$ du -a          # Show disk usage of all files and directories
$ du -h          # Display sizes in human-readable format (e.g., KB, MB, GB)
$ du -a | grep example.txt  # Search for a specific file's usage

Understanding UNIX Directories

A UNIX directory consists of 16-byte chunks:

  • The first two bytes point to the administrative information.
  • The last 14 bytes contain the file name, padded with ASCII null characters (NUL).

Understanding this structure helps in low-level file system debugging and development.

Finding Files with find

The find command searches for files based on criteria like name, type, size, and modification time.

Basic usage:

$ find . -name "example.txt"    # Find a file named 'example.txt' in the current directory
$ find /home -type f -size +10M  # Find files larger than 10MB in /home
$ find /var/log -mtime -7         # Find files modified in the last 7 days in /var/log

Additional Useful Commands

  • ls -l: Lists files with detailed information.
  • stat filename: Displays detailed file metadata.
  • df -h: Shows available disk space in a human-readable format.

With these commands, managing and analyzing files in UNIX becomes efficient and insightful. Mastering them will help streamline system operations and troubleshooting.

Programming in C: Essential Points on Constants

Constants play a crucial role in C programming, providing fixed values that do not change during program execution. Here are some important points to remember when dealing with constants in C:

Integer Constants

  1. Long Constants: A long integer constant is written with an ‘L’ or ‘l’ suffix. For example: long num1 = 1234567697L; long num2 = 567874338l; // Avoid using 'l' (lowercase) as it can be confused with '1'
  2. Unsigned Constants: An unsigned integer constant is written with a ‘U’ or ‘u’ suffix: unsigned int positiveNum = 40000U;
  3. Unsigned Long Constants: These constants have both ‘U’ and ‘L’ suffixes: unsigned long bigPositiveNum = 123456789UL;

Floating-Point Constants

Floating-point constants must contain a decimal point, an exponent (e.g., 1e-1), or both. They are automatically treated as double unless explicitly declared otherwise:

double pi = 3.14159;
float gravity = 9.8F;
double smallValue = 1.23e-4;  // 1.23 × 10⁻⁴

Octal and Hexadecimal Representation

Integer values can be specified in decimal, octal, or hexadecimal notation:

int decimalNum = 31;  // Decimal
int octalNum = 031;   // Octal (leading 0 means octal, equivalent to 25 in decimal)
int hexNum = 0x1F;    // Hexadecimal (leading 0x means hex, equivalent to 31 in decimal)

Character and String Constants

  1. Character Constants: A character constant is essentially an integer representing the corresponding ASCII value. char ch = 'A'; // ASCII value is 65
  2. String Constants (String Literals): A string constant is a sequence of characters enclosed in double quotes. char greeting[] = "Hello, C!";

Constant Expressions

A constant expression is an expression that consists only of constants. Such expressions are evaluated at compile time.

#define PI 3.14159
const int maxValue = 100;
int area = 5 * 10; // Constant expression evaluated at compile-time

Constants in Control Flow Statements

  1. Switch Statements: Each case label must be associated with an integer constant or a constant expression. switch (choice) { case 1: printf("Option 1 selected\n"); break; case 2 + 1: // Constant expression printf("Option 3 selected\n"); break; default: printf("Invalid option\n"); }
  2. Continue Statement:
    • In while and do-while loops, continue immediately jumps to the condition check.
    • In for loops, it moves to the increment step.
    • It does not apply to switch statements.
    for (int i = 0; i < 5; i++) { if (i == 2) continue; // Skips printing '2' printf("%d ", i); } Output: 0 1 3 4

By keeping these fundamental points in mind, you can write cleaner and more efficient C programs.

Programming in C: Important Points on Operators

Operators play a crucial role in C programming, enabling efficient computations and manipulations. Here are some key points to remember about C operators:

1. Cast Operator Precedence

The cast operator () has the same high precedence as other unary operators like sizeof, !, &, *, +, and -. This means that type conversions occur before most binary operations.

Example:

#include <stdio.h>

int main() {
    int x = 10;
    int y = 3;
    double result = (double)x / y; // Casts x to double before division
    printf("%f\n", result); // Output: 3.333333
    return 0;
}

2. Increment and Decrement Operators

The increment (++) and decrement (--) operators can only be applied to variables, not to expressions or constants.

Invalid Example:

(int)10++; // Error: Cannot apply increment to a constant expression

Valid Example:

int a = 5;
a++; // Valid, modifies the variable

3. Bitwise Shift Operators

Bitwise shift operators (<<, >>) allow shifting bits left or right, typically used for performance optimizations or low-level programming.

Right Shift (>>)

The right shift operator moves bits to the right and discards excess bits. The behavior for signed integers is implementation-defined (logical or arithmetic shift).

Example:

#include <stdio.h>

int main() {
    int var = 32; // Binary: 00100000
    int shifted = var >> 2; // Binary: 00001000 (8 in decimal)
    printf("%d\n", shifted); // Output: 8
    return 0;
}

Here, var is shifted 2 positions to the right. The empty bit positions are filled with zeroes for unsigned integers.

Left Shift (<<)

The left shift operator moves bits to the left, filling the empty positions with zeros.

Example:

int b = 5;   // Binary: 00000101
int c = b << 3; // Binary: 00101000 (40 in decimal)
printf("%d\n", c); // Output: 40

4. Bitwise Operators and Integral Types

Bitwise operators (&, |, ^, ~, <<, >>) can only be used with integral types (such as int, char, long). They do not work with floating-point numbers (float, double).

Invalid Example:

float f = 5.5;
int result = f >> 1; // Error: Bitwise shift not allowed on floating-point numbers

Summary

  • The cast operator has the same precedence as other unary operators.
  • Increment (++) and decrement (--) apply only to variables, not expressions or constants.
  • Bitwise shift operators (>>, <<) operate only on integral types and cannot be used with floating-point numbers.
  • The behavior of right shift on negative numbers is implementation-dependent.

By keeping these rules in mind, you can avoid common pitfalls when working with operators in C.