0

Long long time ago I've played with C a lot but forgot everything. Now I am trying to solve easy tasks and failed.

I'd like to write function which takes 2dim array or char* and print it. but my code is buggy and I do not understand why. As I understand products is pointer to 2dim array, so increasing it to i * sizeof(char**) I get pointer to sub-array, and increasing that sub-array pointer I get pointer to char block. But it seems that my code looking to different memory block.

About products array - I know that it has N rows and 2 columns.

#include <stdio.h>
#include <string.h>

void print(char*** products, size_t rows, size_t cols) {
    size_t i;

    for(i = 0; i < rows; i++) {
        printf("Col1: '%s. Col2: %s'\n",
            (products + i * sizeof(char**)),
            (products + i * sizeof(char**) + sizeof(char*))
        );
    }
}

int main(void) {
    const char* a[][3] = {{"abc", "1"}, {"def", "2"}, {"ghi", "3"}};
    print((char***)a, 3, 2);

    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Alex G.P.
  • 9,609
  • 6
  • 46
  • 81

4 Answers4

3

The array defined in this declaration

const char* a[][3] = {{"abc", "1"}, {"def", "2"}, {"ghi", "3"}};

has type const char *[3][3].

Take into account that the array has three "columns" there is explicitly specified 3 columns in the array declaration.

const char* a[][3] =....
               ^^^

Elements in the last column are initialized by NULL.

When an array is used in expressions as for example as an argument it is explicitly converted to pointer to its first element.

That is if you use the above shown array as an argument then it is converted to type

const char * ( * )[3]

It is not the same type as char ***

So the function should ve declared like

void print( const char * ( *products )[3], size_t rows );

or like

void print( const char * products[][3], size_t rows );

and the function should be called like

print( a, 3 );

You may specify one more parameter that sets the number of columns you want for example to output

void print( const char * ( *products )[3], size_t rows, size_t cols );

In this case the function can be called like

print( a, 3, 2 );

However the array itself in any case has 3 columns.:)

Or if the compiler supports variable length arrays like

void print( size_t rows, size_t cols, const char * ( *products )[cols] );

or for readability

void print( size_t rows, size_t cols, const char * products[rows][cols] );

and it can be called like

print( 3, 3, a );

Here is a demonstrative program that shows two ways of the function declaration

#include <stdio.h>
#include <string.h>

#define N 3

void print1( const char *product[][N], size_t rows )
{

    for ( size_t i = 0; i < rows; i++ ) 
    {
        for ( const char **p = product[i]; *p; ++p )
        {
            printf( "%s ", *p );
        }
        printf( "\n" );
    }
}    

void print2( size_t rows, size_t cols, const char *product[rows][cols] )
{

    for ( size_t i = 0; i < rows; i++ ) 
    {
        for ( const char **p = product[i]; *p; ++p )
        {
            printf( "%s ", *p );
        }
        printf( "\n" );
    }
}    


int main( void )
{
    const char * a[][N] = 
    {
        { "abc", "1" }, 
        { "def", "2" }, 
        { "ghi", "3" }
    };

    print1( a, N );
    printf( "\n" );

    size_t n = N;

    const char * b[n][n];

    memcpy( b, a, sizeof( a ) );

    print2( n, n, b );
    printf( "\n" );
}    

Its output is

abc 1 
def 2 
ghi 3 

abc 1 
def 2 
ghi 3 

Take into account that variable length arrays if they are supported by the compiler may not be initialized then they are defined.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

const char* a[][3] = {{"abc", "1"}, {"def", "2"}, {"ghi", "3"}};

In this case, you could actually write:

const char* a[][2] = {{"abc", "1"}, {"def", "2"}, {"ghi", "3"}};

But if you really want a 3 columns array, you should tell print that there are 3 columns, not 2. But your print can print the two first columns only of course ...

Then, any N-dimensions array of T can be viewed a simple 1D array of T. You just have to do some address computation to access the desired element. This is possible once you have a pointer to the first item of the array. So below is a working version:

#include <stdio.h>
#include <string.h>

typedef const char * T;

void print(T *products, size_t rows, size_t cols) {
    size_t i;

    for (i = 0; i < rows; i++) {
        // Each row has cols items so here is the current row (i-th row):
        T *row = products + i * cols;
        printf("{%s, %s}\n", row[0], row[1]);
    }
}

int main(void) {
    T a[][3] = { { "abc", "1" }, { "def", "2" }, { "ghi", "3" } };

    // Tell print that there are 3 cols, not 2
    print(&a[0][0], 3, 3);

    return 0;
}
mikedu95
  • 1,725
  • 2
  • 12
  • 24
1

You've mixed up what you regard as columns and rows. Fix that, then get rid of the three star nonsense, make the function accept variable amount of columns and you end up with this:

#include <stdio.h>

void print(size_t rows, size_t cols, const char* products[rows][cols]) 
{
  for(size_t r=0; r<rows; r++) 
  {
    for(size_t c=0; c<cols; c++)
    {
      printf("Col%zu: %s. ", c, products[r][c]);
    }
    printf("\n");
  }
}

int main (void) 
{
  const char* a[3][2] = {{"abc", "1"}, {"def", "2"}, {"ghi", "3"}};
  print(3, 2, a);

  return 0;
}

And that's that, no need to complicate this futher.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • It seems to me that ANSI C was specified in the title and I doubt that this compile in ANSI C. But maybe the OP is confused and ANSI should be interpreted as "Standard". – mikedu95 Jan 27 '16 at 10:15
  • @mikedu95 Using the term "ANSI C" typically means you are confused over the various standards, [see this](http://stackoverflow.com/questions/17206568/what-is-the-difference-between-c-c99-ansi-c-and-gnu-c-a-general-confusion-reg/17209532#17209532). There is nothing called ANSI C any longer, ANSI themselves call the standard [INCITS/ISO/IEC 9899](http://webstore.ansi.org/FindStandards.aspx?SearchString=9899:2011). Why I should care about what the American standard institute call their standards isn't obvious to me though, since ISO C is an international standard. – Lundin Jan 27 '16 at 10:19
  • Using the term "ANSI C" means that I mean "ANSI C" (ANSI X3.159-1989) and this does not compile with ANSI C (ANSI X3.159-1989) or tell me if I'm wrong. It's that simple. – mikedu95 Jan 27 '16 at 10:33
  • @mikedu95 My point is that the term ANSI C is so heavily abused that you can't know if people refer to C90 or merely to fully standard-compliant C. According to the [C tag wiki](http://stackoverflow.com/tags/c/info), the latest C standard is assumed unless the OP explicitly tells otherwise. Why would you want to use a 27 years old standard anyhow? – Lundin Jan 27 '16 at 10:45
  • I agree with the fact that the term is sometimes misused and that's way I left the possibility that the OP is confused. But you could have at least added that precision in your answer because, precisely, the standard has been explicitely specified. Maybe arbitrarily, but it was, so I think you should at least tell that you didn't take it into account and just used the latest one. – mikedu95 Jan 27 '16 at 11:01
0

Maybe, this is what you wanted, which is more natural!

#include <stdio.h>
#include <string.h>

void print(void *_products, size_t rows, size_t cols) {
    const char* (*products)[cols] = _products;
    size_t i;

    for (i = 0; i < rows; i++) {
        printf("Col1: %s. Col2: %s\n", products[i][0], products[i][1]);
    }
}

int main(void) {
    const char* a[][2] = {{"abc", "1"}, {"def", "2"}, {"ghi", "3"}};
    print(a, 3, 2);

    return 0;
}
Simon Woo
  • 474
  • 3
  • 5