Non-constant constant strings

K

Keith Thompson

BartC said:
A nice feature I came across (I think from the poster known as 'BGB') was a
form of include where the text in the included file was inserted as a string
constant. So if the text in the file was:

one
two
three

then including that file would be equivalent to:

"one\ntwo\nthree\n"

in the source code. (Obviously in C it would need to be allowed inside an
expression.)

#include directives are already allowed inside expressions, though they
still don't have a way to convert the input file content to a string
literal.

For example:

$ cat hello.inc
"Hello, world"
$ cat hello.c
#include <stdio.h>
int main(void) {
puts(
#include "hello.inc"
);
}
$ gcc hello.c -o hello && ./hello
Hello, world
$

(Note that I've named the file with a ".inc" rather than ".h" suffix to
make it clear that it's not an ordinary header file.)
 
S

Seebs

See, and I think that such an "allowance" is patently absurd and should not be a part of any language. :)

Why? It's really convenient as a way to eliminate a gratuitous special
case.

-s
 
B

BartC

Keith Thompson said:
#include directives are already allowed inside expressions, though they
still don't have a way to convert the input file content to a string
literal.

Sure, I forgot you can have a term of an expression on its own line and so
make it possible to insert a # directive.
For example:

$ cat hello.inc
"Hello, world"
$ cat hello.c
#include <stdio.h>
int main(void) {
puts(
#include "hello.inc"
);
}
$ gcc hello.c -o hello && ./hello
Hello, world
$

It seems a fine distinction, but not requiring the text in the included file
to be quoted (ie. written out as one or a series of valid C string constants
complete with escape codes) means text from any source can be used, which
can be created/edited by anyone with any text editor. That was the advantage
that struck me.

Although such an include wouldn't work recursively. This means a C source
file can include a string constant containing the entire C source file
itself!

(I've just spent ten minutes implementing such a feature, to show it's
workable. However it possible, even with C, to read a file into a string
using a single function call (in C, you have write the function first). So
it's mainly useful where large, statically allocated string constants are
needed. As in the OP's requirement perhaps.)
 
R

Rick C. Hodgin

C string literals are intended to be *constant*.
They are most often intended to be constant, but not always. There are
many cases where developers allocate something with an initial value, but
then alter it at runtime.
char defaultOption[] = "4";
In this case, the default option is 4 until the user changes it. It's a
constant bit of text, but is not constant. :)

No, not at all. `defaultOption' (either version) is not a
constant, but a variable. It has an initial value, that's all.
You can change the variable's value with one of

That was my point. In my char* list[] = { ... } case, I want initial values, but not constants. I type them in the same way, but they have different meanings.
In none of these cases is there any need to change the value
of a constant, nor any reason to want to do so.

I don't want to change the value of a constant. By definition one should not be able to do that. :)

What I want is a way to change the value of my variable, the one defined and stored as a literal string in source code, one that I desire to be a variable, yet the one being interpreted by the compiler as a constant instead of a variable with no apparent override available to direct it where I wouldlike it to go even though those members in attendance were content to allow such an ability through common extensions (nearly all of which were laterdropped it seems).

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

Did you miss the part about "Those members ... were content?"

Not at all. I presumed "those members" involved in the ANSI authorizing were a small number, power-seeking representatives of the entire C language developer base of us "little people," while the much larger group (developers who were coding in C in general) contained many developers, and it was many of them who screamed "WHAT!" and then passed out.
At that time I was a developer with not quite twenty years'
worth of C experience, and I neither screamed nor thudded. YMMV.
MMDV.
Quoth the Rationale:
"String literals are not required to be modifiable. This
specification allows implementations to share copies of
strings with identical text, to place string literals in
[...] Those members of the C89 Committee who insisted that
string literals should be modifiable were content to have
this practice designated a common extension [...]"
The word "common" being used very loosely there. LOL! :)
See Appendix J.

Appendix J of what?

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

char defaultOption = "4";
Did you mean "char *defaultOption" or "char defaultOption[]" rather than
"char defaultOptions", or did you mean '4' rather than "4"?

I meant char defaultOption[] = "4";
I would like to be able to specify that with a const prefix, as in this
type of syntax:

char* list[] =
{
"foo1",
const "foo2",
"foo3"
}

In this case, I do not want the second element to be changed, but the
first and third... they can change.

If I were to suggest a new language feature to support that, I'd want an
explicit marker for a string that I *do* want to be able to change.

I realize that. My view, of course, differs. :)
In your proposed C-like language, what would this snippet print?
for (int i = 0; i < 2; i ++) {
char *s = "hello";
if (i == 0) {
s[0] = 'H';
}
puts(s);
}

Interesting.

FWIW, I don't believe in defining variables in this way in C. I believe an
initialization block should exist so it is being done explicitly, both for
documentation purposes, and clarity in reading the source code (it's very
easy to miss a nested declaration when a group of variables is created of
a similar type.

In my proposed language, it would print "Hello" both times because the
char* s definition would've been pulled out of the loop and defined as a
function variable. If the user wanted independent copies each iteration
he would have to create them and manage them himself, which I think
documents the condition of the code more explicitly, making it far
easier for someone to see what's going on rather than inferring from
language peculiarities which may not be consistent across compiler
versions (they probably are in this case, but I've seen other similar
conditions which vary between Visual C++ and GCC).

I would rewrite your function to look like this (because I believe it
should behave this way):

void foo(void)
{
int i;
char* s; // Note I use the D language syntax of keeping the pointer
// symbol near the type, rather than the variable.

// Initialization
s = "hello";

// Code
for (i = 0; i < 2; i ++)
{
// I introduce predicates, which logically operate more like this:
if (i == 0) s[0] = 'H';

// Displays "Hello" both times
puts(s);
}
}

To mimic your functionality, I would code this way:

const char gcHello[] = "hello";

void foo(void)
{
int i;
char s[7];


// Initialization (a compiler switch would make this automatic)
memset(s, 0, sizeof(s)); // Here it's done manually

// Code
for (i = 0; i < 2; i ++)
{
// Iterative re/initialization
memcpy(s, gcHello, -2); // My memcpy would support a p3 of -1 to
// automatically perform strlen() on p3,
// and -2 would be strlen()+1.

// First pass conversion only
if (i == 0) s[0] = 'H';

// Displays "Hello" then "hello"
puts(s);
}
}
In C as it's currently defined, the string literal "hello" corresponds
to an anonymous array object with static storage duration; attempting to
modify it has undefined behavior. As I understand it, you want to
remove the second part of that. The above code has one occurrence of a
string literal, but it's being used in the initializer for two distinct
objects. On the second iteration, does s point to a string with
contents "hello" or "Hello"?

Either interpretation is problematic.

Exactly. So, you don't code that way. :)

You make everything a function-level variable and it's done. You make
all code items read/write unless they are explicitly prefixed with a
const or have some macro wrapper like _rw("foo") or _fo("foo") to
explicitly name them.
I fail to see how this argues for modifiable string literals.

Not just string literals, but a separation of the "before" and the "after."
Programming today is, by default, targeted at multiple CPUs. There are
functions which run top-down, but on the whole we are creating
multi-threaded congruent code execution engines running on commensurate
hardware. The time for a new language syntax is at hand.

I propose new extensions to C in general:

in (thread_name) {
// Do some code in this thread
} and in (other_thread_name) {
// Do some code in this thread
}

And a new tjoin keyword to join threads before continuing:
tjoin this, thread_name, other_thread_name


flow name {
flowto name;

always before {
}

always after {
}

function name {
}

subflow name {
}

error name {
}

} // end flow

And I propose the new concept of a cask, a "pill" that is inserted anywhere
in code to do whatever I like, along with explicit cask definitions that do
certain things based upon called functions which can convey branch logic
upon returning.

Casks look like this (|sample|) and they would operate like this:

Traditional code:
if (some_test(1, 2, 3)) {
// Do something
}

In this case:
push 3
push 2
push 1
call some_test
compare result to 0
if not equal, enter the block

The ability to insert a cask does not alter program logic:
if ( (|cask1|) some_test( (|cask2|) 1, 2, (|cask3|) 3) {
// Do something
}

In this case:
push 3
call cask3
push 2
push 1
call cask2
call some_test
call cask1
compare result from some_test to 0
if not equal, enter the block

The casks are called functions which can be inserted at any point without
otherwise affecting program logic (hence their new shape). I wrote it with
spaces above to make it more clear, but it could be coded like this:
if ((|cask1|)some_test((|cask2|)1, 2, (|cask3|)3)

And I have other ideas. You can read about them on this page. This page
specifically relates to extensions to Visual FoxPro, but my intention is
my RDC (Rapid Development Compiler) which is C-like, but relaxes a lot of
stringent errors in C reporting them only as warnings, such as pointer-to-
pointer conversions, allowing for them to be perfectly valid, and many
other changes as well.

http://www.visual-freepro.org/wiki/index.php/VXB++

Each of these add-ons should be language-level first class citizens which are known to the language and allow for modern CPU architectures with various new data types and volatile extensions which operate around explicitly semaphored access at the language level, along with certain optimization constraints and allowances (as per the developer's dictates, even of the kind which can override "safety" on variable use -- meaning that a particular case could violate atomicity and the compiler knows it, but the compiler is a tool and should allow what the developer dictates because the developer is a person and has real authority).

My opinion. My goals. :)
As a language design issue, I *strongly* disagree with this.
Personally, I like the idea of making everything read-only unless you
explicitly say you want to be able to modify it. (Obviously C isn't
defined this way; equally obviously, this is merely my own opinion.)

I look at computers as being exactly that: computers. They compute. Their
purpose is to take input, process it, and store output. That storage portion
automatically means write abilities, and the input combination means
read-write as it is more common to operate through a chain of processing
where a recently computed and written value is then quickly used thereafter
as input to a follow-on computation.

Everything should be read-write unless explicitly stated as read-only. My
opinion, and my position in any languages I author. :)
BTW, your _c("text") macro would still have to be defined somehow;
a new kind of string literal would probably make more sense.

I think string literals should all use double-quotes, but it should be a
different double-quote character from ASCII-34, and one for left and one
for right, so they can be nested and mate up as parenthesis. I also
believe variables should be allowed to have spaces, but it should be a
different space character than ASCII-32. In my implementation of RDC,
Visual FreePro, my virtual machine, I will introduce ASCII characters which
explicitly serve these purposes at the developer level, allowing for double-
quoted characters to be used as raw input without first escaping them. I
will also allow other explicit ASCII characters between the new double quote
characters in their raw single-symbol form without escaping.

We're past the days of limited abilities. We have GUIs now that can draw any
icon, any character ... it's time to grow up. :)

There are a lot of things which were done badly in the early days of
programming. Many rigid constraints which make difficult-to-read-and-understand source code. The concept of a header, for example, is
no longer required when 16GB of memory is $700 or less and will only
get cheaper over time.

It's time to rethink what's been thought, and undo the damage that's been
done, to look to the current and future needs of multi-core, parallel-thread,
object-oriented design, yet with all of it having an explicit base in the raw
compute needs of the machine. C is ideal for that. C++ takes the idea of
object orientation too far.

In C++, foo->function(). The better syntax is foo.function() (for both
pointers to variables, and declared variables), and such is a mild
extension of the existing struct:

struct SFoo
{
void function(void);

int member_variable;
}

SFoo foo1;
SFoo* foo2 = malloc(SFoo); // Compiler is smart enough to know
foo1.function();
foo2.function(); // Same syntax

No default constructor. No default destructor. Each component must be
explicitly coded and executed in code if needed ... thereby documenting it
without obfuscation, and allowing a straight-forward merging of structures
through multiple inheritance. My opinion.
The bottom line is that standard C cannot, and IMHO should not, cater to
every obscure coding practice. A language can have:
1. mutable string literals;
2. immutable string literals; or
3. both, with distinct syntax.

C has chosen option 2, and it has served us well. I would not strongly
object to option 3, but I'm not convinced that it would be worth the
extra complexity. You're welcome to push for option 1, but don't expect
to succeed.

I don't expect to get anywhere trying to change anything in C. :) It's why
I'm moving to my own language. I hit the GCC group a while back asking for
the self() extension, which allows recursion to the current function without
it being explicitly named or even explicitly populated with parameters. They
said it was not a good idea. I asked for something else (can't remember what)
and they said the same. So ... it was fuel for me to get started on my own..

:)

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

Seems to me that when most people do this, or at least when I do,
I substitute the copy just before writing it out. You need a loop
that goes through and writes out the lines, inside that loop copy
each line to a line buffer, modify as appropriate, then write
it out.

[snip]

Very little work to write.


Still requires that I write some code, and maintain some code, and have a function that leaves allocated memory blocks hanging around (inviting leaks unless I code to handle them all properly all of the time). It's more than"very little work" to do this when, in the alternative, the very definition of the thing you'd be copying in your example would be utilized in my example.

The compiler option removes all issues.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

I don't think so. Back in the late 80s, when I was just starting to
learn C, I was aware that if you had two string literals, and one was
the same characters as the tail end of the other, the compiler might
use the same storage for both. It's really easy to obtain a modifiable
string if I want one, so I don't expect literals to be modifiable, or
indeed, even to occur in code or storage anywhere if they don't really
have to.


When most people encounter something they are in "learning how it works"
mode. They read and study and come to understand the system. However,
some people look at things differently desiring to understand the philosophy
of why something works. This gives them a different perspective than the
user, as they are more akin to a type of author, desiring to peer into the
inner workings and perform mental optimizations upon the design.

In my experience there are about 20% authors and 80% users in the developer
community. That number falls somewhat to 5% to 10% authors and 90% to 95%
users in certain types of developer houses (more long-term maintenance of
established applications, rather than new shops which are writing new apps).

My personal experience. YMMV.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

Why? It's really convenient as a way to eliminate a gratuitous special
case.


Why do we have "void function(void)" when "function()" would work sufficiently at that level in a source file? It's explicitly so we convey that no mistake was given as by accidentally leaving out some parameters. We declare void to indicate "I purposefully intended to leave this empty, there are no return variables, there are no parameters," and so on.

It's the same here. "Oh, another comma ... was the developer finished? Was there supposed to be more? What is missing? What was left out? Please .... I need answers. I'm left hanging in a state of confusion that is disrupting my soul. Whatever do I do?"

Nobody needs that kind of stress in their life. :)

Best regards,
Rick C. Hodgin
 
E

Eric Sosman

[...]
Quoth the Rationale:
"String literals are not required to be modifiable. This
specification allows implementations to share copies of
strings with identical text, to place string literals in
[...] Those members of the C89 Committee who insisted that
string literals should be modifiable were content to have
this practice designated a common extension [...]"
The word "common" being used very loosely there. LOL! :)
See Appendix J.

Appendix J of what?

Sorry; since you've been suggesting changes to the C language,
I supposed without justification that you were familiar with its
defining document. The appendix mentioned is in "ISO/IEC 9899:2011
Programming Language C," and is entitled "Portability Issues." The
relevant part is "J.5 Common Extensions," and the clause describing
the particular matter that upsets you is "J.5.5 Writeable String
Literals."
 
J

James Kuyper

I doubt it - having lived through that period, I don't remember anyone
objecting to that decision. The biggest objections I've seen have been
in the other direction: string literals should have the type "const
char[n]", as they do in C++. As a result, they would automatically
convert to "const char*" in most contexts. If that were the case, most
code that accidentally tries to write to them would be a constraint
violation, requiring a diagnostic, which would make it a bit easier to
write correct code.
Not at all. I presumed "those members" involved in the ANSI authorizing were a small number, power-seeking representatives of the entire C language developer base of us "little people," while the much larger group (developers who were coding in C in general) contained many developers, and it was many of them who screamed "WHAT!" and then passed out.

"Those members" refers to the people on the committee "who insisted that
string literals should be modifiable". You paint an amazingly nasty
picture of those who agreed with your point of view on this issue.

The ANSI committee includes both people who implement C and people who
use C. I'm not sure exactly how many of each were present at, but I
believe that the implementors were actually less numerous, they
certainly were not in absolute control of the proceedings.
 
J

James Kuyper

My current solution to do something along these lines during
initialization:
char* sourceCode[] =
{
"if (foo[9999] == 0) {\r\n",
" // Do something\r\n",
"} else if (foo[9999] == 1) {\r\n",
" // Do something else\r\n",
"} else {\r\n",
" // Do some other things\r\n", "}",
null
};

int i, len;
char* ptr;
for (i = 0; list != null; i++)
{
len = strlen(list) + 1;
ptr = (char*)malloc(len); memcpy(ptr, list[0], len);
list[0] = ptr;
}


And what, exactly, is wrong with the basic principle of this approach?

I personally would have done something like this:
char *read_only[] = { "Rick", "Jane", "Marc", 0 };
char **read_write;

char **init_readwrite(char **readonly) {
unsigned int i, count;
char **readwrite;

for (count=0, i=0; readonly; i++) {
count++;
}
readwrite = malloc(count * sizeof(*readwrite));
/* no check */
return memcpy(read_write, read_only, count * sizeof(*readwrite));
}

read_write = init_readwrite(read_only);

...And then you operate on read_write and ignore read_only.


read_only is an array of pointers; the things that the pointers point at
are not modifiable. It should therefore, for safety, have been declared
as "const char*[]".

You allocate enough space to copy over all of the pointers to
read_write. Then you do copy them over. The new pointers in read_write
still point at the same locations as the ones in read_only; those
locations still cannot be safely written to, so nothing has been gained
by the copy. It is therefore incorrectly named.

That's why you need to create a deep copy, as in Rick's code. It copies
the strings themselves to memory that it guaranteed writable.
 
J

James Kuyper

I personally would have done something like this:
char *read_only[] = { "Rick", "Jane", "Marc", 0 };
char **read_write;

char **init_readwrite(char **readonly) {
unsigned int i, count;
char **readwrite;

for (count=0, i=0; readonly; i++) {
count++;
}
readwrite = malloc(count * sizeof(*readwrite));
/* no check */
return memcpy(read_write, read_only, count * sizeof(*readwrite));
}
read_write = init_readwrite(read_only);

...And then you operate on read_write and ignore read_only.


I have not gone through this deeply or tried it in code, but I'm

thinking the theory of this solution would not work in all cases (and
that this particular implementation also will not work).
Since each read_only[] pointer is to a constant string, and the
compiler creates the entry in read-only memory, it could optimize and
allow lines like "red" and "fred" to be mapped to the same four byte
area, one pointing to "f" and one pointing to "r" after "f". ...

That is quite true, but precisely because it is a statement about
pointers, it's not actually relevant.
... So making a
bulk copy would not copy all things properly in all cases.

This code just copies the pointers themselves, not the objects that they
point to, so the fact that the objects could be overlapping is not a
problem. The fact that it only copies the pointers IS a problem.

It would be possible to do this with a bulk copy only if there were some
way to ensure that all of the strings were stored in adjacent pieces of
memory:

char *read_only =
"if (something[9999])\r\n\0"
"{\r\n\0"
" // Do something\r\n\0"
"} else {\r\n\0"
" // Do something else\r\n\0"
"}";

Read that line carefully. Instead of having six separate string
literals, it has only a single string literal, containing six separate
non-overlapping strings, the first five terminated by explicit null
characters. This is done by the "magic" of string literal concatenation:
two consecutive string literals separated only by white space are
automatically merged into a single string.
You could do a bulk copy of those string literals, but the tricky part
would be figuring out how much space is needed. sizeof(read_only) gives
the size of the pointer. strlen(read_only) gives the length of the first
string, so neither is suitable. You'd have to iterate over all six
strings to find their total length, and you'll have to count them
manually, there's no way to use C constructs to determine that length
for you.

Much simpler would be the following:

char read_write[] =
"if (something[9999])\r\n\0"
"{\r\n\0"
" // Do something\r\n\0"
"} else {\r\n\0"
" // Do something else\r\n\0"
"}";

// Count the strings
int strings=0;
for(char *ptr = read_write; ptr < read_write + sizeof read_write;
ptr++)
if(*ptr = '\0')
strings++;

// Set up an array of pointers to the strings.
char **rw_ptrs = malloc(strings * sizeof *rw_ptrs);
if(rw_ptrs)
{
char *ptr = read_write;
for(int str = 0; str < strings; str++)
{
rw_ptrs[str] = ptr;
while(*ptr++); // move past end of string
}
}
 
J

James Kuyper

char* sourceCode[] =
{
"if (foo[9999]) {\r\n",
" // Do something\r\n",
"} else {\r\n",
" // Do something else\r\n",
"}\r\n"
};

This isn't relevant to your question, but why do you have explicit
"\r\n" line endings? If your program reads and/or writes the
source code in text mode (or if you're on a UNIX-like system),
line endings are marked by a single '\n' character, regardless of
the format used by the operating system.

To be consistent with the source file input I'm processing. Without using both \r and \n it gives warnings when opening the files that the line endings are not consistent.

Do you open the input file and the output file in binary mode or text
mode? If you opened both in text mode, that shouldn't be happening,
unless you're reading an input file that has line endings that are
inconsistent with the conventions for your platform (for instance,
reading a file following Dos/Windows conventions on a Unix-like system).
 
R

Rick C. Hodgin

Do you open the input file and the output file in binary mode or text
mode? If you opened both in text mode, that shouldn't be happening,
unless you're reading an input file that has line endings that are
inconsistent with the conventions for your platform (for instance,
reading a file following Dos/Windows conventions on a Unix-like system).

Quoted as Mr. Miyagi speaking, "Binary. Always open binary." :)

It is code for a Windows system and it uses two character line endings.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

Did you miss the part about "Those members ... were content?"
"Those members" refers to the people on the committee "who insisted that
string literals should be modifiable".

Agreed. They represent a small number of the C developer base, however, who probably later read that it was added only as a "common extension."
You paint an amazingly nasty picture of those who agreed with your
point of view on this issue.

Well I apologize. I was only going for humor, not a literal conveyance of
what might have actually happened. :)
The ANSI committee includes both people who implement C and people who
use C. I'm not sure exactly how many of each were present at, but I
believe that the implementors were actually less numerous, they
certainly were not in absolute control of the proceedings.

Makes sense.

Best regards,
Rick C. Hodgin
 
J

James Kuyper

On 01/21/2014 07:58 AM, Rick C. Hodgin wrote:
....
Why do we have "void function(void)" when "function()" would work
sufficiently at that level in a source file? It's explicitly so we
convey that no mistake was given as by accidentally leaving out some
parameters. We declare void to indicate "I purposefully intended to
leave this empty, there are no return variables, there are no
parameters," and so on.

Backwards compatibility. In C, as originally designed, there were no
function prototypes. When the language was first standardized,
prototypes were added. If the committee had followed your suggestion,
virtually all existing code would have had to be rewritten in order to
compile correctly. That was, at the time of standardization, already
billions of lines of code world-wide. It's easy to say "don't worry
about having to re-write all the old code" - unless you're the one who
has to re-write it.

Instead, the committee decided to allow the old syntax to continue
meaning what it used to mean: the function takes an unknown but fixed
number of arguments (as opposed to variadic functions, which take an
unknown and variable number of arguments). As a result, a different
syntax was needed to specify that the function doesn't take any arguments.

You seem to think the committee was dominated by implementors. If that
were true, this decision is hard to explain - it requires all
implementors to handle two different syntaxes for function declarations
- it would have been much simpler to mandate only one. This is a
concession to the needs of those who write programs in C, at the expense
of those who write C implementations.
It's the same here. "Oh, another comma ... was the developer
finished? Was there supposed to be more? What is missing? What was
left out? Please ... I need answers. I'm left hanging in a state of
confusion that is disrupting my soul. Whatever do I do?"

I don't use that feature, and I don't like it. However, this feature
simplifies the creation of machine-generated C code, and people who
write such generators are apparently sufficiently numerous that the
committee felt a need to accommodate their desires.
 
R

Rick C. Hodgin

Backwards compatibility. In C, as originally designed, there were no
function prototypes. When the language was first standardized,
prototypes were added. If the committee had followed your suggestion,
virtually all existing code would have had to be rewritten in order to
compile correctly. That was, at the time of standardization, already
billions of lines of code world-wide. It's easy to say "don't worry
about having to re-write all the old code" - unless you're the one who
has to re-write it.

Instead, the committee decided to allow the old syntax to continue
meaning what it used to mean: the function takes an unknown but fixed
number of arguments (as opposed to variadic functions, which take an
unknown and variable number of arguments). As a result, a different
syntax was needed to specify that the function doesn't take any arguments..

I'm not sure I would've been keen on that idea. I would rather have maintained it as a deprecated functionality that would have been slated to be removed in a few version releases. The old compilers could've generated object code in a particular version of a compiler that could be maintained for backward compatibility without negating the language in moving forward. My opinion. :)
You seem to think the committee was dominated by implementors. If that
were true, this decision is hard to explain - it requires all
implementors to handle two different syntaxes for function declarations
- it would have been much simpler to mandate only one. This is a
concession to the needs of those who write programs in C, at the expense
of those who write C implementations.

I don't think that. I honestly was going for humor. Nothing more. However, I would argue that the people involved in the decision were the ones whowanted to have their voice heard because they felt a particular way about it. I say that because it's more or less that way in everything.
I don't use that feature, and I don't like it. However, this feature
simplifies the creation of machine-generated C code, and people who
write such generators are apparently sufficiently numerous that the
committee felt a need to accommodate their desires.

Makes sense. I still would've opted for the deprecated allowance and phased it out over time.

Best regards,
Rick C. Hodgin
 
J

James Kuyper

char defaultOption = "4";
Did you mean "char *defaultOption" or "char defaultOption[]" rather than
"char defaultOptions", or did you mean '4' rather than "4"?

I meant char defaultOption[] = "4";

Then you've already got your wish; defaultOption contains a writable
string. It's perfectly legal to overwrite it with:
strcpy(defaultOption, "5");

If you had chosen
char defaultOption = '4';
then you could have modified it with
defaultOption = '5';

If you had chosen
const char *defaultOption = "4";
then you could modify with with
defaultOption = "5";

....
FWIW, I don't believe in defining variables in this way in C. I believe an
initialization block should exist so it is being done explicitly, both for
documentation purposes, and clarity in reading the source code (it's very
easy to miss a nested declaration when a group of variables is created of
a similar type.

I find that both documentation and clarity is best served by defining
each variable with the smallest scope that is consistent with the way it
will be used (except that I will not create a separate compound
statement for the sole purpose of more tightly constraining the scope -
that would require a separate compound statement for each variable; that
way lies madness).
Among other benefits, that approach minimizes the distance I have to
search for the definition of the variable (since such search normally
starts at a point where the variable is being used).
I propose new extensions to C in general:

in (thread_name) {
// Do some code in this thread
} and in (other_thread_name) {
// Do some code in this thread
}

I'm curious - are you familiar with the threading support that was added
to C2011? It's not at all similar to your way of handling threads, but
it does have the advantage of being based upon existing common practice.
As a result, it can be implemented as a thin wrapper over many existing
threading systems, such as those provided by POSIX or Windows. It
requires a somewhat thicker wrapper on other, more exotic threading
systems, but it should be widely implementable.

I doubt that your proposal could be modified in a way that's
sufficiently compatible with C2011's threading support to be a fully
conforming extension - but I haven't studied either one sufficiently
throughly to be sure of that.
 
J

James Kuyper

Quoted as Mr. Miyagi speaking, "Binary. Always open binary." :)

That's a bad idea, if you're reading and writing text files - that's
what text mode is for. It makes your code less portable. Even for code
intended exclusively for a platform that uses a specific method of
handling line endings, if that method is anything other than '\n' (as
is, in fact, the case on your system), it just makes more work for yourself.
 

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
474,085
Messages
2,570,597
Members
47,220
Latest member
AugustinaJ

Latest Threads

Top