Skip to main content

How to Resolve Error "CS0196: A pointer must be indexed by only one value" in C#

The Compiler Error CS0196 is a syntax error specific to Unsafe Code and pointer arithmetic. The message reads: "A pointer must be indexed by only one value".

In C#, managed arrays support multi-dimensional indexing (e.g., array[row, col]). However, raw pointers (int*, float*) represent a linear address in memory. They have no internal metadata regarding dimensions, rows, or columns. Because a pointer is simply a start address for a sequence of bytes, the compiler only supports a single index (offset) to determine where to look.

This guide explains how to handle multi-dimensional logic using linear pointers.

Understanding Pointers vs. Arrays

  • Managed 2D Array (int[,]): The .NET runtime knows the height and width. It allows syntax like matrix[1, 2].
  • Pointer (int*): This is just a memory address (e.g., 0x1234). Indexing it via ptr[5] translates to *(ptr + 5). The compiler doesn't know if this memory represents a grid, a cube, or a line. Therefore, it cannot calculate what [1, 2] means.

CS0196 occurs when you apply the comma syntax [x, y] to a pointer variable.

Scenario: Attempting Multi-dimensional Syntax

This error typically happens when porting image processing code or mathematical algorithms where 2D arrays are common, but pointers are used for performance.

Example of error:

public unsafe void ProcessImage(int* pixels, int width, int height)
{
int x = 5;
int y = 10;

// ⛔️ Error CS0196: A pointer must be indexed by only one value
// The compiler does not know how to map 'x, y' to a single memory address.
int pixelValue = pixels[x, y];
}
note

To compile this code, your project must have "Allow unsafe code" enabled in the build settings.

Solution 1: Linear Indexing (Manual Calculation)

Since memory is linear (a single long strip of bytes), you must map your 2D coordinates (Row, Column) to a 1D index.

The standard formula for Row-Major order is: index = (y * width) + x (or row * stride + col).

Solution: perform the math manually inside the brackets.

public unsafe void ProcessImage(int* pixels, int width, int height)
{
int x = 5;
int y = 10;

// ✅ Correct: Manually calculate the 1D offset
// We skip 'y' full rows (width * y) and add the 'x' offset.
int pixelValue = pixels[(y * width) + x];

System.Console.WriteLine($"Value at {x},{y} is {pixelValue}");
}

Solution 2: Using Pointers to Pointers

If you are working with a "jagged" structure (an array of arrays) in unsafe code, you might have a pointer to a pointer (int**).

In this case, you are not accessing a grid; you are accessing a list of pointers, then following one of them to a value. The syntax for this is two sets of brackets, not one set with a comma.

The difference:

  • ptr[x, y] = Invalid (CS0196).
  • ptr[x][y] = Valid (only if ptr is Type**).
public unsafe void ProcessJagged(int** jaggedPtr)
{
int row = 2;
int col = 3;

// ⛔️ Incorrect: CS0196 still applies here.
// int val = jaggedPtr[row, col];

// ✅ Correct: Dereference the first pointer to get a row,
// then index that row.
int val = jaggedPtr[row][col];
}
warning

Using int** (pointer to pointer) is significantly slower and more complex than using a single linear int* with math (Solution 1) due to double indirection and memory fragmentation.

Conclusion

CS0196 reminds you that pointers are low-level tools without high-level abstractions like dimensions.

  1. Check the Syntax: Are you using [x, y] on a pointer?
  2. Linearize: If representing a 2D grid, use ptr[y * width + x].
  3. Check Indirection: If using pointers-to-pointers, use ptr[x][y] (chained brackets).