Question about the efficiency of using an array or individual variables

Click For Summary
The discussion centers on the efficiency of using arrays versus individual boolean variables in programming. It suggests that individual variables may offer faster access due to direct addressing, while arrays may benefit from memory caching. The initialization of an array with multiple values is clarified, explaining that using a single initializer only sets the first element, leaving others at zero. Additionally, the conversation highlights the limitations of fixed-size arrays, advocating for dynamic arrays or vectors for user-defined sizes. The complexities of user input handling for dynamic arrays are also addressed, suggesting the use of sentinel values for easier input management.
  • #121
jedishrfu said:
The ++ pre and post operators can make some interesting reading as compilers differ as to how they are interpreted. As an example, (++x - x++) = ? where x is some integer.
That reminded me to check. Yes, the obsfucated C contest is still active.

https://www.ioccc.org/
 
  • Like
Likes jedishrfu
Technology news on Phys.org
  • #122
Mark44 said:
My advice is to definitely study Ch. 13. The concepts presented in that chapter start to cover what makes C++ an object-oriented language. Without it, what you've been doing is pretty much C plus the C++ take on I/O (i.e., using the cout and cin stream objects). The fact that your grandson's class covered only the first 11 chapters shouldn't enter into your decision. After all, his class was constrained by time, with either 10 weeks for a quarter of 15 weeks or so for a semester. No doubt his class was in Intro to Programming with C++. You don't have those constraints.
I would also advise that you study at least the first half of Ch. 14, where it discusses writing your own classes. If you don't want to do the whole chapter, you could skip that latter part where Gaddis goes over operator overloading. Since you don't intend to become an expert, you could skip Ch. 15, which gets into the concepts of inheritance, polymorphism, and virtual functions.
Not very much. You'll need another book for data structures and algorithms, but what you learn from the book you're working in will be helpful, as it will likely show examples in C++ (unless you get a book that is Java-based). The data structures will include things like singly- and doubly-linked lists, binary trees, and other structures, plus algorithms for searching through the various structures or sorting them. A fair amount of the presentation on algorithms will be focused on how efficient various algorithms are.
Ch. 13 and at least the parts in Ch. 14 that are about writing classes will definitely help.
Thank you for the advice. I will study cpt 13 and at least the part you suggested on cpt 14. What is the next logical thing I should study after that? I suspect I will finish those by Christmas.

I don't know what to expect and what I should do, what direction I should take (not that it is important for me as I am not looking for a career). I just don't know enough about computers now a days. Part of the reason I even get into this is because my grandson is CS major. I was even joking with him when he said he need more motivation. I said maybe the best motivation is when grandpa nipping on the grandson's heel on programming!

I am not into gaming, what intriguing to me is when I watch tv on shows people deal with virus attack, how they kill the virus in the tv shows. I want to learn more about the inner working of computers and virus prevention stuffs. They even had a show called CSI Cyber.
 
  • #123
I have a problem I just cannot solve. This is about passing pointer as reference to a function to create an array in the function, then passing the pointer( NOT by return ptr;) as parameter back to main(). No matter how I try, there is always error in compile. The difficulty is I want to use arr = new int[5] in the function to get memory from computer, then later to delete[] in main().
C++:
//Function return pointers
#include <iostream>
using namespace std;

void getRNum(int*, int);//return a pointer from the functionint main()
{
    int size = 5;
    int *number;//initiate a pointer number
    getRNum(number, size);//get the pointer to array of 5 random numbers
    for (int count = 0; count < 5; count++)// display the numbers
        cout << *(number + count) << "\n\n";
    delete[] number;//free the memory of 5 element array number[5]
    return 0;
}
void getRNum(int *arr, int num)//receive an integer 5 to num, return a pointer to array arr.
{
    arr = new int[num]; //request 5 elements of int of memory from computer
    for (int count = 0; count < num; count++)// input 5 integers
    {
        cout << " Enter number " << count + 1 << " = ";
        cin >> arr[count]; cout << "\n\n";
        cout << " In memory location: " << (arr + count) << "\n\n";
    }
}

I don't like to use return ptr to return the pointer as shown in the book. I want to do this through passing parameters. Is there any way to do that?

Thanks
 
  • #124
yungman said:
This is about passing pointer as reference

You're not passing the pointer by reference, you're passing it by value. If you want to pass the pointer by reference, you need to define a variable that holds a pointer to the pointer; that variable is what then would get passed to the function.

For example, you would initialize the variable in your main function like this:

C:
int** pNumber;

The signature of the function you call would look like this:

C:
void getRNum(int** pArr, int num);

And inside the getRNum function you would have to do this to pass the address of the array of 5 numbers back to the main function:

C:
*pArr = arr;

[Note: the above code has been edited from the original version of this post.]

yungman said:
I don't like to use return ptr to return the pointer as shown in the book.

Why not? It's a lot simpler than the above rigmarole, for no benefit that I can see.
 
Last edited:
  • #125
yungman said:
I don't like to use return ptr to return the pointer as shown in the book.

Why not?
 
  • Like
Likes jedishrfu
  • #126
PeterDonis said:
Why not?
Thanks for the reply.

You deleted the other post! I actually tried and it did not work. All the difficulty is because I want to do dynamic memory allocation, that I use *arr = new int [num] in the function and I want to delete[] number to free up the memory in main after getting the values of the array from the function.

The reason I want to do that is because I don't want to restrict to one parameter passing. I want to have the freedom to pass multiple pointers to array to the function.

I have been search up and down the chapter the whole last night, I have not found a way to do that. Passing pointers of array to function is easy, BUT with dynamic memory allocation is a different story.

Thanks
 
  • #127
yungman said:
You deleted the other post! I actually tried and it did not work.

What went wrong? What error messages did you get?
 
  • #128
yungman said:
want to have the freedom to pass multiple pointers to array to the function.

The usual way to do this would be to define a struct containing multiple array pointers, and return a pointer to an instance of the struct created with new.
 
  • #129
yungman said:
You deleted the other post!

I've undeleted it now, with a correction.
 
  • #130
PeterDonis said:
The usual way to do this would be to define a struct containing multiple array pointers, and return a pointer to an instance of the struct created with new.
Thank
Maybe I should move on as I have not learn that, I don't want to trouble you to teach me ahead where I can learn in the later chapters. I am just being adventurous.

I kind of suspect this is beyond my level for the moment. But I did practice a lot trying to find a way to make it work, it's like reviewing the chapter over and over again.

thanks
 
  • #131
yungman said:
I did practice a lot trying to find a way to make it work

Without any specific information on what error messages you saw when you tried different things, there's not much we can do to help.
 
  • #132
yungman said:
Thanks for the reply.

You deleted the other post! I actually tried and it did not work. All the difficulty is because I want to do dynamic memory allocation, that I use *arr = new int [num] in the function and I want to delete[] number to free up the memory in main after getting the values of the array from the function.

The reason I want to do that is because I don't want to restrict to one parameter passing. I want to have the freedom to pass multiple pointers to array to the function.

I have been search up and down the chapter the whole last night, I have not found a way to do that. Passing pointers of array to function is easy, BUT with dynamic memory allocation is a different story.

Thanks

Allocating memory in one function and deleting later in the calling function is a bad paradigm to follow. Users of your function or even yourself may forget to delete the allocated memory.

Best practice is to have two functions that are paired like my_alloc() / my_free() where memory is allocated in one and freed in the other and then the calling function calls my_alloc() does something with the data and the calls my_free() when finished.

C:
// pseudo code example

my_function(...) {

    data = my_alloc()

    .. do something with data...

    data.my_free();  // alternatively my_free(data);

}
 
  • Like
Likes yungman
  • #133
jedishrfu said:
Allocating memory in one function and deleting later in the calling function is a bad paradigm to follow. Users of your function or even yourself may forget to delete the allocated memory.

Best practice is to have two functions that are paired like my_alloc() / my_free() where memory is allocated in one and freed in the other and then the calling function calls my_alloc() does something with the data and the calls my_free() when finished.

C:
// pseudo code example

my_function(...) {

    data = my_alloc()

    .. do something with data...

    data.my_free();  // alternatively my_free(data);

}
Thanks

I just follow the book, this is the exercise in the book that I did, it got the memory in the function and release the memory in main. The difference is the program uses return arr instead of passing back as parameter. This is the original program:
C++:
#include <iostream>
#include <cstdlib>//for rand and srand
#include <ctime>//for time function
using namespace std;

int *getRNum(int);//return a pointer from the functionint main()
{
    int *number; // to point to number
    number = getRNum(5);//get the pointer to array of 5 random numbers
    for (int count = 0; count < 5; count++)// display the numbers
        cout << number[count] << "\n\n";
    delete[] number;//free the memory of 5 element array number[5]
    number = 0;//set pointer to 0
    return 0;
}
int *getRNum(int num)//receive an integer 5 to num, return a pointer to array arr.
{
    int *arr;// initialize point to array arr to hold 5 elements, num = 5
    if (num <= 0)
        return NULL;
    arr = new int[num]; //request 5 elements of int of memory from computer
    srand(time(0)); // Seed the random number
    for (int count = 0; count < num; count++)
        arr[count] = rand();
    return arr;//return the starting address of arr.
}
 
  • #134
PeterDonis said:
Without any specific information on what error messages you saw when you tried different things, there's not much we can do to help.
I tried what you suggested, this is the program and the error message:
C++:
#include <iostream>
using namespace std;

void getRNum(int**, int);//return a pointer from the functionint main()
{
    int size = 5;
    int **pNumber;//initiate a pointer number
    getRNum(pNumber, size);//get the pointer to array of 5 random numbers
    for (int count = 0; count < 5; count++)// display the numbers
        cout << *(pNumber + count) << "\n\n";
    delete[] pNumber;//free the memory of 5 element array number[5]
    return 0;
}
void getRNum(int **pArr, int num)//receive an integer 5 to num, return a pointer to array arr.
{

    int *Arr = new int[num]; //request 5 elements of int of memory from computer
    *pArr = Arr;
    for (int count = 0; count < num; count++)// input 5 integers
    {
        cout << " Enter number " << count + 1 << " = ";
        cin >> *pArr[count]; cout << "\n\n";
        cout << " In memory location: " << (pArr + count) << "\n\n";
    }
}
Compile error Listing 7.2.jpg


There are just different error messages for different way, I kind of understand the error messages, I just cannot fix it.

What is the ** mean? Like I said, if it is something I have not learned yet, I just let this go and wait until the time comes to learn struct.

Thanks
 
  • #135
yungman said:
I kind of understand the error messages, I just cannot fix it.

The pNumber variable needs to be initialized. The obvious way to initialize it is with a null pointer: int** pNumber = NULL;.

yungman said:
What is the ** mean?

As I said before, it's a pointer to a pointer. In other words, it's a pointer that stores the address of a variable that stores a pointer. In this case the variable that stores the pointer is pNumber, and you are passing the address of that variable to the getRNum function so the function can assign a value to it, namely the address of the array you allocate with new.
 
  • Like
Likes yungman
  • #136
PeterDonis said:
The pNumber variable needs to be initialized. The obvious way to initialize it is with a null pointer: int** pNumber = NULL;.
As I said before, it's a pointer to a pointer. In other words, it's a pointer that stores the address of a variable that stores a pointer. In this case the variable that stores the pointer is pNumber, and you are passing the address of that variable to the getRNum function so the function can assign a value to it, namely the address of the array you allocate with new.
Thanks, I did not know about assigning NULL to pointer. Yes, that eliminates the error. But it still did not return the pointer back to main.

I did try assigning NULL in my original program that doesn't use pointer to a pointer. I eliminated the error and it ran BUT the same thing, it cannot return the pointer from function back to main. This is the program, if you can help me fixing this, I am good.
C++:
//Function return pointers
#include <iostream>
using namespace std;

void getRNum(int*, int);//return a pointer from the functionint main()
{
    int size = 5;
    int* pNumber = NULL;//initiate a pointer number
    getRNum(pNumber, size);//get the pointer to array of 5 random numbers
    cout << " Back in main from getRNum.\n\n";
    for (int count = 0; count < 5; count++)// display the numbers
        cout << *(pNumber + count) << "\n\n";
    delete[] pNumber;//free the memory of 5 element array number[5]
    return 0;
}
void getRNum(int* pArr, int num)//receive an integer 5 to num, return a pointer to array arr.
{
    pArr = new int[num]; //request 5 elements of int of memory from computer
    for (int count = 0; count < num; count++)// input 5 integers
    {
        cout << " Enter number " << count + 1 << " = ";
        cin >> pArr[count]; cout << "\n\n";
        cout << " In memory location: " << (pArr + count) << "\n\n";
    }
}

Again thanks for you time to help me.
 
  • #137
yungman said:
it still did not return the pointer back to main.

That's because the type of the parameter pArr in the function signature for getRNum is still wrong; it needs to be int**, not int*. Then you pass the address of pNumber to the function using that parameter.
 
  • #138
PeterDonis said:
That's because the type of the parameter pArr in the function signature for getRNum is still wrong; it needs to be int**, not int*. Then you pass the address of pNumber to the function using that parameter.

To expand on this a bit, consider:

What your code is doing now:

(1) Initializing a variable whose type is "pointer to a variable of type int" to be a null pointer.

(2) Passing that null pointer to a function.

(3) That function treats the null pointer as the value of a local variable.

(4) The function then overwrites the null pointer stored in that local variable with the return value of new.

(5) When the function returns, the value stored in the local variable gets thrown away, since that variable is local to the function, and never gets passed back to the caller.

What the code will do if you change the types as I suggest:

(1) Initializing a variable whose type is "pointer to a variable of type int" to be a null pointer.

(2) Passing the address of that variable to a function.

(3) That function treats the address passed to it as a local variable.

(4) The function then writes the return value of new to the variable at the address that was passed to it.

(5) When the function returns, the caller has the return value of new--i.e., the pointer to the array of ints that was initialized in the function--in the variable whose address was passed.

Do you see the difference?
 
  • #139
Some comments on your (yungman) code for main() in post #136.
C++:
int main()
{
    int size = 5;
    int* pNumber = NULL;//initiate a pointer number
    getRNum(pNumber, size);//get the pointer to array of 5 random numbers
    cout << " Back in main from getRNum.\n\n";
    for (int count = 0; count < 5; count++)// display the numbers
        cout << *(pNumber + count) << "\n\n";
    delete[] pNumber;//free the memory of 5 element array number[5]
    return 0;
}
All line numbers referred to below start at the beginning of the code body.
Line 2 - We say "initialize" not "initiate." Also, your comment isn't much more helpful than no comment at all. A comment should not restate what the code is doing.
Line 3 - You have a variable named size. Any comments should refer to size, not its value. That way, you can change the value of size, and your comments will still be correct.
Line 5 - Use size instead of hard-coding 5 in the loop's condition expression.
Line 7 - It has already been mentioned that splitting the memory allocation and deallocation between two program elements is questionable. Besides this, you should not have 5 hard-coded here. Also, if the array has 5 elements, then number[5] is the element one past the end of the array.
Line 8 - A return statement used to be necessary in main(), but one of the new C++ standards, I believe, dispensed with that requirement.
 
  • #140
Hi Everyone
I have been doing a lot of googling, trying to learn pointer to a pointer and how to solve my problem I think I found it. But I just need to read more and let it sink in first before I post back. I don't want to jump the gun again. Here is the very simple program that works already, I just need to digest first, I still have a little difficulty bending my brain to say I got it.
C++:
// Pointer to a pointer
#include <iostream>
using namespace std;
void changePtVal(int**);
int Gvar = 42;
int main()
{
    int var = 23;   
    int* ptr = &var;
    cout << " Before calling changePtVal, *ptr = var = " << *ptr << " ptr = " << ptr << "\n\n";// the content in address ptr = var =23.
    changePtVal(&ptr);//**pptr = &ptr.
    cout << " After calling changePtVal, *ptr = " << *ptr << "\n\n";
    cout << " address of var = " << &var << " ptr = " << ptr << " *ptr = " << *ptr << "\n\n";
    return 0;
}
void changePtVal(int** pptr)
{
    *pptr = &Gvar;
}

//The result is:

//Before calling changePtVal, * ptr = var = 23 ptr = 012FFE48

//After calling changePtVal, *ptr = 42

//address of var = 012FFE48 ptr = 00EEC008 * ptr = 42

In case, this is the site I am reading:https://www.geeksforgeeks.org/passing-reference-to-a-pointer-in-c/
 
  • #141
I think I got it, I need to practice more though. This is the program using pointer to pointer:
C++:
#include <iostream>
using namespace std;

void getRNum(int**, int);//return a pointer from the functionint main()
{
    int size = 5;
    int*pNumber = NULL;//initiate a pointer number
    getRNum(&pNumber, size);//pass address of pNumber
    cout << " Back to main, pNumber = " << pNumber << "\n\n";
    cout << " The array pointed by pNumber = {";
    for (int count = 0; count < size-1; count++)// display the numbers
        cout << *(pNumber + count) << " ";
    cout << *(pNumber + size-1) << "}\n\n";
    delete[] pNumber;//free the memory of 5 element array number[5]
    return 0;
}
void getRNum(int** pArr, int num)//receive &pNumber the addresses of pNumber.
{
    int* Arr = NULL; // set pointer Arr to Null
    Arr = new int[num]; //request 5 elements of int of memory from computer pointed by Arr.
    *pArr = Arr;// *pArr = address of Arr[0].
    for (int count = 0; count < num; count++)// input 5 integers to Arr[0..4].
    {
        cout << " Enter number " << count + 1 << " = ";
        cin >> *(*pArr + count);//*pArr = address of Arr[0], pArr + elements = address of Arr[1...]
        cout << " In memory location of Arr: " << (Arr + count) << "\n\n";
    }
}
/*
Enter number 1 = 2
Enter number 1 = 2
 In memory location of Arr: 012B4D50

 Enter number 2 = 3
 In memory location of Arr: 012B4D54

 Enter number 3 = 4
 In memory location of Arr: 012B4D58

 Enter number 4 = 5
 In memory location of Arr: 012B4D5C

 Enter number 5 = 6
 In memory location of Arr: 012B4D60

 Back to main, pNumber = 012B4D50

 The array pointed by pNumber = {2 3 4 5 6}
*/

I copy the display from running the program. It ask for 5 integer, I put in 2, 3, 4, 5, 6. You can see the physical address for each element. I print out the address of Arr( same as Arr[0]), they when the function exit back to main, I printed out the content of pNumber. You can see both address match. So the pointer is PASSED back to main from the function.

Thanks
 
  • #142
yungman said:
he pointer is PASSED back to main from the function.

More precisely, the function writes the pointer that new returns to the variable whose address is contained in the function parameter pArr. Since the caller passed the address of the pointer variable pNumber in that function parameter, that variable is what the function writes to.
 
  • #143
PeterDonis said:
More precisely, the function writes the pointer that new returns to the variable whose address is contained in the function parameter pArr. Since the caller passed the address of the pointer variable pNumber in that function parameter, that variable is what the function writes to.
Ha ha, I can barely bent my mind to this so far, I need to play with this more to get a better feel of this. I think I have enough for today. Believe me, I have been working on this. It's hard to to twist around the address point to an address and all that. This is the first time in all 9 chapters that is difficult to me, or somehow, I have a mental block on this.

I am going to try the other examples from the geeksforgeeks. They are quite good, hopefully I get a better feel going through more of this.
 
  • #144
yungman said:
It's hard to to twist around the address point to an address and all that.

There's a saying that the two hardest concepts in programming for people to wrap their minds around are pointers and recursion. So you're certainly not alone. Hang in there.
 
  • Like
Likes yungman
  • #145
Why this work:
C++:
#include <iostream>
using namespace std;
int main()
{
    int var = 23;
    int* ptr= &var;
    int* pptr = ptr;
    cout << " var = " << var << "  *ptr = " << *ptr << "  **pptr = " << *pptr << "\n\n";
    return 0;
}
BUT, this doesn't work?
C++:
#include <iostream>
using namespace std;
int main()
{
    int var = 23;
    int* ptr =&var;
    int* pptr;
    *pptr = ptr;// Error
    cout << " var = " << var << "  *ptr = " << *ptr << "  **pptr = " << *pptr << "\n\n";
    return 0;
}

Compile error Listing 7.2.jpg
that is int* pptr = ptr; in the first one works, but int* pptr; *pptr = ptr; in the second one doesn't.
 
  • #146
yungman said:
that is int* pptr = ptr; in the first one works, but int* pptr; *pptr = ptr; in the second one doesn't.

What does the error message say? What kind of clue does that give you?
 
  • #147
PeterDonis said:
What does the error message say? What kind of clue does that give you?
I posted the error message right under the program " cannot convert int* to int."

I understand ptr is an integer, *pptr is a pointer and it doesn't match. Thereby an error. But isn't the two say exact the same thing? I even try pptr=&ptr, obviously it doesn't work also. BUT no matter how you look at it, pptr is a pointer to the address of ptr.

For example, say the pointer ptr is located in physical address 0x00FF.

int *pptr = ptr means the pointer pptr is pointing to address 0x00FF.

int*pptr; creates a pointer pptr. Then I set the content *pptr = 0x00FF which is *pptr = 0x00FF.

This is where a lot of the confusion for me.
 
  • #148
Try to think about what's in memory. You have an int variable, var (which is occupying 4 bytes somewhere in memory). You have two pointer to an int variables, ptr and pptr (which each are occupying 8 bytes somewhere in memory assuming you have a 64 bit OS).

int * pptr; (on line 7) declares an uninitialized pointer to an int. Being uninitialized/unset, it has what you would call a garbage value (meaning you have no clue what bits might be there), and it probably doesn't happen to be a valid address. *pptr = ptr; (on line 8) de-references pptr (note this is another use of * that is different from what it means in line 6,7), in this case meaning it takes the random garbage value that happened to be there, considers it to be a memory address, follows it to the corresponding chunk of memory, where it assumes there is some allocated place for a 4 byte signed integer to be (int), and then it tries to store a memory address (8 bytes) there.
 
  • #149
yungman said:
BUT, this doesn't work?
C++:
#include <iostream>
using namespace std;
int main()
{
    int var = 23;
    int* ptr =&var;
    int* pptr;
    *pptr = ptr;// Error
    cout << " var = " << var << "  *ptr = " << *ptr << "  **pptr = " << *pptr << "\n\n";
    return 0;
}

Try to think about what's in the actual memory. Maybe work it out on paper.

You have an int variable, var (which is occupying 4 bytes somewhere in memory). You have two pointer to an int variables, ptr and pptr (which each are occupying 8 bytes somewhere in memory, assuming you have a 64 bit OS).

int * pptr; (on line 7) declares an uninitialized pointer to an int. Being uninitialized and unset, it has what you would call a garbage value (meaning you have no clue what bits might be there).

*pptr = ptr; (on line 8) dereferences pptr before assigning ptr, in this case meaning it takes the random garbage value that happened to be in those 8-bytes stored by pptr, considers it to contain the memory address of an int, follows it to the corresponding chunk of memory at that address, where it assumes there is some allocated place for a 4 byte signed integer to be, and then it tries to store the value of ptr (an 8 byte memory address) there. The types mismatch (int) vs (int*) and it finds it should throw an error. But if it did compile, it would be a memory corruption bug.

For example, you might have this in memory up to line 7:

bytes 1 - 4 : stores the value of var, which is 23
bytes 2 - 10 : stores the value of ptr, which is 1 (the address of var)
bytes 11-18 : stores the value of pptr, which is garbage (who knows what's there) but let's say it is 27
...
byte 27: some memory we don't know much about

Line 8 says: treat bytes 11:18 as if they encode an address to an int variable, go to that address and put the value of ptr (an int*) there. e.g. it is something like:

(int )(bytes 27-30) = (int*)(bytes 2-10)

In the first/correct version, the example goes like this:

bytes 1 - 4 : stores the value of var, which is 23
bytes 2 - 10 : stores the value of ptr, which is 1 (the address of var)
line 7 says: allocate an int * and store the value of ptr there. Then,
bytes 11-18 : stores the value of ptr, which is 1
 
Last edited:
  • #150
yungman said:
I understand ptr is an integer, *pptr is a pointer and it doesn't match.
No, this is the same mistake you made before.
Both ptr and pptr have the same type: pointer to int (despite the misleading name for pptr, whose name would suggest that it is supposed to be a pointer to a pointer to an int).
yungman said:
Thereby an error. But isn't the two say exact the same thing?
No.

To fix your 2nd example, just remove the * from line 8, the line you've commented as an error.
 

Similar threads

  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 7 ·
Replies
7
Views
2K
  • · Replies 6 ·
Replies
6
Views
2K
Replies
1
Views
2K
Replies
3
Views
1K
  • · Replies 5 ·
Replies
5
Views
2K
  • · Replies 75 ·
3
Replies
75
Views
6K
  • · Replies 17 ·
Replies
17
Views
2K
  • · Replies 9 ·
Replies
9
Views
2K
  • · Replies 6 ·
Replies
6
Views
4K