Modifying the arguments to a function is a bad idea

c

    Next

  • 1. Pointer-to-pointer realloc problem
    Hello! I'm a beginner in C, and I'm having trouble with a pointer-to-pointer reallocation. This piece of code works well, but Valkyrie warns some parts (pointed below), and is breaking my real code. #include <stdio.h> #include <stdlib.h> int main() { int i; int **p = (int **)calloc(2, sizeof(int *)); for (i = 0; i < 2; i++) *(p+i) = (int *)calloc(1, sizeof(int)); *(*(p+0)+0) = 0; *(*(p+0)+1) = 1; // invalid write of size 4 *(*(p+1)+0) = 2; *(*(p+1)+1) = 3;// invalid write of size 4 printf("%d\n", *(*(p+0)+0)); printf("%d\n", *(*(p+0)+1)); // invalid read of size 4 printf("%d\n", *(*(p+1)+0)); printf("%d\n", *(*(p+1)+1)); // invalid read of size 4 p = (int **)realloc(p, 3); *(p+2) = (int *)calloc(1, sizeof(int)); // invalid write of size 4 *(*(p+2)+0) = 4; // invalid read of size 4 *(*(p+2)+1) = 5; // invalid read of size 4 printf("%d\n", *(*(p+2)+0)); // invalid read of size 4 printf("%d\n", *(*(p+2)+1)); // invalid read of size 4 free(*(p+0)); // invalid read of size 4 free(*(p+1));// invalid read of size 4 free(*(p+2));// invalid read of size 4 free(p); return 0; } In my real code, glibc detects the "double free or corruption (out)" error. Where's my mistake? Thanks a lot!
  • 2. problem with a casted pointer
    -------------------------------My dud program------------------------------------------ #include <stdioi.h> #include <stddef.h> typedef struct dummy{ int x, y, z; }point; int main() { point a = {1, 2, 3}; int *p = (int *)(&a + offsetof(point, y); int *q = (int *)(&a + offsetof(point, z); printf("%d %d", *p, *q); return 0; } ------------------------------------------------------------------------------------------------- my question is will *p and *q really point to (a.x) and (a.y) ? i ran this program on DevC++ 4.9.9.2 and i'm getting some garbage values as output!!!
  • 3. use of volatile ?
    can any one give an example which use volatile keyword ?
  • 4. storing related structs
    I have two related structs: struct A { int x ; void * data ; }; and struct B { int x, y ; double z ; void * data1, *data2 ; }; These structures (not pointers to them), need to be stored in a another struct: struct C { unsigned id ; char name ; DATA_TYPE the_struct ; }; Where DATA_TYPE is EITHER a struct A or struct B. For compatability with existing libraries, I cannot use a void * (for instance), and cast between struct A and B. Is there a soln to this problem ?
  • 5. Read a 8-bit grayscale JPEG image.
    Hi, Could anyone point to some simple code just to read a 8-bit grayscale JPEG image. Thanks a ton, Speed.

Modifying the arguments to a function is a bad idea

Postby jacob navia » Fri, 31 Jul 2009 08:10:52 GMT

his discussion started in the thread:
"Help a beginner - simple lowercase to uppercase and so on function"
but it has actually nothing to do with the subject of that thread so I
start a new one.

My arguments for justifying the thesis in the subject line are:

o It makes the function not restartable
o Under the de{*filter*} you can't know what was the parameter you
received when the function misbehaves. You can't debug it.
o The small gain in storage space is not worth the associated risks.

To my surprise, the eternal heathfield doesn't agree with this obvious
stuff.

He says to the first point:

> You lost me. I am sure it is possible to construct a function in
> which modifying its parameter makes it non-restartable, but you
> don't /have/ to construct functions that way.

Well, apparently he doesn't get it. Suppose a function

int fn(int a)
{
// some code
a = 56;
// more code
}

After that assignment it is impossible to go to the first line of the
function and restart it, since the original value of a is lost.

This is completely obvious but somehow not obvious to some.

To the second point, he argues ( I cite again the second point
for clarity)

<jn>
> o Under the de{*filter*} you can't know what was the parameter
> you received when the function misbehaves. You can't debug it.
<end of jn quote>

<heathfield>
Maybe you can't, but I can - by setting a breakpoint at an
appropriate place and making a note of the value before the mod.
<end of heathfield>

The obvious situation where the function is called 4765 times
before it crashes doesn't occur to him. Maybe because he has never been
in the frustrating situation of trying to know what is the specific
parameter that makes the function crash. :-)

He continues:
<heathfield>
What risk do you see in:

void intcopy(int *s, int *t, int term)
{
while((*s++ = *t++) != term) {}
}
< end of heathfield>

I see MANY risks in that construct, the first and foremost
is that "t" is a vector of integers that is MISSING the
"term" terminator!

Of course THAT "can't happen", and heathfield doesn't even consider
that case.

BUT, let's suppose that this happens, since in the real world,
bugs DO appear.

How you will see the effects of that?

(1) The program as written will crash when it arrives at the end of
available memory . Since you do not have the original data, you
can't figure out what parameters were passed in.

(2) The program will find a random integer in memory that
corresponds to "term", after having overwritten a lot of memory.
That function will return as if nothing had happened leaving
the rest of the software in an unknown state. Since this depends
on the contents previously stored in unknown data areas, the
program will (or will not) go on, maybe crashing sometimes
or (worst) never crashing but giving *sometimes* strange results or
crashes hours later.

(3)
Another obvious problem with the function above is that no SIZE
information is passed to limit the copy, so that if "s" points to
an area too small to receive "t" a buffer overflow will happen.

That has in principle nothing to do with the modification of the
parameters but having the parameters still available it would
allow you to see the corresponding address and you can figure out
its size and location within the de{*filter*}.

Actually, I wanted to highlite this prob

Re: Modifying the arguments to a function is a bad idea

Postby Richard Heathfield » Fri, 31 Jul 2009 08:45:16 GMT

acob navia said:

<snip>


If you need to do that, you obviously preserve the value of a. But
it's quite a rare thing to have to do, surely?


What is completely obvious to me is that you seem to be referring to
a special situation. I don't think it's a situation I've ever come
across. If I want to repeat a section of code, I use a loop. If I
need to preserve a value for each iteration of that loop, I
preserve the value. But that's a reason for preserving the value.
It isn't a reason for having a general rule of not modifying
parameters.


Why should it? If the program is *crashing*, then just get a
backtrace and take it from there.


No, I can't say I have. Now, the posit is that modifying function
parameters (or "arguments" as you rather confusingly call them in
the Subject line) is a bad idea. But to make that case, you have to
show that there are no circumstances in which it is a good idea. In
so doing, you have to explain why Kernighan does precisely that, in
several code fragments in K&R2. Is it because Kernighan is a bad
programmer?


NO, sir, that is not "end of heathfield". That is selective
quotation. You have snipped the equivalent construct that does not
modify its parameters, which I provided for comparison purposes,
precisely so that we could stick to the point of the discussion.


Yes, that's a risk - but the *same* risk is there in the version of
the code that does not modify its parameters, and it is therefore
irrelevant to this discussion. You drifted.


It is precisely because I did consider that case that I provided a
control. The rest of your commentary is irrelevant because it
doesn't have anything to do with modifying parameters, which is
what the example was dealing with.

<snip>


Again, this problem appears in both versions of the code and is
therefore not a risk that is peculiar to the practice of modifying
parameters.


Right.


So? If the program is segfaulting in that function, it's pretty
obvious that you've got a bounds error, isn't it?


You do realise, I suppose, that it's a reasonably faithful int
parallel of K&R2's strcpy implementation?


So what?


Who cares?


I disagree with that. It's perfectly debuggable.


Right. It was not intended to report an error. It was intended to
allow you to demonstrate risks related to parameter modification.
It is hard to see how a return value would be relevant to that.


Calling functions pass argument expressions, not parameters. Anyway,
I disagree. If the function segfaults, you know straight away that
it's a bounds error. What's the problem?


That isn't what he said. He pointed out that it's easy to follow
three things than five things. That doesn't mean he finds five
things difficult to follow. It just means he finds three things
even easier (and therefore less risky).


You haven't the faintest idea what you're talking about. Ben Pfaff
is a fine programmer, as you ought to know.


And not breathing is easier than breathing (after an initial ten
minute training period). Would you therefore recommend that
everyone stop breathing? How ridiculous your arguments are! Ben
Pfaff is a very good programmer - certainly better than I am - and
you do yourself a huge disservice by being so incivil towards him.


Do you really think the smiley makes it okay? If I'd said about you
what you just said about Ben Pfaff, would

Re: Modifying the arguments to a function is a bad idea

Postby Morris Keesan » Fri, 31 Jul 2009 08:49:16 GMT



I agree with your basic position, which is that modifying the values
of function parameters is generally a bad idea, primarily because
unless the function is extremely simple, like Richard Heathfield's
string copy example, it's too easy to confuse a maintainer of the
code, who is likely to lose track of what the values of the parameters
are.

   But you've lost me with the argument cited above.  What exactly
do you mean by "restarting" a function?  Under what circumstances,
and how, would you go to the first line of the function and restart it?
You're not really suggesting that a goto statement is an argument
against anything, are you?

Re: Modifying the arguments to a function is a bad idea

Postby jacob navia » Fri, 31 Jul 2009 08:54:10 GMT





Sorry, I am always thinking of debugging in a de{*filter*}.

When a function crashes, in the de{*filter*} you can go to the
first line and restart the function to see how the crash happened

If you have modified the arguments that's impossible.


Re: Modifying the arguments to a function is a bad idea

Postby Morris Keesan » Fri, 31 Jul 2009 08:58:27 GMT

On Wed, 29 Jul 2009 19:45:16 -0400, Richard Heathfield  




No, only if you want to make the case that it's ALWAYS a bad idea.
I maintain that driving above legal speed limits is a bad idea,
but that doesn't mean that I believe that there are no circumstances
where it's okay.

Re: Modifying the arguments to a function is a bad idea

Postby Richard Heathfield » Fri, 31 Jul 2009 09:13:29 GMT

Morris Keesan said:



Well, at least your position is clear! And I agree with you about 
speed limits. If you're in the third lane, you're already at the 
speed limit, you're approaching a crashed car in your lane, and 
there's a guy too close behind you and a lorry on your left, you'd 
be a fool not to accelerate towards the obstacle (thus breaking the 
speed limit BUT overtaking the truck) and then swing in front of 
the truck. Probably your only way to survive (and avoid killing any 
survivors of the first crash), and it involved breaking the speed 
limit. Nevertheless, observing speed limits is a good general rule.

Back to parameters, then. I think it's a bad idea to have two 
objects representing the same concept (which is not to say that 
there might be exceptional circumstances where it's the least bad 
idea available). If you copy a function parameter to another object 
purely to avoid modifying the function parameter, you have two 
objects representing the same concept, and you fall foul of the 
one-to-one object-concept thing.

So just saying that parameter modification is a bad idea is 
insufficient. There are advantages and disadvantages to parameter 
modification. You seem to think the disadvantages outweigh the 
advantages. I disagree. So - how does one quantify this?

-- 
Richard Heathfield < http://www.**--****.com/ >
Email: -http://www. +rjh@
Forged article? See 
 http://www.**--****.com/ 
"Usenet is a strange place" - dmr 29 July 1999

Re: Modifying the arguments to a function is a bad idea

Postby bartc » Fri, 31 Jul 2009 09:16:29 GMT







K&R2 says "Because arguments are passed by value, strcpy can use the 
parameters s and t any way it pleases". Which is true, and in their example 
it's really using the arguments to initialise local variables, instead of 
copying them and wasting a bit of time. That's fine.

However their's was a 4-line function optimised further down the page to a 
2-line function; not exactly complex.

Just today I came across one of my functions that had a parameter q which 
was a linked list root. The list had to be traversed twice; as written, the 
first loop used a copy of q, the second just used q (and at the end q was 
null).

This looked awkward and would have been better if both traversals used a 
copy (so the iteration code was identical), and at the end q would have been 
preserved in case of a third traversal or I needed to do something else with 
it. Even better would be to rename q as qroot or something so that modifying 
it would look wrong.

Anyway the subject just says modifying parameters is a bad idea, that's all. 
Not that it must never be done.

-- 
Bartc



Re: Modifying the arguments to a function is a bad idea

Postby Beej Jorgensen » Fri, 31 Jul 2009 09:47:16 GMT



I think he might be talking about restarting the function in a de{*filter*}.

-Beej


Re: Modifying the arguments to a function is a bad idea

Postby Old Wolf » Fri, 31 Jul 2009 10:11:05 GMT




I have to say that I would be quite capable of debugging
this function without the need to manually change the
program flow, like Mr. Navia seems to be suggesting.


Re: Modifying the arguments to a function is a bad idea

Postby Keith Thompson » Fri, 31 Jul 2009 10:13:00 GMT

acob navia < XXXX@XXXXX.COM > writes:
[big snip]

I agree that it's usually a bad idea. I don't agree that it's
*always* a bad idea. On the other hand, I suggest that it this
may be a special case of a more general rule of thumb.

Let's step back and look at the bigger picture. Parameters are
essentially local variables; the only special thing about them is
that their initial values are taken from the arguments in the call.

Consider:

int square(int n) {
n = n * n; /* poor style */
return n;
}

Here n, on entry to the function, hold the number whose square
we want to compute. The code then re-uses the same variable for
a different purpose, to hold the result of squaring the original
value. The problem, for a human reader, is that "n" has, not just
a different value, but a different *meaning*, depending on where
you are in the function.

Instead, I might write:

int square(int n) {
int result = n * n;
return result;
}

Or even:

int square(const int n) {
const int result = n * n;
return result;
}

And if the compiler is sufficiently clever, it might store both in
the same location or elimate result altogether.

(Of course in real life I'd just write "return n * n;", but let's
ignore that; I chose this example for its simplicity.)

But what's so special about parameters? Why shouldn't the same
apply to other variables?

int v;
/* ... */
v = some_value;
v = v * v;
/* ... */

Again, "v" has a different meaning depending on where you are in
the code, but now v is just a local variable, not a parameter.

Functions are the main unit of encapsulation in a C program, and
that does make parameters special. But they're not *that* special.

And conversely, there are times when it makes sense to modify a
variable after it's been initialized; the same applies to parameters,
particularly for very short functions. In particular, if modifying
the value doesn't change the *meaning*, I don't see much wrong
with it.

There are even languages that don't allow objects
to be modified once they've been initialized; see
<http://en.wikipedia.org/wiki/Single_assignment>. I won't
necessarily argue that such a strict rule is a good idea, but I
do suggest that minimizing re-assignment might make code easier
to analyze. And in fact, I'd suggest declaring objects with
"const" *unless* there's a specific requirement to modify them
after initialization.

Doing the same thing with parameters is slightly problematic in C.
The problem is that the constness of a parameter is of no concern to
the caller, so a "const" keyword in a prototype is just confusing.
But if you want to enforce the constness of a parameter within the
function, you need the "const" keyword in the definition.

You can legally write something like this:

void func(int param);

/* ... */

void func(const int param) {
/* implementation of func */
}

But in all other cases it's possible, and IMHO good practice, for
a function's declaration (prototype) and the declaration part of
its definition to be identical.

If I were designing a new language, "const" (or "readonly") would
be the default for all declared objects; if you want something you
can modify later (a variable), you'd have to add a keyword to the
declaration. (Apparently Scala does something like this, though
wit

Re: Modifying the arguments to a function is a bad idea

Postby cri » Fri, 31 Jul 2009 10:33:11 GMT

On Thu, 30 Jul 2009 00:13:29 +0000, Richard Heathfield




Just as a side note, in some sense the value passed in and the modified
value are separate things.  That is, the argument holds the value of a
parameter for the current execution instance; when we modify it we are
changing it into a local variable which is a subtly different kind of
thing.

Whether one should concern one's self with the difference is another
matter.  If you never use a de{*filter*} I doubt that it matters.  


Richard Harter,  XXXX@XXXXX.COM 
 http://www.**--****.com/ ~cri,  http://www.**--****.com/ 
No one asks if a tree falls in the forest 
if there is no one there to see it fall.

Re: Modifying the arguments to a function is a bad idea

Postby Richard Heathfield » Fri, 31 Jul 2009 11:47:56 GMT

Beej Jorgensen said:




Yes, this has become apparent during the discussion.

It seems to me that this may be a powerful argument for those who 
habitually restart functions in de{*filter*}s. I am not such a person. 
Therefore, the argument carries no real weight with me, but that is 
not to say that it will not carry weight with others.

It does not seem unreasonable to claim "modifying parameter values, if 
you habitually restart functions in de{*filter*}s, is a bad idea", but 
the qualifier is important.

-- 
Richard Heathfield < http://www.**--****.com/ >
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
This line unintentionally left unblank

Re: Modifying the arguments to a function is a bad idea

Postby Richard Heathfield » Fri, 31 Jul 2009 11:59:41 GMT

Richard Harter said:

<snip>

More formally, the parameter holds the value of the argument! :-)

But do the passed value and the modified value necessarily represent 
different things? Well, different *values*, obviously - but a great 
deal of the point of objects is that they can hold different values 
at different times. We wouldn't say that we've changed the nature of 
LoopCounter when we ++LoopCounter, after all - it's still a loop 
counter, right? - so why should we say we've changed the nature of s 
when we s++?


Well, no. Parameters have automatic storage duration, so it's hard to 
see how they differ significantly from other local objects, once the 
function is properly under way (i.e. yes, of course the different way 
in which a parameter receives its initial value is important, and 
yes, of course the placement of its definition is important, but 
these don't matter a jot once we're into the function body proper).


The way I see it, this is (largely) what const is for - to protect 
your parameters when you know you *don't* want to modify them.

-- 
Richard Heathfield < http://www.**--****.com/ >
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
This line unintentionally left unblank

Re: Modifying the arguments to a function is a bad idea

Postby robertwessel2@yahoo.com » Fri, 31 Jul 2009 12:20:14 GMT




We've disagreed in the past about the use of de{*filter*}s, and I guess
we'll continue to disagree on this one - I can't bring myself to be
moved by that particular side effect of modifying parameters.  And
there are all sorts of reasons a function might not be restartable in
the way you suggest, any modification of persistent state (alter a
global, alter something referenced via a passed in pointer, open a
file, etc.) will do the same.

If there's an argument for not doing that, it's that it can be
confusing in larger routines, especially to beginners.  In a large
routine it can be easy to miss the modification, and then incorrectly
assume that the parameter is unchanged later on (simply because it's
so rare that it *is* changed).  For a small routine it can be a semi-
useful shorthand.  And I'd expect any semi-competent compiler to
generate the same code in most cases if I copied the values to "real"
locals first or not.

Personally I usually avoid the form, simply because it unusual, and
converting to the more common form is trivial.

Re: Modifying the arguments to a function is a bad idea

Postby Morris Keesan » Fri, 31 Jul 2009 13:05:17 GMT




I think the de{*filter*} issue is a red herring.
I would say if you're never unlucky enough to try
to use the (original) value of the parameter after another programmer
has modified it, then it doesn't matter.

Similar Threads:

1.Nested conditional expressions ---- good idea/bad idea?

Below you will see an example of a nested conditional expression that
this colleague of mine loves.  He claims that it is more efficient that
a multi-level if-else-if structure.  Moreover, our complexity analyzer
tool supposedly does not pick it up.  Is it really more efficient?
Personally I find this coding style extremely cryptic, misleading and
error-prone.

I believe that I have removed all traces of proprietary-ness from this
coding example by multiple text substitutions.

--NS


/*************************************************************/
#include <stdio.h>

#define QWER 0x1000
#define TYUI 0x2000
#define ASDF 0x4000
#define GHJK 0x8000
#define ZXCV 0x10000
#define BNML 0x20000


void
print_value(int code, int type)
{
printf(" ==>  %s, Type %02X \n",
(code == QWER      ? "QWER":
code == TYUI      ? "TYUI":
code == ASDF      ? "ASDF":
code == GHJK      ? "GHJK":
code == ZXCV      ? "ZXCV":
code == BNML      ? "BNML": "????"),
type);
}


main(void)
{
print_value(BNML, 256);
print_value(ZXCV, 512);
print_value(GHJK, 1024);
print_value(ASDF, 768);
print_value(TYUI, 128);
print_value(QWER, 64);
  print_value(BNML|QWER, 32);
}

2.Modifying subroutine or function arguments

Hi,

I'd like to write a routine which internally modifies arguments that are 
passed to it, but I do not want these variables to be modified in the 
parent program. For example:

program test
  integer :: a
  a = 0
  call do(a)
  print *,a
end program test

subroutine do(b)
  integer :: b
  b = b + 2
end subroutine

is not what I am looking for, since the print statement will return '2'. 
Adding intent(in) to integer :: b will not work since most compilers 
will not then allow b to be modified. Making do() a function instead of 
a subroutine does not work either:

program test
  integer :: a
  a = 0
  print *,do(a)
  print *,a
end program test

function do(b)
  integer :: b
  b = b + 2
  do = 0
end function

also prints out '2'. Therefore, I am wondering if the *only* way is then 
to create a new variable inside the subroutine and copy the contents of 
the arguments into it:

subroutine do(b)
  integer :: b
  integer :: c
  c = b
  c = c + 2
end subroutine

Obviously in this case, this only adds a couple of lines of code, but 
the case I am working with has over 20 arguments (this is legacy code 
and I can't switch to derived types).

Any ideas?

Thanks!

Thomas

3.Function pointers, variable argument functions calling other variable-argument functions (sort of)

Hi

I have been searching the web and comp.lang.c of a method of using
variable-argument function pointers and the like. And some of the
questions arising in this post are answered partly in these posts, but
I ask mainly for a way to found a way to solve this problem. I'm a
hobbyist so there might be an alltogether better solution to the
problem, so please come with any solution you might think of. I'm open
to ideas. --- And i'm not bound by C. Suggestions from C++ are
welcome.

I need an extensible multithreaded application (i'm thinking windows
here since I have no experience on other systems, but I think the
problem is quite similar on other os). I would like to make the
extensibility as natural for the user-programmer as possible without
that programmer needing to think (very much) about multithreading. For
this reason I would like the user-programmer to be able to define a
function like this:


int Filter1(DataSegment segment, float a, float b, int value);
int Filter2(DataSegment segment, float start, float end);

then in the program itself, it should be possible to make a call
something like this


InvokeFilter(&Filter1, 0.5f, 43.0f, 120);

or

InvokeFilter(&Filter2, 0.2f, 1.2f);


InvokeFilter should then be able to spawn the necessary threads and
call Filter1 with the arguments 0.5f, 43.0f, 120 (and an appropriate
DataSegment, which tells the filter where to operate). My first
thought of solving this problem was to define a function-pointer of
the form


typedef int (*PROC_MULTITHREAD)(DataSegment segment, ...);


and then define InvokeFilter as

int InvokeFilter(PROC_MULTITHREAD filter, ...)

where InvokeFilter will call the function pointed to by filter, and
pass the extra (...) arguments. But as I have read around the net,
this is not supported by C (which I find a bit odd, since I can't see
the essential difference.). It is not possible for me to define som
general model which all filter-functions will conform to. The obvious
solution is ofcourse to implement a list containing the arguments, but
I think it would be somewhat annoying for the programmer of the
filter-functions to use some object for retrieving arguments.

Comments? Suggestions?

Sen Gammelmark

4.bad-idea-p?

5.tk/delphi...idea good/bad ?

hi-

in my personal tcl/sqlite  table structure changer, db query
program i came up with an idea.

right now to load the table structure and make changes i have a tcl gui 
form.  i thought why not scrap that and use delphi for the structure 
change part of the gui. i'd keep the rest in tcl/tk.

basically how it works is i get the sqlite create table statement from 
sqlite_master and parse the hell out of it and feed it to the gui for 
structure viewing.  then i allow changes in the viewer and write to some 
.sql files for changing the table structure.

i thought delphi would be good because they have a nice tree control and 
a listbox with a checkbox next to each item in the listbox.  i thought i 
could load the structure in the tree view with items and sub-items and 
use the listbox with checkbox to keep track of the multi-field primary 
and unique constraints.

i am doing this because i want to add features like unique column 
constraints, multi-field primary and unique constraints and the ability 
to generate temp/temporary tables.  oh and i'd like to package my 
program with a star-kit to if possible...right now it uses freewrap.
maybe i can throw table view capability in there too.

on the plus side i have all the manuals and software needed for delphi.
on the minus side delphi and tcl cant communicate with one another like
c or c++ and tcl (free, open, and easy).  i dont think any communication 
is needed though.

am i missing any design issue that can break this program?

thanks,
jim



6. asm+C I am doing a bad thing?

7. passing pointer to function - modify in different function not working

8. modifying INTENT(in) argument



Return to c

 

Who is online

Users browsing this forum: No registered users and 37 guest