Thread: pointer to a 2D array

  1. #1
    Registered User shruthi's Avatar
    Join Date
    Jan 2012
    Posts
    59

    pointer to a 2D array

    Hi Everyone!
    Is there another way to store and access the the 2D array using pointer to a 2D array.

    Code:
    #include<stdio.h>
    int main()
    {
        int a[10][10],i,j,r,c;
        int (*p)[10]=a;
        printf("Enter row and col size\n");
        scanf("%d%d",&r,&c);
      for(i=0;i<r;i++)
      {
        for(j=0;j<c;j++)
            scanf("%d",(*(p+i)+j));
      }
     for(i=0;i<r;i++)
      {
         for(j=0;j<c;j++)
       printf("%d\n",*(*(p+i)+j));
    }
    return 0;
    }
    

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,666
    Well you could try
    int (*p)[10][10]=&a;

    But it seems rather an academic exercise.

    I mean, you could just do
    scanf("%d",&a[i][j]);
    and not worry if you got the address expression right or not.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    For arrays and matrices I recommend
    Code:
    #include <stdlib.h>
    #include <errno.h>
    
    #define MATRIXDATA int
    
    struct matrix {
        long    rows;
        long    cols;
        size_t  rowstep;
        size_t  colstep;
        MATRIXDATA data[];
    };
    
    struct matrix *free_matrix(struct matrix *const m)
    {
        if (m && m->rows > 0L && m->cols > 0L) {
            m->rows = 0L;
            m->cols = 0L;
            m->rowstep = 0;
            m->colstep = 0;
        }
        return NULL;
    }
    
    struct matrix *new_matrix(const long rows, const long cols)
    {
        const size_t  cells = (size_t)rows * (size_t)cols;
        const size_t  bytes = cells * sizeof (MATRIXDATA);
        const size_t  total = bytes + sizeof (struct matrix);
        struct matrix *m;
    
        /* Invalid number of rows or cols? */
        if (rows < 1L || cols < 1L) {
            errno = EINVAL;
            return NULL;
        }
    
        /* Too many cells? Relies on C99 casting rules to detect overflow/wrapping. */
        if (total < bytes || (long)(bytes / (size_t)rows) != (long)(cols * sizeof (MATRIXDATA))) {
            errno = ENOMEM;
            return NULL;
        }
    
        m = malloc(total);
        if (!m) {
            errno = ENOMEM;
            return NULL;
        }
    
        m->rows = rows;
        m->cols = cols;
        m->rowstep = (size_t)cols;
        m->colstep = 1;
    
        return m;
    }
    
    #define CELL(matrix, row, col)  ((m)->data[ (row)*(m)->rowstep + (col)*(m)->colstep ])
    
    static inline MATRIXDATA matrix_get(const struct matrix *const m, const long row, const long col)
    {
        if (!m || row < 0L || col < 0L || row >= m->rows || col >= m->cols)
            return (MATRIXDATA)0;
        else
            return m->data[ row * m->rowstep + col * m->colstep ];
    }
    
    static inline MATRIXDATA matrix_set(struct matrix *const m, const long row, const long col, const MATRIXDATA value)
    {
        if (!m || row < 0L || col < 0L || row >= m->rows || col >= m->cols)
            return (MATRIXDATA)0;
        else
            return m->data[ row * m->rowstep + col * m->colstep ] = value;
    }
    This is basic dynamic memory management. You can #define MATRIXTYPE to whatever numeric type or pointer you want, too.

    You create a new matrix or table using
    Code:
        struct matrix *thing;
    
        thing = new_matrix(rows, columns);
        if (!thing) {
            fprintf(stderr, "Could not create a new thing!\n");
            exit(1);
        }
    After you no longer use it, you free it,
    Code:
        thing = free_matrix(thing);
    (If you do not, then it will stay in memory until the program exits.) Most people use free_matrix(thing);, but I recommend the above format because it also sets thing to NULL. If you accidentally use it after freeing it -- use-after-free is a very common error --, you'll always crash (via a segment violation), instead of it sometimes working and sometimes not. Bugs are better fixed than hidden.

    To set the value of a matrix element, you can use
    Code:
        matrix_set(thing, row, column, new_value);
            or
        CELL(thing, row, column) = new_value;
    To get the value of a matrix element (which will be semi-random when created), you can use
    Code:
    value = matrix_get(thing, row, column);
            orvalue = CELL(thing, row, column);
    The difference is that CELL is a preprocessor macro which does not do any checks, whereas matrix_get() and matrix_set() safely return zero if you try to access outside the matrix (without any buffer overruns or such happening).

  4. #4
    Registered User shruthi's Avatar
    Join Date
    Jan 2012
    Posts
    59
    Quote Originally Posted by Salem View Post
    Well you could try
    int (*p)[10][10]=&a;

    But it seems rather an academic exercise.

    I mean, you could just do
    scanf("%d",&a[i][j]);
    and not worry if you got the address expression right or not.
    isn't int (*p)[10][10];
    different from
    int (*p)[10];
    isn't the first one used to point to a 3D array and the second one used to point to the 2D array or are they both same?

  5. #5
    Registered User shruthi's Avatar
    Join Date
    Jan 2012
    Posts
    59
    Quote Originally Posted by Salem View Post
    Well you could try
    int (*p)[10][10]=&a;

    But it seems rather an academic exercise.

    I mean, you could just do
    scanf("%d",&a[i][j]);
    and not worry if you got the address expression right or not.
    Code:
    scanf("%d",&a[i][j]); // this is fine
    

    I wanted to know is there any other way to access the array using pointer,other than the way I have mentioned in my program?

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by shruthi
    isn't int (*p)[10][10];
    different from
    int (*p)[10];
    Obviously

    Quote Originally Posted by shruthi
    isn't the first one used to point to a 3D array and the second one used to point to the 2D array or are they both same?
    No, p in the former is used to point to a 2D array and p in the latter is used to point to a 1D array. Note that if you have a 3D array, then any element of the array is a 2D array, whereas if you have a 2D array, any element of the array is a 1D array.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Registered User shruthi's Avatar
    Join Date
    Jan 2012
    Posts
    59
    Quote Originally Posted by laserlight View Post
    Obviously


    No, p in the former is used to point to a 2D array and p in the latter is used to point to a 1D array. Note that if you have a 3D array, then any element of the array is a 2D array, whereas if you have a 2D array, any element of the array is a 1D array.
    Code:
    #include<stdio.h>
    int main()
    {
         int arr[3],*q=arr,i;
         int (*p)[3]=arr;
              for(i=0;i<3;i++)
                 scanf("%d",(q+i));
    
              for(i=0;i<3;i++)
                 printf("%d",*(q+i));
    }
    How do I need to use (*p)[3] in the above program instead of *q.
    So,(*p)[3] is a pointer that can only point to an array of 3 elements and nothing else,whereas *q can point to a variable and an array?

  8. #8
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by shruthi View Post
    How do I need to use (*p)[3] in the above program instead of *q.
    Depends on what you're trying to achieve.

    Quote Originally Posted by shruthi View Post
    So,(*p)[3] is a pointer that can only point to an array of 3 elements and nothing else
    The "and nothing else" is redundant. For anything except a void pointer (which can point at actual variables or objects of any type).
    Quote Originally Posted by shruthi View Post
    whereas *q can point to a variable and an array?
    No. q contains the address of an int.

    By convention, the name of an array is implicitly converted to a pointer (and the value of that pointer is the address of the array's first element). q can therefore validly point to any int. That int can even be an element of an array. However, q cannot point to an array. A "pointer to array of three int" is a different thing from "a pointer to int".
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by grumpy View Post
    By convention, the name of an array is implicitly converted to a pointer (and the value of that pointer is the address of the array's first element).
    No. You can use the name of an array as a pointer to its first element.

    Wait! I have a real point; I'm not trying to be a smart-arse.

    What I mean is that given
    Code:
    char name[25] = "Grumpy";
    both name and &name are both pointers to the first character, 'G', and can be used interchangeably.

    name is a pointer to the first character, because as I said, you can use the name of an array as a pointer to its first element.

    &name takes the address of the array, which is always the same as the address of its first element. (Arrays are their elements, and the element addresses grow upwards, per C rules. Therefore the address of an array is the address of its first element.) Therefore this too evaluates to a pointer to the first element of the array.

    (There are minor differences on exactly how the compiler determines the type of the resulting pointer, depending on the compiler, but in all cases the two pointers are compatible. To avoid a harmless warning about possibly incompatible types, you may need a cast to char * . I've only seen it with complicated type definitions involving consts at different steps, but those tend to need clarifying casts anyway.)

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Nominal Animal
    No. You can use the name of an array as a pointer to its first element.
    Actually, I'd say "yes, hence you can use the name of an array as a pointer to its first element". The C standard (at least the 1999 edition) states the array is converted to a pointer to its first element except certain cases, e.g., when the array is the operand of the address of operator & and sizeof. But due to this conversion in general, the name of the array is effectively a pointer to its first element. So you're wrong, but otherwise you're right

    EDIT:
    Quote Originally Posted by Nominal Animal
    both name and &name are both pointers to the first character, 'G', and can be used interchangeably.

    name is a pointer to the first character, because as I said, you can use the name of an array as a pointer to its first element.
    So, with reference to the standard, neither name nor &name is a pointer to the first character: name is an array; &name results in a pointer to the array. However, name is converted to a pointer to the first character, so the distinction is not important here, and the value of &name is the address of the first character. Considering this conversion, they can be used interchangeably (along with &name[0]) with respect to value, but not type, thus pointer arithmetic would have different effects.

    Quote Originally Posted by Nominal Animal
    &name takes the address of the array, which is always the same as the address of its first element. (Arrays are their elements, and the element addresses grow upwards, per C rules. Therefore the address of an array is the address of its first element.) Therefore this too evaluates to a pointer to the first element of the array.
    The catch is that the types are different even though the value of the pointer, i.e., the address, is the same.
    Last edited by laserlight; 10-17-2012 at 03:07 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by laserlight View Post
    The C standard (at least the 1999 edition) states the array is converted to a pointer to its first element except certain cases
    I know. This point is one of the features of the C standards I don't like. Exceptions to basic evaluation rules are evil. If they'd just say "array name can be used as a pointer to its first element" there would be no exceptions to remember.

    Quote Originally Posted by laserlight View Post
    So, with reference to the standard, neither name nor &name is a pointer to the first character: name is an array; &name results in a pointer to the array.
    Correct, but very difficult to explain correctly to aspiring C programmers. I tried a shortcut.

    Quote Originally Posted by laserlight View Post
    Considering this conversion, they can be used interchangeably (along with &name[0]) with respect to value, but not type, thus pointer arithmetic would have different effects.
    Aww crap, I forgot about the effects to pointer arithmetic: (char *)(&(&name)[1]) == (char *)(&(name)[20]).

    Bah, need a new thinking cap. But thanks, laserlight, for catching my error.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Nominal Animal
    If they'd just say "array name can be used as a pointer to its first element" there would be no exceptions to remember.
    I think the exceptions are still there: for an array x, &x gives us a pointer to x. If x is used as a pointer to its first element here, then &x would give us a pointer to a pointer to the first element of x instead. Likewise, sizeof(x) gives us the size of x, not the size of a pointer to the first element of x, because we don't use x as a pointer to its first element in that context. Basically, saying that an "array name can be used as a pointer to its first element" leads to the next question: when is it that an array name cannot be used as a pointer to its first element? Remembering those cases is equivalent to remembering exceptions to the rule that an array is converted to a pointer to its first element.

    Quote Originally Posted by Nominal Animal
    Correct, but very difficult to explain correctly to aspiring C programmers. I tried a shortcut.
    I think the right way to do this is with an image or a diagram (ASCII art?) rather than a shortcut that has its own pitfalls concerning type. Hmm...
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by laserlight View Post
    I think the right way to do this is with an image or a diagram (ASCII art?) rather than a shortcut that has its own pitfalls concerning type. Hmm...
    The entire problem stems from the difficulty of conceptualizing exactly what an array is in C. C99 even allows "flexible array members" as final members in arrays; their size being completely undefined (and counted as zero in sizeof (array type with flexible member)).

    Personally, I believe array in C is best understood as a purely abstract concept, closely related to struct. The array itself is just the type, the conceptual container for its elements, just like a struct is for its members. If you use a visual metaphor like a pigeonhole, how do you explain that the pigeonhole itself does not exist, it just the description of the way the contents are organized?

    The concept of a pointer is different, but at least as important: it specifies the memory address of something, the location of something in memory. The type of the pointer defines the type of that something, while casting (type conversion or type punning) allows you to redefine the type of that something; the interpretation of that something. (I'd also like to point out that reading the type of a variable from right to left gives you the correct description in English. For example, char *const foo; says that foo is a constant that points to a (non-constant) char.)

    The last complication in this soup is the difference between (foo).member and (foo)->member when accessing members of a structure, and (*foo) when dereferencing (traversing) a pointer. (foo).member is only used when (foo) is a struct, and (foo)->member when (foo) is a pointer to a struct. Now, if foo is a pointer to a struct, are ((*(foo)).member) and ((foo)->member) exactly equivalent? (I do believe they are.)

    I have not taught C to anyone, but I'd wager getting the above correct makes one a better than average C programmer. The reason I'm looking for conceptual "shortcuts" for describing the above is that I know (feel in my ample gut) that there exists a simple, straightforward way to explain the above to a novice C programmer in a way that gives them the correct concepts -- or at least concepts that lead to correct code (regardless of whether they match exactly to the standards). I'd just love to find out what it is, as my explanations above and elsewhere are certainly not it.

    In a recent Slashdot review, Linus Torvalds said that there are very few C programmers that really understand pointers. For example, when inserting a value to a sorted singly-linked list, most C programmers, myself included, produce something like the following:
    Code:
    struct node {
        struct node *next;
        int value;
    }
    
    void insert(struct node **list, struct node *node)
    {
        struct node *prev = NULL;
        struct node *curr = *list;
    
        while (curr && curr->value < node->value) {
            prev = curr;
            curr = curr->next;
        }
    
        if (!prev) {
            node->next = *list;
            *list = node;
        } else {
            node-next = prev->next;
            prev->next = node;
        }
    }
    Linus pointed out that using a pointer to a pointer leads to more efficient code; there is no need for the if clause at end.
    Code:
    void insert(struct node **const list, struct node *const node)
    {
        struct node **ptr = list;
        struct node *curr = *list;
    
        while (curr && curr->value < node->value) {
            ptr = &(curr->next);
            curr = curr->next;
        }
    
        node->next = *ptr;
        *ptr = node;
    }
    It is true, the latter compiles to shorter and more efficient code on x86-64 at least. I checked.

    I was pretty disappointed when I realized this latter form is not the one that naturally occurs to me. While the difference in the two functions above is minimal, it means my "natural approach", or instinctive approach, is still far from optimal. Because I can write the better version when it occurs to me, I believe the problem is in my concepts, my approach.

    Which leads back to the reason why I raised the point originally: We need clear concepts and expressions when teaching pointers, arrays and structures in C. They don't necessarily need to be what the standards say, not exactly anyway, as long as they help programmers learn how to write correct, efficient C.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Nominal Animal
    In a recent Slashdot review, Linus Torvalds said that there are very few C programmers that really understand pointers. For example, when inserting a value to a sorted singly-linked list, most C programmers, myself included, produce something like the following: (...) Linus pointed out that using a pointer to a pointer leads to more efficient code; there is no need for the if clause at end.
    This is somewhat off-topic, but I would take such statements from Linus Torvalds with a pinch of salt: the example is not evidence that "there are very few C programmers that really understand pointers". What it might be is evidence that even when optimising, programmers often do not consider patterns of thought outside of those that they are familiar with.

    Quote Originally Posted by Nominal Animal
    The last complication in this soup is the difference between (foo).member and (foo)->member when accessing members of a structure, and (*foo) when dereferencing (traversing) a pointer. (foo).member is only used when (foo) is a struct, and (foo)->member when (foo) is a pointer to a struct. Now, if foo is a pointer to a struct, are ((*(foo)).member) and ((foo)->member) exactly equivalent? (I do believe they are.)
    Yes, they are equivalent, but then if a beginner is taught about what pointer dereference means and what struct member access means, it should be easy to understand the two expressions are equivalent, once he/she persuaded to examine the expressions part by part despite how complicated they might look to a newbie's eyes at a glance.

    I did my own teaching contribution in Java (the syllabus changed to use C after my time, heheh), and I found that those diagrams that explained Java's reference concept (akin to C pointers without pointer syntax) helped beginners quite a bit. Something like that could be used here too.

    Quote Originally Posted by Nominal Animal
    If you use a visual metaphor like a pigeonhole, how do you explain that the pigeonhole itself does not exist, it just the description of the way the contents are organized?
    Just admit that then. All analogies/metaphors/etc break down at some point (otherwise they would be what you are trying to explain in itself), but as long as they get the idea across, that's okay.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered User shruthi's Avatar
    Join Date
    Jan 2012
    Posts
    59
    I tried pointing
    int (*p)[10] to a single dimensional array,the compiler was giving a warning,but when I made same kind of pointer to a 2d array,it excuted cleanly without throwing any warning.
    Code:
    #include<stdio.h>
    int main()
    {
        int arr[5]={1,2,3,4,5},i;
         int (*p)[5]=arr;
         for(i=0;i<5;i++)
            printf("%d\n",(*p)[i]);
       return 0;
    }
    when I compiled this code,it gave a warning
    Code:
    but when I executed the below given code
    #include<stdio.h>
    int main()
    {
        int arr[2][3]={1,2,3,4,5,6},i;
         int (*p)[3]=arr;
         for(i=0;i<6;i++)
            printf("%d\n",(*p)[i]);
       return 0;
    }
    It didn't throw any warning and complied and executed.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 01-28-2010, 02:44 AM
  2. Casting from a 1D array pointer to a scalar pointer
    By komkamol in forum C Programming
    Replies: 8
    Last Post: 09-25-2009, 01:44 AM
  3. pointer to pointer that points to a char array
    By steve1_rm in forum C Programming
    Replies: 2
    Last Post: 01-14-2009, 12:03 AM
  4. Replies: 1
    Last Post: 03-24-2008, 10:16 AM
  5. A pointer to a character pointer array... can't pass
    By Lynux-Penguin in forum C Programming
    Replies: 9
    Last Post: 10-12-2003, 10:53 PM