0

I recently started writing chunks of C code as part of my university's programming lessons (so you can freely assume that I am a complete idiot). I'm trying to write a function that writes a 2D array's data to a file, but I'm having difficulties. I declare the array in main, I have its x and y dimensions saved as #defines, and I want to call my function() like so;

include "function.h"
#define /* x_res, y_res */
int main(){
  static unsigned char pic[x_res][y_res];
  /* do some operations on pic*/
  function(pic,x_res,y_res);
}

The function itself is saved in a header file and is intended to be included at the very top of my main .c file. It goes something like this;

void function(unsigned char arry,int x_res,int y_res){
    /* some calculations, declaring file pointer with fopen() */
    for(int i=0;i<y_res;i++){
        for(int j=0;j<x_res;j++){
            fprintf(f,"%c",arry[i][j]);
        }
    }
}

I'm greeted with an error in the line fprintf(f,"%c",arry[i][j]); saying that the "subscripted value is neither array nor pointer nor vector", which is false since I know that arry is an array. Furthermore, if I try to replace said line with something like fprintf(f,"%c",arry[i*j+j]);, the error goes away, but the file output is gibberish (I'm assuming I'm only printing the addresses of the first-dimension elements of arry).

The question, then; Why can't 2D arrays be accessed like their 1D counterparts, and how do I work around this? I would imagine that an int array[][]={{0,1},{2,3}}; would give an output of

array[0] -> 0
array[1] -> 1
array[2] -> 2
array[3] -> 3

, but this is not the case -- it prints 0, 2, and then two memory addresses.

I've tried declaring my function to accept arguments as void function(unsigned char arry[*value of x_res*][*value of y_res*],x_res,y_res), which works but is not how I would like the function to work.

I've looked at some other online examples but it seems few people have had a similar problem. I tried some answers from this question but again things do not work. For example, using void function(unsigned char **arry,x_res,y_res) works with accessing the array as 2D (arry[i][j]), but again, like with the example above, most values (all that aren't in the first column) are trash.

Community
  • 1
  • 1
  • Most probably you're not assigning values to ALL elements of your `pic[res_x][res_y]`, that's why you get rubbish values. In your `/* do some operations on pic*/` part of code make sure that ALL elements are assigned to OR that you're not overflowing (`unsigned char` gives you values from 0 to 255). – Rogus Apr 15 '17 at 16:10
  • I do roll over the entire array, and I keep the values strictly between 0 and 255 using `%255` somewhere inbetween. Though even if I didn't, wouldn't the fact that the array is static make up for it? I was told uninitialized static and global variables are automatically given the value of 0. –  Apr 15 '17 at 16:12
  • 2
    What is this: `void function(unsigned char arry, ....`? You just pass *exactly one* `unsigned char` here. And this is passed by value. So applying the `[]` operator to `arry` should make the compiler yell out in pain, if not refusing to compile this at all. – alk Apr 15 '17 at 16:12
  • 1
    *"which is false*" the compiler never lies! ;-) – alk Apr 15 '17 at 16:16
  • 2
    If the compiler in use supports VLAs you can do: `void function(size_t x_res, size_t y_res, unsigned char arry[x_res][y_res]) { ...` – alk Apr 15 '17 at 16:19
  • @JariBeguš if you have static pointer then by default it is defined as null pointer, only arithmetic type is given 0 by default. – FilipRistic Apr 15 '17 at 16:21
  • Sorry didn't notice the `static` part. Also make it `%256` instead of `%255`, because now you're getting values from 0 to 254. What do you mean by garbage values? – Rogus Apr 15 '17 at 16:23
  • @alk We were taught that pointing with arrays would let you move up and down the memory using the subscriptor operator ([]). I don't know whether my understanding of that is plain wrong or if it just doesn't work with 2D arrays. Using size_t seems to work now though. @Rogus Yes that's what I meant sorry, I use `%256` to get values 0-255. I mean garbage values as in seemingly random memory addresses. –  Apr 15 '17 at 16:38
  • It's not using `size_t` that makes it work but the order of the parameters and defining `arry` as array. – alk Apr 15 '17 at 17:14
  • @filipristic: I see no "*static pointer*". – alk Apr 15 '17 at 17:22
  • Apologies for misunderstanding, I'm still in the early stage of learning this. So if using the subscriptor operator and 'subscripting values' declares variables as arrays, even in function calls, what would happen should I pass a 2D array to a function whose function declaration seeks an array[value]? Is this illegal and would cause the compiler to throw an error, or would the program compile, but assume the values of array[i][0]? –  Apr 15 '17 at 17:24
  • @alk I think @filipristic was referring to the declaration of `pic`, which is static. (But only so because this avoids a stack overflow for large values of `x_size` and `y_size`.) –  Apr 15 '17 at 17:25
  • @alk I thought that he used pointer somewhere because he didn't include whole code, now I saw that he has static unsigned char pic[x_res][y_res]; but default value will be "\0". – FilipRistic Apr 15 '17 at 17:38
  • Learning is good, so you might want to continue confusing yourself by reading the answers to this question: http://stackoverflow.com/q/6290956/694576 it's worth it. ☺ – alk Apr 15 '17 at 17:49
  • @JariBeguš I also noticed that you have inverted x_res and y_res in your for loops. – FilipRistic Apr 15 '17 at 17:56
  • @alk, That, I'm sure is correct. I go from the top left corner to the top right, then left to right over each line. (Lines here being analogous to the second-dimension/nested arrays starting at `pic[i][0]`.) –  Apr 15 '17 at 19:16

1 Answers1

1

In C99 and later, it is possible to have a VLA

 void function(int x_res, int y_res, int char[][y_res])
 {
     for(int i=0;i<x_res;i++)
     {
          for(int j=0;j<y_res;j++)
          {
              fprintf(f,"%c",arry[i][j]);
          }
     }
 }

The problem is that support of an implementation for VLAs was made optional in C11 (i.e. a C11 compiler is not required to support them). And VLAs are definitely not supported in C90 (the ISO C standard of 1990).

An declared array is contiguous in memory, so can be treated like a flat 1D array. For example;

 void function2(int x_res, int y_res, unsigned char *arr)
 {
     for(int i=0;i<x_res;i++)
     {
          for(int j=0;j<y_res;j++)
          {
              fprintf(f,"%c",arr[i*y_res + j]);
          }
     }
 }

 int main()
 {
      unsigned char x[10][20];
      unsigned char y[10*20];
      unsigned char *z = malloc(10*20*sizeof(*z));

      /*  initialise elements x, y, and z  */

      function2(10,20, (unsigned char *)x);
      function2(10,20, &x[0][0]);

      function2(10,20, y);
      function2(10,20, z);
 }

The type conversion in the first call of function() is needed since a 2D array of unsigned char cannot be implicitly converted to a unsigned char *. However, the address of x and the address of x[0][0] have the same value, even though they have different types.

A gotcha with this technique is that the dimensions passed (first two arguments of function2()) are not checked at compile time. For example;

   int xx[5][6];

   function2(10, 20, (unsigned char *)xx);   /* danger, Will Robinson!! */
   function2(10, 20, &xx[0][0]);             /*  danger, danger!! */

will compile but, since the dimensions of xx are less than the first two arguments tell function2() to expect, will cause function2() to have undefined behaviour for both calls.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • I would very much like to avoid having to declare another y (1D) array the size of x (2D) as my use case expects numbers up to ~10k, which already results in a ~100MB array in `.bss`, [says Wikipedia](https://en.wikipedia.org/wiki/Data_segment#BSS). As for your fears regarding passing wrong dimensions, I have that covered already :) (or so I hope). For my usage, the type conversion in the function call (`function2(10,20, (unsigned char *)x);`) seems to work perfectly fine, and seems to be a more inviting method than VLAs (already suggested by @alk). Thank you! –  Apr 15 '17 at 17:18