#Strings in C
42 messages in this thread
Having yet again tried to get to grips with C, I have a little question
for you old hands:
How do I define in C (using DICE) the string which in assembler is:
dc.b $9B,"0 p",0
If I use "\x9B0 p" I get the equivalent of $B0," p" with a warning that
the hex value has more than two characters (i.e. the 9 has been ignored by
DICE), and if I use "\x9B 0 p" I get the string $9B," 0 p" – i.e. with a
superfluous space.
If it makes any difference, I would like to #define the above string.
Regards,
Shraddhan (via AP from Hertfordshire, England)
I don't do assembler. If you state your question in english maybe I can help.
> If you state your question in English maybe I can help
Right, given that I can define a string in C as, for example, "ABCD" –
which a represents a pointer to the byte values 65, 66, 67, 68, 0 – I
would like to know if there is a convenient way of representing a string
which contains characters not directly accessible via the keyboard.
My original example was a string containing the byte values 155, 48, 32,
112, 0 which corresponds to the character '\x9B' followed by the
characters which comprise the C string "0 p"
Now, the problem is that if I try to define the string as "\x9B0 p" I find
that DICE objects to this, saying that I have too many digits. It takes
the '0' character as being a third digit to the \x9B specification, rather
than being the next valid ASCII character in the string.
So, for example, "\x9B p" is correctly translated by DICE into the
sequence 155, 32, 112, 0 whereas "\x9B0 p" becomes (after a warning) the
sequence 176, 32, 112, 0
Finally, my question is: what is the simplest, most convenient way to get
round the behaviour described above, so that I can enter arbitrary
characters (expressed as hexadecimal numbers) into arbitrary strings
(containing digits)?
One way that works is to convert the whole lot into hex or octal
characters. This, I find, is a pain, as my skills in ASCII -> octal
conversion are not good. Another way is to specify the whole lot on a
chracter-by-character basis, which is clumsy. Both these techniques are
too prone to errors.
…………………………………………………
While I have your attention, can I ask a second question on a totally
different topic? I would like to create a look-up table of alternating
pointers to strings and pointers to corresponding functions to be
executed. What is the syntax for specifying such a table? The overall
aim is to write a parser that looks at an input file, picks out a possible
keyword at the start of a line, looks it up in the table, and if found,
executes the appropriate function to interpret the remainder of the line.
Many thanks for any help.
Regards,
Shraddhan (via AP from Hertfordshire, England)
If your function returns a void and takes no parameters, you could use :
struct function_entry
{
char *keyword ; /* keyword is a pointer to a string */
void ( *action ) ( void ) ; /* action is a pointer to a function */
} ;
struct function_entry lookup_table [100] ; /* Table of 100 entries */
Change the void at the front if your funtion returns something, change the
void in the brackets if it needs parameters.
You can fill in the function entries by assigning to the name of a
function without the brackets, e.g. :
void process_start ( void ) ; /* Defined somewhere in the program */
lookup_table[0].action = process_start ;
Then you can execute the function by putting the brackets after it, e.g.:
lookup_table[0].action () ; /* Executes funtion through pointer */
I hope this is of help.
Peter Wade
Autopiloting from London, England
Peter,
Thanks for showing me how to create arrays of structures.
I now have a little problem that has had me baffled for the last three
hours, and I have discovered that it is to do with the use of EXTERN.
I'm having some difficulty referring in one file to an array defined in
another file. One file contains:
struct Atom {
char *_Name;
APTR atom_Value;
APTR atom_Next;
};
struct Atom Object[] = {
{ "alpha" },
{ "beta" },
{ "gamma" },
};
/* …… to here */
The second file contiains:
#define Value_Beta Object[1].atom_Value
as a convenient way of referring to the value pointer of an array item.
(I woould really like to be able to refer by name to something like 30 out
of the 2500 or so memory locations used by the array.)
However, when I try to use Value_Beta, I keep getting errors which imply
that the structures in the array elements are not defined. I have tried
the following two lines to no avail in the second file:
extern struct Atom Object[];
extern Object[];
The code seems to compile with no problem if the two files are combined.
Why won't the compiler (DICE) recognise that the array referred to in one
file is defined in the other file?
Regards,
Shraddhan (via AP from Hertfordshire, England)
Shraddhan –
I don't know if this will help or not, but I had a similar experience
while breaking up a large program in TurboC. I ended up having to put my
struct definitions in a header file which I #included in every file that
made up the project.
In your case, struct.h would contain your Atom structure, your main
program (or another header) could define the Object[] array, and your
subprogram would include the lines "#include <struct.h>" and "extern
struct Atom Object[];".
– Jim, on AutoPilot!
If I understand you rightly, you only have
struct Atom {
char *_Name;
APTR atom_Value;
APTR atom_Next;
};
in the first file. Am I correct ?
If this is the case then that is your problem. The compiler treats each .c
file seperatly, it does not remember the declaration of struct Atom when
it is on the second file. The line :
extern struct Atom Object[] ;
tells it that Object is an external array of struct Atom but it still does
not know what a struct Atom actually is.
The way round this is to take your "struct Atom { …" declaration out of
the first file and put it into a header file, e.g. atom.h, then put the
line:
#include "atom.h"
at the tope of both of your .c files. Keep the "struct Atom Object[] …"
definition in your first file and use the line:
extern struct Atom Object[];
in your second file.
> Why won't the compiler (DICE) recognise that the array referred to in
> one file is defined in the other file?
C compilers treat each .c file they compile as a seperate operation even
if they are on the same command line. That is why you have to #include all
of the system header files into each file that uses them. You can use
extern to tell it about an external variable which is a data type it
already knows, but you have to include the structure declarations for any
structures that you use.
N.B. The difference between declaring a variable and defining it is that
defining it creates the variable and allocates memory for it but declaring
it just says what type it is. So :
struct Atom Object[1000] ; /* This is a definition */
extern struct Atom Object[] ; /* This is a declaration */
This is worth remembering if you are reading textbooks on the subject.
Well, that is probably longer than you were expecting. I hope it is of
help. Happy Programming!
Peter Wade
Autopiloting from London, England
Personally I'd typedef the structure def.
-sja
IMHO Its a matter of style. I've never got into the habbit of using
typdefs, probably because they were not used much in the course where I
learnt C. They do save typeing though (and forgetting to put the word
struct in a definition is one of my commonest typos). It is also closer to
the way C++ handles structs.
Peter Wade
Autopiloting from London, England
Hmm. Maybe I can return your favor. 🙂
>> How do I define in C (using DICE) the string which in assembler is:
>> dc.b $9b,"0 p",0
I'm not too sure about #defining it (or about DICE, as I use TurboC++),
but if you were using an array, you might try:
char string[] = { '\0x9b',"0 p",'\0' };
(Or char *string; whatever suits you better.)
I suppose it wouldn't hurt to try this, though:
#define STRING { '\0x9b',"0 p",'\0'}
Or, you could create the aforementioned array and just do this:
#define STRING *string
Hope it helps.
– Jim, on AutoPilot!
Jim,
I tried your suggestions, and got none of them to work 🙁
In the line:
char string[] = { '\0x9b',"0 p",'\0' };
aren't you mixing up characters and strings?
My current thinking is that there seems to be no easy, convenient
solution. I _knew_ there was a reason why I hated C! 🙂
Regards,
Shraddhan (via AP from Hertfordshire, England)
I finally got it to work. Just do this:
#define STRING "\x9b\x20 p" // dc.b $9b,"0 p",0
Or, you could find out which key combo produces a $9b character (on my PC
it's the monetary cent sign) and use it instead of the '\x9b.'
If converting the characters following the special characters is too much
trouble, you could always write a program to parse your dc.b statements
into #defines with hex or octal strings for you. (Or is that more trouble?
Nah! <g>)
– Jim, on AutoPilot!
>> #define STRING "\x9b\x20 p" // dc.b $9b,"0 p",0
Oops. That should be "\x9b\x30 p".
Jim,
Thanks for your help. I've got it all sorted out now.
> If converting the characters following the special characters is too
> much trouble, you could always write a program to parse your dc.b
> statements into #defines with hex or octal strings for you. (Or is
> that more trouble?
It seems to me that I can get DICE's VMake to run such a program for me,
so it's an idea worth considering for the future. Alos, writing such a
program would give me something to do when I get totally stuck next time
and have to wait a few hours for help. 🙂
Regards,
Shraddhan (via AP from Hertfordshire, England)
Shraddhan,
dc.b $9B,"0 p",0 may be written as "/233/60/40/120" in C.
– wkc – … via AP from Hamburg, Germany
Werner,
Thanks for your suggestion for writing the whole string in octal.
I have used your suggestion, as it works, with the addition of a comment
to show me what the string really is, as I find a string such as
"\233\60\40\160" to be totally unpenetrable.
Regards,
Shraddhan (via AP from Hertfordshire, England)
Shraddan,
I did suggest to use octals because this is compatible with K & R (first
edition). The escape-sequence \x (and \v) was introduced later in ANSI-C,
so there might be some portability problems on older systems.
Of course, either using octal or hex, the problem is, that a digit that is
following the expression will be interpreted to be a digit of the number
itself.
For portability reasons, integer numbers greater than 255 must be allowed
to be assigned to a variable of type char, because sizeof(char) may be
greater than 1 on some alien environment.
– wkc – … via AP from Hamburg, Germany
Werner,
I can appreciate the logic of sticking to octal. I had just hoped that
there might have been an easy way of avoiding the confusion between
interpreting a digit as octal or hex, and interpreting it as an ASCII
character.
Oh well, I do have that bit of code sorted out well enough for the time
being.
Thanks.
Regards,
Shraddhan (via AP from Hertfordshire, England)
Whoops…please disregard that last bit of information. I just tried it
out on TCC and it didn't work. :l
If #define STRING "\x9B0 p" almost works for you, you could try:
#define STRING "\x9B\48 p"
That, I should think, will solve the $9B0 -> $B0 problem.
(But I wonder why none of this seemed to work on TurboC….)
– Jim, on AutoPilot!
The \48 won't work because \nn syntax expects the number to be in octal. I
tried \48 with SAS C and it treated it as octal 4 followed by "8" since 8
is not an octal digit. \x30 or \60 should work.
Peter Wade
Autopiloting from London, England
Oh. <g> I hadn't ever had that problem before…does a decimal use a \nnn
syntax? You see, I have this looong header file filled with PC key
combination names and their decimal character equivalents. I _really_
don't want to re-do it (have to see if I have to), but it's worked fine so
far.
I knew I should have left the line "I've never done this before" in….
– Jim, on AutoPilot!
Kernighan and Ritchie (second edition) don't mention any way of specifying
the character code in decimal, only hex and octal. Some compilers might
have their own extensions to this but they would have to be different from
\nnn otherwise the compiler would have no way to tell if \123 was decimal
or octal (or they would not be compatible with ANSI C).
Peter Wade
Autopiloting from London, England
That's what I was afraid of. 🙁
Oh, I just remembered…I didn't use the '\nnn' notation on the keydefs.
I used integers, as in "#define ESC 27." So, I guess it's no loss; just
something to avoid in the future. 🙂
(Who in the universe uses octal anyway?! <g>)
– Jim, on AutoPilot!
Jim,
> (Who in the universe uses octal anyway?! <g>)
CIS user IDs are octal numbers. 😉
Malcolm
>> CIS user IDs are octal numbers. 😉
<gasp> I never thought of that! You know, I don't remember ever having
seen an "8" in a user ID number (not that I ever paid that much attention
anyway <g>).
– Jim, on AutoPilot!
Jim,
Think DEC … user ids are more appropriately known as PPNs — programmer
project numbers.
-sja
Jim,
the United States Navy, for one. The Harris 300 is all octal <g>.
Yeah, it's what is known as a three-bit computer …
DJ
On AP from Queens, the thrill and excitement of the Big Apple
Where a little paranoia keeps you healthy and sane …
Harris 300? As in Harris Computer Systems? I've worked at Harris in the
Security group for the last couple of years, and I've never even heard of that
system. (Not surprisingly, I guess….They made a lot of different systems).
I almost went to work for Harris in Melbourne, FL a long time ago. They
had some neat phototypesetting stuff, plus computers and semiconductors,
at the time.
DJ
On AP from Queens, the thrill and excitement of the Big Apple
Where a little paranoia keeps you healthy and sane …
Steve,
as I recall, the official Navy designation for the beast is the
AN/UYK-62(V). Anyway, there were two different cases, but basically the
same guts. One mainframe size, and one mini-case (for submarines). All
octal, ECL, and actually quite speedy, if the program was written
correctly (and some weren't!).
PS: Sorry about delay, just got out of the hospital.
Jim,
And I'd been hoping that there was an easy answer to defining arbitrary
strings. I _knew_ that I hated C, but had forgotten why.
Thanks for your help.
Regards,
Shraddhan (via AP from Hertfordshire, England)
Hey, I know! Just use the string "x0 p" and then change it to $9b"0 p" in
the executable with NewZap! <g>
Seriously, I'll work on that string thing and get back to you.
– Jim, on AutoPilot!
You have come across a problem wuth the way C handles hex character
constants. According to Kernighan and Ritchie the \xnn syntax can have as
many hex digits as you like. This means if you try and follow it with a
character which is also a hex digit the compiler thinks it is part of the
hex humber.
You can get round this by coding the 0 as another hex constant. The space
after the 0 is not a problem. Try :
#define my_string "\x9B\x30 p"
N.B. You don't need to include the final 0 because C string handling
functions always assume a 0 at the end of a string.
Peter Wade
Autopiloting from London, England
> According to Kernighan and Ritchie the \xnn syntax can have as many
> hex digits as you like.
Did these K & R guys do any _real_ programming, I wonder? Back in the
days when they invented C, surely all characters were at most 8 bits long.
Thanks for your help.
Regards,
Shraddhan (via AP from Hertfordshire, England)
Hi Shraddan,
To answer your second Q:
Define a struct: struct Lookup {
char *string;
int (*func)(void); /* Pointer to func taking no params and returning
int */ };
Now you lookup table is an array of these, ie struct Lookup Table[]= {
{"String 1", func1},
{"String 2", func2} };
Obviously, you must have some functions like int func1(void);
Steve the G. [BEDFORDSHIRE, UK]
> Did these K & R guys do any _real_ programming, I wonder?
Well Ritchie invented Unix using C 🙂
> Back in the days when they invented C, surely all characters were at
> most 8 bits long.
Maybe not. I've heard that some of the minis and mainframes they used in
those days had some weird word lengths. Before my time though.
Peter Wade
Autopiloting from London, England
"weird word lengths"
True. One of the CDC series (6400, maybe?) had a 60 bit word.
Well, I've read lots of answers to this question about the string constant,
but there is one relatively easy solution that the ANSI committee suggests
that works real well:
#define WHATEVER "\x9b" "0 p"
The ANSI standard says that adjacent string constants are concatenated by
the tokenizer.
Next best, but slightly less understandable, is the now-famous "\x9b\x30 p".
–Doug
Doug,
Having asked the question that started this thread off, I went away and
used the advice I had received. Some days later, I came back with an even
more difficult problem (for me), but before I could compose my question, I
found that you had already answered it. – Thanks!
I had no idea that adjacent string constants would be concatenated by the
tokenizer. I wonder why the authors of the books I looked at didn't seem
to mention this – or at least, not bother to refer to the technique in the
indexes to their books.
Maybe I'm just too pedantic at heart, as I really like to be able to refer
to books which give me the facts, the whole facts, and nothing but the
facts. Unfortunately, such books seem nowadays to be few and far between
– and out of print, too.
Many years ago, I was dogmatically anti-'C'. A lot of my dislike of the
language can be traced to the original K&R book, which has always struck
me as being a half-baked attempt at defining a language. It seems to me
that most 'C' books since then have followed their style.
What I'm trying to say really is that I think I would have little hope of
being able to write anything decent in C without the support I am able to
receive here.
Thanks again.
Regards,
Shraddhan (via AP from Hertfordshire, England)
Thanks – glad I could be of help.
I don't know what books you are using, but string concatenation was an
extension added by the ANSI committee and wasn't present in K&R. If you're
using older books, it might not be mentioned (or maybe they were just too
lazy to revise the example…)
At the same time as string concatenation, the ANSI committee added "token
pasting" and "stringization" operators to preprocessor macros. They work
like this:
#define FOO(x) abc##x
#define STR(x) #x
The macro invocation
FOO(xyz)
expands to
abcxyz
and the macro invocation
STR(xyz)
expands to "xyz". These are pretty useful in certain specific cases.
–Doug