Exploring Pointer Arithmetic in C Language: Part 1

What is the essence of the C language? More than 90% of C engineers would definitely say it is pointers. Pointers are something that C engineers both love and hate. They are often a nightmare for many junior engineers, with terms like dangling pointers, wild pointers, and memory leaks… each one can make you lose a handful of hair (hahaha).Given this, I feel it is necessary to back up the usage of pointers with a few articles, summarizing as I write. The order may not be very logical, please forgive me.Pointer Addition: The Tool for Array Access in C Language

The core rule of pointer arithmetic is: When performing addition or subtraction on pointers with integers, the amount of change in the pointer value is based on the size of the data type it points to..

In other words, <span>p + n</span> does not simply add the address value with <span>n</span>, but adds <span>n * sizeof(*p)</span> bytes.

Exploring Pointer Arithmetic in C Language: Part 1Why does not adding 1 to all types move 1 byte?Because different data types occupy different amounts of memory.<span>char *</span> adds 1 to move 1 byte,<span>int *</span> adds 1 to move 4 bytes, while a complex <span>struct *</span> adds 1 to move the entire size of the structure. This ensures that pointer arithmetic always points to the starting address of the next (or previous) element of the same type.

Pointer Subtraction: A Ruler for Precisely Calculating Element Distances

In C language, pointers can not only perform addition but also subtraction. The unit of the pointer result is “elements”, which represents the number of elements between two pointers. This “ruler” is extremely useful in algorithms and data structures.

Similar to pointer addition, pointer subtraction also follows the size-based rule. <span>p2 - p1</span> is essentially calculated as: <span>(address value of p2 - address value of p1) / sizeof(data type)</span>

Exploring Pointer Arithmetic in C Language: Part 1Why is the result 3?

  • Assuming <span>arr</span> starts at address <span>0x1000</span>, and <span>int</span> occupies 4 bytes.

  • <span>start</span> points to <span>arr[1]</span>, with an address of <span>0x1004</span>.

  • <span>end</span> points to <span>arr[4]</span>, with an address of <span>0x1010</span>.

  • <span>(0x1010 - 0x1004) / 4 = (12) / 4 = 3</span>.

Key Points:

  1. Type <span>ptrdiff_t</span>: This is a type specifically used in the standard library to represent the result of pointer subtraction (usually defined in <span><stddef.h></span>). It is a signed integer type, formatted with <span>%td</span>.

  2. Meaning of the Result: The result <span>3</span> indicates that moving from <span>start</span> to <span>end</span> requires moving 3 <span>int</span> elements backward. If <span>p1</span> is after <span>p2</span>, the result will be negative.

🚀 Advantages: Why Use Pointer Arithmetic?

  • Higher Efficiency: At a low level, directly calculating addresses and accessing them through pointers is usually closer to the machine’s execution method than accessing through array indexing <span>arr[i]</span>, allowing the compiler to generate more optimized code. This difference is magnified in performance-critical loops and algorithms.

  • Foundation for Data Structures: Almost all high-level data structures rely on pointer arithmetic for their implementation.

    • Dynamic Arrays: Use <span>malloc</span> to allocate a block of memory, then traverse it using pointers.

    • String Operations: C-style strings are character arrays, and library functions like <span>strcpy</span>, <span>strlen</span> frequently use pointer arithmetic internally.

    • Traversal of Linked Lists and Tree Nodes: Although these structures are linked by pointers, pointer arithmetic is still used when handling array data within nodes.

    • Foundation of Iterators: The concept of iterators in C++ largely stems from the behavior of pointers.

  • Quickly Calculate Length or Offset: Without maintaining a counter, you can directly know how many elements have been processed by the difference in pointer positions.

Exploring Pointer Arithmetic in C Language: Part 1

  • Core of Algorithm Implementation: In scenarios like binary search, traversing subarrays, and handling memory blocks, pointer subtraction is an indispensable tool.

Exploring Pointer Arithmetic in C Language: Part 1

⚠️ Five Pitfalls of Pointer Arithmetic

With great power comes great responsibility. Misusing pointer arithmetic can lead to disastrous consequences.1. Out-of-Bounds AccessExploring Pointer Arithmetic in C Language: Part 1Consequences: This isundefined behavior. At best, it reads a garbage value; at worst, it causes the program to crash (segmentation fault). Even more frightening, it may sometimes seem to “work normally” but explode at the most unexpected times, making debugging extremely difficult.2. Dereferencing Uninitialized or Wild PointersExploring Pointer Arithmetic in C Language: Part 13. Subtracting Pointers Pointing to Different ArraysExploring Pointer Arithmetic in C Language: Part 1Why? The C standard only specifies that subtracting pointers pointing to the same array object (or the position just after its end) is meaningful. The compiler cannot predict the layout of two independent arrays in memory, so the calculated “distance” is meaningless.4. Subtracting Pointers of Different TypesExploring Pointer Arithmetic in C Language: Part 1

  1. Why? Different types of pointers have different step sizes (<span>sizeof(int)</span> vs <span>sizeof(char)</span>), so subtracting them has no logical meaning. The compiler usually prevents this behavior at compile time.

  2. 5. Comparisons Also Have Constraints

  3. When using <span>></span>, <span><</span>, <span>>=</span>, or <span><=</span> to compare pointers, it is also required that the pointers point to the same array or a position just after it; otherwise, it is also undefined behavior. However, <span>==</span> and <span>!=</span> can be used between any two pointers.

🛡️ Six Avoidance Tips

1. Always Ensure Pointers Are Within Valid RangesThis is the most important principle. Always perform boundary checks before accessing memory through pointer arithmetic.Exploring Pointer Arithmetic in C Language: Part 12. Always Ensure Pointers Point to the Same ArrayThis is a hard rule! Before subtracting or comparing, be clear about the origins of the two pointers. Especially after complex pointer operations, always be aware of where the pointer currently points. Avoid performing incorrect offset calculations on pointers that have already been moved.Exploring Pointer Arithmetic in C Language: Part 13. Understand That Arrays Degenerate into PointersWhen passing arrays to functions, what is actually passed is a pointer to the first element. Therefore, within the function, you cannot use <span>sizeof(arr)</span> to get the size of the array; you must pass the size as an additional parameter.Exploring Pointer Arithmetic in C Language: Part 14. Be Cautious with <span>++</span> and <span>--</span>

<span>*p++</span> and <span>(*p)++</span> are two completely different operations:

  • <span>*p++</span>: First, get the value pointed to by <span>p</span>, then move <span>p</span> forward by one element.

  • <span>(*p)++</span>: Increment the value pointed to by <span>p</span>, while <span>p</span> itself remains unchanged.

<strong><span>5. Understand That the “Over-Tail Pointer” Is Legal</span></strong><span><span> A pointer pointing to the position just after the last element of an array</span></span><strong><span> is valid and can be used for pointer subtraction.</span></strong>Exploring Pointer Arithmetic in C Language: Part 1<strong><span>6. Use Standard Type for Pointer Subtraction</span><strong><span> Always use </span><code><span>ptrdiff_t</span> to receive the result of pointer subtraction, rather than <span>int</span> or <span>long</span>, to ensure code portability.

đź’Ž Summary

  • Pointer arithmetic is a double-edged sword: One side provides an efficient and flexible way to access memory; the other side requires programmers to have an absolute clarity about the memory they are operating on.

    Remember: Always perform boundary checks and be clear about where pointers point to, in order to tame this tool without being harmed. Remember its benefits, respect its severity, and develop good habits—this is the key to mastering the essence of the C language.

<span>The personal views are for reference only; please feel free to criticize and correct any issues.</span>

Please open in the WeChat client

Leave a Comment