Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #158441 > unrolled thread
| Started by | T <T@invalid.invalid> |
|---|---|
| First post | 2021-01-18 17:01 -0800 |
| Last post | 2021-01-25 21:20 -0800 |
| Articles | 13 on this page of 33 — 9 participants |
Back to article view | Back to comp.lang.c
I need help understand a struct T <T@invalid.invalid> - 2021-01-18 17:01 -0800
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-19 01:37 +0000
Re: I need help understand a struct T <T@invalid.invalid> - 2021-01-18 17:41 -0800
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-19 01:52 +0000
Re: I need help understand a struct T <T@invalid.invalid> - 2021-01-18 18:03 -0800
Re: I need help understand a struct Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-01-18 19:00 -0800
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-19 11:34 +0000
Re: I need help understand a struct Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2021-01-19 04:18 -0800
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-19 13:01 +0000
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-19 21:39 +0000
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-19 23:20 +0000
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-20 00:25 +0000
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-20 12:42 +0000
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-20 17:39 +0000
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-20 19:32 +0000
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-20 20:45 +0000
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-21 16:12 +0000
Re: I need help understand a struct Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-01-21 17:10 +0000
Re: I need help understand a struct Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2021-01-21 11:51 -0800
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-22 12:26 +0000
Re: I need help understand a struct Kaz Kylheku <563-365-8930@kylheku.com> - 2021-01-21 20:16 +0000
Re: I need help understand a struct Bart <bc@freeuk.com> - 2021-01-21 23:04 +0000
Re: I need help understand a struct "jfbod...@gmail.com" <jfbode1029@gmail.com> - 2021-01-25 11:59 -0800
Re: I need help understand a struct Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-01-25 21:18 -0800
Re: I need help understand a struct Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-01-27 07:53 -0800
Re: I need help understand a struct Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-01-27 08:35 -0800
Re: I need help understand a struct Tim Rentsch <tr.17687@z991.linuxsc.com> - 2021-01-28 07:48 -0800
Re: I need help understand a struct Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-01-28 09:25 -0800
Re: I need help understand a struct Kaz Kylheku <563-365-8930@kylheku.com> - 2021-01-28 18:03 +0000
Re: I need help understand a struct Guillaume <message@bottle.org> - 2021-01-28 20:46 +0100
Re: I need help understand a struct Kaz Kylheku <563-365-8930@kylheku.com> - 2021-01-28 21:31 +0000
Re: I need help understand a struct Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-01-29 11:15 -0800
Re: I need help understand a struct T <T@invalid.invalid> - 2021-01-25 21:20 -0800
Page 2 of 2 — ← Prev page 1 [2]
| From | Kaz Kylheku <563-365-8930@kylheku.com> |
|---|---|
| Date | 2021-01-21 20:16 +0000 |
| Message-ID | <20210121115930.474@kylheku.com> |
| In reply to | #158522 |
On 2021-01-21, Bart <bc@freeuk.com> wrote:
> On 20/01/2021 20:45, Ben Bacarisse wrote:
>> Bart <bc@freeuk.com> writes:
>
>>> You might remember that Algol 68 was perceived as being 'difficult',
>>> backed up by complex ideas.
>>
>> The way the grammar was specified was considered difficult and turned
>> out to be something of a flop. I don't think the actual language was
>> considered difficult.
>
> The syntax itself wasn't that hard as a finished result, if you avoided
> the special grammar they tried to use.
>
> But for me the difficulty was in not having high enough confidence in
> knowing exactly was going on in a bit of code, not helped by all those
> implicit conversions (rowing, proceduring etc).
>
>>
>>> My job was to take the parts I liked, such as the syntax, especially
>>> for type denotations, and use that as the basis for a simple systems
>>> language.
>>>
>>> Since I could never figure out exactly what any bit of Algol68 was
>>> supposed to do, I had to provide simpler, more obvious semantics.
>>
>> i.e. different semantics. As I said, your ref is not so much like Algol
>> 68's REF as it is like C's *. Your ref is almost exactly C's *.
>
> Probably. This would have been over 10 years before I had any dealings
> with C. Apart from ASM, my experience with pointers had been with
> Pascal. Remember this is for a systems language.
>
>>> I would say however that Algol68 REFs /are/ pointers,
>>
>> That would just confuse people.
>
> Look at what REFs are used for (outside of their being needed in that
> language to implement variables); that role will done by pointers in
> languages only having raw pointers.
>
> It doesn't matter, because no one here
>> is going to learn Algol 68 anytime soon, but you do this with C as all.
>> You paint C as more complex than it really is because you insist on
>> using incorrect descriptions.
>>
>
> C makes nearly everything more complicated than it need be:
C is complicated to master to the point that you can crank out
a reliable product.
Almost the points you mention simplify the implementation, though.
> * Type declarations
C is easy to parse in a one-pass, recursive descent, including
the declarations.
The declarations have some saving graces, like declaration mimicking
use.
> * Line continuation (see that other thread)
Simple to implement by a completely self-contained state machine
which handles only line continuations and nothing else.
Very easy to understand in the code. Whenever a physical line ends with
a backslash, regardless of the syntax, that is a line continuation.
"regardless of syntax" is easier to grok than "depending on syntactic
context".
> * Pointless extra namespaces (tags and labels)
Easy to implement. On entry into a function, the parser can create a
dictionary just for goto labels. When looking up a label in this
dictionary, if an object is found, that object is guaranteed to be a
goto label, not requiring to be subject to further checks.
Easy to use: no annoying clashes between a label called foo and
an int variable called foo.
No ambiguity: labels are function wide, not having to follow block
scope, and so you can't have two labels called foo at different levels
of nesting, which shadow each other, causing confusion in following the
goto graph.
> * Index arrays OR pointers
Easy to implement with like three or four lines of Yacc code
to recognize expr [ expr ] and turn it into the same AST node
as * ( expr + expr ).
> * Extern names that are also exported ...
Easy to implement; agnostic of various linkage models.
> * ... and in general, allowing multiple declarations of the same name
> outside of functions, with special rules as to whether disimilar types
> and attributes are compatible or not
Actually, no: dissimilar types are not compatible, which is simple.
Compatibility between types in separate translation units has rules, but
not any rules that lead to code in the compiler: just to undefined
behavior when the rules are broken.
> * Structs with or without a tag, with or without a typedef name,
Trivial parser case: is struct followed by a { token or an identifier?
This could be separate rules in a Yacc-like tool. The one without
the identifier generates a unique symbol and makes that the tag.
The typedef facility is an easy-to-understand type name aliasing
facility completely orthogonal to structs. Orthogonality is simple.
Typedefs are basically "hard links" to type nodes.
> declared separately or in situ (C will allow most of those 8
> combinations; I allow exactly one)
>
> * Nested header files (I mean, includes used in place of an import
> feature), with arbitrary combinations of absolute and relative file
> paths at each level
There is no reason to disallow a header file from containing an #include
that reads another file. It's just straightforward recursion.
The traditional treatment (not required in ISO C) that #include "...."
looks in the same directory as the file which contains that #include
is a very good design decision that helps with program organization.
> * /Needing/ some subset of 29 standard headers in every module, for the
> most basic language features
Easy to implement: vendor just deposits some files in an include
directory, which can be maintained independently of compiler work.
> * 20-28 different ways of specifying a 64-bit int (the extra 8 are when
> long is the same size as long long), not including an infinite number of
> ways of applying 'const'
That is wrong, a signed 64 bit int, since C99 and <inttypes.h>, is
specified in exactly one way: int64_t.
The long type is not a specification for a 64 bit int, even if it
happens to be one; in a situation when we need a 64 bit int, we don't
reach for long.
> * Print format codes (especially when using int64_t etc)
Library matter, except for situations when the compiler diagnoses
bad format strings. Granted, C99 PRIXXX macros are atrocious.
> * Having to use scanf to read user input
I hope you don't mean interactive input; that should never be done.
scanf, and more particularly sscanf, can occasionally be a very handy
and pretty accurate tool. E.g. if you had to match a YYYY-MM-DD date
using only ISO C, and extract the parts as integers, sscanf will do it
nicely, including rejection of bad inputs.
> * The rules that determine whether the result of a binary op with two
> integer types are signed or unsigned
The situation is pretty ugly here with the promotions and conversions
and whatnot, and a source of programming errors.
> I think you would claim this stuff isn't complicated because with each
> one, you can explain exactly why it is so. I say it is complicated
> because it is unnecessary. People have enough trouble learning C.
The trouble is that people have trouble learning C, but they don't
*know* they are having trouble because they are having a jolly
easy time learning a whole lot of misconceptions.
--
TXR Programming Language: http://nongnu.org/txr
[toc] | [prev] | [next] | [standalone]
| From | Bart <bc@freeuk.com> |
|---|---|
| Date | 2021-01-21 23:04 +0000 |
| Message-ID | <eMnOH.623993$IbZ9.529190@fx10.ams4> |
| In reply to | #158528 |
On 21/01/2021 20:16, Kaz Kylheku wrote:
> On 2021-01-21, Bart <bc@freeuk.com> wrote:
>> C makes nearly everything more complicated than it need be:
>
> C is complicated to master to the point that you can crank out
> a reliable product.
A lot of the 'features' make it easier to write wrong code, including
undetectable errors.
> Almost the points you mention simplify the implementation, though.
>
>> * Type declarations
>
> C is easy to parse in a one-pass, recursive descent, including
> the declarations.
The reason it is easier is that usually things have to be defined before
use, and context is used while parsing to determine for example if a new
identifier is a type.
C types ARE harder to parse when written in C's inside-out convoluted
style, which may or may not be wrapped around a name, although not
impossible to do; just following the grammar makes some things easier.
The complications I'm talking about however are to do with learning it
and writing it and reading it.
>
> The declarations have some saving graces, like declaration mimicking
> use.
That's not a saving grace.
>> * Line continuation (see that other thread)
>
> Simple to implement by a completely self-contained state machine
> which handles only line continuations and nothing else.
Yes, that means having an entirely separate pass over the source code,
which is how I added it. But in my case it was optional.
I didn't want to waste time doing a parse that most of the time is
pointless. Without that pass, dealing with token-based line continuation
(rather than character-based), is zero-cost as it is done on-demand.
> Very easy to understand in the code. Whenever a physical line ends with
> a backslash, regardless of the syntax, that is a line continuation.
>
> "regardless of syntax" is easier to grok than "depending on syntactic
> context".
Except it doesn't work:
#define M abc \
def \ // comment
ghi \
jkl
This goes wrong; you can't have comments the right side of the \
character. And neither can you have // comments on the left side, as the
\ means the comment continues onto the next line:
#define M abc \
def // comment \
ghi \
jkl
Here, M expands to 'abc def', not the expected 'abc def ghi jkl'.
>> * Pointless extra namespaces (tags and labels)
> Easy to use: no annoying clashes between a label called foo and
> an int variable called foo.
What about all the clashes between extern functions, variables, and
typedefs in the same block all called foo?
> No ambiguity: labels are function wide, not having to follow block
> scope, and so you can't have two labels called foo at different levels
> of nesting, which shadow each other, causing confusion in following the
> goto graph.
Namespaces are little to do with block scope. You'd just make labels
belong to the outermost block in a function.
But you really think this /simplifies/ parsing and name resolution? My
languages ONE function-wide scope for everything; /that/'s simple!
>> * Index arrays OR pointers
>
> Easy to implement with like three or four lines of Yacc code
> to recognize expr [ expr ] and turn it into the same AST node
> as * ( expr + expr ).
Yes, but it's wrong!
You declare a pointer to an array where you have to access an element
using (*A)[i] - deref first then index.
But get it wrong, index first then deref: *A[i], and the language can't
help you. Plus it gets horribly confusing with an extra dimension, when
it fact, looking at other people's code, you can't tell if they intended
using 2D arrays, or 1D arrays, because the outer * can be intepreted and
used either way.
>> * Extern names that are also exported ...
>
> Easy to implement; agnostic of various linkage models.
>
>> * ... and in general, allowing multiple declarations of the same name
>> outside of functions, with special rules as to whether disimilar types
>> and attributes are compatible or not
>
> Actually, no: dissimilar types are not compatible, which is simple.
This is the baggage each name has:
- static or not
- extern or not
- initialised or not
- for functions, types and names of parameters and a return type
- for variables, a type
With the last two, now you have to start looking for compatible types.
So int[5] is not compatible with int[6], but both are compatible with int[].
It's a mess. If I write:
extern int abc;
static int abc;
gcc reports an error. But if I write:
static int abc;
extern int abc;
then it's fine!
In my current language, an entity is defined, program-wide, exactly
once. The need to check for compatible declarations never arises. THAT
is simple!
> The typedef facility is an easy-to-understand type name aliasing
> facility completely orthogonal to structs. Orthogonality is simple.
> Typedefs are basically "hard links" to type nodes.
It's not easy. And is full of hairy corner cases that few know about.
For example that you can write this:
newint typedef int;
or typedef an actual function, not just a function pointer:
typedef int F(int,int);
> There is no reason to disallow a header file from containing an #include
> that reads another file. It's just straightforward recursion.
>
> The traditional treatment (not required in ISO C) that #include "...."
> looks in the same directory as the file which contains that #include
> is a very good design decision that helps with program organization.
Where does it look if it can't find it? You have stack nested include
paths, plus a set of paths the compiler has been told to look for
includes; where will it look next?
>> * /Needing/ some subset of 29 standard headers in every module, for the
>> most basic language features
>
> Easy to implement: vendor just deposits some files in an include
> directory, which can be maintained independently of compiler work.
You need an include directory. (My C compiler mitigates that by
incorporating the 40 or system headers within the executable,
simplifying matters greatly. However most will have issues with
installation directories and environment variables and getting versions
mixed up.)
My comment however was about the user having to write a dozen #includes
for basic features.
>> * 20-28 different ways of specifying a 64-bit int (the extra 8 are when
>> long is the same size as long long), not including an infinite number of
>> ways of applying 'const'
>
> That is wrong, a signed 64 bit int, since C99 and <inttypes.h>, is
> specified in exactly one way: int64_t.
So long long int etc, and long on systems where that is 64 bits, are
illegal? See: https://github.com/sal55/langs/blob/master/ctypes.md
And how would you print an int64_t type, since the language itself still
only knows about int, long and long long?
More here about the subject of /compiling/ C:
"What Makes C Hard To Compile":
https://github.com/sal55/langs/blob/master/compilec.md
The language is in my view sloppy and badly defined (eg. the number of
{} around initialisation data, or what the possibilities are with
designated initisers).
It seems to leave most of the responsibility of deciding whether a 'C'
program is correct or not (ie. fail or pass a compilation), up to the
implementation, which in many compilers leaves it up to compiler
options, which themselves are up to the user.
So effectively, people can choose themselves whether their program is
legal or not!
(I've had generated code of 40,000 lines result in either 0 warnings and
errors, or 28,000 lines' worth with a different set of options.
Was there anything wrong with my program or not? I'd rather the language
decided, not me!)
> The long type is not a specification for a 64 bit int, even if it
> happens to be one; in a situation when we need a 64 bit int, we don't
> reach for long.
>
>> * Print format codes (especially when using int64_t etc)
>
> Library matter, except for situations when the compiler diagnoses
> bad format strings. Granted, C99 PRIXXX macros are atrocious.
>
>> * Having to use scanf to read user input
>
> I hope you don't mean interactive input; that should never be done.
Remember this is about making things complicated for the user. In my
everyday language, a systems one like C, I do interactive input like this:
print "?"
readln a,b,c
println a,b,c
One run is:
?10,20 30
10 20 30
(Notice one separator is ",", the other is " ".) What are the types of
a,b,c? It doesn't matter; they can be floats or ints, the language takes
care of it.
Again, that's what I call simple!
[toc] | [prev] | [next] | [standalone]
| From | "jfbod...@gmail.com" <jfbode1029@gmail.com> |
|---|---|
| Date | 2021-01-25 11:59 -0800 |
| Message-ID | <478ca6d1-babd-47b4-b9f5-09fba105d9f6n@googlegroups.com> |
| In reply to | #158441 |
Yeah, late to the party, as usual.
On Monday, January 18, 2021 at 7:01:20 PM UTC-6, T wrote:
> Hi All,
>
>
> https://gitlab.freedesktop.org/xorg/app/xclipboard/-/blob/master/xclipboard.c
>
> 66: typedef struct _Clip {
> 67: struct _Clip *next, *prev;
> 68: char *clip;
> 69: char *filename;
> 70: size_t avail;
> 71: } ClipRec, *ClipPtr;
>
> Okay now I am confused. What??? Yes, AGAIN!
>
> 66 defines a structure called "_Clip". So far
> so good
It's a bit more complicated than that.
Lines 66 through 71 define the type "struct _Clip" and then introduce two typedef
names (aliases) for the struct type and a pointer type. The following code is
equivalent, and hopefully makes clearer what's actually happening:
/**
* The following lines create the actual struct type
*/
struct _Clip {
struct _Clip *next, *prev;
char *clip;
char *filename;
size_t avail;
};
/**
* The following lines create typedef names, or aliases,
* for the struct type and a pointer to that type
*/
typedef struct _Clip ClipRec;
typedef struct _Clip *ClipPtr;
Thus, you can either use "struct _Clip" or "ClipRec" to declare an *instance*
of the struct type:
struct _Clip clip1;
ClipRec clip2;
And you can either use "struct _Clip *" or "ClipPtr" declare a pointer to
the struct type:
struct _Clip *ptr1;
ClipPtr ptr2; // note no * operator in the declaration, since ClipPtr is an alias for a pointer type
Using a typedef to "hide" the pointerness of a type almost always leads to heartburn
in my experience. I hate the practice.
>
> 67 look like it is doing it again inside
> the original definition. Is this a structure
> embedded in another structure reusing the
> same name ????
It's declaring two pointers to different instances of the same type - based on the
naming convention, this type represents a node in a linked list, and the
"prev" member points to the list node preceding this one, and the "next" member
points to the list node following.
In this case we have to use "struct _Clip *" instead of "ClipPtr"
to declare the pointers because the "ClipPtr" typedef name doesn't
exist yet. You could get around it by forward-declaring the
struct tag:
struct _Clip; // forward declaration of the tag name, type is *incomplete* at this point
typedef struct _Clip *ClipPtr; // you can create typedef names for incomplete types
struct _Clip {
ClipPtr prev, next;
...
};
>
> 68 & 69 look like they a declaring pointers to
> a character. Are not pointers usually
> int64's?
No, they are not. Pointers are not integers - they do not behave like integers,
they do not have integer semantics. Likewise integers do not have pointer
semantics.
Pointer values *may* be represented as 64-bit integer values, but they don't have
to be. Pointers to different types may have different representations from each
other.
>
> 70 What is an "avail"? Some kind of structure?
size_t is an unsigned integer type - this is obviously a counter of some
kind.
>
> 71 looks like it is returning two somethings
> from the structure definition. Is this a
> function or a structure or both?
It's a structure definition, along with two typedef name
definitions.
>
> Yours in confusion,
> -T
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2021-01-25 21:18 -0800 |
| Message-ID | <ruo8na$742$1@dont-email.me> |
| In reply to | #158610 |
On 1/25/2021 11:59 AM, jfbod...@gmail.com wrote:
>
> In this case we have to use "struct _Clip *" instead of "ClipPtr"
> to declare the pointers because the "ClipPtr" typedef name doesn't
> exist yet. You could get around it by forward-declaring the
> struct tag:
>
> struct _Clip; // forward declaration of the tag name, type is *incomplete* at this point
>
> typedef struct _Clip *ClipPtr; // you can create typedef names for incomplete types
>
> struct _Clip {
> ClipPtr prev, next;
> ...
> };
>
To be pedantic, the first declaration (the "forward declaration") is
redundant in this example. There no need to forward-declare the type.
You could simply do
typedef struct _Clip *ClipPtr;
and this by itself would be sufficient. In this case the mere mention of
a yet-unknown type `struct _Clip` inside a typedef-declaration would
provide a "forward declaration" for it (with a file scope) and at the
same time declare an alias for a pointer type.
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2021-01-27 07:53 -0800 |
| Message-ID | <86ft2ml6js.fsf@linuxsc.com> |
| In reply to | #158624 |
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
> On 1/25/2021 11:59 AM, jfbod...@gmail.com wrote:
>
>> In this case we have to use "struct _Clip *" instead of "ClipPtr"
>> to declare the pointers because the "ClipPtr" typedef name doesn't
>> exist yet. You could get around it by forward-declaring the
>> struct tag:
>>
>> struct _Clip; // forward declaration of the tag name,
>> // type is *incomplete* at this point
>>
>> typedef struct _Clip *ClipPtr; // you can create typedef names
>> // for incomplete types
>>
>> struct _Clip {
>> ClipPtr prev, next;
>> ...
>> };
>
> To be pedantic, the first declaration (the "forward declaration") is
> redundant in this example. There no need to forward-declare the
> type. You could simply do
>
> typedef struct _Clip *ClipPtr;
>
> and this by itself would be sufficient. [...]
Yes but....
This pattern is a dangerous habit to acquire because there are
other cases where it does make a difference. IMO what the rules
are here is one of the backwaters of C that is best to avoid, and
an easy way to avoid it is to always separate the declaration of
a struct type from the typedef that depends on the struct. If
that feels cumbersome then it's easy to write a macro so as not
to have to re-write the boilerplate.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2021-01-27 08:35 -0800 |
| Message-ID | <rus4op$d18$1@dont-email.me> |
| In reply to | #158650 |
On 1/27/2021 7:53 AM, Tim Rentsch wrote:
> Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
>
>> On 1/25/2021 11:59 AM, jfbod...@gmail.com wrote:
>>
>>> In this case we have to use "struct _Clip *" instead of "ClipPtr"
>>> to declare the pointers because the "ClipPtr" typedef name doesn't
>>> exist yet. You could get around it by forward-declaring the
>>> struct tag:
>>>
>>> struct _Clip; // forward declaration of the tag name,
>>> // type is *incomplete* at this point
>>>
>>> typedef struct _Clip *ClipPtr; // you can create typedef names
>>> // for incomplete types
>>>
>>> struct _Clip {
>>> ClipPtr prev, next;
>>> ...
>>> };
>>
>> To be pedantic, the first declaration (the "forward declaration") is
>> redundant in this example. There no need to forward-declare the
>> type. You could simply do
>>
>> typedef struct _Clip *ClipPtr;
>>
>> and this by itself would be sufficient. [...]
>
> Yes but....
>
> This pattern is a dangerous habit to acquire because there are
> other cases where it does make a difference. IMO what the rules
> are here is one of the backwaters of C that is best to avoid, and
> an easy way to avoid it is to always separate the declaration of
> a struct type from the typedef that depends on the struct. If
> that feels cumbersome then it's easy to write a macro so as not
> to have to re-write the boilerplate.
Um... No.
Firstly, my remark above is not intended as a stylistic advice to "avoid
standalone forward declarations altogether since you can piggyback them
on something else". My remark above is merely intended to demonstrate a
fairly curious language feature, which can indeed be dangerous to an
inexperienced user. In C a mere mention of `struct T` type specifier by
itself in virtually any context serves as a forward declaration of that
type in the given scope. Some of these contexts can be rather
surprising, like arguments of `sizeof` or casts. (Plus the peculiar
behavior in function declarations and definitions). This is just a bit
of language trivia.
Secondly, from the stylistic point of view: when a forward declaration
is necessary, I am all for forward-declaring types deliberately and
explicitly (instead of relying on byproducts of other declarations).
However, for a user who generally follows the popular typedef idiom
(i.e. "give each `struct` type a compact typedef alias") combining the
forward declaration and the `typedef` into a single declaration is a
perfectly safe and elegant _idiomatic_ way to kill both birds with one
stone. This is an example of where brevity is a virtue.
(And the aforementioned typedef idiom is a very good idiom to follow
specifically because of the aggressively self-declaring behavior of
full-blown `struct T` type specifiers, even if the compilers are
supposed to catch most of the accompanying errors.)
Relying on some random "first" declaration to introduce a new `struct`
type is indeed a dangerous pattern, which is many cases will not do what
an inexperienced user intended it to do - I'd agree with this point. But
using the `typedef struct T T` idiom as a unified method of
forward-declaring a new `struct` type _and_ assigning it a compact
typedef alias is not only perfectly fine, but is actually highly
recommended.
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2021-01-28 07:48 -0800 |
| Message-ID | <86h7n1jc5j.fsf@linuxsc.com> |
| In reply to | #158659 |
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
> On 1/27/2021 7:53 AM, Tim Rentsch wrote:
>
>> Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
>>
>>> On 1/25/2021 11:59 AM, jfbod...@gmail.com wrote:
>>>
>>>> In this case we have to use "struct _Clip *" instead of "ClipPtr"
>>>> to declare the pointers because the "ClipPtr" typedef name doesn't
>>>> exist yet. You could get around it by forward-declaring the
>>>> struct tag:
>>>>
>>>> struct _Clip; // forward declaration of the tag name,
>>>> // type is *incomplete* at this point
>>>>
>>>> typedef struct _Clip *ClipPtr; // you can create typedef names
>>>> // for incomplete types
>>>>
>>>> struct _Clip {
>>>> ClipPtr prev, next;
>>>> ...
>>>> };
>>>
>>> To be pedantic, the first declaration (the "forward declaration") is
>>> redundant in this example. There no need to forward-declare the
>>> type. You could simply do
>>>
>>> typedef struct _Clip *ClipPtr;
>>>
>>> and this by itself would be sufficient. [...]
>>
>> Yes but....
>>
>> This pattern is a dangerous habit to acquire because there are
>> other cases where it does make a difference. IMO what the rules
>> are here is one of the backwaters of C that is best to avoid, and
>> an easy way to avoid it is to always separate the declaration of
>> a struct type from the typedef that depends on the struct. If
>> that feels cumbersome then it's easy to write a macro so as not
>> to have to re-write the boilerplate.
>
> Um... No.
>
> Firstly, my remark above is not intended as a stylistic advice to
> "avoid standalone forward declarations altogether since you can
> piggyback them on something else".
No, I didn't think it was.
> My remark above is merely intended
> to demonstrate a fairly curious language feature, which can indeed be
> dangerous to an inexperienced user. In C a mere mention of `struct T`
> type specifier by itself in virtually any context serves as a forward
> declaration of that type in the given scope. Some of these contexts
> can be rather surprising, like arguments of `sizeof` or casts. (Plus
> the peculiar behavior in function declarations and definitions). This
> is just a bit of language trivia.
>
> Secondly, from the stylistic point of view: when a forward declaration
> is necessary, I am all for forward-declaring types deliberately and
> explicitly (instead of relying on byproducts of other
> declarations).
Yes, I pretty much took that as a given.
> However, for a user who generally follows the popular
> typedef idiom (i.e. "give each `struct` type a compact typedef alias")
> combining the forward declaration and the `typedef` into a single
> declaration is a perfectly safe and elegant _idiomatic_ way to kill
> both birds with one stone. This is an example of where brevity is a
> virtue.
My point is that it is NOT _perfectly_ safe, and there are
situations where
typedef struct clip_struct *ClipPtr;
and
struct clip_struct;
typedef struct clip_struct *ClipPtr;
do different things (and when they do, it's more likely that the
second meaning is what was intended). Granted, such cases don't
occur very often, but that only makes them more insidious when
they /do/ occur. I daresay most C programmers aren't aware of
this anomaly; certainly I wasn't before becoming sensitized to
it some years back when reading a discussion in this newsgroup.
> (And the aforementioned typedef idiom is a very good idiom to follow
> specifically because of the aggressively self-declaring behavior of
> full-blown `struct T` type specifiers, even if the compilers are
> supposed to catch most of the accompanying errors.)
>
> Relying on some random "first" declaration to introduce a new `struct`
> type is indeed a dangerous pattern, which is many cases will not do
> what an inexperienced user intended it to do - I'd agree with this
> point.
That isn't what I was suggesting. Explicit forward declarations
are unequivocally better than "accidental" forward declarations.
> But using the `typedef struct T T` idiom as a unified method of
> forward-declaring a new `struct` type _and_ assigning it a compact
> typedef alias is not only perfectly fine, but is actually highly
> recommended.
The recommendation should come with a caveat that there are
situations where it doesn't do the right thing, and to be
absolutely safe the pattern
struct whatever_tag;
typedef struct whatever_tag <typename-declarator>;
should be used instead (or perhaps always, if someone doesn't
want to be bothered with learning what the odd cases are).
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2021-01-28 09:25 -0800 |
| Message-ID | <ruus23$e95$1@dont-email.me> |
| In reply to | #158685 |
On 1/28/2021 7:48 AM, Tim Rentsch wrote: > > My point is that it is NOT _perfectly_ safe, and there are > situations where > > typedef struct clip_struct *ClipPtr; > > and > > struct clip_struct; > typedef struct clip_struct *ClipPtr; > > do different things (and when they do, it's more likely that the > second meaning is what was intended). Granted, such cases don't > occur very often, but that only makes them more insidious when > they /do/ occur. I daresay most C programmers aren't aware of > this anomaly; certainly I wasn't before becoming sensitized to > it some years back when reading a discussion in this newsgroup. > Well, that wouldn't be complete without an actual example of at least one such situation, would it? -- Best regards, Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Kaz Kylheku <563-365-8930@kylheku.com> |
|---|---|
| Date | 2021-01-28 18:03 +0000 |
| Message-ID | <20210128091216.48@kylheku.com> |
| In reply to | #158685 |
On 2021-01-28, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
> My point is that it is NOT _perfectly_ safe, and there are
> situations where
>
> typedef struct clip_struct *ClipPtr;
>
> and
>
> struct clip_struct;
> typedef struct clip_struct *ClipPtr;
The declaration "struct clip_struct;" has a special status: it declares
a new type in a block scope, shadowing any like-named tag in an other
scope.
A "struct clip_struct" embedded in the declaration of some other
identifier does not do that; it refers to the outer tag, if it exists.
E.g.
struct foo { int x; };
void foo()
{
//struct foo;
typedef struct foo *ptr;
struct foo x;
}
If we uncomment "struct foo;", then a diagnostic is required; struct foo
becomes incomplete and so x cannot be defined.
A vague analogy can be made:
int x;
void foo()
{
// this is like "typedef struct foo ...;"
extern int x; // inherited x
}
void foo()
{
// this is like "struct foo;"
int x; // new, local x
}
I can't think of any issues at file scope though, where we most often
declare incomplete structures and pointers thereto.
The standard draws attention to this issue with an example of a pair of
mutually-referential structures s1 and s2 declared in a block scope. It
recommends the forward declaration "struct s2;" to be placed ahead of
them, so that the first declaration doesn't accidentally refer to an
inherited "struct s2" from an outer scope.
--
TXR Programming Language: http://nongnu.org/txr
[toc] | [prev] | [next] | [standalone]
| From | Guillaume <message@bottle.org> |
|---|---|
| Date | 2021-01-28 20:46 +0100 |
| Message-ID | <ruv4b7$11ev$1@gioia.aioe.org> |
| In reply to | #158694 |
Le 28/01/2021 à 19:03, Kaz Kylheku a écrit :
> (...)
> A "struct clip_struct" embedded in the declaration of some other
> identifier does not do that; it refers to the outer tag, if it exists.
>
> E.g.
>
> struct foo { int x; };
>
> void foo()
> {
> //struct foo;
> typedef struct foo *ptr;
> struct foo x;
> }
>
> If we uncomment "struct foo;", then a diagnostic is required; struct foo
> becomes incomplete and so x cannot be defined.
Yeah. This is a C quirk, the ability to define 'local' data types being
per se questionable IMHO.
But here, as with variables, shadowing a given identifier this way is
usually considered bad practice. This is part of the CERT-C
recommendantions:
https://wiki.sei.cmu.edu/confluence/display/c/DCL01-C.+Do+not+reuse+variable+names+in+subscopes
https://wiki.sei.cmu.edu/confluence/display/c/DCL23-C.+Guarantee+that+mutually+visible+identifiers+are+unique
[toc] | [prev] | [next] | [standalone]
| From | Kaz Kylheku <563-365-8930@kylheku.com> |
|---|---|
| Date | 2021-01-28 21:31 +0000 |
| Message-ID | <20210128132228.931@kylheku.com> |
| In reply to | #158701 |
On 2021-01-28, Guillaume <message@bottle.org> wrote:
> Le 28/01/2021 à 19:03, Kaz Kylheku a écrit :
>> (...)
>> A "struct clip_struct" embedded in the declaration of some other
>> identifier does not do that; it refers to the outer tag, if it exists.
>>
>> E.g.
>>
>> struct foo { int x; };
>>
>> void foo()
>> {
>> //struct foo;
>> typedef struct foo *ptr;
>> struct foo x;
>> }
>>
>> If we uncomment "struct foo;", then a diagnostic is required; struct foo
>> becomes incomplete and so x cannot be defined.
>
> Yeah. This is a C quirk, the ability to define 'local' data types being
> per se questionable IMHO.
>
> But here, as with variables, shadowing a given identifier this way is
> usually considered bad practice.
Unfortunately, GCC's -Wshadow doesn't cover this case.
If a bad practice is not covered by a compiler diagnostic, it happens.
It can happen by accident, and you don't know.
> This is part of the CERT-C
> recommendantions:
>
> https://wiki.sei.cmu.edu/confluence/display/c/DCL01-C.+Do+not+reuse+variable+names+in+subscopes
This rule is only practical because we can get compilers to enforce it.
--
TXR Programming Language: http://nongnu.org/txr
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2021-01-29 11:15 -0800 |
| Message-ID | <rv1mt6$5t8$1@dont-email.me> |
| In reply to | #158701 |
On 1/28/2021 11:46 AM, Guillaume wrote: > > But here, as with variables, shadowing a given identifier this way is > usually considered bad practice. > recommendantions: > It is a bad practice when done deliberately, which is usually a consequence of someone being lazy with variable names. But on the other hand, the whole scoping/shadowing mechanism is actually designed to _prevent_ problems caused by inadvertent collisions of unrelated names in nested scopes. And it works well in that role. Frankly, I have never seen it leading to real-life problems (if we exclude from consideration mistakes made by total newbies). -- Best regards, Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | T <T@invalid.invalid> |
|---|---|
| Date | 2021-01-25 21:20 -0800 |
| Message-ID | <ruo8qv$72e$1@dont-email.me> |
| In reply to | #158610 |
On 1/25/21 11:59 AM, jfbod...@gmail.com wrote:
> Yeah, late to the party, as usual.
>
> On Monday, January 18, 2021 at 7:01:20 PM UTC-6, T wrote:
>> Hi All,
>>
>>
>> https://gitlab.freedesktop.org/xorg/app/xclipboard/-/blob/master/xclipboard.c
>>
>> 66: typedef struct _Clip {
>> 67: struct _Clip *next, *prev;
>> 68: char *clip;
>> 69: char *filename;
>> 70: size_t avail;
>> 71: } ClipRec, *ClipPtr;
>>
>> Okay now I am confused. What??? Yes, AGAIN!
>>
>> 66 defines a structure called "_Clip". So far
>> so good
>
> It's a bit more complicated than that.
>
> Lines 66 through 71 define the type "struct _Clip" and then introduce two typedef
> names (aliases) for the struct type and a pointer type. The following code is
> equivalent, and hopefully makes clearer what's actually happening:
>
> /**
> * The following lines create the actual struct type
> */
> struct _Clip {
> struct _Clip *next, *prev;
> char *clip;
> char *filename;
> size_t avail;
> };
>
> /**
> * The following lines create typedef names, or aliases,
> * for the struct type and a pointer to that type
> */
> typedef struct _Clip ClipRec;
> typedef struct _Clip *ClipPtr;
>
> Thus, you can either use "struct _Clip" or "ClipRec" to declare an *instance*
> of the struct type:
>
> struct _Clip clip1;
> ClipRec clip2;
>
> And you can either use "struct _Clip *" or "ClipPtr" declare a pointer to
> the struct type:
>
> struct _Clip *ptr1;
> ClipPtr ptr2; // note no * operator in the declaration, since ClipPtr is an alias for a pointer type
>
> Using a typedef to "hide" the pointerness of a type almost always leads to heartburn
> in my experience. I hate the practice.
>
>>
>> 67 look like it is doing it again inside
>> the original definition. Is this a structure
>> embedded in another structure reusing the
>> same name ????
>
> It's declaring two pointers to different instances of the same type - based on the
> naming convention, this type represents a node in a linked list, and the
> "prev" member points to the list node preceding this one, and the "next" member
> points to the list node following.
>
> In this case we have to use "struct _Clip *" instead of "ClipPtr"
> to declare the pointers because the "ClipPtr" typedef name doesn't
> exist yet. You could get around it by forward-declaring the
> struct tag:
>
> struct _Clip; // forward declaration of the tag name, type is *incomplete* at this point
>
> typedef struct _Clip *ClipPtr; // you can create typedef names for incomplete types
>
> struct _Clip {
> ClipPtr prev, next;
> ...
> };
>
>>
>> 68 & 69 look like they a declaring pointers to
>> a character. Are not pointers usually
>> int64's?
>
> No, they are not. Pointers are not integers - they do not behave like integers,
> they do not have integer semantics. Likewise integers do not have pointer
> semantics.
>
> Pointer values *may* be represented as 64-bit integer values, but they don't have
> to be. Pointers to different types may have different representations from each
> other.
>
>>
>> 70 What is an "avail"? Some kind of structure?
>
> size_t is an unsigned integer type - this is obviously a counter of some
> kind.
>
>>
>> 71 looks like it is returning two somethings
>> from the structure definition. Is this a
>> function or a structure or both?
>
> It's a structure definition, along with two typedef name
> definitions.
>
>>
>> Yours in confusion,
>> -T
Thank you!
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | comp.lang.c
csiph-web