Non-constant constant strings

I

Ian Collins

Rick said:
Rick said:
On Wednesday, January 22, 2014 9:08:04 PM UTC-5, Seebs wrote:

Example:
char* list[] = { "one", "two", "three" };

In this case, list[0] should contain a writable character array of four bytes,
lines[1] likewise, and list[2] should be six bytes.

How can it when list is an array pointers?

The pointers should point to read-write values.

I'm not going to revisit that argument... As you are compiling the code
as C++, I would expect the compiler to warn you about the deprecated
conversion.
They are not implemented on all C compilers. Visual C++ does not support
the syntax, but GCC does. I used it as a well-known example of a compiler
that supports it.

Visual C++ is a pretty hopeless C compiler.
I think string literals should only exist as constants when used like this:
printf("Hi, mom!\n");
Or when they are explicitly type qualified, as in:
const char foo[] = "Hi, mom!";
Without using the const prefix, those string literals should always be
converted at compile-time into read-write values.

You haven't declared a literal, you have declared an array of char which
is writable.

I've used a string literal to initially populate it.

Populating is different from writing to the original. It's no different
from saying you want to write to "2" in

int n = 2;
It doesn't sound like a "version of C".
It uses nearly all C syntax and conventions. It just greatly relaxes many
restraints and constraints, converting errors into warnings. This code
will not compile in Visual C++ without an error:

Because you are compiling it as C++.
#include <stdlib.h>
int main(int argc, char* argv[])
{
union {
char* p;
int _p;
};

This isn't valid C.

I did not know that. It's why I use a C++ compiler to compile my C code.
It has many syntax allowances C does not.

Then why don't you use std::string instead of trying to make C do
something is isn't specified to do?
Yeah, but not really.

Yes really. It isn't a legal conversion in C++.
It's only a violation of C++'s protocol for pointer
exchange. As far as the machine goes, it's a pointer and they can be
exchanged. In my opinion, the compiler should allow it, and only warn
about it.

C++ has stronger type safety rules than C. If you want the flexibility
of C++, you have to operate within c++'s rules.
 
D

David Brown

Correct. I have limited experience in embedded development. That consisted
primarily of an 8-bit micro-controller and JTAG debugging. Very primitive,
but the apps I wrote there were also very small.


LOL! :)


This is confusing to me. Literals encode something at compile time. The
data they represent is then initialized at some place, loaded by the abi
loader, and then is available by offset into data space at runtime. That
data is not a literal at that point. It is data. The literal only
describes what was encoded at compile time.

If the block of memory that "literal" occupies as a variable now exists
in read-write memory, then it will not remain a literal. If it exists in
read-only memory then it will remain a literal.


Yes. But, he was crazy. :)


Yes! Alright. This makes sense.


Agreed. I was just using different words not coming from a C language
education with C language words being imprinted into my brain.

My bad. :)

Best regards,
Rick C. Hodgin

Exact C terminology is not easy - and there can easily be differences
between the "normal" language meaning of words, and their technical
meaning in the C standards. It's even more fun if the technical
meanings are subtly different between C and C++, as many people deal
with both languages. I've been working with C for twenty years, and
while I usually get the terms roughly right - good enough for practical
use - I am often corrected by the real experts in this group.

Back to your original issues - neither string literals nor
const-qualified objects may be legally changed in C. The compiler
and/or linker may place the corresponding data in memory areas that are
read-only, and in the case of string literals, the compiler may also
combine them (such as if you use the same literal string several times
in the same program).

In both cases, the compiler may use the read-only property to optimise
away the memory, especially for const objects that are static and don't
have their address taken (literals don't have scopes, linkage or
lifetimes - but are somewhat similar to global static const).

So if you write:

const unsigned int scaleFactor = 2;

unsigned int scale(unsigned int x) {
return x * scaleFactor;
}

the compiler could implement scale() as "return x + x;" if it wants.

(It would still have to put "scaleFactor" in memory, in case another
translation unit had "extern const unsigned int scaleFactor".)
 
D

David Brown

No. I consider all literal numbers to already be constants. What I mean is
that every variable created should be read-write by default, unless explicitly
type qualified as const. In such a case, then it becomes a read-only constant.
Until that time, all variables are read-write.

Example:
char* list[] = { "one", "two", "three" };

In this case, list[0] should contain a writable character array of four bytes,
lines[1] likewise, and list[2] should be six bytes. However, today unless
you use the compound literal syntax on GCC, the string literals are converted
to read-only constants. In my view, that should not be.

I feel you are misunderstanding something fundamental here about
strings, pointers and arrays.

Your example declares an array "list" of three pointers-to-char. The
compiler (I'm using the term loosely - some of this is handled by the
linker and maybe even the OS link/loader) will place the strings "one",
"two" and "three" into a section of memory marked "read only" and
usually linked along with the code section. It will allocate space in
the "data" section for an array of three pointers. It will create a
block of data in the initialised data section containing the addresses
of the three string literals within the code section. And it will
arrange for the pre-main startup code to copy these three addresses into
the read-write memory identified by "list".

After that, when main() starts, you can change the values in the "list"
array. But you cannot change the data they initially point to, as that
data is read-only memory.

As far as C is concerned, "one" is interpreted as a constant char* value
corresponding to the address of the fixed string "one" in memory.

At no point here have you allocated any character arrays - you just have
an array of character pointers.

It is a historical oddity in C that your example compiles at all - in
C++, it at least gives a warning "deprecated conversion from string
constant to 'char*'" (from gcc), making it clear that the string cannot
be modified.

I think string literals should only exist as constants when used like this:
printf("Hi, mom!\n");

Or when they are explicitly type qualified, as in:
const char foo[] = "Hi, mom!";

Remember, there is a /vast/ difference between:

char foo[] = "Hello";

and

char *bar = "Hello";

Even though you can use both in similar situations such as "printf(foo);
printf(bar);", they have very different meanings to the compiler.

In the case of foo[], you are asking the compiler to make a character
array "foo" of size big enough to hold the string "Hello", and
initialise it with a /copy/ of the string "Hello" when the program
starts. In the case of bar, you are asking the compiler to make a
character /pointer/ and initialise it with a /pointer/ to the string
"Hello" somewhere in read-only memory. If you use both, then the
compiler can quite happily point "bar" to the read-only copy of "Hello"
that is used to initialise "foo". And if "foo" were declared "const",
then since the compiler knows you cannot legally change the data, it
could even point "bar" at "foo" !
 
D

David Brown

The language is only a mechanism to get something down to the mechanical
level. It takes human readable source code and converts it into 1s and
0s in a particular pattern which represents the peculiarities of the CPU's
machine code and data models.

I look at computing as real computing, meaning what takes place inside of
the CPU. It reads data in to its registers. It executes them through a
processing engine (an adder, shifter, etc.), and then produces output which
is stored back into registers, or into memory.

There are NO computers anywhere which do not do this. They all require
input, do something with that input, and generate output. It is read/write.
All of it.

There are /many/ ways to do computation without a register-based cpu.
You can have stack machines that don't have any need for registers. You
can have Turing machines, cellular automata, genetic computing, quantum
computing, and a dozen other architectures that don't use registers.
When I studied programming and computability at university, we didn't
use register-based cpus - we read from the blackboard and wrote in our
notebooks. Programs were written on the blackboard and proven
mathematically, not run on register-based cpus.

Claiming that all computing needs registers or read-write operations is
like claiming that all timekeeping needs moving parts - after all,
inside a digital watch you have electrons moving about.


Back to real-world computing, it is true that most cpus have some sort
of registers (some have a few, some have many). But there is a major
class of programming language which does not have a modifiable state -
functional programming languages. They are very popular in some areas,
precisely because the lack of modifiable state makes them easier to
reason about mathematically, and be sure that they are correct. At the
programming language level, it does not matter that they will often end
up running on a register-based cpu. And one area in which such
languages are becoming increasingly of interest is in parallel
programming - the lack of read-write data means you avoid many of the
problems with shared resources and synchronisation that are seen in
imperative languages.
 
R

Rick C. Hodgin

Rick C. Hodgin wrote:
Then why don't you use std::string instead of trying to make C do
something is isn't specified to do?

Because I keep looking to the future when I will have written my own
compiler, and I will not support std::string. I intend to migrate all
of my code once I get the dev environment up and running.
Yes really. It isn't a legal conversion in C++.
:)

C++ has stronger type safety rules than C. If you want the flexibility
of C++, you have to operate within c++'s rules.

I realize this is the way the compiler is today. It's one of the things
I intend to relax in mine.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

For additional grins create an array such as ...
char* list[] =
{
"one",
"one",
"one",
"one"
}

Then piddle about with one or more of the elements and options, then
print the results.

Any attempt to modify these elements results in an access violation exception,
due to those strings being created in read-only memory. In GCC I can use:

char* list[] = {
(char []) { "one" },
(char []) { "one" },
(char []) { "one" },
(char []) { "one" },
}

And it will work. In Visual C++ (whether compiling as .c or .cpp) it
generates a handful of errors for each line. :)

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

Example:
char* list[] = { "one", "two", "three" };

In this case, list[0] should contain a writable character array of four
bytes, lines[1] likewise, and list[2] should be six bytes...

I feel you are misunderstanding something fundamental here about
strings, pointers and arrays... At no point here have you allocated
any character arrays - you just have an array of character pointers.

Those character pointers point to character arrays, of four bytes, four
bytes, six bytes, which are initially populated with the string literal
values.

char* list[] = { "one", "two", "three", NULL };

int main(int argc, char* argv[])
{
int i, j;

for (i = 0; list; i++)
{
// Used as pointer
puts(list);

// That pointer's data elements used as an array
for (j = 0; list[j] != 0; j++)
printf("%c\n", list[j]);

// Blank line :)
puts("\n");
}
}
Or when they are explicitly type qualified, as in:
const char foo[] = "Hi, mom!";
In the case of foo[], you are asking the compiler to make a character
array "foo" of size big enough to hold the string "Hello", and
initialise it with a /copy/ of the string "Hello" when the program
starts.

"asking the compiler to make a character array..."

String literals are converted to read-write values. The string literals
used to populate values in the list[] example above are not. Those pointers
point only to areas of read-only memory, which is what I don't want, and
what I found the solution for using this:

char* list[] =
{
(char []) { "one" },
(char []) { "two" },
(char []) { "three" }
}

Using this (at least in GCC) list[0], list[1], and list[2] point to
read-write memory, which is what I wanted it to do without the compound
literal syntax.

Best regards,
Rick C. Hodgin
 
R

Rick C. Hodgin

There are /many/ ways to do computation without a register-based cpu.
You can have stack machines that don't have any need for registers. You
can have Turing machines, cellular automata, genetic computing, quantum
computing, and a dozen other architectures that don't use registers.
When I studied programming and computability at university, we didn't
use register-based cpus - we read from the blackboard and wrote in our
notebooks. Programs were written on the blackboard and proven
mathematically, not run on register-based cpus.

Claiming that all computing needs registers or read-write operations is
like claiming that all timekeeping needs moving parts - after all,
inside a digital watch you have electrons moving about.

Back to real-world computing, it is true that most cpus have some sort
of registers (some have a few, some have many). But there is a major
class of programming language which does not have a modifiable state -
functional programming languages. They are very popular in some areas,
precisely because the lack of modifiable state makes them easier to
reason about mathematically, and be sure that they are correct. At the
programming language level, it does not matter that they will often end
up running on a register-based cpu. And one area in which such
languages are becoming increasingly of interest is in parallel
programming - the lack of read-write data means you avoid many of the
problems with shared resources and synchronisation that are seen in
imperative languages.

There are no computers anywhere in existence, be they men or machine, that
do not:

(1) Take some form of input
(2) Perform some operation on it
(3) Generate output

It is the fundamental definition of a "computer," ie "one who computes" or
"something that computes". They all follow this course:

(1) input
(2) process
(3) output

And in all cases, that absolutely means read-write. There are no exceptions.

All computers are read-write by default. The only things which make them
read-only are imposed protocols which inhibit their otherwise unrestricted
operation.

Best regards,
Rick C. Hodgin
 
B

BartC

The phrase you're looking for there isn't "hardly wasteful," but
"relatively
wasteful." Compared to the amount of resources used in common programs,
it
is not relatively wasteful. However, it is, by definition, wasteful. It
reserves bytes that will never be used, and only take up space, and are
not
needed by the application, and are actually nothing but total digital
flotsam.
:) They are wasteful, by definition, and are not needed with the
compound
literal ability Joe demonstrated.

You mentioned in your OP that about 100 lines of text are involved. That's
not going to make much of a dent in your 8GB ram and 1TB storage you
mentioned elsewhere. (And once the contents of read-only memory are copied
to a writeable area, the page containing the r/o data will probably languish
in a swap file for the rest of the program's lifetime.)
Joe's solution is elegant, and exactly what I was looking for. It is not
wasteful, and it addresses the exact need I had, which was to include text
strings at compile time that can be later modified at runtime without any
re-allocation. I simply use what was defined in source code, and go.

That's elegant.

I thought it was an unsatisfactory solution. Partly because I don't
understand compound literals: why should a string constant "ABC" behave
differently if it's written as a compound literal? It doesn't make sense to
me.

It seems also unintuitive. In other languages, equivalent constructs to
compound literals tend not to be mutable l-values. And even in C, if you see
something like:

fn(x=(char[]){"ABC"});

you might reasonably expect that "ABC" is passed to fn each time this line
is executed, not something different every time!

I'm not even sure it fully does what you intend: the resulting strings
might be 'writeable', in that they reside in the right sort of memory, but
you probably can't make them longer (whereas the solution using a fixed
128-char block per string doesn't have that problem: strings can shrink and
grow up to the limit imposed).

Another reason is the ugly syntax (for which I had to reread another post to
remember how it went). And it also relies on a feature that may or may not
be present in a particular C compiler.

All this might be worthwhile if it was for something significant. But giving
initial values to a set of string variables is something that must have been
done a million times before C99 came along, using any number of ways to do
it.
 
R

Rick C. Hodgin

You mentioned in your OP that about 100 lines of text are involved. That's
not going to make much of a dent in your 8GB ram and 1TB storage you
mentioned elsewhere. (And once the contents of read-only memory are copied
to a writeable area, the page containing the r/o data will probably languish
in a swap file for the rest of the program's lifetime.)

Yeah ... it shouldn't be like that. In my ABI it won't. :) The strings
will be loaded directly from disk to the data segment they belong. No
copying, no duplication ... just a data load.
Joe's solution is elegant, and exactly what I was looking for. It is not
wasteful, and it addresses the exact need I had, which was to include text
strings at compile time that can be later modified at runtime without any
re-allocation. I simply use what was defined in source code, and go.
That's elegant.

I thought it was an unsatisfactory solution. Partly because I don't
understand compound literals: why should a string constant "ABC" behave
differently if it's written as a compound literal? It doesn't make sense to
me.

It seems also unintuitive. In other languages, equivalent constructs to
compound literals tend not to be mutable l-values. And even in C, if you see
something like:
fn(x=(char[]){"ABC"});

you might reasonably expect that "ABC" is passed to fn each time this line
is executed, not something different every time!

I'm not even sure it fully does what you intend: the resulting strings
might be 'writeable', in that they reside in the right sort of memory, but
you probably can't make them longer (whereas the solution using a fixed
128-char block per string doesn't have that problem: strings can shrink and
grow up to the limit imposed).

I have no requirement to make them longer. I just need to swap out a
particular portion with another portion many times at runtime.
Another reason is the ugly syntax (for which I had to reread another post to
remember how it went). And it also relies on a feature that may or may not
be present in a particular C compiler.

That's why I created the RW() macro, to hide the syntax:
#define RW(x) (char []) { x }

Then you just use:
char* list[] = { RW("one"), "two" };

In this case, list[0] is read-write, list[1] is read-only.
All this might be worthwhile if it was for something significant. But giving
initial values to a set of string variables is something that must have been
done a million times before C99 came along, using any number of ways to do
it.

It was as much a mental exercise as anything. I know how I will handle it
in my compiler. I was curious if/how C handled it. I also had a working
solution for some time before I came here.

I also discussed this previously for several days on:
comp.os.ms-windows.programmer.win32

....in a thread of similar name before someone advised I come here. I bet
you all wish you could've intercepted that post, eh? :)

Best regards,
Rick C. Hodgin
 
D

David Brown

I've tried Eclipse. I prefer Netbeans. What is it about Eclipse that is
so great? I'm not aware of many things Eclipse can do that VS can't,
especially when the Whole Tomato Visual Assist X extension is added.

Eclipse can run on Linux. VS cannot. That is a killer feature.

Apart from that, I don't have any lists over the features and plugins
that the different tools support, even without considering which might
actually be useful for /me/. The point is merely that when I need a
"big" IDE, I've got one - I don't need VS and have nothing to gain from
it. Other people have different preferences.

And the /real/ point was that sometimes a big IDE is /not/ what I want,
and would be a terrible choice for a particular editing task - in which
case I have a "middle weight" choice (gedit) and a lightweight
text-based choice (nano).
Use an Oracle VirtualBox instance running an older version of Windows. Or
get an updated version of Visual Studio. Personally I use Visual Studio 2008
only and it runs fine on everything from Windows 2000 up.

The "problem" I have (it's not a real problem, as I don't /want/ to run
VS) is that the only Windows machine I have runs XP SP2 - and MS has
declared that unsuitable for their software. Almost everything windowy
that I need will run on it, except reasonably recent versions of MS VS
and MS Office. Fortunately, I need neither.

I use VirtualBox extensively for many purposes, and occasionally use it
for other Windows versions. So yes, I /could/ use a slightly newer
version of Windows (at least XP sp 3) in a virtual box on my Linux
desktop, in order to run VS. Or I could just use Eclipse natively.
I has shocked everyone I've shown it to. The ability to fix an error and
keep on going in your debug environment ... people are floored by it.

That may apply to the people you are familiar with, but it would take a
great deal more than that to shock /me/.
Perhaps you were not using it correctly. Edit-and-continue has flaws. It
won't handle certain things. If you introduce an error it doesn't always
give you the correct error code or explanation, etc. But, on the whole it
is well tried and debugged.

The whole concept has huge limitations. I can see it can be somewhat
useful at times - it was sometimes useful when I did VB3 development.
But I didn't miss it when switching to better tools (the day Delphi 1.0
was released).

I use Notepad++ on some things. I use Sammy Mitchell's The SemWare Editor
on many others. On Linux I use nano when I'm on ssh, but for most local
things I use Gedit.

And there you have it. Different interfaces, different types of tools,
for different purposes. Gui is /not/ better than text, big IDE (like VS
or Eclipse) is /not/ better than mid level editor (gedit, Notepad++) or
text-based editor (nano). They are different, and have their advantages
and disadvantages at different times.

Go back a few posts in this thread and read the nonsense you wrote about
gui's being "far superior" than text interfaces. Then re-read your
paragraph above. Gui's are "far superior" than text interfaces for many
uses, but certainly not for everything.
Visual Studio's IDE lacks several features. Visual Assist X adds most of
those lacking features back in. It makes Visual Studio much more like
Eclipse or Netbeans. Refactoring is one of the biggest with Ctrl+Alt+R to
rename a symbol. It also provides many speedups, has an Alt+G "goto
definition" lookup, an Ctrl+Alt+F "find all references" and so on.

Plus, Visual Studio itself has a Code Definition window which constantly
shows you the code definition line for the current symbol. It too is one
of the greatest time savers there is. Especially when going back in to
maintain older code.


With the soon-to-be many cores (64+) it will be done truly in parallel.

And what do you believe will be the benefits? Who cares if your 10
tasks run for 0.5 ms in parallel or in series? I have some 380+
processes running on my machine at the moment - of which typically 1 or
sometimes 2 are actually /running/. During a large compilation I can
use the 4+4 cores effectively, but outside that there is zero difference
to having 380 processes sleeping on one core, or 380 processes sleeping
on 380 cores.

Intel have had cpu designs with large numbers of cores for a good while
now. Sun (now Oracle) have chips with 16 cores per chip, 8 threads per
core - they are good for special tasks, but useless for normal computing.
What history teaches me is that technologies progress until there is something
radical that changes the nature of the thing. Horse selling businesses were
wide, as were horse facilities, buggies, whips, farriers, and so on. When the
automobile came along it changed everything. Not all at first, but ultimately.

What you learned there is that things change /slowly/.

Even in the computing world, progress is often very slow. Hardware
/implementations/ have been getting faster and more powerful at an
impressive rate, and consumer opinions change quickly, but the
fundamentals do not change at the same rates. The C language has gone
through a lot of improvements over the years, but it is still much the
same as 30 years ago. Most of the algorithms and theories in computing
were developed decades ago. Parallel computers have existed for several
decades, but most tasks run by most computers are mostly single-threaded.

I will certainly agree that we will see many more parallel tasks in the
future, but it will not be the earth-shattering revolution that you are
imagining. Part of that is that "normal" computers are already fast
enough for most purposes (supercomputers will never be fast enough, of
course). Intel is currently having trouble because their projections
about chip sales have gone wrong - it turns out that people don't need
new, faster computers, and are happy with what they've got. And once
everyone has got a decent Android Pad, sales for these will fall off
too. No revolutions, just the ebb and flow of the market.
The same will happen with computers. People will be building programming
stables, programming buggies, programming whips, being programming farriers,
until finally they step away from those forms and move to the new thing,
which will be the massively parallel computing model.


That's because of the existing hardware. The algorithm I described does things
which are serial in nature... in parallel. It's a new thing.

It's old hat.
I did not say those things. I realize other people have done so.

Ah, so this dramatic prediction is different because /you/ said it,
rather than all those others who got it wrong?
What I do
believe is we will see the shift to a new CPU core that handles parallelism
at what today is only serial code, in a way that changes the paradigm. As
such a new language will be needed to describe it. That's what I'm building.

First, there is /no/ way to magically change serial code into parallel
code. Even if it were not possible to prove this (and I believe it is),
there are a great many other people who are a lot smarter than you or I,
with far greater resources than us, and who are working on such ideas.
The best they have come up with is superscaler processors which use
complex scheduling, register renaming, branch prediction, speculative
execution, etc., to make bits of serial code run faster.

Secondly, there already exist languages designed for running code in
parallel. And there are already systems for running bits of C code in
parallel, so that you can with relative ease use multiple cores on the
parts of your code where you can benefit from them.
Agreed. It has been very hard on me to continue. I face exceeding opposition
whenever I describe these things.

Don't stop trying - you /might/ come up with something new. But don't
get disappointed when someone tells you these particular ideas are not
realistic, or are already old. Instead, use that information to come up
with different ideas.
On top of which I profess Christianity as
the driving force for me doing this (God gave me certain abilities, and as a
believer I desire to give those abilities back to Him, and unto all men).

Both of these facets cause barriers between myself and others. It is a hard
thing for me to figure out how to get around. I believe it will require a
special type of person to do so: (1) a devout believer, (2) someone highly
skilled in software development, and software and hardware theory.

If you find your religious beliefs give you hope and encouragement,
that's fine. But please don't mix religion with programming - it
degrades both of them.


But for your interest, there is a character in comp.lang.c++ that talks
like this - maybe you two would get along.
 
S

Seebs

The language is only a mechanism to get something down to the mechanical
level.

No. The language is a tool for expressing things which may have little to
no relationship to things that happen on the mechanical level.
I look at computing as real computing, meaning what takes place inside of
the CPU. It reads data in to its registers. It executes them through a
processing engine (an adder, shifter, etc.), and then produces output which
is stored back into registers, or into memory.

From my point of view, this is an implementation detail. :)
There are NO computers anywhere which do not do this. They all require
input, do something with that input, and generate output. It is read/write.
All of it.

There's no particular logical requirement that any given thing be both
readable and writeable, though, and it makes lots of sense for some things to
be only one or the other.

Mostly, it really comes across as though there is way too much theory that
you're not aware of that's hampering your ability to understand why people
have done things. You're acting as though the most likely explanation is that
everyone who has ever worked on C is an idiot. You might consider the
alternative theory that they had information you don't.

-s
 
S

Seebs

I've never heard of that model.
giyf
It's not just about string literals, but it is a way of life. There area
handful of things I remain in "learn how to use mode" because I don't care
to understand the underlying philosophy. Most things in my life I am in
"pursue the philosophy" mode so I can understand it.

Makes sense.
In my experience that is a rare trait. It took me many years to realize and
identify the differences in people. It was observation-based over a long
time. It's not an attempt to be judgmental upon myself or others, but to
recognize that some people do this, and others don't (most others).

Well, yes. But it's a trait that might be significantly less rare in a
community of programmers, especially programmers who are still hanging around
in Usenet. :)

-s
 
S

Seebs

Here's an interesting idea, the concept of the -1 member, which does not
exist as data, but exists as a placeholder to allow the commas to work
as being always prepended, rather than appended.

Too counterintuitive, rejected. It's "a, b", not "a ,b".

-s
 
B

BartC

BartC said:
And even in C, if you see
something like:

fn(x=(char[]){"ABC"});

you might reasonably expect that "ABC" is passed to fn each time this line
is executed, not something different every time!

(I tried to see if fn could in fact be passed a string other than ABC
because of manipulation via x after previous calls.

But each time is was passed "ABC". That was because, in my gcc, the string
was reconstructed at the assignment point above.

This just makes the behaviour of compound literals even more of a mystery:
if I *did* in fact want that behaviour, then this wouldn't work!

It also rather peculiarly, for shorter strings, constructed the string
32-bit bits at a time at each call, while for longer ones, the string was
stored in read-only memory! And copied presumably to local writeable
memory.)
 
S

Seebs

Are you using a GUI for each bash instance in a terminal window? A GUI
supports easy access to multiple terminal windows. A text-based interface
does not.

screen, tmux, etc.
Try using Visual Studio with the Visual Assist X plugin. You'll be floored
at how much more productive you are.

A couple of observations. The first, and most significant: Humans
are absolute crap at measuring their own speed of performing tasks,
because we tend to notice some kinds of time more than others. It's
very easy for an environment to *feel* very productive while actually
being slow.

Consider: Do you use WYSIWYG or markup to write? Which do you think
is faster? Basically everyone knows that they are spending less
time on formatting using WYSIWYG tools, because it's so easy and
intuitive. Basically everyone is wrong. We did tests back in the
day. For producing ordinary college papers, we found that it took
about equally long to do the first paper in TeX and in MS Word.
After that first paper was done and you'd done all your messing
about to get settings right, the papers written in MS Word continued
to take about that long, and the papers written in TeX took about
half as long.

There's places for each tool, and if you don't pay attention to that,
you'll lose out.
Incorrect. In my opinion. I do not believe there will be anything that
remains completely in serial for much longer.

Writing itself seems to be pretty much stuck being serial.

-s
 
S

Seebs

Would that such a feature works everywhere. It doesn't. It's why I began
working with binary files a long time back (1990s). I have never looked
back. It may have been due to buggy function implementations back then,
but nonetheless, when I have control over code myself it's never an issue.

The circumstances under which I control all the code I'm working with are...
uhm... I actually can't think of a time when that's been the case.
It's part of having total ownership of the code.

.... So, remember that "... feature works everywhere" comment?

Having total ownership of the code doesn't work everywhere.
I haven't seen an editor yet that does that.

Who said anything about editors? Text files might be scripts for some
interpreted language, source for a compiler to process, or any of a dozen
other things.
I want a literal input, byte per byte, on my input files. No translation
except what I do to the files after they're loaded.

For code which will run on computers that use other programs, if I am writing
or reading text files, I want the C runtime to handle native text files
appropriately so I don't have to think about it. Text mode does this.

-s
 
S

Seebs

You seem to habitually denigrate anything old as obsolete. In time,
you'll learn better, as you yourself start getting older. Knives are
among the oldest of weapons, dating back to the stone age and even
before. Rifles, artillery, or nuclear bombs are clearly greatly superior
to knives for the purposes for which those weapons were designed; yet
the military still trains soldiers how to fight with knives. Think about
that for a while, and when you've fully understood why that's a
reasonable decision for the military to make, you'll have a better
understanding of the disconnect between "old" and "obsolete".

I occasionally meet people who insist that "marriage" is "obsolete".
Some people seem to still it to have some appeal. Similarly, even
though we pretty much have the technology to do all fertilization
in labs, people seem to like the "obsolete" method.

Things which are old, and therefore presumably obsolete, include:
* Having friends.
* Music.
* Written language.
* Spoken language.
* Pets.
* Carbon-and-oxygen metabolisms.

-s
 
S

Seebs

No. I consider all literal numbers to already be constants. What I mean is
that every variable created should be read-write by default, unless explicitly
type qualified as const. In such a case, then it becomes a read-only constant.
Until that time, all variables are read-write.
Okay!

Example:
char* list[] = { "one", "two", "three" };
In this case, list[0] should contain a writable character array of four bytes,

This is not what you just said. You're confused, because you don't understand
what is being declared.

Let's have parallel examples:

int list1[] = { 1, 2, 3 };
char *list2[] = { "one", "two", "three" };

These have essentially the same semantics in C. list1[0] and list2[0] are both
writeable, but each has been initialized from something that can't be written
to. You can't change the literal 1, and you can't change the literal "one".

So after this, you could execute:
list1[0] = 4;
list2[0] = "four";

and both would work. But the value that's being put into a writeable variable
is the address. You're allowed to replace that with a different address. But
the thing it is the address *of* is the literal, and is not writeable, for the
same reason that the literal 1 isn't writeable.

-s
 
D

David Brown

Example:
char* list[] = { "one", "two", "three" };

In this case, list[0] should contain a writable character array of four
bytes, lines[1] likewise, and list[2] should be six bytes...

I feel you are misunderstanding something fundamental here about
strings, pointers and arrays... At no point here have you allocated
any character arrays - you just have an array of character pointers.

Those character pointers point to character arrays, of four bytes, four
bytes, six bytes, which are initially populated with the string literal
values.

char* list[] = { "one", "two", "three", NULL };

int main(int argc, char* argv[])
{
int i, j;

for (i = 0; list; i++)
{
// Used as pointer
puts(list);

// That pointer's data elements used as an array
for (j = 0; list[j] != 0; j++)
printf("%c\n", list[j]);

// Blank line :)
puts("\n");
}
}
Or when they are explicitly type qualified, as in:
const char foo[] = "Hi, mom!";
In the case of foo[], you are asking the compiler to make a character
array "foo" of size big enough to hold the string "Hello", and
initialise it with a /copy/ of the string "Hello" when the program
starts.

"asking the compiler to make a character array..."

String literals are converted to read-write values. The string literals
used to populate values in the list[] example above are not. Those pointers
point only to areas of read-only memory, which is what I don't want, and
what I found the solution for using this:

char* list[] =
{
(char []) { "one" },
(char []) { "two" },
(char []) { "three" }
}

Using this (at least in GCC) list[0], list[1], and list[2] point to
read-write memory, which is what I wanted it to do without the compound
literal syntax.


Your string literals are not being "converted" here. What you are doing
with the "(char[]) { "one" }" syntax is asking the compiler to give you
an array of characters (of size 4 chars) initialised by the literal
"one". You are telling the compiler not to bother giving it a name, but
to put a pointer to that data into the initialisation data for list[].

This will give you the effect you are looking for, even though your
explanation is not quite accurate.

Of course, everything will go to pot when you try to change the
characters in list[0] to something longer than three letters.
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
474,079
Messages
2,570,575
Members
47,207
Latest member
HelenaCani

Latest Threads

Top