Why does this work? (rot13 function)

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

  1. Eirik

    Eirik Guest

    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
    #1
    1. Advertising

  2. 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
    #2
    1. Advertising

  3. 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
    #3
  4. 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
    #4
  5. 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
    #5
  6. 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
    #6
  7. Eirik

    Eric Sosman Guest

    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
    #7
  8. [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
    #8
  9. Eirik

    CBFalconer Guest

    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
    #9
  10. 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
    #10
  11. Eirik

    Alex Guest

    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
    #11
  12. Eirik

    CBFalconer Guest

    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
    #12
  13. 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
    #13
  14. Eirik

    Alex Guest

    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
    #14
  15. 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
    #15
  16. Eirik

    Randy Howard Guest

    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
    #16
    1. Advertising

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.
Similar Threads
  1. Horace Nunley

    why why why does function not work

    Horace Nunley, Sep 27, 2006, in forum: ASP .Net
    Replies:
    1
    Views:
    456
    =?Utf-8?B?UGV0ZXIgQnJvbWJlcmcgW0MjIE1WUF0=?=
    Sep 27, 2006
  2. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,839
    Smokey Grindel
    Dec 2, 2006
  3. Andy Dingley

    rot13 in a more Pythonic style?

    Andy Dingley, Feb 14, 2007, in forum: Python
    Replies:
    19
    Views:
    556
    Andy Dingley
    Feb 16, 2007
  4. thor
    Replies:
    3
    Views:
    343
    Daniel Pitts
    May 29, 2008
  5. Agent Spikes

    Shorter Rot13 Pure-C Implementation

    Agent Spikes, Mar 7, 2010, in forum: C Programming
    Replies:
    19
    Views:
    922
    Michael Foukarakis
    Mar 17, 2010
Loading...

Share This Page