compare a large number of variables

D

Douglas A. Gwyn

Dietmar said:
Should there be any discrepancy between what the authors were meaning to
express and what is written down in the standard, then I think the
written wording would be authoritative, since compiler implementors as
well as most users cannot be expected to be mind readers.
Perhaps you have too little confidence in the standard document's
authors' ability to express themselves.

What is written always requires comprehension by the reader,
and sometimes that is lacking, whether from linguistic
difficulties, lack of clarity of the expression, or failure
to apply context from other parts of the document.

It is certainly the case that most integer types can have
trap representations, and that such representations are not
supposed to express "values", at least for most purposes.

The example -1|-2 using ones-complement representation is
specified as producing "minus zero", which may be a trap
representation. Such possibilities suggest that it is
wisest to use unsigned types for bitwise operations.
 
K

Keith Thompson

6.2.5p5: "Certain object representations need not represent a value of
the object type. ... Such a representation is called a trap
representation."

Thus, if an indeterminate value is a trap representation, it does NOT
represent a value. The phrase "indeterminate value" is C jargon: you
can't conclude that it's a value just because it's name contains the
word "value". It's often a value, hence the name, but it's not always a
value. This isn't unusual: for instance, a "null pointer constant"
isn't necessarily a pointer, sometimes it's an integer constant
expression with a value of 0, and there's no meaningful sense within
the standard in which a "null pointer" actually points at anything.
When the standard provides an explicit definition for a phrase, that
definition is the only thing that applies within the context of the
standard - you can't analyse the meanings of the component words of the
phrase to get any additional information about the meaning.

If you look at the English grammar of these terms, in the phrase "null
pointer constant" the phrase "null pointer" modifies the noun
"constant". The implication is that a "null pointer constant" is a
constant, not that it's a null pointer. (In fact it can't be a null
pointer; a null pointer constant exists only in the program source,
and a null pointer exists only during execution.)

Similarly, the grammar of the phrase "indeterminate value" implies
that it's a value -- and ideally all terms defined in C should obey
the rules of English grammar. If they don't, confusion is inevitable.
 
N

Netocrat

]
(How can I store something in an object without using a type?)

memcpy()

Or accessing its individual bytes with a char pointer.

A char pointer is a type.

More to the point, char is a type.

You are both literally correct; but it seemed to answer Dietmar
Schindler's question in the context of the snipped material which prompted
him to ask it:
Second, it makes sense to talk about
values only when given a type under which to interpret a given
representation.

In this sense the char type is better considered as representing only a
part of the object rather than being a representation of the object
itself, so I think my comment was valid.
 
T

Tim Rentsch

Dietmar Schindler said:
Tim said:
Dietmar Schindler said:
Tim Rentsch wrote:
Look at 6.2.6, "Representations of Types". Pay special
attention to "trap representations". The description of
trap representations is so permissive that seems like
bitwise operators on negative operands can result in
undefined behavior at any time. Remember the rule - if
behavior isn't explicitly defined, it's undefined. However,
if that isn't enough, a case of undefined behavior is called
out explicitly in 6.2.6.2 p2,p4. The simple expression

-1|-2

may generate a bit pattern that corresponds to a "negative
zero" (on a ones-complement machine); if the implementation
doesn't support negative zeros, the behavior is undefined.

6.2.6.1 General

[#8] ... Where a
value is stored in an object using a type that has more than
one object representation for that value, it is unspecified
which representation is used, but a trap representation
shall not be generated.

In this case the bit pattern that is generated is not a
value, so 6.2.6.1 p8 doesn't apply.

I just discovered a bad mistake that I made. I had looked up 6.2.6.2,
paragraph 4 in the old draft WG14/N869, but this has become paragraph 6.
WG14/N1124 has this paragraph:

6.2.6.2 Integer types
4 If the implementation does not support negative zeros, the behavior of
the &, |, ^, ~, <<, and >> operators with arguments that would produce
such a value is undefined.

According to this, your assertion of the possibility of undefined
behavior is of course correct,

Glad to see the source of this part of the confusion was
discovered.

Note that my position is that undefined behavior is possible
even when two's complement representation is used. Two's
complement doesn't have a representation for negative zero,
so it isn't covered by the paragraph cited above. Even so,
bitwise operators on signed types that are represented using
two's complement may still result in undefined behavior.

although in my opinion not for the reason
"the bit pattern that is generated is not a value", but simply because
it is expressly stated in the above paragraph.

You say more about values in your other posting; I'll pick
up the discussion about values in a followup to that.

I apologize for my fault, but perhaps you enjoyed the discussion in the
other subthread, even if we didn't come to an agreement.

I would say that we neither did, nor did not, come to an
agreement. Rather, we didn't achieve understanding. Until
there is understanding the question of agreement can't
really be answered: how can someone know whether they agree
or disagree if they don't know what it is that they are
agreeing or disagreeing with?
 
T

Tim Rentsch

Keith Thompson said:
[snip]
If you look at the English grammar of these terms, in the phrase "null
pointer constant" the phrase "null pointer" modifies the noun
"constant". [snip]

<OTish>

That isn't the rule I learned.

When a noun is used in an adjective position, as "pointer" is in
this case, associate to the right: "easy programming manuals"
means programming manuals that are easy, not manuals about easy
programming. Similarly, the phrase "null pointer constant"
should be read as "null (pointer constant)".

To get the other reading, hyphenate: "(null pointer) constant"
should be written "null-pointer constant".

I'm not saying Keith's statement is wrong, only that it doesn't
agree with the usage rule that I was taught.

</OTish>
 
D

Dietmar Schindler

6.2.5p5: "Certain object representations need not represent a value of
the object type. ... Such a representation is called a trap
representation."

Thus, if an indeterminate value is a trap representation, it does NOT
represent a value.

Logic error: "not being a value of the object type" does not imply "not
being a value".
 
D

Dietmar Schindler

Douglas said:
What is written always requires comprehension by the reader,
and sometimes that is lacking, whether from linguistic
difficulties, lack of clarity of the expression, or failure
to apply context from other parts of the document.

That sounds reasonable.
It is certainly the case that most integer types can have
trap representations, and that such representations are not
supposed to express "values", at least for most purposes.

Some people (at least the two that I know of) are not so certain about
the latter.
 
K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
[snip]
If you look at the English grammar of these terms, in the phrase "null
pointer constant" the phrase "null pointer" modifies the noun
"constant". [snip]

<OTish>

That isn't the rule I learned.

When a noun is used in an adjective position, as "pointer" is in
this case, associate to the right: "easy programming manuals"
means programming manuals that are easy, not manuals about easy
programming. Similarly, the phrase "null pointer constant"
should be read as "null (pointer constant)".

To get the other reading, hyphenate: "(null pointer) constant"
should be written "null-pointer constant".

I'm not saying Keith's statement is wrong, only that it doesn't
agree with the usage rule that I was taught.

</OTish>

Ok, I see your point. My parsing of the phrase "null pointer
constant" was influenced by knowing what the term actually means.

But "easy programming manuals", whether they're programming manuals
that are easy or manuals about easy programming, are manuals. They
aren't programming.

A null pointer constant, whether it's a pointer constant that's null
or a constant for a null pointer, is a constant; it's not a pointer.

(In fact, the interpretation that it's a constant for a null pointer
is most consistent with the actual meaning, even if it doesn't
necessarily follow from the English grammar.)
 
T

Tim Rentsch

Dietmar Schindler said:
What you write below clarifies your view. It seems that we just can't
follow each other's lines of thought. I'll insert my judgement where it
differs from yours. If we don't get further evidence from a third party,
we probably have to agree to disagree.

First we need to understand what the other person is saying.
I certainly wouldn't want to agree to disagree without first
knowing whether we're in agreement or in disagreement. :)

3.17.2
1 indeterminate value
either an unspecified value or a trap representation

Since even a trap representation is a value (however indeterminate), I
can't see how any given representation can not be a value.

Defined terms like this one are meant to be read as a single
indivisible unit. For example, 3.7.1 p1 defines "character"
as "single-byte character" or "<C> bit representation that
fits in a byte". In 3.7.2 p1, "multibyte character" is
defined as "sequence of one or more bytes representing a
member of the extended character set of either the source or
the execution environment." A "multibyte character" needn't
be a "character" in the sense of 3.7.1 p1. Nor in the sense
of 3.7: multibyte characters represent abstract characters,
but they are not actual characters - they are sequences of
bytes. Also, note that a multibyte character may be only
one byte long, so it need not be "multibyte".

And so: Saying X is an "indeterminate value" doesn't mean
that X is a value.

Whether a particular setting of bits corresponds to a trap
representation depends on what type is used to interpret the
bits in question. An eight-bit byte with all bits equal to
one could be a trap representation when accessed as a (one's
complement) signed char; but it wouldn't be (and couldn't
be) a trap representation when accessed as an unsigned char.
Saying an object holds a trap representation is meaningful
only when the object's contents are interpreted as some
particular type. If, being accessed using some type T1, an
object's contents correspond to a trap representation, then
they don't correspond to a value; if, being accessed using
some other type T2, those same contents correspond to a
value, then they don't correspond to a trap representation.

The previous paragraph uses "value" in the sense of 3.17 p1.

(The question also depends on implementation choices of which
representations correspond to values under each particular type.)

Also, the term "value" is used in different senses in the C99
document. The definition of value is stated (in 3.17 p1) as:

precise meaning of the contents of an object when
interpreted as having a particular type

Notice two things about this definition. First, it's impossible
to store a "value" in an object. Objects hold representations,
or if you prefer, bits; they do not hold "meanings" (whether
precise or otherwise). Second, it makes sense to talk about
values only when given a type under which to interpret a given
representation.

[see note 1]
Your first point, as I see it, is contradicted at several places in the
standard, such as

""Modify includes the case where the new value being stored is the
same as the previous value."

"Such an object exists and retains its last-stored value during the
execution of the block ..."

"An object exists, has a constant address,25) and retains
its last-stored value throughout its lifetime."

My statements were made about the definition in 3.17 p1, not
about other uses of the term "value". If there are uses of the
term "value" that are inconsistent with what the definition
implies, then those uses must be using the term "value" in a
different sense than 3.17 p1.

The statements you give support my assertion that the term
"value" is used with different senses in different places in
the Standard. What do you think "value" means in the
statements you quote, since it can't mean the same thing as the
definition in 3.17 p1?

I think I read through the sections thoroughly and carefully, but I'm
afraid I read them differently.

How *do* you read them? Not what conclusions do you draw, but
how do you read them? Can you articulate what (you think)
they say? Can you articulate what (you think) I think they
say?






[1] > (How can I store something in an object without using a type?)

Can't in C. Even functions like memcpy() act on "arrays
of character type and other objects treated as arrays of
character type" (7.21.1 p1).
 
D

Dietmar Schindler

Tim said:
First we need to understand what the other person is saying.
I certainly wouldn't want to agree to disagree without first
knowing whether we're in agreement or in disagreement. :)

That sounds reasonable.
Defined terms like this one are meant to be read as a single
indivisible unit. For example, 3.7.1 p1 defines "character"
as "single-byte character" or "<C> bit representation that
fits in a byte". In 3.7.2 p1, "multibyte character" is
defined as "sequence of one or more bytes representing a
member of the extended character set of either the source or
the execution environment." A "multibyte character" needn't
be a "character" in the sense of 3.7.1 p1. Nor in the sense
of 3.7: multibyte characters represent abstract characters,
but they are not actual characters - they are sequences of
bytes. Also, note that a multibyte character may be only
one byte long, so it need not be "multibyte".

Your example seemed convincing to me at first, but unfortunately it
turned out not to be. You say "multibyte characters represent abstract
characters". I see that as compelling evidence that

3.7
1 character
<abstract> member of a set of elements used for the organization,
control, or representation of data

very well is the definition that applies to the sense in which it is
used in the term "multibyte character". So, a "multibyte character" is a
character (in the sense of 3.7).
And so: Saying X is an "indeterminate value" doesn't mean
that X is a value.

This is the very point which is contrary to my understanding of language
and logic - I'm afraid I just can't believe it.
My statements were made about the definition in 3.17 p1, not
about other uses of the term "value". If there are uses of the
term "value" that are inconsistent with what the definition
implies, then those uses must be using the term "value" in a
different sense than 3.17 p1.

What you say here is consistent, but
(a) 3.17 is the only place where the single term "value" is defined, and
(b) I can see no reason to assume that there are indeed uses of the term
"value" that are inconsistent with what the definition implies.
The statements you give support my assertion that the term
"value" is used with different senses in different places in
the Standard. What do you think "value" means in the
statements you quote, since it can't mean the same thing as the
definition in 3.17 p1?

In my opinion, in these statements

""Modify" includes the case where the new value being stored is the
same as the previous value."

"Such an object exists and retains its last-stored value during the
execution of the block ..."

"An object exists, has a constant address,25) and retains its
last-stored value throughout its lifetime."

the meaning of "value" is not at all different from its definition in
3.17. When I insert the definition into the statements, I get

""Modify" includes the case where the new precise meaning of the
contents of the object when interpreted as having its specific type
being stored is the same as the previous precise meaning of the
contents of the object when interpreted as having its specific type."

"Such an object exists and retains its last-stored precise meaning of
the contents of the object when interpreted as having its specific
type during the execution of the block ..."

"An object exists, has a constant address,25) and retains its
last-stored precise meaning of the contents of the object when
interpreted as having its specific type throughout its lifetime."

This may sound a little clumsy, but it is not illogical, is it?
How *do* you read them? Not what conclusions do you draw, but
how do you read them? Can you articulate what (you think)
they say? Can you articulate what (you think) I think they
say?

Above I made an attempt to do that.
 
K

kuyper

Dietmar said:
Logic error: "not being a value of the object type" does not imply "not
being a value".

True, a trap representation, if interpreted using a type different from
it's actual object type, may represent a value. There's relatively few
cases where it's legal to access a object using a different type. If
it's a compatible type, the trap repsentation of the object will also
necessarily be a trap representation of the compatible type.

However, any object can be accessed as if it were an array of unsigned
char whose length is sizeof(object). When you access the repseentation
one byte at a time using an lvalue of unsigned char type, you aren't
retrieving a trap repesentation, since unsigned char is not allowed to
have any.
 
T

Tim Rentsch

Dietmar Schindler said:
Your example seemed convincing to me at first, but unfortunately it
turned out not to be. You say "multibyte characters represent abstract
characters". I see that as compelling evidence that

3.7
1 character
<abstract> member of a set of elements used for the organization,
control, or representation of data

very well is the definition that applies to the sense in which it is
used in the term "multibyte character". So, a "multibyte character" is a
character (in the sense of 3.7).

Let's explore that. Characters are things like a, b, c, .... A
multibyte character is a sequence of bytes, ie a sequence of
memory locations. The set of characters is of some fixed size;
there are only so many of them. The set of (sequences of) memory
locations is of potentially unbounded size; certainly there can
be more memory locations than there are characters. If the set
of characters is smaller than the set of memory locations, how
can the set of memory locations be a subset of the set of
characters? If the set of memory locations is not a subset of
the set of characters, how can a memory location be a character?
A sequence of memory locations can represent characters, but they
are not themselves characters.

Similarly, the bit patterns in an eight-bit byte can represent
the numbers from zero to 255. We don't mean that a memory
location IS a number; a memory location -- or lots of different
memory locations -- can REPRESENT a number by having a certain
bit pattern stored in it (or them). There are only 256 numbers
between 0 and 255, but there are a lot more than 256 memory
locations -- there aren't enough numbers to go around so that
every memory location is a number. Or do you mean to say two
distinct memory locations can be the same, because they are
both (for example) the number 5?.

I think you're using "is" in the sense of "can stand in for".
And in that sense, I think I would agree with you -- a sequence
of memory locations (and the bit patterns stored therein) can
stand in for a character. But in technical writing the word "is"
is normally used in a different sense: if an X is a Y, then
the set of X's is a subset of the set of Y's. That sense of "is"
doesn't fit with what your statement about multibyte characters
implies.

This is the very point which is contrary to my understanding of language
and logic - I'm afraid I just can't believe it.

Yes, I suspect the thread has lasted as long as it has because
your understanding of language and logic differs from that of
some other readers (mine in particular, but also some others).

What you say here is consistent, but
(a) 3.17 is the only place where the single term "value" is defined, and
(b) I can see no reason to assume that there are indeed uses of the term
"value" that are inconsistent with what the definition implies.

Here's an example. We store the bit pattern 1000000, with a
value of 0 (on a signed magnitude implementation), into an
eight-bit byte. Subsequent reads of the object alternately
return 00000000, 10000000, 00000000, ..., all of which still have
the value 0. Is this behavior allowed by the statement that "an
object retains its last-stored value"?

Second example:

int n;
unsigned char *p = (void*) &n;
p[0] = 0, p[1] = 1, p[2] = 2, p[3] = 3;

What is the last-stored value (in the sense of 3.17) of the
object n? Suppose the value (in the sense of 3.17) of n is
0x00010203; how could this value have been stored, since
no expression in the running program yields that value?

The statements you give support my assertion that the term
"value" is used with different senses in different places in
the Standard. What do you think "value" means in the
statements you quote, since it can't mean the same thing as the
definition in 3.17 p1?

In my opinion, in these statements [snipped to one representative]

"An object exists, has a constant address,25) and retains its
last-stored value throughout its lifetime."

the meaning of "value" is not at all different from its definition in
3.17. When I insert the definition into the statements, I get
[snipped to one representative]

"An object exists, has a constant address,25) and retains its
last-stored precise meaning of the contents of the object when
interpreted as having its specific type throughout its lifetime."

This may sound a little clumsy, but it is not illogical, is it?

Part of my problem with your reformulation is that I don't know
what the revised wording is meant to say. What is it that's
being stored? Is it a meaning, or a bit pattern? Let's look at
a slightly different wording:

"An object exists, has a constant address,25) and retains the
precise meaning of its last-stored contents of the object when
interpreted as having its specific type throughout its lifetime."

Does your statement mean the same thing as the rewording, or does
it mean something different? The rewording makes it clear that
what is being stored is "contents" rather than "meaning". A
"value" under 3.17 is a "meaning", so a 3.17-value is not what's
being stored (if the rewording is accurate). So which is it? Is
the statement about retaining the last-stored value inconsistent
with the 3.17 notion of value, or do you mean something different
by your statement than my proposed rewording? If the latter, then
what rewording would you propose?

I wouldn't say I find your revised definitions illogical,
but they do seem (at least to me) nonsensical.

Above I made an attempt to do that.

Yes, and I give you credit for that. I've tried to divine what
it is you've been trying to say, and some of that is expressed
above (how I think you mean "is", for example). So let's see if
I can articulate what I think is the Standard is trying to say.
Disclaimer: very off-the-cuff remarks follow.

The word "value" is used in several senses in the Standard.
The definition given in 3.17 uses "value" in the sense of
how we are to interpret the contents of an object, when
interpreted in a particular context (namely, as what type).

Many places in the Standard use "value" not in the sense of 3.17,
but the sense of "raw bit pattern". Thus, when we say that an
object retains its last-stored "value", this wording is meant
to say that an object retains the last bit pattern stored into
that object. Similarly, we say a write access modifies an
object even if the raw bit pattern being stored matches the
bit pattern already in the object.

There is some confusion between the two senses, and that
confusion has prompted some esoteric discussion in the comp.*.c
newsgroups. For example, suppose a particular address is stored
in a pointer variable, and then the memory at that address is
deallocated (by calling free(), for example). Is the bit pattern
that was stored in the pointer allowed to change? I remember
there being discussion at some point on this question, although I
don't remember the outcome. However, part of what prompted the
discussion in the first place -- and what made it difficult -- is
the confusion over which sense of "value" is meant in statements
like "an object retains its last-stored value". If "value" means
"raw bit pattern" you reach one conclusion, and if "value" means
value-ala'-3.17 you (can) reach another. Some of the debate
centered around this confusion, although I don't remember if it
was ever explicitly identified.

Note that I'm not saying that one sense is the true sense and the
other sense is wrong; there is some ambiguity in the Standard
itself, and that ambiguity deserves (IMO) a DR (which hopefully
would result in revised and improved wording, but let's not get
into that right now). But -- and here is the important thing --
many places in the Standard where "value" is used are best read
as though "value" meant "raw bit pattern". And that sense of
value is definitely different than "value" as defined in 3.17.

Does that all make sense?
 
T

Tim Rentsch

[groups revised back to just comp.lang.c]

Keith Thompson said:
Tim Rentsch said:
Keith Thompson said:
[snip]
If you look at the English grammar of these terms, in the phrase "null
pointer constant" the phrase "null pointer" modifies the noun
"constant". [snip]

<OTish>

That isn't the rule I learned.

When a noun is used in an adjective position, as "pointer" is in
this case, associate to the right: "easy programming manuals"
means programming manuals that are easy, not manuals about easy
programming. Similarly, the phrase "null pointer constant"
should be read as "null (pointer constant)".

To get the other reading, hyphenate: "(null pointer) constant"
should be written "null-pointer constant".

I'm not saying Keith's statement is wrong, only that it doesn't
agree with the usage rule that I was taught.

</OTish>

Ok, I see your point. My parsing of the phrase "null pointer
constant" was influenced by knowing what the term actually means.

But "easy programming manuals", whether they're programming manuals
that are easy or manuals about easy programming, are manuals. They
aren't programming.

Right. And certainly a null pointer constant is a constant and
not a pointer, no matter which grouping is used. Oh wait, you're
just about to say that...

A null pointer constant, whether it's a pointer constant that's null
or a constant for a null pointer, is a constant; it's not a pointer.

(In fact, the interpretation that it's a constant for a null pointer
is most consistent with the actual meaning, even if it doesn't
necessarily follow from the English grammar.)

I can see that point of view; there is such a thing as a null
pointer, and a (null pointer) constant is a constant that turns
into a null pointer.

I tend (slightly) to read it as null (pointer constant), because
there are other pointer constants:

static char buffer[100];
static char *buffer_p = &buffer[0];

It seems nice to say that what a static pointer variable can
initialized to is a pointer constant, and a pointer constant can
be either a null (pointer constant) or a non-null (pointer
constant), like the '&buffer[0]' initializer above. [I don't
know if the Standard ever uses the term "pointer constant"
(without "null" that is); the index doesn't have an entry for
it. I mean the term as one I happen to use, not an official
one.]

Again, I'm not saying my reading is better or righter than your
reading. It's just the reading that seems, to me, slightly more
satisfying.
 
D

Dietmar Schindler

Tim said:
Glad to see the source of this part of the confusion was
discovered.

Note that my position is that undefined behavior is possible
even when two's complement representation is used. Two's
complement doesn't have a representation for negative zero,
so it isn't covered by the paragraph cited above. Even so,
bitwise operators on signed types that are represented using
two's complement may still result in undefined behavior.

Yes, however your wording "seems like bitwise operators on negative
operands can result in undefined behavior at any time" seems somewhat
exaggerated to me. With two's complement representation, I find exactly
one possibility for undefined behavior, and that's in

6.2.6.2 Integer types
2 ...
Which of these applies is implementation-defined, as is whether the
value with sign bit 1 and all value bits zero ... is a trap
representation or a normal value.

The exclusion of other possibilities of undefined behavior of bitwise
operators when two's complement representation is used is suggested, as I
understand it, by footnotes 44 and 45, which say "no arithmetic operation
on valid values can generate a trap representation other than as part of
an exceptional condition such as an overflow."
 
R

Richard Bos

Keith Thompson said:
Ok, I see your point. My parsing of the phrase "null pointer
constant" was influenced by knowing what the term actually means.

But "easy programming manuals", whether they're programming manuals
that are easy or manuals about easy programming, are manuals. They
aren't programming.

A null pointer constant, whether it's a pointer constant that's null
or a constant for a null pointer, is a constant; it's not a pointer.

What's more, TTBOMK there are no other pointer constants; a "non-null
(pointer constant)" doesn't even exist, so the parse "null (pointer
constant)" is spurious and the parse "(null pointer) constant" is, to
me, the obvious one.

Richard
 
K

kuyper

Richard said:
What's more, TTBOMK there are no other pointer constants; a "non-null
(pointer constant)" doesn't even exist, so the parse "null (pointer
constant)" is spurious and the parse "(null pointer) constant" is, to
me, the obvious one.

Since the standard assigns no special meaning to the term "pointer
constant", we're supposed to use ordinary rules of english usage to
interpret it. In the constext of the C standard, it has a meaning that
seems clear, obvious, and unambiguous to me (YMMV): a constant
expression with pointer type. In other words, it's essentially
synonymous with what the standard calls an "address constant". If it
weren't for the fact that the standard does provide a definition of
"null pointer constant", the obvious (to me) interpretation would be "a
constant expression with a pointer type and a null value"; which is NOT
how the standard defines it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top