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 likematrix[1, 2]. - Pointer (
int*): This is just a memory address (e.g.,0x1234). Indexing it viaptr[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];
}
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 ifptrisType**).
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];
}
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.
- Check the Syntax: Are you using
[x, y]on a pointer? - Linearize: If representing a 2D grid, use
ptr[y * width + x]. - Check Indirection: If using pointers-to-pointers, use
ptr[x][y](chained brackets).