Thread: Arrays and Pointers (very confused)

  1. #1
    Registered User
    Join Date
    Sep 2018
    Posts
    14

    Arrays and Pointers (very confused)

    All three codes seem to work just fine, are any of them technically incorrect? I've read so much on arrays and pointers but at this stage I'm getting more and more confused!

    I'd greatly appreciate it if someone can break down why the changes are allowed and which changes are not allowed.

    Code:
    int array[3]={30,40,50};
    
    int *pointer_array[3];
    
    
        for(int i=0;i<3;i++){
            pointer_array[i]=&array[i];}
    
        for(int i=0;i<3;i++){
            printf("value of array[%d]= %d \n", i, *pointer_array[i]);}
    Code:
    int array[3]={30,40,50};
    
    int *pointer_array[3];
    
    
        for(int i=0;i<3;i++){
            pointer_array[i]=array[i];} //removed &
    
        for(int i=0;i<3;i++){
            printf("value of array[%d]= %d \n", i, pointer_array[i]);} //removed *
    Code:
    int array[3]={30,40,50};
    
    int *pointer_array; // removed [ ]
    
    
        for(int i=0;i<3;i++){
            pointer_array=array;} // removed & and [ ]
    
        for(int i=0;i<3;i++){
            printf("value of array[%d]= %d \n", i, pointer_array[i]);} // removed *

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    It's not as though your compiler is happy about it:
    (for version 2:
    Code:
    H:\>gcc -Wall -std=c99 -o tester.exe tester.c
    tester.c: In function 'main':
    tester.c:7:25: warning: assignment makes pointer from integer without a cast [enabled by default]
    tester.c:10:9: warning: format '%d' expects argument of type 'int', but argument 3 has type 'int *' [-Wformat]
    array[i] is an integer value; it can be stored in a pointer-type variable, because 1's and 0's are 1's and 0's, but it is unlikely to be valid pointer data. Pointers are not supposed to be printed with %d, but rather with %p, hence the warning for printf.

    In version 3, the first loop is redundant (nothing inside the loop depends on i at all), so you do the same assignment three times. This assignment is fine; array by itself can represent the address of the beginning of the array, so you are assigning a pointer value to a pointer variable. The [] indexing notation doesn't care whether it is indexing an array name or a pointer, so it is perfectly happy to do pointer_array[i]. (This is an important thing for dynamic memory; if you allocate a block of data with malloc(), for example, you can still access it using array notation from a pointer in the right way.)

  3. #3
    Registered User
    Join Date
    May 2010
    Posts
    4,633
    My first question is what does your compiler say about each snippet?

    Second question what has you confused about each snippet?

    Third the third snippet really doesn't need the first for() loop since you're assigning the same value to the pointer each time through the loop.

  4. #4
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    I think that the trick to getting this code is knowing that 'array' is an array of 3 ints and 'pointer_array' is an array for 3 addresses for ints
    Fact - Beethoven wrote his first symphony in C

  5. #5
    Registered User
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by tabstop View Post
    It's not as though your compiler is happy about it:
    (for version 2:
    Code:
    H:\>gcc -Wall -std=c99 -o tester.exe tester.c
    tester.c: In function 'main':
    tester.c:7:25: warning: assignment makes pointer from integer without a cast [enabled by default]
    tester.c:10:9: warning: format '%d' expects argument of type 'int', but argument 3 has type 'int *' [-Wformat]
    array[i] is an integer value; it can be stored in a pointer-type variable, because 1's and 0's are 1's and 0's, but it is unlikely to be valid pointer data. Pointers are not supposed to be printed with %d, but rather with %p, hence the warning for printf.

    In version 3, the first loop is redundant (nothing inside the loop depends on i at all), so you do the same assignment three times. This assignment is fine; array by itself can represent the address of the beginning of the array, so you are assigning a pointer value to a pointer variable. The [] indexing notation doesn't care whether it is indexing an array name or a pointer, so it is perfectly happy to do pointer_array[i]. (This is an important thing for dynamic memory; if you allocate a block of data with malloc(), for example, you can still access it using array notation from a pointer in the right way.)
    For version 2

    1) So firstly I have stored integers where addresses should've been stored, which the compiler allows because the way actual addresses look (eg/ 28ff10), as compared to integers, are the same to a computer?

    2) Then, I print those integers out and force them to be displayed with integers using %d. But the compiler is expecting addresses to be printed out and hence expects %p. However, in my case if I used %p I would get the undesired results because doing so would actually print out the addresses of the value 30, 40, 50 instead of the values themselves?

    For version 3

    1) Why is the removal of the [3] in line 3 (in the initial declaration of *pointer_array) necessary?

    2) During the assignment, does the computer always automatically assign pointer_array's [i]th index with the address of the [i]th index of array (which only needs to be represented by array instead of &array)?

    3) Why must the last line remove the * (ie. why isn't the last line *pointer_array[i])?

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Valour549 View Post
    For version 2

    1) So firstly I have stored integers where addresses should've been stored, which the compiler allows because the way actual addresses look (eg/ 28ff10), as compared to integers, are the same to a computer?
    Correct. An address is still a number, it's just that the number "stands for" a memory location, instead of just representing its own value.
    Quote Originally Posted by Valour549 View Post
    2) Then, I print those integers out and force them to be displayed with integers using %d. But the compiler is expecting addresses to be printed out and hence expects %p. However, in my case if I used %p I would get the undesired results because doing so would actually print out the addresses of the value 30, 40, 50 instead of the values themselves?
    The compiler is not going to go looking for random things to print; it's going to print the numbers you tell it to print (in this case, 30, 40, and 50).

    Quote Originally Posted by Valour549 View Post
    For version 3

    1) Why is the removal of the [3] in line 3 (in the initial declaration of *pointer_array) necessary?
    It isn't. Leaving the [3] in there is perfectly valid. It would just make some of the other things later not work.

    All removing the [3] does is mean you have one pointer instead of three pointers.

    Quote Originally Posted by Valour549 View Post
    2) During the assignment, does the computer always automatically assign pointer_array's [i]th index with the address of the [i]th index of array (which only needs to be represented by array instead of &array)?
    Once you took the [3] off, pointer_array is not an array any more. It's just a pointer. Maaaaaaaaaaaaaybe we should review what an array is: an array is just a contiguous block of n identically-sized things. Declaring a variable, as in
    Code:
    int array[3]={30,40,50};
    causes the compiler to set aside a piece of "land" for that variable to live in -- in this case, it gets three consecutive blocks since you used [3]. Just to have values to use, let's suppose that you get blocks #100, #101, and #102. The 30 will then live in block #100, the 40 will live in block #101, and the 50 will live in block #102. The way the C language is set up, using the array name by itself refers to it's beginning block, and tacking something on in [brackets] tells it to offset. So "array" by itself will refer to address #100, array[0] to the value that is stored in that block (30).

    Since pointer_array is a pointer-to-int, that means it can hold an address, so
    Code:
    pointer_array = array
    then puts the beginning address of array, #100, as the value stored in the variable pointer_array. (So pointer_array itself lives somewhere, but the value stored there is #100, referring to the beginning of array.)

    Quote Originally Posted by Valour549 View Post
    3) Why must the last line remove the * (ie. why isn't the last line *pointer_array[i])?
    Now, the C language also allows us to use that same [bracket notation] to refer to a combined offset-a-pointer-and-dereference-the-value. So:
    pointer_array stores the value #100
    *pointer_array will give us the value stored in #100, namely 30
    pointer_array[0] will do a combined offset-by-zero-and-obtain-value, which means it is also 30
    pointer_array[1] will offset by 1, so it will give us the value stored in address #101, which means it is 40
    and so on.

  7. #7
    Registered User
    Join Date
    Sep 2018
    Posts
    14
    Version 2

    Quote Originally Posted by tabstop View Post
    The compiler is not going to go looking for random things to print; it's going to print the numbers you tell it to print (in this case, 30, 40, and 50).
    You're wrong about this. If you try version 2 and use %p in the printf (instead of %d), it certainly gives you the addresses of 30, 40, 50 instead of those values themselves. I personally think it makes sense.

    Version 3

    Quote Originally Posted by tabstop View Post
    It isn't. Leaving the [3] in there is perfectly valid. It would just make some of the other things later not work.

    All removing the [3] does is mean you have one pointer instead of three pointers.
    Again the results beg to differ, if you try version 3 but leave in the [3] during the declaration, you get an error (error: assignment to expression with array type). I'm still confused about this.
    Quote Originally Posted by tabstop View Post
    Once you took the [3] off, pointer_array is not an array any more. It's just a pointer. Maaaaaaaaaaaaaybe we should review what an array is: an array is just a contiguous block of n identically-sized things...
    I know all that. Yes, 'array' by itself refers to the address of the first element, which is then assigned to 'pointer_array'. But that doesn't explain why the values 40 and 50 gets assigned to pointer_array[1] and pointer_array[2]. That was my question regarding line 6 of version 3.

    Quote Originally Posted by tabstop View Post
    Now, the C language also allows us to use that same [bracket notation] to refer to a combined offset-a-pointer-and-dereference-the-value. So:
    pointer_array stores the value #100
    *pointer_array will give us the value stored in #100, namely 30
    pointer_array[0] will do a combined offset-by-zero-and-obtain-value, which means it is also 30
    pointer_array[1] will offset by 1, so it will give us the value stored in address #101, which means it is 40
    and so on.
    This kind of explains my question from the previous section, but how come what you say here applies only to version 3 and not version 1? Because when I remove the * from the printf of version 1, it certainly doesn't print out the correct values of 30, 40 and 50.

    I'm guessing it's something to do with how the addresses of 'array' is assigned to 'pointer_array'?

    In version 1: pointer_array[i]=&array[i];
    In version 3: pointer_array=array;

  8. #8
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Valour549 View Post
    Version 2

    You're wrong about this. If you try version 2 and use %p in the printf (instead of %d), it certainly gives you the addresses of 30, 40, 50 instead of those values themselves. I personally think it makes sense.
    I am, in fact, correct. It prints them in hex because that's how pointers are supposed to look, but 0x1e is actually 30, 0x2b is actually 40, and 0x32 is in fact 50. Unless you are programming on some supermachine (and probably not even then) you do not have access to those memory addresses.

    Quote Originally Posted by Valour549 View Post
    Version 3

    Again the results beg to differ, if you try version 3 but leave in the [3] during the declaration, you get an error (error: assignment to expression with array type). I'm still confused about this.
    I said the declaration was fine (and it is), but it would lead to errors later on if you tried to use it with the [3]. Specifically, you can assign to a slot of an array, but not to the array-as-a-whole.

    Quote Originally Posted by Valour549 View Post
    I know all that. Yes, 'array' by itself refers to the address of the first element, which is then assigned to 'pointer_array'. But that doesn't explain why the values 40 and 50 gets assigned to pointer_array[1] and pointer_array[2]. That was my question regarding line 6 of version 3.
    In version 3, pointer_array[1] does not exist. Until you understand that to be true, then this whole thread is going to be meaningless, so let's try it again. The declaration
    Code:
    int * pointer_array;
    does not give you an array (you may have used the word array in the name of the variable, but that does not actually affect reality). If you had a [3] (or any number) after it, then you would have an array. But you don't; therefore you don't have an array, you have a single pointer. (In C, the main thing about declarations that is important here is that they are the part of the program that actually sets aside space on the stack for variables to live in: int array[3] gives you space to hold three ints (probably 24 bytes); int *pointer_array gives you space to hold one address (probably 16 bytes). Typing int *pointer_array[3] would give you space to hold three addresses, not just one.)

    Now, you can use * to dereference a pointer; using the #s I was using before, pointer_array has the value #100, and therefore *pointer_array would fetch the number stored at address #100, which is 30. You can then point at the "next" data object in line with pointer_array + 1 -- since pointer_array has the value #100, pointer_array + 1 has the value #101, so we can dereference that value and see that *(pointer_array + 1) would be 40 (the value stored at #101).

    At some point, someone realized that that was really annoying to type. Therefore, the C language allows a shortcut to that notation, using (or abusing, if you like) the [] notation: pointer_array[1] is turned into *(pointer_array + 1). However, there is no way for the language to guarantee (i.e., this is your job as a programmer) that pointer_array + 1 actually is an address that belongs to you. I could just as easily write pointer_array + 4, and the language would be just as happy; but then that would refer to address #104, and I don't know who or what lives there because that's not part of my array.

    [Footnotes: not all variables are the same size, so the pointer arithmetic does know to take that into account; and at some point you will discover that this mechanism of pointer-addition-plus-dereference is also used for arrays nearly all the time, because why duplicate work?]

  9. #9
    Registered User
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by tabstop View Post
    now, you can use * to dereference a pointer; using the #s i was using before, pointer_array has the value #100, and therefore *pointer_array would fetch the number stored at address #100, which is 30. You can then point at the "next" data object in line with pointer_array + 1 -- since pointer_array has the value #100, pointer_array + 1 has the value #101, so we can dereference that value and see that *(pointer_array + 1) would be 40 (the value stored at #101).
    I understand everything that you said. Including how version 1 and version 3 are fundamentally different.

    Still, questions remain.

    1) For version 3, all we did was give pointer_array the address of 30. Why does the computer automatically put the address of the values 40 and 50 into pointer_array + 1 and pointer_array + 2 respectively?

    2) Is this behaviour the whole reason why pointers are useful in the first place? So we don't have to define beforehand a possibly-unknown number of array-of-pointers, only then to have to
    tediously and explicitly link each element of an array with each pointer in the array-of-pointers?

    3) Can you give me an example (as simple as possible) where only a pointer variable would work, whereas a normal variable would not?

    4)
    In the below program (moving onto strings lol), there are no pointers. But what's happening during scanf? Is "hello" being read as an array, and each letter's address assigned to the elements of text (like version 1)? Or is only the address of the first letter assigned to the zeroth element of text (like version 3)?

    5) Furthermore, in this case we have specifically declared text[10], yet we seem to have not assign to the slot of the array, but rather to the array-as-a-whole, which I thought is not allowed?
    Code:
        char text[10];    
        scanf("%s", text); //enter hello
    
        for(int i=0; i<strlen(text); i++) //prints hello
        printf("%c", text[i]);


  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by Valour549
    1) For version 3, all we did was give pointer_array the address of 30. Why does the computer automatically put the address of the values 40 and 50 into pointer_array + 1 and pointer_array + 2 respectively?
    I think it would be helpful to understand that for version 3, pointer_array is not a pointer array. Despite the name, pointer_array is a lone pointer to int. We did not "give pointer_array the address of 30"; we gave pointer_array the address of array[0], and the value of array[0] happens to be 30. To reiterate, this piece of code:
    Code:
    pointer_array=array;
    causes pointer_array to point to the first element of the array of ints named array, i.e., the array was converted to a pointer to its first element, and this resulting pointer was assigned to pointer_array. It is because of this that pointer_array + 1 points to the second element of that array of ints. The compiler didn't "automatically put the address of the values 40 and 50 into pointer_array + 1 and pointer_array + 2 respectively". Rather, it is a consequence of pointer_array pointing to array[0], and of pointer arithmetic. Again, "the address of the values 40 and 50" is a wrong way of looking at it. Rather, pointer_array + 1 contains the address of array[1], and pointer_array + 2 contains the address of array[2]. The value of array[1] happens to be 40, and the value of array[2] happens to be 50, but it is not the address of 40 and 50 that we're dealing with here.

    Quote Originally Posted by Valour549
    2) Is this behaviour the whole reason why pointers are useful in the first place? So we don't have to define beforehand a possibly-unknown number of array-of-pointers, only then to have to tediously and explicitly link each element of an array with each pointer in the array-of-pointers?
    Yes, or rather it is why the implicit conversion of an array to be a pointer to its first element is useful.

    Quote Originally Posted by Valour549
    3) Can you give me an example (as simple as possible) where only a pointer variable would work, whereas a normal variable would not?
    One obvious example would be to simulate pass by reference:
    Code:
    #include <stdio.h>
    
    void foo(int *p)
    {
        *p = 1;
    }
    
    int main(void)
    {
        int x = 0;
        printf("%d\n", x);
        foo(&x);
        printf("%d\n", x);
        return 0;
    }
    Quote Originally Posted by Valour549
    4) In the below program (moving onto strings lol), there are no pointers. But what's happening during scanf? Is "hello" being read as an array, and each letter's address assigned to the elements of text (like version 1)? Or is only the address of the first letter assigned to the zeroth element of text (like version 3)?
    The array named text is converted to a pointer to its first element when passed to scanf, so in fact "there are no pointers" is false. Seeing that the format specifier is %s, scanf reads the input as a string and stores it into text via that pointer.

    Quote Originally Posted by Valour549
    5) Furthermore, in this case we have specifically declared text[10], yet we seem to have not assign to the slot of the array, but rather to the array-as-a-whole, which I thought is not allowed?
    Look again. There is no assignment to an array in your code.
    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
    Registered User
    Join Date
    Sep 2018
    Posts
    14
    Quote Originally Posted by laserlight View Post
    The array named text is converted to a pointer to its first element when passed to scanf, so in fact "there are no pointers" is false. Seeing that the format specifier is %s, scanf reads the input as a string and stores it into text via that pointer.
    I hope it's obvious that I'm trying to make a comparison with the codes in the opening post.

    We were storing the values from "array" into "pointer_array". Pointer_array is the one doing the storing, array is the one being stored. Hence "array" is the one whose pointer to its first element is needed, as in the line pointer_array = array.

    Now, "text" is the one doing the storing, and the user's input is the one being stored. Yet you say: "text" is converted to a pointer to its first element? Shouldn't it be: the input is converted to a pointer to its first element, and this pointer assigned to "text"?

    As a result the part where you state: scanf stores the input into "text" via that pointer (pointer to "text") is extremely unclear.

  12. #12
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Valour549 View Post
    [COLOR=#333333]I understand everything that you said. Including how version 1 and version 3 are fundamentally different.

    1) For version 3, all we did was give pointer_array the address of 30. Why does the computer automatically put the address of the values 40 and 50 into pointer_array + 1 and pointer_array + 2 respectively?
    These two statements/questions are fundamentally incompatible; if you understood even a slight amount about what you had quoted of my post, then you wouldn't even be able to ask the second question, since you would know that pointer_array + 1 isn't a thing that can have anything put in it, in the same way that we can't put "7" into the expression "4+3". The expression "4+3" has the same value as "7", in the same way "#100 + 1" has the same value as "#101", but we are evaluating, not storing. Because arrays are guaranteed to be made of consecutive memory locations, once we know the beginning address is #100, then the following elements have to live in #101 and #102.

  13. #13
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Valour549 View Post
    I hope it's obvious that I'm trying to make a comparison with the codes in the opening post.

    We were storing the values from "array" into "pointer_array".
    Assuming we are still taking about version #3 above, then this statement is false. We are not storing the values from "array" anywhere. Those values do not move, and there is not a second copy of those values in existence anywhere. What is being stored is (a copy of) the original address of the array, i.e., the memory location of the block. The memory footprint of the program might look like:
    #100: 30
    #101: 40
    #102: 50
    #103: #100
    with the program knowing that "array" refers to #100 and pointer_array refers to #103. Notice that 30, 40, and 50 are not duplicated; nor is #101 or #102 stored anywhere at all (those address are always computed using pointer arithmetic or a [i] suffix).

    Now if you have used scanf before, you might remember that scanf doesn't work with variables; it only works with memory locations (remember having to put & in front of all your numeric variables in a scanf?) -- and so to store the input from the user, we have to provide scanf with the memory location we want it to use. Passing in the bare array name "text" will refer to that original memory location.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by Valour549
    As a result the part where you state: scanf stores the input into "text" via that pointer (pointer to "text") is extremely unclear.
    Let's not talk about scanf then: the implementation of scanf is implementation defined, by definition

    Consider this naive variant of strcpy:
    Code:
    void cboard_strcpy(char *dest, const char *source)
    {
        while (*dest++ = *source++);
    }
    Now consider sample usage:
    Code:
    int main(void)
    {
        char text[12];
        cboard_strcpy(text, "hello world");
        return 0;
    }
    As you can see, cboard_strcpy does not assign to an array. Rather, it loops over the characters pointed to by source until *source evaluates to '\0', copying character by character to each successive character pointed to by dest, and it so happens that for our function call, dest initially points to text[0]. When we say that you cannot assign to an array, it means you cannot do this:
    Code:
    int main(void)
    {
        char text[12];
        text = "hello world";
        return 0;
    }
    Notice though that assignment is not the same as initialisation, hence you can do this:
    Code:
    int main(void)
    {
        char text[12] = "hello world";
        return 0;
    }
    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 Kernelpanic's Avatar
    Join Date
    Sep 2018
    Location
    Berlin
    Posts
    105
    Code:
    int array[3]={30,40,50};
    
    int *pointer_array[3];
    
    
        for(int i=0;i<3;i++){
            pointer_array[i]=&array[i];}
    
        for(int i=0;i<3;i++){
            printf("value of array[%d]= %d \n", i, *pointer_array[i]);}

    Altogether many text! I hope I have understand what you want . . . With "pointer array[i]=&array[i];" you can/want see the addresses of the individual values of the array.

    Code:
    #include <stdlib.h>
    #include <stdio.h>
    
    
    int main(void)
    {
        int array[3]={30,40,50}; 
        int *pointer_array[3];
        
        //Oder so Startadresse
        int *pointer2 = &array[0];
     
     
        for(int i = 0; i < 3; i++)
            {
          pointer_array[i]=&array[i];
                printf("Adressen von array[%2d] = %ld\n", i, pointer_array[i]);
            }
            printf("\n\n");
     
        for(int i = 0; i < 3; i++)
            {
          printf("value of array[%2d]= %3d \n", i, *pointer_array[i]);
            }
            printf("\n\n");
            
            //Pointer 2
            for(int i = 0; i < 3; i++)
            {
                printf("Value of array[%2d] = %2d --- Adresse im RAM: %ld\n", i, pointer2[i], &pointer2[i]);
            }
                    
        return(0);
    }
    
    Arrays and Pointers (very confused)-valour549-jpg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Arrays and pointers, confused.
    By jjohan in forum C Programming
    Replies: 5
    Last Post: 09-25-2014, 12:22 PM
  2. Confused about Pointers
    By JoshD75 in forum C Programming
    Replies: 7
    Last Post: 03-28-2011, 09:43 PM
  3. Still confused with pointers
    By desmond5 in forum C Programming
    Replies: 8
    Last Post: 02-23-2008, 09:32 PM
  4. confused with pointers
    By Mahdi123 in forum C Programming
    Replies: 2
    Last Post: 04-25-2007, 01:08 PM
  5. Very confused with pointers
    By killerasp in forum C++ Programming
    Replies: 5
    Last Post: 01-30-2002, 06:44 PM

Tags for this Thread