Types in C

U

Uncle Steve

It can be bad because you might end up on a machine where 32-bit types
are intrinsically slower than 64-bit types, so by mandating that ONLY
32 bits be used, you're slowing the program down.

I suppose it's possible, but that seems a bit like putting the cart
before the horse. If I say "do not blaspheme" because it might
piss-off God, I am presupposing the existence of God, which could very
well be false. You are presupposing that a hardware manufacturer is
going to build a CPU with those characteristics. How likely is that?
See above. Imagine that you have something that you know only needs to
be 16 bits, so you specify i16 for it. You have an array of them. You're
on a machine where access on anything other than 64-bit boundaries adds
noticeable latency. Poof!

Well now. I think I said (or implied) that I was attempting to avoid
constructs that break when assumptions about type sizes are wrong.
You seem to be saying that program correctness is subordinate to
performance in this instance. I beg to differ.
This kind of thing really does happen. There's a reason C gives minimum
ranges for the basic types.

I suppose it does, but I am not going to assume something for which
there is no supporting evidence, in relation to this particular topic.
Machine-specific code which does something that's stupid on a particular
machine is not faster than portable code which lets the compiler pick
something smart.
In general, the vast majority of code does not gain any performance from
trying to be more machine-specific, and the cases where it hurts, it hurts
BADLY.

Exploding software hurts too. At any rate, I'm not being
machine-specific, I am attempting to be type-specific within an
arbitrary algorithm across multiple platforms. Those are two
very different approaches.



Regards,

Uncle Steve
 
U

Uncle Steve

On 5/23/2011 4:53 PM, Uncle Steve wrote:
51 AM, Uncle Steve wrote:
size_t may be exactly the same type as unsigned long,
since a typedef merely creates an alias for an existing type, not
a new and distinct type. plain char and signed char (for example)
are distinct in a way that size_t and unsigned long (for example)
are not.

True. Although, possibly confusingly, there are places where it seems
that the thing a typedef creates is itself then called "a type". The
wording is a bit fuzzy.

We all know what we mean, I think. You could say it several ways:

Typedef does not actually create types; instead, it creates
names which refer to existing types.

Typedef creates types, but those types are identical to existing
types and can be used interchangably with them.

How about the idea that typedefs create aliases for the basic built-in
types supplied by the language spec.?

A typedef may provide an alias to any existing type, built-in, user
defined or even incomplete.

There are no "user defined" types in C. Structures and unions are
aggregates of basic types (and structures/unions). I believe
incomplete types are a (usually temporary) artifact of the compilation
process and you won't ever see them in a running program.

There are function types, too.

typedef struct {
char uncle[5];
} s_uncle;
typedef s_uncle * f_new_uncle(int, const char *, const s_uncle *);
static f_new_uncle new_uncle_unsafe;
f_new_uncle new_uncle;

That function type appears to be user-defined.

Sure, but a function definition is not a basic type.

Ok, but now you've said "basic type" rather than "\"user defined\" types".

Beyond that, the second typedef in the example is not a function
definition. It includes the definition of a function type.

Ah, sorry. It's getting late and I'm misreading things.
The standard definition in C99, if I'm not mistaken, includes:

"The meaning of a value stored in an object or returned by a function
is determined by the /type/ of the expression used to access it..."

Doesn't that specify when and how a cast occurs in expression
evaluation?

int function(void) {
return(42);
}

....

long v = function();

So what I gather from the quoted text is that the return value of
function() is promoted to long before the assignment occurs. Is that
correct?
Well, "aggregate" goes more like this, if I'm not mistaken:

"Array and structure types are collectively called /aggregate types/."

I think that the best argument against "user-defined types" might go
more like:

"\"user-defined\" does not appear in such-and-such standard of C."

I think that when you are using "aggregate," that perhaps you mean
"derived."

"Any number of /derived types/ can be constructed from the object,
function, and incomplete types, as follows..."

Sure, I'm happy with 'derived'.
He has previously and kindly offered that using his first name is
acceptable, so I'd probably use that. Other possibilities include
"foo", "bar", "baz", "cat", "dog".

It just seemed odd.



Regards,

Uncle Steve
 
U

Uncle Steve

Just intended as some kind words of advice as you appear to be
frequenting this newsgroup these days: Whatever you do, please avoid use
of the word "claims" with the poster who you replied to with the above.
You haven't made this "mistake," so far. :)

What, is 'claim' some sort of trigger word that makes your skin turn
green and drives you to throw stray automobiles all over the place?



Regards,

Uncle Steve
 
U

Uncle Steve

No, it doesn't. The word "type" denotes the category into which all
of those things fit.

It's like "number". The word "number" includes rationals and irrationals
and complexes and counting numbers... so what? It's a good word because
it lets us characterize the stuff they have in common.

Until we trip over the hidden differences.
All the "types" have things in common in terms of their meaning to the
C type system.

True. It's all about variables and differentiating them from each
other.
That's right.


Right. That is because function pointer types are not object pointer types.

But function types and object types still have underlying commonalities.
It is useful to refer to those commonalities in some way; we use the word
"type".

Yes, I noticed that.
Sure.

But they are differences *within* the concept of "defining a type".

True, however the bare 'type' nomenclature conceals the differences,
which are perhaps only of non-trivial interest to compiler-writers.
gcc 4.4 requires GMP 4.1+ and MPFR 2.3.2+.

Just to clarify: My day job is toolchain tech lead. The actual hard
compiler stuff is outsourced to Code Sourcery (who are awesome), but I do
maintain a certain basic familiarity with compiler build stuff. Long
story short, the core tools like this are only relatively recently (I
think it's a gcc 4 decision) willing to assume that they have access to a
compiler which supports prototypes.

So gcc 3 and earlier will compile stage one on such a compiler? That
is scary. I think I saw one of those compilers back in the beginning
of the nineties on the Amiga: Dice, Manx or Lattice C; I no longer
recall the details. Never did much programming back then, and anyways
the code I wrote is long gone.



Regards,

Uncle Steve
 
S

Seebs

I suppose it's possible, but that seems a bit like putting the cart
before the horse. If I say "do not blaspheme" because it might
piss-off God, I am presupposing the existence of God, which could very
well be false. You are presupposing that a hardware manufacturer is
going to build a CPU with those characteristics. How likely is that?

I've used several. There have been MANY systems where larger types were
faster than smaller types. It's common.

Idle question: Have you ever used an Intel processor? Like, an x86?

I wrote a little routine which does a trivial tight loop on objects.

chars: 4.084635 secs.
shorts: 4.161859 secs.
ints: 2.729034 secs.
longs: 3.284039 secs.
quads: 3.134092 secs.

So, if you "knew" that you needed a 16-bit type, and used short, you'd
be taking a 50% performance hit for picking an inefficient type. If all
you need is "can store values up to 30000", that would be silly. Heck,
you'd get better performance on *64-bit* objects than you would on
16-bit shorts.

Use specific types when you are solving a problem which needs them.
Otherwise, favor "int", it's usually fast.
Well now. I think I said (or implied) that I was attempting to avoid
constructs that break when assumptions about type sizes are wrong.
You seem to be saying that program correctness is subordinate to
performance in this instance. I beg to differ.

No.

I'm saying that you should never pick an exact type size when an "at least
N" will do.

To put it in terms of the C99 spec, the following types exist:
int_least32_t
int32_t

The former type MUST exist and will have at least 32 bits. It may have more
than 32 bits, if that's more convenient and expressive.

The latter type MAY exist, and if it does will have exactly 32 bits. It
may be painfully slow, though.

What I'm saying is I don't think I've yet written a program where I would
have used int32_t instead of int_least32_t.
Exploding software hurts too. At any rate, I'm not being
machine-specific, I am attempting to be type-specific within an
arbitrary algorithm across multiple platforms. Those are two
very different approaches.

I don't think type-specific buys you anything. You know the required
range. Pick a type guaranteed to be large enough, but don't demand an
exact number of bits.

-s
 
S

Seebs

What, is 'claim' some sort of trigger word that makes your skin turn
green and drives you to throw stray automobiles all over the place?

In many cases, the word "claim" is used to indicate that you do not accept
a statement. For instance, if you said "I'm a pretty smart guy" and I said
"Uncle Steve claims to be a pretty smart guy", there is an implication that
I disbelieve it. (Note: Purely an example; I do not disbelieve that claim.)

At one point, Shao made some remark to me which used the word "claim" in
that way and I pointed out that this was, IMHO, rude. (I plonked him
quite a while back for being, so far as I could tell, not interested
in sincere discussions of technical issues. Nothing I've seen quoted since
has changed my mind.)

So he's probably pointing out that you challenged something I said, and
that I have a history of being touchy about things. The distinction being,
I don't expect people to take my word on technical claims, just on claims
about who I am or what I do. :) Technical claims are by nature subject
to dispute or argumentation.

-s
 
S

Seebs

Until we trip over the hidden differences.

I don't think the differences are hidden -- as long as you realize that
"type" is a very broad term in C, and doesn't carry all the extra
meanings of "basic type". Which is why the standard defines terms
for things like "basic types" and "derived types".
True, however the bare 'type' nomenclature conceals the differences,
which are perhaps only of non-trivial interest to compiler-writers.

I don't think it conceals them, it just doesn't refer to them. When I
refer to someone as "human", I'm not "concealing" the differences between
men and women, or between people who do and don't have cancer. I'm just
not talking about those differences.
So gcc 3 and earlier will compile stage one on such a compiler? That
is scary. I think I saw one of those compilers back in the beginning
of the nineties on the Amiga: Dice, Manx or Lattice C; I no longer
recall the details. Never did much programming back then, and anyways
the code I wrote is long gone.

Manx and Lattice, at least, supported prototypes when I used them.

Honestly, I think it pretty much comes down to "default compilers shipped
with some commercial Unix systems".

-s
 
S

Shao Miller

Doesn't that specify when and how a cast occurs in expression
evaluation?

No. That quotation is from here[n1256.pdf:6.2.5p1]. The slashes I
injected into the quoted text were intended to denote italics in the
original. For italics in the original, we have that "... Other terms
are defined where they appear in /italic/ type or on the left side of a
syntax rule. ..."[n1256.pdf:3p1] *

As far as I know, a cast occurs when one uses something like:

(type)expression

in code. Perhaps you were thinking of "conversion" rather than "cast"?
int function(void) {
return(42);
}

Just out of curiosity, do you ever override the 'return' keyword with a
function-like macro? That's one possible reason for wrapping return
values with parentheses and it'd be interesting to get a sense of how
many people have that habit for that intention.
...

long v = function();

So what I gather from the quoted text is that the return value of
function() is promoted to long before the assignment occurs. Is that
correct?

Actually, I don't think so. I think that "...the value of the right
operand is converted to the type of the assignment expression and
replaces the value stored in the object designated by the left
operand."[n1256.pdf:6.5.16.1p2]

But anyway, in regards to "user defined types," because that doesn't
appear in a standard of C (or does it?), I think it's possible that it's
a subject where discussion can be challenging by having different
understandings of the term for different people.

I _think_ that what you have offered is congruent with "you, the
programmer, don't get to invent/specify a type which is not a derived type."

I _think_ what at least one other poster has offered is congruent with
"if you, the programmer, specify a derived type, then that type could be
called a youser^H^H^H^H^H^Huser-defined type, since _something_ defined it."

Or maybe not.

* I hope to some day purchase some actual standards. The efforts that
have gone into them can certainly save me some time == money while
attempting to write portable C. :)
 
S

Shao Miller

What, is 'claim' some sort of trigger word that makes your skin turn
green and drives you to throw stray automobiles all over the place?

No. It's really too bad that I didn't have the request not to quote
that in the very same post, as it seems you didn't read it in time not
to quote the post. Thus it has already begun to back-fire. Hopefully
that's the end of it.

I was merely offering some advice which was intended to be friendly, as
your prolonged argument/discussion with a particular poster triggered a
memory.

Even worse than "claim" is "invent". Obviously you are free to use or
discard any advice. :)

Have a nice day.
 
K

Keith Thompson

Ian Collins said:
A pointer to an incomplete type is frequently used as an handle type to
avoid exposing the type definition to the user. FILE* is the most
obvious example.

FILE is specifically required to be an object type.
 
M

Martin Ambuhl

The idea that you could discuss the main argumentation
of my posts just doesn't even occur to you...

Please stop repeating this mantra like a spoiled child. If you actually
look at your "arguments' they are nothing but examples of the fallacy
called "argument by assertion". You state claims and then you repeat
those claims as "arguments" in support of themselves. When you actually
post an actual argument, I will find the time to respond to it, whether
in support, in opposition, or simply for clarification.
And you CONFIRM what I say by snipping the MAIN part of my post:

The "MAIN part of your post" is a trivial restatement of your thesis and
not worth bothering about.
 
J

jacob navia

Le 24/05/11 09:17, Martin Ambuhl a écrit :
Please stop repeating this mantra like a spoiled child. If you actually
look at your "arguments' they are nothing but examples of the fallacy
called "argument by assertion". You state claims and then you repeat
those claims as "arguments" in support of themselves. When you actually
post an actual argument, I will find the time to respond to it, whether
in support, in opposition, or simply for clarification.


The "MAIN part of your post" is a trivial restatement of your thesis and
not worth bothering about.

Of course.

"Spoiled child"
"your arguments are nothing but ... fallacy"

The ambuhlance is back OH despair...
 
J

jacob navia

Le 24/05/11 01:39, Seebs a écrit :
My guess is for preprocessor arithmetic, which has to be implemented
reliably in the largest type available on the target, which may not be the
host. You can cross-compile from a 32-bit machine to a 64-bit, so the
preprocessor has to be able to do 64-bit math.

Using gmp is probably a lot simpler than writing another thing just like
it. :)

-s

Gcc had a multiple precision package for that purpose. It was simple and
efficient and never gave any problems, all features that made it obsolete.

Why make it simple when you can make it very complicated?

They now use GMP, a HUGE package that can multiply numbers of ANY
size, up to thousands of digits precision, complete with assembler
code tailored for each processor, with obscure macros, with thousands
of complications that aren't at all needed for a compiler.

That is progress my friend. PROGRESS means:

If it is simple, it is wrong.
 
J

James Kuyper

There are no "user defined" types in C. Structures and unions are
aggregates of basic types (and structures/unions).

True, structures, unions are aggregates of other types, which in no way
contradicts the fact that they are two of the three categories of
user-defined types in C (the third category is enumerations). The only
requirement for "user-defined types" is that they be user-defined (which
is definitely permitted for all three categories), and types (which is
true for all three categories). I expect you to disagree, so please
clarify the nature of your disagreement: do you think that someone other
than the user has defined these types, or do you think that they are not
types?

... I believe
incomplete types are a (usually temporary) artifact of the compilation
process and you won't ever see them in a running program.

Types in general are compile-time constructs; but an imcomplete type can
reach the end of the compilation process without ever having been completed.

....
Correct me if I'm wrong, but I've never heard of a C compiler that
allows you to define something like a BCD integer type when the
compiler has no built-in support for it.

Even if true, that doesn't change the fact that the struct, union, and
enumeration types which C does allow you to define are types, and are
user-defined.

However, nothing in C prevents the declaration of a structure containing
the representation of a BCD integer, nor the definition of functions
implementing all of the standard operations on such an integer. Without
operator overloading, C cannot make such a type behave the same as a
built-in type: operator expressions would have to be replaced with
function call expressions. However, the explicit function calls needed
to implement such a type in C need not be any less efficient than the
implicit function calls that could be used to implement it in C++. In
both cases, a heavy reliance on inlining would be needed for best results.
 
J

James Kuyper

Absolutely correct, but I think it may be an error to use the word
"type" to mean several different things: 'basic' types,
aggregates of basic types, enumerated types, unions, structures, and
function definitions.

You might feel that way, but if you wish to communicate clearly with
people about C, you need to use the term 'type' in a way consistent with
it's definition by the C standard. You can reasonable argue that the
definition should be changed (though such a suggestion is more usefully
discussed on comp.std.c). However, using the word 'type' in a way that
is inconsistent with the definition provided by the C standard, serves
only to confuse people who are familiar with that definition.

The correct way, within a C context, to say what you want to say, is
"There are no user-defined basic types in C". It's a perfectly true,
utterly non-controversial statement, and also one that would be
completely irrelevant in the context where you said, incorrectly, "There
are no user-defined types in C".
... All these things have wildly different
semantics and varying syntax. They aren't all interchangable, and

They are interchangeable within every context in which the C standard
uses the term "type" without modification.
 
J

James Kuyper

The compiler writers. See my recent reply to Peter Seebach.

So, when I write

struct mystruct {
int hello;
float goodbye;
};

The definition of struct mystruct was actually provided by the compiler
writers? Even though the definition was written by me (who is only a
user), and never seen by the compiler writers? How exactly is this magic
performed?
void * is a non-specific pointer, with special semantics contrasted to
normal pointer types. It is not "incomplete".

The standard defines void as "an incomplete type that cannot be
completed" (6.2.5p19).
... Perhaps my internal
representation of "incomplete type" is wrong. I labor under the
delusion that a structure defined with an element whose type is not
known is incomplete:

struct incomplete {
int foo;
struct bar something;
};

Yes, your internal representation is wrong, and that is a delusion.
until the compiler encounters a definition of "struct bar", struct
incomplete is precisely that, and cannot be used to define anything.

Unless struct bar is already a complete type at the point where it is
mentioned in the above definition, it is a constraint violation (6.7.2.1p2).

Here are three canonical examples of incomplete types:

struct bar; /* Declares an incomplete type. */
extern int array[]; /* Declares an object with an incomplete type.*/
void *p; /* Defines an object with a complete type, that is a pointer to
an object with an incomplete type. */
 
U

Uncle Steve

In many cases, the word "claim" is used to indicate that you do not accept
a statement. For instance, if you said "I'm a pretty smart guy" and I said
"Uncle Steve claims to be a pretty smart guy", there is an implication that
I disbelieve it. (Note: Purely an example; I do not disbelieve that claim.)

Language lawyering... Hmm. Dictionary.com does not seem to contain a
definition that supports your imputation of the pejorative.

2.) Assert that one has gained or achieved (something)

If someone claims something it does not require that they use the word
'claim' in order to make the assertion. If you say that I claim
something, that does not perforce disbelief. You are merely
stating that I made a claim of some sort, and without further
qualification we cannot know whether you accept it or not. It is in
fact a mundane statement of fact (that there is a claim).
At one point, Shao made some remark to me which used the word "claim" in
that way and I pointed out that this was, IMHO, rude. (I plonked him
quite a while back for being, so far as I could tell, not interested
in sincere discussions of technical issues. Nothing I've seen quoted since
has changed my mind.)

It's so hard to tell, though. More than half of of the Usenet
population seems to be concerned primarily with argumentation for the
sake of argumentation, and the nominal topic du jour is merely an
excuse to engage in the activity. But as many of them are clever
enough to affect sincerity it is not always obvious who the defective
trolls really are.
So he's probably pointing out that you challenged something I said, and
that I have a history of being touchy about things. The distinction being,
I don't expect people to take my word on technical claims, just on claims
about who I am or what I do. :) Technical claims are by nature subject
to dispute or argumentation.

Ha, well on Usenet nothing can be taken for granted. What say you do
and who you say you are is always subject to verification. "Trust,
but verify' is the necessary heuristic when there are so many rabid
FUD-spinners on the 'net. Not all of them present as loons either.
Many of them have mastered the art of faking normality for short
stretches at a time, and so Eternal Vigilance is the necessary rule.

As I'm new here I'll reserve my opinions on individual posters for the
time being.



Regards,

Uncle Steve
 
U

Uncle Steve

Doesn't that specify when and how a cast occurs in expression
evaluation?

No. That quotation is from here[n1256.pdf:6.2.5p1]. The slashes I
injected into the quoted text were intended to denote italics in the
original. For italics in the original, we have that "... Other terms
are defined where they appear in /italic/ type or on the left side of a
syntax rule. ..."[n1256.pdf:3p1] *

The full paragraph (from a draft version) is more compelling:

6.2.5 Types

#1

The meaning of a value stored in an object or returned by a
function is determined by the type of the expression used to access
it. (An identifier declared to be an object is the simplest such
expression; the type is specified in the declaration of the
identifier.) Types are partitioned into object types (types that
describe objects), function types (types that describe functions),
and incomplete types (types that describe objects but lack
information needed to determine their sizes).
As far as I know, a cast occurs when one uses something like:

(type)expression

in code. Perhaps you were thinking of "conversion" rather than "cast"?


Just out of curiosity, do you ever override the 'return' keyword with a
function-like macro? That's one possible reason for wrapping return
values with parentheses and it'd be interesting to get a sense of how
many people have that habit for that intention.

It's a cognitive semantic quirk. Return kind-of looks like it is a
special function that transfers control to the original caller, and so
I put return values in parenthesis because that's what you do with
real functions. I know it's not a function, but try to tell that to
my brain.
...

long v = function();

So what I gather from the quoted text is that the return value of
function() is promoted to long before the assignment occurs. Is that
correct?

Actually, I don't think so. I think that "...the value of the right
operand is converted to the type of the assignment expression and
replaces the value stored in the object designated by the left
operand."[n1256.pdf:6.5.16.1p2]

But anyway, in regards to "user defined types," because that doesn't
appear in a standard of C (or does it?), I think it's possible that it's
a subject where discussion can be challenging by having different
understandings of the term for different people.

Indeed, and as real technical specifications typically use language
with more precision than colloquial written English, there is plenty
of room for misunderstanding between those who have differing degrees
of understanding.
I _think_ that what you have offered is congruent with "you, the
programmer, don't get to invent/specify a type which is not a derived type."

I _think_ what at least one other poster has offered is congruent with
"if you, the programmer, specify a derived type, then that type could be
called a youser^H^H^H^H^H^Huser-defined type, since _something_ defined it."

Or maybe not.

I was mostly taking issue with the idea that a derived type is
qualitatively equivalent to a /de-novo/ basic type, which was the
implication given in the way some were using the term 'type' in their
statements -- by my reckoning of their meaning. When the word 'type'
is used with precision similar to a word like 'run' ("the computer
runs"), it is difficult to be entirely sure what is really meant.
* I hope to some day purchase some actual standards. The efforts that
have gone into them can certainly save me some time == money while
attempting to write portable C. :)

Actual printed copies are fairly expensive from what I gather.
Available from Global Engineering Documents, as per the C-FAQ, section
5.2.



Regards,

Uncle Steve
 
U

Uncle Steve

No. It's really too bad that I didn't have the request not to quote
that in the very same post, as it seems you didn't read it in time not
to quote the post. Thus it has already begun to back-fire. Hopefully
that's the end of it.

I was merely offering some advice which was intended to be friendly, as
your prolonged argument/discussion with a particular poster triggered a
memory.

Possibly it would be better to use email in such cases, but that is a
minor quibble.
Even worse than "claim" is "invent". Obviously you are free to use or
discard any advice. :)

True enough.



Regards,

Uncle Steve
 
K

Keith Thompson

jacob navia said:
What bothers me is that I get never a discussion about the main
argumentation of my posts but always

"Just a quibble" and the we start discussing about small details
that are just that: details

Details are important. If I don't comment on your main point,
it's usually because I don't have much to say about it.

You've got perhaps a dozen people commenting on your posts, and
nearly 7 billion saying nothing at all -- but you choose to complain
about the ones who've actually taken the time to comment.

If you want me to spend my time discussing what you're interested
in, we can discuss consulting rates.
What do you think of the two definitions
(1) Functional (a type is an algorithm description)
(2) Conceptual (A type embodies a concept in the program)

And I think I am missing one:

(3) A type implies a set of operations allowed with it.

This would be different than (1) since (1) refers to calculating
the "value" stored in a type.

I am sure (3) is missing but can't really identify it yet as
definitely distinct of (1).

The idea of a type being an algorithm doesn't seem quite right, even
after reading some of the followups in this thread. There might be any
number of algorithms associated with a type. I think you're thinking of
an algorithm whose end result tells us the meaning of a value, but I'd
say it's that meaning, rather than the method used to determine it,
that's significant.

The standard's definition of "type" is probably a decent starting point.
C99 6.2.5p1:

The meaning of a value stored in an object or returned by a
function is determined by the *type* of the expression used
to access it. (An identifier declared to be an object is
the simplest such expression; the type is specified in the
declaration of the identifier.)

Like many of the standard's "definitions", it's not really a definition.
It's a statement about types, not a statement of exactly what the word
"type" means.

For a tutorial, I might write something like:

A stored value consists of a collection of bits. A *type*
is what determines the meaning of that collection of bits.

For example, a 32-bit object containing the bits:
1000000010010010000111111011011
might represent the integer value 1078530011, the floating-point
value 3.1415927 (approximately), or a pointer value, depending
on the type of the object.
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,023
Latest member
websitedesig25

Latest Threads

Top