C and its many quirks

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
Does anyone here ever use, or advocate this kind of contrivance:

Code:
typedef struct {
    int age;
    int height;
} Person[3];
See here. I'm asking mainly about the concept of a typedef being an array.
 

Papabravo

Joined Feb 24, 2006
22,084
Does anyone here ever use, or advocate this kind of contrivance:

Code:
typedef struct {
    int age;
    int height;
} Person[3];
See here. I'm asking mainly about the concept of a typedef being an array.
That does not make much sense. It looks like you are trying to do two things at once. Do you have a compiler that will swallow this construct?
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
That does not make much sense. It looks like you are trying to do two things at once. Do you have a compiler that will swallow this construct?
Sure, it seems totally legal C, it compiles fine, instances of the type 'Person' are a 3 element array:

1674417074135.png
 

WBahn

Joined Mar 31, 2012
32,890
Does anyone here ever use, or advocate this kind of contrivance:

Code:
typedef struct {
    int age;
    int height;
} Person[3];
See here. I'm asking mainly about the concept of a typedef being an array.
What's the problem with it?

Is it the ability to typedef something as an array of structures that is bothering you?

Does the ability to typedef something, say a double, as an array also cause heartache?

For instance, being able to typedef a complex number as a 2D array of doubles, or typedef a position in space as a 3D array of doubles?

Code:
typedef double Complex[2];
typedef double Coord3D[3];
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
What's the problem with it?

Is it the ability to typedef something as an array of structures that is bothering you?

Does the ability to typedef something, say a double, as an array also cause heartache?

For instance, being able to typedef a complex number as a 2D array of doubles, or typedef a position in space as a 3D array of doubles?

Code:
typedef double Complex[2];
typedef double Coord3D[3];
The question has come up as a matter of language design, I'm simply looking at what opinions come up, not sure if I should adopt the concept or not, in a new language I'm working on.
 

WBahn

Joined Mar 31, 2012
32,890
The question has come up as a matter of language design, I'm simply looking at what opinions come up, not sure if I should adopt the concept or not, in a new language I'm working on.
To a certain degree, it depends on how orthogonal you want your language to be.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
Also it's syntax is odd, it has an array bounds specifier after the type name but ordinarily in C we put the bounds specifier after the name of the variable we're declaring.

We can't do this for example

Code:
uint8_t[4] counters;
So it seems inconsistent.
 

WBahn

Joined Mar 31, 2012
32,890
Also it's syntax is odd, it has an array bounds specifier after the type name but ordinarily in C we put the bounds specifier after the name of the variable we're declaring.

We can't do this for example

Code:
uint8_t[4] counters;
So it seems inconsistent.
Actually, the fact that you can't do that it what lends consistency to it.

Consider:

typedef double MYARRAY[4];

MYARRAY myarray1, myarray2;

The 'MYARRAY' is replaced by everything in the typedef statement between the typedef and the name of the new type. Everything after the name of the new type is appended to each declared variable.

So this becomes:

double myarray1[4], myarray2[4];

This is completely consistent with the simpler use case:

typedef double MYDOUBLE;

MYDOUBLE mydouble1, mydouble2;

becomes

double mydouble1, mydouble2;

If you want some inconsistency, look at typedefs of pointers.

typedef double* MYDBLPTR;

MYDBLPTR dblptr1, dblptr2;

This does NOT become

double* dblptr1, dblptr2;

but rather

double *dbltptr1, *dbltptr2;

The usual handwaving that is done to make this go away is to claim that typedefs are applied independently to each variable that is declared as if they were on separate lines.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
Actually, the fact that you can't do that it what lends consistency to it.

Consider:

typedef double MYARRAY[4];

MYARRAY myarray1, myarray2;

The 'MYARRAY' is replaced by everything in the typedef statement between the typedef and the name of the new type. Everything after the name of the new type is appended to each declared variable.

So this becomes:

double myarray1[4], myarray2[4];

This is completely consistent with the simpler use case:

typedef double MYDOUBLE;

MYDOUBLE mydouble1, mydouble2;

becomes

double mydouble1, mydouble2;

If you want some inconsistency, look at typedefs of pointers.

typedef double* MYDBLPTR;

MYDBLPTR dblptr1, dblptr2;

This does NOT become

double* dblptr1, dblptr2;

but rather

double *dbltptr1, *dbltptr2;

The usual handwaving that is done to make this go away is to claim that typedefs are applied independently to each variable that is declared as if they were on separate lines.
Well you raise some reasonable points I guess. But look at this again:

Code:
typedef struct {
    int age;
    int height;
} Person[3];
How does one declare a single instance of the underlying struct? or an array of two of them? One could argue "well that's the prerogative of the developer" but the problem vanishes if we can never create "array typedefs" in the first place. We'd be forced to only ever be able to define single struct types and thereafter declare them as arrays.
 

WBahn

Joined Mar 31, 2012
32,890
Well you raise some reasonable points I guess. But look at this again:

Code:
typedef struct {
    int age;
    int height;
} Person[3];
How does one declare a single instance of the underlying struct? or an array of two of them? One could argue "well that's the prerogative of the developer" but the problem vanishes if we can never create "array typedefs" in the first place. We'd be forced to only ever be able to define single struct types and thereafter declare them as arrays.
But why would you want to?

The whole idea is you are creating a new data type, in this case Person, that is defined to be an array of three of those structures. That's all you are doing. You are NOT defining a structure of Person and then typedef'ing it to be an array of three of those.

The example is a poor one because it has no intuitive use case.

But again consider the example I gave of typedef'ing a 3D point. How does one declare a single instance of the underlying double? Or an array of two of them? You DON'T! The typedef defined an new type that consists of an array of three doubles. Period.

Perhaps a better example might have been something like:

Code:
typedef struct {
    double x;
    double y;
} Triangle[3];
Personally, I would have broken this apart and defined the underlying structure as it's own entity, say POINT, and then used that to build up a TRIANGLE type. But that is because it's natural to view a point as being a more generic entity that can be used to build up more things than just triangles. But I can easily envision that cases exist in which the underlying structure doesn't have a more general use and the object being defined simply consists of a fixed size number of them.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
But why would you want to?

The whole idea is you are creating a new data type, in this case Person, that is defined to be an array of three of those structures. That's all you are doing. You are NOT defining a structure of Person and then typedef'ing it to be an array of three of those.

The example is a poor one because it has no intuitive use case.

But again consider the example I gave of typedef'ing a 3D point. How does one declare a single instance of the underlying double? Or an array of two of them? You DON'T! The typedef defined an new type that consists of an array of three doubles. Period.

Perhaps a better example might have been something like:

Code:
typedef struct {
    double x;
    double y;
} Triangle[3];
Personally, I would have broken this apart and defined the underlying structure as it's own entity, say POINT, and then used that to build up a TRIANGLE type. But that is because it's natural to view a point as being a more generic entity that can be used to build up more things than just triangles. But I can easily envision that cases exist in which the underlying structure doesn't have a more general use and the object being defined simply consists of a fixed size number of them.
Well as to "But why would you want to?" Well I might be asked to, told to! I might have a new problem, a change to make to the code that is best made by me creating an array of five 'Person' structs. The fact is I cannot do so, the language makes it hard.

I must either change the original typedef and then change very single place that it is used OR I must literally copy/paste the struct layout into a new typedef that I might call "Individual". Either of these options is the antithesis of good design, the language itself has allowed us (perhaps unwittingly) to get into this messy situation.

If the data structure was much more complex too, you might see my point better, as it stands its tiny, almost trivial.

Each of the two options is very bad.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
Now regarding your triangle example, it can still be elegantly coded, if the ability to create "array typedefs" did not exist it would be easy to do what you want still, you'd just do it differently:

Code:
typedef struct {
    double x;
    double y;
} Coordinate2D;

typedef struct {
   Coordinate2D points[3];
} Triangle;

typedef struct
   Coordinate2D points[4];
} Square;
etc.
 

WBahn

Joined Mar 31, 2012
32,890
Now regarding your triangle example, it can still be elegantly coded, if the ability to create "array typedefs" did not exist it would be easy to do what you want still, you'd just do it differently:

Code:
typedef struct {
    double x;
    double y;
} Coordinate2D;

typedef struct {
   Coordinate2D points[3];
} Triangle;

typedef struct
   Coordinate2D points[4];
} Square;
etc.
If you want to do it this way, then do it this way.

What you are basically saying is that your idea of "good" language design is that you force everyone to do everything exactly the one way that YOU think it should be done and that it is simply not possible that any one using your language could possibly have any reason for ever wanting to do it any other way than the one way that you have decreed that it must be done.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
If you want to do it this way, then do it this way.

What you are basically saying is that your idea of "good" language design is that you force everyone to do everything exactly the one way that YOU think it should be done and that it is simply not possible that any one using your language could possibly have any reason for ever wanting to do it any other way than the one way that you have decreed that it must be done.
No Sir, with all due respect I did not say that at all.

My position is that the capability to define a "typedef array" like the Person[3] example, is pointless, it offers nothing to the developer that he can't already do readily. Furthermore it carries the hidden cost that I've expounded upon already.

This is nothing whatsoever to do with "my idea of a good language" or "forcing everyone to do what I think should be done". I'm not suggesting C be changed or that anyone do anything differently.

If you can demonstrate any material, tangible negative consequence that would arise if C did not permit this, then I'd like to hear of it, I'm asking - why should I include this capability in a new language that also allows types, aliases to be defined.

I'm designing a new language and this is called doing my due diligence.
 

WBahn

Joined Mar 31, 2012
32,890
The same could be said for C having three kinds of loops when there is nothing that can't readily be done with just a while() loop (or any of the three, for that matter).

If you feel it is pointless, then don't include it in your language. But, in doing so, at least acknowledge that you ARE forcing people to conform to how YOU think they must write their code.

There's nothing intrinsically wrong with this -- and every language has to make those same decisions.

There are philosophical advantages to only being able to do things one way in a language. But there are also advantages to being able to do things in different ways to give the programmer options in deciding what constructs best match their logic or their approach to a problem. It's a balancing act.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
The same could be said for C having three kinds of loops when there is nothing that can't readily be done with just a while() loop (or any of the three, for that matter).

If you feel it is pointless, then don't include it in your language. But, in doing so, at least acknowledge that you ARE forcing people to conform to how YOU think they must write their code.

There's nothing intrinsically wrong with this -- and every language has to make those same decisions.

There are philosophical advantages to only being able to do things one way in a language. But there are also advantages to being able to do things in different ways to give the programmer options in deciding what constructs best match their logic or their approach to a problem. It's a balancing act.
I have challenged you to demonstrate any material, tangible negative consequence that would arise if C did not permit this specific capability.

If a capability offers less benefit than cost then that is a reasonable way to decide these matters, wouldn't you agree? This is not a matter of what I "feel" or "forcing" people to do anything!

It is seeking an objective justification to include/exclude some potential capability, I'm doing so on a cost/benefit basis.

If you were devising a new programming language, would you allow this or not? and what reason can you give for you decision?
 

WBahn

Joined Mar 31, 2012
32,890
I've already mentioned that by not allowing it, you decrease the orthogonality of the language. Whether that is a negative depends on how valuable you believe orthogonality to be. I am not going to spend a bunch of my time trying to imagine every possible way that every programmer might choose to use a particular construct searching for one that would somehow satisfy you that not being able to do it the way that they choice would constitute some material, negative consequence. If you don't want to allow it in your language, then don't allow it.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
I've already mentioned that by not allowing it, you decrease the orthogonality of the language. Whether that is a negative depends on how valuable you believe orthogonality to be. I am not going to spend a bunch of my time trying to imagine every possible way that every programmer might choose to use a particular construct searching for one that would somehow satisfy you that not being able to do it the way that they choice would constitute some material, negative consequence. If you don't want to allow it in your language, then don't allow it.
By the way, shouldn't your signature text read:

"There are 11 types of people in the world: those who understand binary, those who don't, and those who can work in any number base".
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
I'm starting to develop a strong distaste for function's that don't take any arguments. Many languages permit this not just C, but the more I ponder it the less I like the idea.

If a language did not support this, if a function always needed at least one argument, then this would make more sense. So in C we'd force the user to use this instead

Code:
void get_date (date_ptr);
Any function that takes no argument must always be - in principle - doing some kind of "get" anyway. Mathematically speaking too a function is a mapping from one set to another, mapping an empty set to a non-empty set seems absurd, a function is always a function of something.
 
Top