[C language] Consideration of passing an array as an argument (up to a two-dimensional array)

Introduction

Many sites have introduced how to pass an array as an argument in a C language function. However, there are multiple methods for two-dimensional arrays, and it is necessary to use them properly, so I will write it including consideration of that.

I will attach a chart for easy understanding and write in order from a one-dimensional array to a two-dimensional array. The environment I'm using is mac and the compiler is gcc.

Pass a one-dimensional array (numeric type) as an argument

First, as a basic form, it is about a one-dimensional array of numeric type (int). In C language, the array itself cannot be passed as an argument, so a pointer is passed as an argument. Specifically, in the following source code, it will be at num_arr (num, numlen); on the 4th line of the main function.

__ ● Source code __

num_arr.c


#include <stdio.h>

void num_arr(int *num, int numlen) {
  for (int i = 0; i < numlen; i++) {
    printf("%d ", num[i]);
  }
}

int main(void) {
  int num[] = { 13, 5, 33, 69, 37, 14, 98, 23 };
  int numlen = sizeof(num) / sizeof(int);
  num_arr(num, numlen);
  return 0;
}

__ ● Run in terminal __

$ gcc num_arr.c 
$ ./a.out
13 5 33 69 37 14 98 23

The point to note here is that you are passing not only the num, which indicates the start pointer of the array, but also the number of elements of the array, numlen. This is because the pointer num has only the information of the start address of the arraynum []( 1500) as shown in the following image.

__ ● Image diagram __ スクリーンショット 2020-06-21 17.52.00.png

On the num_arr function side of the source code, even if you receive only the information of the start address of the array (num = 1500), you cannot know the number of elements as an array. To make up for this, the number of elements in the array numlen is required as an argument.

Since the array num [] is specified as an int type, the array address is reserved every 4 bytes such as 1500, 1504, 1508 (depending on the environment, the int type is not 4 bytes). in some cases.). Each increment of the pointer will advance the address by 4 bytes.

Pass a one-dimensional array (character type) as an argument

Next, when passing a char type array (character string) as an argument. Since the character string in C language is basically a char type array, the character string (array) itself cannot be passed, and the start pointer of the character string is passed as an argument. Specifically, in the following source code, it is str_arr (str); on the third line of the main function.

__ ● Source code __

chr_arr.c


#include <stdio.h>

void str_arr(char *str) {
  printf("%s\n", str);
}

int main(void) {
  char *str = "HELLO";
  str_arr(str);
  return 0;
}

__ ● Run in terminal __

$ gcc chr_arr.c 
$ ./a.out
HELLO

Unlike numeric types, you only need to pass str, which points to the start pointer of the array, as an argument. This is because the null character '\ 0' is at the end of the character string (char type array), so the number of arrays (number of elements) can be easily known even on the receiving side (str_arr function).

__ ● Image diagram __ スクリーンショット 2020-06-21 17.53.20.png

On the str_arr function side of the source code, if only the information of the start address of the array (str = 3300) is received, it is sufficient to recognize from that to the end null character as a character type array.

Pass a two-dimensional array (numeric type) as an argument

(1) General method of two-dimensional array (numerical type)

Next, I would like to talk about a two-dimensional array of numeric type (int type). In general, you can pass it as an argument by doing the following:

__ ● Source code __

num_arr2-1.c


#include <stdio.h>

void num_arr2(int num[][5], int numline) {
  for (int i = 0; i < numline; i++) {
    for (int j = 0; j < 5; j ++) {
      printf("%d ", num[i][j]);
    }
    printf("\n");
  }
}

int main(void) {
  int num[][5] = {
    { 32, 4, 78, 34, 64 },
    { 74, 5, 66, 36, 42 },
    { 56, 13, 55, 3, 81 },
    { 7, 56, 33, 83, 4 },
    { 32, 85, 50, 24, 39 },
    { 16, 24, 56, 43, 6 },
    { 75, 35, 27, 34, 83 },
    { 69, 41, 62, 2, 88 }
  };

  num_arr2(num, 8);

  return 0;
}

__ ● Run in terminal __

$ gcc num_arr2-1.c 
$ ./a.out
32 4 78 34 64 
74 5 66 36 42 
56 13 55 3 81 
7 56 33 83 4 
32 85 50 24 39 
16 24 56 43 6 
75 35 27 34 83 
69 41 62 2 88 

In the source code, the num_arr2 function, which is the receiver of the argument, specifies the array to be received in the format of ʻint num [] [5]` with the first argument as follows.

sample.c


void num_arr2(int num[][5], int numline)

This is because the program does not recognize it as a two-dimensional array unless you specify the number of elements (number of columns) for each row. The data of the two-dimensional array num [] [] is stored in the following form.

__ ● Image __ スクリーンショット 2020-06-21 16.16.12.png As mentioned above, all the data in each row is connected, and even if the data is a two-dimensional array, it is structurally the same as a one-dimensional array. Therefore, the num_arr2 function 1st argument ʻint num [] [5] seems to be passed a double pointer like ʻint ** num, but in reality, it is . You are passing a single pointer similar to int * num.

For the same reason as a one-dimensional array, in a two-dimensional array, the number of rows ʻint numline and the number of columns ʻint numlen must be passed separately as arguments. However, in this format, the number of columns is fixed in the form of ʻint num [] [5] `, so it is not necessary to pass the number of columns as an argument.

(2) How to pass a two-dimensional array (numeric type) with a single pointer

If you pass an array by the general method, the number of elements will be fixed in advance and it will be less versatile. So, if you dare to pass an array with a single pointer, it will be as follows.

__ ● Source code __

num_arr2-2.c


#include <stdio.h>

void num_arr2(int *num, int numline, int numlen) {
  for (int i = 0; i < numline; i++) {
    for (int j = 0; j < numlen; j ++) {
      printf("%d ", num[i * numlen + j]);
    }
    printf("\n");
  }
}

int main(void) {
  int num[][5] = {
    { 32, 4, 78, 34, 64 }, 
    { 74, 5, 66, 36, 42 }, 
    { 56, 13, 55, 3, 81 }, 
    { 7, 56, 33, 83, 4 }, 
    { 32, 85, 50, 24, 39 }, 
    { 16, 24, 56, 43, 6 }, 
    { 75, 35, 27, 34, 83 }, 
    { 69, 41, 62, 2, 88 }
  };

  int numlen = 5;
  int numline = sizeof(num) / sizeof(int) / numlen;
  int *np = (int *)num;
  num_arr2(np, numline, numlen);
  
  return 0;
}

__ ● Run in terminal __

$ gcc num_arr2-2.c 
$ ./a.out
32 4 78 34 64 
74 5 66 36 42 
56 13 55 3 81 
7 56 33 83 4 
32 85 50 24 39 
16 24 56 43 6 
75 35 27 34 83 
69 41 62 2 88 

By writing the above source code, you can pass an array with a single pointer. However, since it is not described according to the rules such as ʻint num [] [5], it is not recognized as a two-dimensional array in the program, but as a one-dimensional array. Therefore, the form num [i] [j]` cannot be used to retrieve the array, so the following roundabout method is used to retrieve the array (you can see the meaning of the formula by looking at the image above). .).

sample.c


printf("%d ", num[i * numlen + j]);

The problem remains a little, but since the number of columns (number of elements) is not fixed by the num_arr2 function on the receiving side, it can be used for general purposes.

In the case of the format passed by this single pointer, since the number of columns is not fixed, the number of columns ʻint numlen is passed as an argument together with the number of rows ʻint numline.

(3) How to pass a two-dimensional array (numerical type) with a double pointer

Now, next is how to pass an array with a double pointer. To do this, you need to change the structure of the array as shown in the image below. スクリーンショット 2020-06-21 16.47.24.png The address 10 indicated by the double pointer is the start address of the single pointer. And the address 100 indicated by this single pointer is the address where the target data is stored. If you rewrite the source code according to this, it will be as follows.

__ ● Source code __

num_arr2-3.c


#include <stdio.h>
#include <stdlib.h>

void num_arr2(int **num, int numline, int numlen) {
  for (int i = 0; i < numline; i++) {
    for (int j = 0; j < numlen; j ++) {
      printf("%d ", num[i][j]);
    }
    printf("\n");
  }
}

int main(void) {
  int input_num[][5] = {
    { 32, 4, 78, 34, 64 }, 
    { 74, 5, 66, 36, 42 }, 
    { 56, 13, 55, 3, 81 }, 
    { 7, 56, 33, 83, 4 }, 
    { 32, 85, 50, 24, 39 }, 
    { 16, 24, 56, 43, 6 }, 
    { 75, 35, 27, 34, 83 }, 
    { 69, 41, 62, 2, 88 }
  };

  //Use pointers to create a two-dimensional structure
  int numlen = 5;
  int numline = sizeof(input_num) / sizeof(int) / numlen;
  int **num = malloc(numline * sizeof(int *));
  for (int i = 0; i < numline; i++) {
    num[i] = malloc(numlen * sizeof(int));
    for (int j = 0; j < numlen; j++) {
      num[i][j] = input_num[i][j];
    }
  }

  // num_Execution of arr2 function
  num_arr2(num, numline, numlen);

  //Free memory
	for (int i = 0; i < numline; i++) {
		free(num[i]); //Free memory for each row
	}
	free(num);

  return 0;
}

__ ● Run in terminal __

$ gcc num_arr2-3.c 
$ ./a.out
32 4 78 34 64 
74 5 66 36 42 
56 13 55 3 81 
7 56 33 83 4 
32 85 50 24 39 
16 24 56 43 6 
75 35 27 34 83 
69 41 62 2 88 

It will be a little wasteful, so I think it depends on the purpose whether it is necessary to do so far. However, by doing this, it has become possible to transfer two-dimensional arrays in a general-purpose manner (* personally) without any discomfort.

(4) Easy way to pass a two-dimensional array (numeric type) (only if it conforms to C99)

As the last of the two-dimensional array (numerical type), I will write the method that can be used with C99 (* partially modified in response to your indication). This is based on the article "Pass a multidimensional array as an argument in C language".

__ ● Source code __

num_arr2-c99.c


#include <stdio.h>

void num_arr2(int numline, int numlen, int num[numline][numlen]) {
  for (int i = 0; i < numline; i++) {
    for (int j = 0; j < numlen; j ++) {
      printf("%d ", num[i][j]);
    }
    printf("\n");
  }
}

int main(void) {
  int num[][5] = {
    { 32, 4, 78, 34, 64 },
    { 74, 5, 66, 36, 42 },
    { 56, 13, 55, 3, 81 },
    { 7, 56, 33, 83, 4 },
    { 32, 85, 50, 24, 39 },
    { 16, 24, 56, 43, 6 },
    { 75, 35, 27, 34, 83 },
    { 69, 41, 62, 2, 88 }
  };

  int numlen = 5;
  int numline = sizeof(num) / sizeof(int) / numlen;
  num_arr2(numline, numlen, num);
  
  return 0;
}

(* Since the execution result is the same, it is omitted)

If you have a C99 compliant environment, you'll find it useful (in my environment, it works with gcc, but not with Visual Studio). Please note that the argument ʻint num [numline] [numlen] `passing the pointer of the array must be the last (third argument) to be read and an error will occur.

Pass a two-dimensional array (character type) as an argument [Reference]

Basically, I think that character arrays (char type arrays) can be handled in the same way as numeric types.

For reference, the source code when the number of bytes of each line is experimentally acquired with variable length is introduced below. The details are described in this article "Reading file information in C language and storing it in a character array". I was still a beginner at the time of writing this original article, so please forgive me for the fact that it is a poor description.

__ ● Source code __

chr_arr2.c


#include <stdio.h>
#include <stdlib.h>

int get_ftext(char **str, const char *fname, int lines, int *len);
int get_ftext_lines(const char *fname);
int get_ftext_len(int *len, const char *fname);

int main(void) {
  //Read file (get)_Until the execution of the ftext function)
  const char *fname = "file01.txt";  //Set the file name (in the same folder as the source code)
  int lines = get_ftext_lines(fname);  // get_ftext_Get the number of lines in the file using the lines function
  int *len = (int *)malloc(lines * sizeof(int));  //The variable len is for storing the number of bytes in each row
  get_ftext_len(len, fname);  // get_ftext_Use the len function to put the number of bytes in each line into the variable len
  char **str = (char **)malloc(lines * sizeof(char *));
  for (int i = 0; i < lines; i++) {
    str[i] = (char *)malloc((len[i] + 1) * sizeof(char)); //Allocate memory to store character strings on each line
  }
  get_ftext(str, fname, lines, len); // get_Store the character string data of the file in the memory prepared by str using the ftext function.
  
  //Confirmation of execution result
  printf("<The stored data of str is as follows>\n");
  for (int i = 0; i < lines; i++) {
    printf("%s", str[i]);
  }
  
  //Free memory
  for (int i = 0; i < lines; i++) {
    free(str[i]); //Free memory for each row
  }
  free(str);
  free(len);
  
  return 0;
}

// get_ftext function (a function that stores character string data in a file line by line using a two-dimensional array)
int get_ftext(char **str, const char *fname, int lines, int *len) {
  FILE *fp = fopen(fname, "rb");
  if (fp == NULL) {
    printf("file open error!\n");
    return -1;
  }
  for (int i = 0; i < lines && fgets(str[i], len[i] + 1, fp) != NULL; i++) {}
  fclose(fp);
  return 0;
}

// get_ftext_lines function (function to get the number of lines in a file)
int get_ftext_lines(const char *fname) {
  FILE *fp = fopen(fname, "rb");
  int c;
  int lines = 0; //Variable to count rows
  if (fp == NULL) {
    printf("file open error!\n");
    return -1;
  }
  
  while (1) {
    c = fgetc(fp);
    if (c == '\n') { //Count lines every time there is a line break (in the case of fgets'\n'Is recognized as a line break)
      lines++;
    } else if (c == EOF) { //Measures to pick up as one line even if there is no line break in the last line
      lines++;
      break;
    }
  }
  
  fclose(fp);
  return lines;
}

// get_ftext_len function (pointer[len]Stores the byte length of each line of the file in)
int get_ftext_len(int *len, const char *fname)
{
  FILE *fp = fopen(fname, "rb");
  int c;
  int i = 0;
  int byt = 0; //For counting the number of bytes
  int byt_tmp = 0; //For primary copy of variable byte
  if (fp == NULL) {
    printf("file open error!\n");
    return -1;
  }
  
  while (1) {
    c = fgetc(fp);
    byt++;
    if (c == '\n') {
      len[i] = byt - byt_tmp;
      i++;
      byt_tmp = byt; //byt number byt_Copy to tmp
    } else if (c == EOF) {
      if ((byt - byt_tmp) <= 1) { //If the end ends with a newline
        len[i] = 0;
      } else {
        len[i] = byt - byt_tmp; //If the end does not end with a newline
      }
      break;
    }
  }
  
  fclose(fp);
  return 0;
}

The following function is passing a two-dimensional array.

sample.c


int get_ftext(char **str, const char *fname, int lines, int *len);

What is passed to the double pointer str is (a pointer to) an empty area with enough memory to hold the text in the file.

at the end

For the first time in a few months, I dealt with C language. It's easy to forget the basics, so if you have a good theme, I'll write an article little by little.


Added on October 9, 2020
Partially modified "** General method of 2D array (numeric type) **". Before the modification, the number of columns was also passed as an argument in the form of void num_arr2 (int num [] [5], int numline, int numlen), but it is obvious that numlen = 5. Therefore, the number of columns has been modified so that it is not passed as an argument.

Recommended Posts