# Why does this work? (rot13 function)

Discussion in 'C Programming' started by Eirik, Dec 16, 2003.

1. ### EirikGuest

This is a little function I wrote, inspired by the thread
"Urgent HELP! required for Caesar Cipher PLEASE"

\$ cat /home/keisar/bin/c/ymse/rot13.h

char rot13(char character)
{
int changed;
changed = character - 'a' + 'n';
return changed;
}

I find two things strange about this code:
1) I don't have to specify that b should be replaced by n,
c by o and so on. How come?
2) The function returns a char(char rot13), but changed
is an integer. How is that possible?

Eirik, Dec 16, 2003

2. ### Joona I PalasteGuest

Eirik <> scribbled the following:
> This is a little function I wrote, inspired by the thread
> "Urgent HELP! required for Caesar Cipher PLEASE"

> \$ cat /home/keisar/bin/c/ymse/rot13.h

> char rot13(char character)
> {
> int changed;
> changed = character - 'a' + 'n';
> return changed;
> }

> I find two things strange about this code:
> 1) I don't have to specify that b should be replaced by n,
> c by o and so on. How come?

Actually, if we're strict about the C standard, you DO have to
specify that b should be replaced by n and so on. You happen to be
using a character set where 'a'...'z' are contiguous, but the C
standard allows for other character sets.
Provided 'a'...'z' are contiguous, the above code works, because
chars are just integer types in C.

> 2) The function returns a char(char rot13), but changed
> is an integer. How is that possible?

Because chars are integer types. Unlike Pascal, C does not require
conversion functions between characters and their numeric values,
but treats them as interchangable by themselves.

--
/-- Joona Palaste () ------------- Finland --------\
\-- http://www.helsinki.fi/~palaste --------------------- rules! --------/
"Show me a good mouser and I'll show you a cat with bad breath."
- Garfield

Joona I Palaste, Dec 16, 2003

3. ### Hallvard B FurusethGuest

Eirik wrote:

> This is a little function I wrote, inspired by the thread
> "Urgent HELP! required for Caesar Cipher PLEASE"
>
> \$ cat /home/keisar/bin/c/ymse/rot13.h
>
> char rot13(char character)
> {
> int changed;
> changed = character - 'a' + 'n';
> return changed;
> }

See Joona's reply. Except, it only works for characters a-m and A-M.
It gives the wrong result for n-z and N-Z. Try this:

#include <ctype.h>

char rot13(char character)
{
int changed;
if (tolower((unsigned char)character) >= 'n')
changed = character - 'n' + 'a';
else
changed = character + 'n' - 'a';
return changed;
}

Still only works for some character sets, of course.

The argument to tolower() is cast to unsigned char because a 'char'
can be negative, while tolower() & co expect characters to be in
the range of 'unsigned char'.

--
Hallvard

Hallvard B Furuseth, Dec 16, 2003
4. ### Arthur J. O'DwyerGuest

On Tue, 16 Dec 2003, Eirik wrote:
>
> This is a little function I wrote, inspired by the thread
> "Urgent HELP! required for Caesar Cipher PLEASE"
>
> \$ cat /home/keisar/bin/c/ymse/rot13.h

Something nobody else has pointed out yet: Executable code
like the function below should *not* be in a header file (ending
with ".h", as you have above). It should be in a separate
translation unit, in a source file ending with ".c", and you
should learn how to use your compiler to compile projects
consisting of multiple ".c" source files.
<OT> Using gcc, it's easy:
% gcc -W -Wall -ansi -pedantic -O2 mainfile.c rot13.c
(and any other source files in the project). All the options
are only there to catch mistakes in your code; if you write
perfect code, you don't need them. ;-) [In other words, you
*do* need them. Always.]
</OT>

> char rot13(char character)
> {
> int changed;
> changed = character - 'a' + 'n';
> return changed;
> }
>
> I find two things strange about this code:
> 1) I don't have to specify that b should be replaced by n,
> c by o and so on. How come?

Because your system, like most systems in the world today,
uses ASCII to represent characters. Part of the ASCII character
table looks like this:

"...]^_`abcdefghijklmnopqrstuvwxyz{|}..."

See how all the lowercase letters are packed together, in order?
That's why your code works (do the math yourself as to why it
converts 'b' to 'o').
As Joona notes, all your code is doing is adding 'n'-'a', or 13
(assuming ASCII), to the character it receives. Which is why it
fails miserably to convert 'o' to 'b', or 'z' to 'a'.
Your code won't work on some other real-life systems out there,
and it might even cause demons to fly out of your nose on the
Death Station 9000. (Google for it.) So it's not really the
best way to do it if you're writing code that's supposed to work
everywhere.

> 2) The function returns a char(char rot13), but changed
> is an integer. How is that possible?

You wrote 'int' instead of 'char', that's how. In C,
characters are treated just like little integers, so you can
do arithmetic on them, as you've already figured out. And of
course you can assign 'char' to 'int' and vice versa, because
that just involves narrowing or widening the integers involved.

Here's how you could write that code more portably, so it
wouldn't depend on the organization of the letters in your
character set:

#include <ctype.h>

int rot13(int c)
{
static char lookup[UCHAR_MAX] = {0};
static char Alpha[] = "abcdefghijklmnopqrstuvwxyz";
static int not_initialized_yet = 1;

if (not_initialized_yet) {
unsigned int i;
for (i=0; i < sizeof lookup; ++i) {
lookup = i;
}
for (i=0; i < sizeof Alpha; ++i) {
lookup[Alpha] = Alpha[(i+13) % 26];
lookup[toupper(Alpha)] = toupper(Alpha[(i+13) % 26]);
}
not_initialized_yet = 0;
}

return lookup[c];
}

Doesn't that look complicated, now? But note that most of the
time -- *all* the time after the first time you call the function --
it doesn't even need to do any arithmetic! It's just a simple
table lookup, plus some complicated stuff to initialize the table.

I changed 'char' to 'int' to bring 'rot13' in line with similar
standard functions like 'toupper', which take and return 'int'.
As you've found out, 'char' is *almost* always replaceable by 'int'
(one big exception being text strings, obviously).

HTH,
-Arthur

Arthur J. O'Dwyer, Dec 16, 2003
5. ### Larry DoolittleGuest

Arthur J. O'Dwyer wrote:
> <OT> Using gcc, it's easy:
> % gcc -W -Wall -ansi -pedantic -O2 mainfile.c rot13.c
> (and any other source files in the project). All the options
> are only there to catch mistakes in your code; if you write
> perfect code, you don't need them. ;-) [In other words, you
> *do* need them. Always.]
></OT>

My biggest complaint about gcc -W -Wall is that it barks at unused
parameters. If someone tells me how to make

int foo(void);
int bar(char*cd)
{
foo();
return 0;
}

standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi -std=c99"
without warnings, I'd like to hear about it. My default set of
flags to gcc now also includes
-Wpointer-arith -Wcast-qual -Wshadow

- Larry

Larry Doolittle, Dec 16, 2003
6. ### Larry DoolittleGuest

In article <>, Larry Doolittle wrote:
>> <OT> Using gcc, [the mandatory flags are]:
>> % gcc -W -Wall -ansi -pedantic -O2 mainfile.c rot13.c
>></OT>

>
> My biggest complaint about gcc -W -Wall is that it barks at unused
> parameters. If someone tells me how to make [a perfectly good c program]
> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi -std=c99"
> without warnings, I'd like to hear about it.

Sorry, guys, somehow I thought the fix might be _inside_ the C program.
But just a _little_ more experimentation taught me that there is really
nothing wrong with the program, and I just need to add -Wno-unused-parameter
to the gcc options string. So this whole thing is still <OT>, although
I had myself confused enough I didn't realize it.

- Larry

Larry Doolittle, Dec 16, 2003
7. ### Eric SosmanGuest

Larry Doolittle wrote:
>
> Arthur J. O'Dwyer wrote:
> > <OT> Using gcc, it's easy:
> > % gcc -W -Wall -ansi -pedantic -O2 mainfile.c rot13.c
> > (and any other source files in the project). All the options
> > are only there to catch mistakes in your code; if you write
> > perfect code, you don't need them. ;-) [In other words, you
> > *do* need them. Always.]
> ></OT>

>
> My biggest complaint about gcc -W -Wall is that it barks at unused
> parameters. If someone tells me how to make
>
> int foo(void);
> int bar(char*cd)
> {
> foo();
> return 0;
> }
>
> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi -std=c99"
> without warnings, I'd like to hear about it. My default set of
> flags to gcc now also includes
> -Wpointer-arith -Wcast-qual -Wshadow

I've had success with

int bar(char *cd) {
(void)cd;
foo();
return 0;
}

Of course, the Standard provides no way to prevent the compiler
from issuing whatever warnings it feels like. If it objects to
the color of your socks it's free to say so, so long as it
accepts and runs an otherwise conforming program despite the
sartorial cluelessness of the coder.

--

Eric Sosman, Dec 16, 2003
8. ### Martin DickoppGuest

[OT] gcc and unused parameters (was Re: Why does this work? (rot13 function))

Larry Doolittle <> writes:

> My biggest complaint about gcc -W -Wall is that it barks at unused
> parameters. If someone tells me how to make
>
> int foo(void);
> int bar(char*cd)
> {
> foo();
> return 0;
> }
>
> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi -std=c99"
> without warnings, I'd like to hear about it.

This is not standard conforming, but usually close enough in practice:

#ifdef __GNUC__
# define unused __attribute__ ((unused))
#else
# define unused
#endif

int foo(void);
int bar(char*cd unused)
{
foo();
return 0;
}

(BTW, `-ansi' is an alias for `-std=c89', so it makes little sense specify
both `-ansi' and `-std=c99'.)

Martin

Martin Dickopp, Dec 16, 2003
9. ### CBFalconerGuest

Larry Doolittle wrote:
>

.... snip ...
>
> My biggest complaint about gcc -W -Wall is that it barks at unused
> parameters. If someone tells me how to make
>
> int foo(void);
> int bar(char*cd)
> {
> foo();
> return 0;
> }
>
> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi
> -std=c99" without warnings, I'd like to hear about it. My default
> set of flags to gcc now also includes
> -Wpointer-arith -Wcast-qual -Wshadow

Rewrite it as:

int foo(void);
int bar(void)
{
foo();
return 0;
}

and you will get no warnings You will also save the overhead
of passing unused parameters in each call. You should also be
using -pedantic.

--
Chuck F () ()
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!

CBFalconer, Dec 17, 2003
10. ### Larry DoolittleGuest

In article <>, CBFalconer wrote:
> Larry Doolittle wrote:
>>

> ... snip ...
>> If someone tells me how to make
>>
>> int foo(void);
>> int bar(char*cd)
>> {
>> foo();
>> return 0;
>> }
>>
>> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi
>> -std=c99" without warnings, I'd like to hear about it. [chop]

>
> Rewrite it as:
>
> int foo(void);
> int bar(void)
> {
> foo();
> return 0;
> }
>
> and you will get no warnings You will also save the overhead
> of passing unused parameters in each call.

The point is that bar(char *) is defined to fit into a
general interface. Other functions are defined that _do_
use their parameters, and function pointers to both bar
and these other functions are passed around or stored in
tables. These function pointers have to all have the
same type.

Even a contrived example would take more space than I think
people want to wade through on this newsgroup. For a widely
used example, just look at <OT> POSIX sigaction subsystem,
in particular the sa_sigaction function </OT>.

> You should also be using -pedantic.

I do. And as pointed out elsethread, I "fixed" the (gcc-specific)
warnings by adding the (gcc-specific) "-Wno-unused-parameter" flag.
Interestingly, this adjustment is not needed with either "-W" or
"-Wall", only with both. In retrospect, I might have been able
to deduct that from the man page.

- Larry

Larry Doolittle, Dec 17, 2003
11. ### AlexGuest

Larry Doolittle <> wrote:
> In article <>, CBFalconer wrote:
>> Larry Doolittle wrote:
>>>

>> ... snip ...
>>> If someone tells me how to make
>>>
>>> int foo(void);
>>> int bar(char*cd)
>>> {
>>> foo();
>>> return 0;
>>> }
>>>
>>> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi
>>> -std=c99" without warnings, I'd like to hear about it. [chop]

>>
>> Rewrite it as:
>>
>> int foo(void);
>> int bar(void)
>> {
>> foo();
>> return 0;
>> }
>>
>> and you will get no warnings You will also save the overhead
>> of passing unused parameters in each call.

> The point is that bar(char *) is defined to fit into a
> general interface. Other functions are defined that _do_
> use their parameters, and function pointers to both bar
> and these other functions are passed around or stored in
> tables. These function pointers have to all have the
> same type.

> Even a contrived example would take more space than I think
> people want to wade through on this newsgroup. For a widely
> used example, just look at <OT> POSIX sigaction subsystem,
> in particular the sa_sigaction function </OT>.

If this is the case, then it is far better to explicitly
state your intention to disregard the parameter by casting
it to void. If you find that you have to do this most of
the time then the design of your general interface could
use some work.

Alex

Alex, Dec 17, 2003
12. ### CBFalconerGuest

Alex wrote:
> Larry Doolittle <> wrote:
> > In article <>, CBFalconer wrote:
> >> Larry Doolittle wrote:
> >>>
> >> ... snip ...
> >>> If someone tells me how to make
> >>>
> >>> int foo(void);
> >>> int bar(char*cd)
> >>> {
> >>> foo();
> >>> return 0;
> >>> }
> >>>
> >>> standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi
> >>> -std=c99" without warnings, I'd like to hear about it. [chop]
> >>
> >> Rewrite it as:
> >>
> >> int foo(void);
> >> int bar(void)
> >> {
> >> foo();
> >> return 0;
> >> }
> >>
> >> and you will get no warnings You will also save the overhead
> >> of passing unused parameters in each call.

>
> > The point is that bar(char *) is defined to fit into a
> > general interface. Other functions are defined that _do_
> > use their parameters, and function pointers to both bar
> > and these other functions are passed around or stored in
> > tables. These function pointers have to all have the
> > same type.

>

.... snip ...
>
> If this is the case, then it is far better to explicitly
> state your intention to disregard the parameter by casting
> it to void. If you find that you have to do this most of
> the time then the design of your general interface could
> use some work.

If he needs to drive a family of functions via something like a
pointer array, and only one needs a parameter, all must receive
such. Ignoring my earlier somewhat facetious reply, it is also
possible to use the parameter and let the compiler optimize that
use out. The results will depend on the compiler. The existance
of a warning does not indicate an error, it simply indicates that
the programmer should know what is going on.

int foo(int bar)
{
bar;
return 0;
}

--
Chuck F () ()
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!

CBFalconer, Dec 17, 2003
13. ### Larry DoolittleGuest

In article <>, CBFalconer wrote:
> Alex wrote:
>>
>> If this is the case, then it is far better to explicitly
>> state your intention to disregard the parameter by casting
>> it to void.

I don't understand this comment.

>> If you find that you have to do this most of
>> the time then the design of your general interface could
>> use some work.

>
> If he needs to drive a family of functions via something like a
> pointer array, and only one needs a parameter, all must receive
> such.

This is, in fact, my situation. I find many cases where
table-driven code is shorter to write and faster to execute
than equivalent written-out code, even with the occasional
superfluous parameter.

> Ignoring my earlier somewhat facetious reply, it is also
> possible to use the parameter and let the compiler optimize that
> use out. The results will depend on the compiler. The existance
> of a warning does not indicate an error, it simply indicates that
> the programmer should know what is going on.

Yes, but warnings that the knowledgable programmer cannot
suppress (with good code) are _bad_, because (as I suspect
you also believe) they reduce the emotional impact of new
and possibly dangerous warnings. Such warnings can appear
when code is modified (i.e., maintained), or compiled in a
different environment with different header files.

When I try to "use" a parameter as CBFalconer suggested,
gcc barks in a different way:
warning: statement with no effect
I can in turn fix that by modifying the "use" the statement to:
(void) cd;
Is this what Alex meant, above?

- Larry

Larry Doolittle, Dec 17, 2003
14. ### AlexGuest

Larry Doolittle <> wrote:
>> Alex wrote:
>>>
>>> If this is the case, then it is far better to explicitly
>>> state your intention to disregard the parameter by casting
>>> it to void.

> I don't understand this comment.

<snip>

> When I try to "use" a parameter as CBFalconer suggested,
> gcc barks in a different way:
> warning: statement with no effect
> I can in turn fix that by modifying the "use" the statement to:
> (void) cd;
> Is this what Alex meant, above?

Yes, that is indeed what I was talking about. This doesn't just
shut up the compiler, either. It makes it easier for the
programmer maintaining your code to see that you really didn't
care about the parameter.

Alex

Alex, Dec 17, 2003
15. ### David ResnickGuest

Larry Doolittle <> wrote in message news:<>...
> In article <>, Larry Doolittle wrote:
> >> <OT> Using gcc, [the mandatory flags are]:
> >> % gcc -W -Wall -ansi -pedantic -O2 mainfile.c rot13.c
> >></OT>

> >
> > My biggest complaint about gcc -W -Wall is that it barks at unused
> > parameters. If someone tells me how to make [a perfectly good c program]
> > standards-conforming, non-bloated, and run past "gcc -W -Wall -ansi -std=c99"
> > without warnings, I'd like to hear about it.

>
> Sorry, guys, somehow I thought the fix might be _inside_ the C program.
> But just a _little_ more experimentation taught me that there is really
> nothing wrong with the program, and I just need to add -Wno-unused-parameter
> to the gcc options string. So this whole thing is still <OT>, although
> I had myself confused enough I didn't realize it.
>
> - Larry

This discussion has gotten rather off topic, but...

An alternative way to do this is the following. It will fix your
problem with gcc, and won't do any harm with other compilers as far as
I can see. It requires that you intentionally state that the
parameter is not used, not a bad thing since that COULD indicate an
error rather than an interface over which you have no control. And it
has the benefit of documenting that the parameter is not used by the
function...

#include <stdio.h>

#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif

int foo(void)
{
printf("You've got foo!\n");
return 0;
}

int bar(char *cd UNUSED)
{
foo();
return 0;
}

int main(void)
{
char s[] = "hi there";
bar(s);
return 0;
}

gcc -W -Wall -ansi -std=c99 -pedantic -o foo foo.c
gives no complaints on the above...

-David

David Resnick, Dec 17, 2003
16. ### Randy HowardGuest

In article <>,
says...
> #include <stdio.h>
>
> #ifdef __GNUC__
> #define UNUSED __attribute__((unused))
> #else
> #define UNUSED
> #endif

This works for the simple case where the code simply never
used the parameter, in which case I would prefer to change the
interface if that is a possibility. However, where I usually
see this is in code that has conditional compilation, such
that some build options use the parameter and others do not.

Example, writing code to portably handle something (behind some
#ifdef platform stuff) that uses the parameter in some cases,
but not in others. You can still accomplish that with some
ugly conditional code in function prototypes and implementation
using a combination of the conditional platform logic and the
above, but it gets real ugly, real fast.

A simpler option for gcc (which the above only handles anyway)
is to use the -Wno-unused-parameter flag and not have to
bury strange looking UNUSED incantations throughout your
implementation. Much better to modify the makefile than the
source (IMO) just to quiet a minor warning.

--
Randy Howard _o
2reply remove FOOBAR \<,
______________________()/ ()______________________________________________
SCO Spam-magnet:

Randy Howard, Dec 18, 2003

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.