Pointers and 2D-arrays in C
Gabriel M.
Linux Systems Engineer | IT Infrastructure | Security | Virtualization | Automation | AI | C and Shell Scripting
When taking the Engineering or CS undergrad path, like 1+1 leads to 2 (let's keep it simple here, shall we?), students will take C as their first programming language. Good old TurboC days. Well, once they get to face pointers, at first, hell comes to Earth (for most of them).
Pointers are cool! They allow you to play directly with memory space! But, as uncle Ben has already said, "with great power comes great responsibility". That being said, you should take care when dealing with pointers. Possible consequences of bad pointers usage can be memory leakage or a incredibly unstable application, just to say an example. If you are note careful enough, finding out that you left pointers hanging out there after they're not useful anymore, or that you have lots of uninitialized pointers can cause you a lot of trouble!
In addition, when dealing with arrays, things can get nasty in terms of how you write the code to access the contents of that desired pointer. Extra dimension added to the array will increase the complexity of how you will write your code to access it, especially if you are willing to pass that array to a function. In this case, you'll want to pass a reference to the array, NOT copy it all to the function, as this would be a waste of resources.
Let's imagine a single integer:
int x = 10;
We can define a pointer to this integer and make it point to it:
int *ptr_int; /* Pointer to an int */
ptr_int = &x; /* Pointer points to that int (receives the address) */
So, now that your pointer has the address of that int (& operator), it can be de-referenced to give you the contents of that address it points to:
fprintf(stdout,"%d", *ptr_int);
Now, if you think of an array, try to picture that structure in memory. Yes! It is flat! A sequential allocation of positions of the same type. So, in order to create an array of integers, you just need to write:
int array[3] = { 1,2,3 }; /* an array of 3 integers */
How would you declare a pointer for an array of integers? Now C starts playing with its notations. If you thought of int *ptr[3]; you are wrong! That is not an pointer to an array of integers, but an array of pointers to integers! So, instead of holding 3 integers, this array holds 3 memory addresses for int values.
To have a pointer to an array of integers, you would simply use a pointer!
int array[3] = { 1,2,3 }; /* an array of 3 integers */
int *ptr; /* Pointer to an 1D-array of integers */
Another cool thing is that when dealing with arrays, they come down to memory addresses (pointers!). So if you want to make the pointer point to that array and use it, there is no need for the & operator at this time, as we are using the address directly:
int array[3] = { 1,2,3 }; /* an array of 3 integers */
int *ptr; /* Pointer to an 1D-array of integers */
/* Makes ptr points to the same location array points to!*/
ptr = array;
/* Using arrays index notation */
fprintf(stdout, "First element: %d\n", ptr[0]); /* Prints 1 */
fprintf(stdout, "First element: %d\n", ptr[1]); /* Prints 2 */
fprintf(stdout, "First element: %d\n", ptr[2]); /* Prints 3 */
/* Using pointers arithmetics notation */
fprintf(stdout, "First element: %d\n", *(ptr + 0)); /* Prints 1 */
fprintf(stdout, "First element: %d\n", *(ptr + 1)); /* Prints 2 */
fprintf(stdout, "First element: %d\n", *(ptr + 2)); /* Prints 3 */
Now, let's think of a Matrix. A Matrix is an array of one or more 1D arrays. The same logic above is used to access its elements. Consider a square 2DArray int 2DArray[2][2] as follows:
int 2DArray[2][2] = { {0,1}, {2,3}};
Its memory representation is linearly consecutive, from the computer's point of view. 2DArray: [0][1][2][3]
That being said, we can use a pointer to an array of ints that will be able to traverse each of the arrays of the 2DArray, just adding a "new line" after each array, so we can reproduce the visualization of the 2DArray.
In addition, these are how to point to the above 2DArray elements:
2DArray --> Whole 2D Array
*(2DArray) --> First ROW of the 2D array
Acessing rows:
*(2DArray + 0) --> Same as *(2DArray)
*(2DArray + 1) --> Second ROW of the 2D array
*(2DArray + N) --> Nth ROW of the 2D array
Acessing columns:
*( *(2DArray + 0) + 0) --> Points to 2DArray[0][0]
*( *(2DArray + 0) + 1) --> Points to 2DArray[0][1]
*( *(2DArray + 1) + 0) --> Points to 2DArray[1][0]
*( *(2DArray + 1) + 1) --> Points to 2DArray[1][1]
Here is a sample program to illustrate what we have been talking about throughout this post.
mkdir -p src/{1,2}d
touch src/{{CMakeLists.txt,main.c,includes.h},{1d/1d_arrays.{c,h},2d/2d_arrays.{c,h}}}
领英推荐
CMakeLists.txt file:
# Just set policy CMP0054 to NEW to avoid CMP0054 policy violations
cmake_policy(SET CMP0054 NEW)
# Minimum CMake Version
cmake_minimum_required(VERSION 3.10)
# Project name
set(PROJECT_NAME Arrays_Study)
project(${PROJECT_NAME} C CXX)
# Where to find includes
include_directories(?
? ? ${PROJECT_SOURCE_DIR}/?
? ? ${PROJECT_SOURCE_DIR}/1d?
? ? ${PROJECT_SOURCE_DIR}/2d?
)
# additional code as shared libs
add_library(1d SHARED 1d/1d_arrays.c)
add_library(2d SHARED 2d/2d_arrays.c)
# the main executable
add_executable(arrays_study main.c)
# links additional code with main target
target_link_libraries(arrays_study 1d 2d)
includes.h file:
#ifndef _INCLUDES_H_
#define _INCLUDES_H_
#include <stdio.h>
#include <stdlib.h>
/* Square matrix size */
#define _size 3
/* Beautify arrays printing ;) */
#define _OPEN_CHAR_ '|'
#define _CLOSE_CHAR_ '|'
#endif
main.c file:
#include "includes.h"
#include "1d/1d_arrays.h"
#include "2d/2d_arrays.h"
int main
(int argc, char *argv[])
{
/* A 1D array */
int Array1D[_size] = {1,2,3};
/* A 2D array */
int Array2D[_size][_size] = {
{1,2,3},
{4,5,6},
{7,8,9}
};
fprintf(stdout, "Printing a 1D Array\n");
print_1d_array(&Array1D, _size);
fprintf(stdout,"\n");
fprintf(stdout, "Printing a 2D Array\n");
print_2d_array(Array2D, _size);
exit(EXIT_SUCCESS);
}
1d_arrays.h:
#ifndef _1D_ARRAYS_
#define _1D_ARRAYS_
/* Prints 1D arrays
* Args:
* a pointer to an array of ints
* size of that array
*/
void print_1d_array (int (*array)[], int size);
#endif
1d_arrays.c:
#include "../includes.h"
#include "1d_arrays.h"
void print_1d_array
(int (*_array)[], int size)
{
int (*ptr)[size] = _array;
fprintf(stdout, "%c ", _OPEN_CHAR_);
for (int i=0; i < size; i++)
fprintf(stdout, "%d ", (*ptr)[i]);
fprintf(stdout, "%c", _CLOSE_CHAR_);
fprintf(stdout, "\n");
}
2d_arrays.h:
#ifndef _2D_ARRAYS_
#define _2D_ARRAYS_
/* Prints 1D arrays
* Args:
* a pointer to an array of ints
* size of that array
*/
void print_2d_array (int(*_matrix)[], int size);
#endif
2d_arrays.c:
#include "../includes.h"
#include "2d_arrays.h"
void print_2d_array
(int (*_matrix)[], int size)
{
/* Points to the base address of the 2D array */
int (*ptr)[size] = _matrix;
for (int i=0; i < size; i++) {
fprintf(stdout, "%c ", _OPEN_CHAR_);
for (int j=0; j < size; j++ )
/* line i, row j */
fprintf(stdout, "%d ", *( *(ptr + i) + j));
fprintf(stdout, "%c", _CLOSE_CHAR_);
fprintf(stdout, "\n");
}
}
Building the program:
cd src # if not already there
mkdir build
cd build
cmake ../ # of course you'll need cmake, make and gcc
make
When running, it simply prints the two arrays (1D and 2D), but at this point you should understand pointers and arrays :-)
./arrays_study?
Printing a 1D Array
| 1 2 3 |
Printing a 2D Array
| 1 2 3 |
| 4 5 6 |
| 7 8 9 |
I hope this can be of any help.
-- FIN --
Senior IP Network Engineer and Consultant
4 年Nice one, Gabriel M.! Pointers were source of pain when learning C! Interesting that the first programming languages I learned were quite different: Basic when a kid, Pascal plus Assembly on vocational school. Tertiary studies began with FORTRAN and Assembly again - only at the end I was exposed to some C! Now it's just Python :)