Subtracting strings

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Krumble Bunk

    Subtracting strings

    Hi all,

    I have an elementary question, of which I never grasped, and i'd like
    to finally put it to rest.

    I have the following code, which although makes use of the string
    library, and could therefore be labelled as OT, the actual question I
    have relates to the code around it. This is the highly abridged
    version:

    const char *fname;
    const char *execname="/usr/bin/foo";
    char *pathname;
    char *str;

    ....
    strcpy(pathname ,execname); <- on my platform
    I plan to use strlcpy() :-)
    if((str=strrchr (pathname,'/')) != NULL)
    {
    *++str='\0';
    fname=execname+ (str - pathname); <- somehow, fname
    ends up being "foo" as intended
    } else {
    fname=execname;
    *pathname='\0';
    }

    I have checked the ascii values, for the above, and turning the
    resulting ascii codes (which seem to be concat'd together), I do not
    produce foo.

    Would appreciate any help on this, as it's frying my peonic brain!

    thanks

    KB.
  • Chris Dollin

    #2
    Re: Subtracting strings

    Krumble Bunk wrote:
    I have an elementary question, of which I never grasped, and i'd like
    to finally put it to rest.
    (You never actually pose a question, but I assume it to be
    "why does this work?")
    I have the following code, which although makes use of the string
    library, and could therefore be labelled as OT, the actual question I
    have relates to the code around it. This is the highly abridged
    version:
    >
    const char *fname;
    const char *execname="/usr/bin/foo";
    char *pathname;
    char *str;
    >
    ...
    strcpy(pathname ,execname); <- on my platform
    I plan to use strlcpy() :-)
    if((str=strrchr (pathname,'/')) != NULL)
    {
    *++str='\0';
    fname=execname+ (str - pathname); <- somehow, fname
    ends up being "foo" as intended
    `str` and `pathname` are pointers. Subtraction of pointers
    (that point into the same object) produces the difference
    between them -- how much to add to the second to get the first.
    It isn't "string subtraction".

    So after the test, if it succeeds, `str` points to the last `/`
    in `pathname`. Then it gets incremented (and a 0 written over
    the `/`, so that `pathname` points to the string up-to-but-not-
    including the `/`, ie, "/usr/bin"). Now the difference between
    `str` and `pathname` -- notice that `str` points into the same
    object as `pathname` does -- is the number of characters before
    the "foo" in the string. Adding that to `execname` gets a pointer
    to the "foo" in execname's "/usr/bin/foo".
    } else {
    fname=execname;
    *pathname='\0';
    }
    >
    I have checked the ascii values, for the above,
    Why? The code works whatever the character set is. (It might not
    be /useful/ on a machine with a different character set, but that's
    another matter.)
    and turning the
    resulting ascii codes (which seem to be concat'd together), I do not
    produce foo.
    >
    Would appreciate any help on this, as it's frying my peonic brain!
    Ah, well, that's the problem -- you should have asked for a
    /positronic/ brain, not a /peonic/ one.

    --
    "Thereafter , events may roll unheeded." /Foundation/

    Hewlett-Packard Limited registered office: Cain Road, Bracknell,
    registered no: 690597 England Berks RG12 1HN

    Comment

    • Krumble Bunk

      #3
      Re: Subtracting strings

      On Apr 17, 3:41 pm, Chris Dollin <chris.dol...@h p.comwrote:
      Krumble Bunk wrote:
      I have an elementary question, of which I never grasped, and i'd like
      to finally put it to rest.
      >
      (You never actually pose a question, but I assume it to be
      "why does this work?")
      >
      >
      >
      I have the following code, which although makes use of the string
      library, and could therefore be labelled as OT, the actual question I
      have relates to the code around it. This is the highly abridged
      version:
      >
      const char *fname;
      const char *execname="/usr/bin/foo";
      char *pathname;
      char *str;
      >
      ...
      strcpy(pathname ,execname); <- on my platform
      I plan to use strlcpy() :-)
      if((str=strrchr (pathname,'/')) != NULL)
      {
      *++str='\0';
      fname=execname+ (str - pathname); <- somehow, fname
      ends up being "foo" as intended
      >
      `str` and `pathname` are pointers. Subtraction of pointers
      (that point into the same object) produces the difference
      between them -- how much to add to the second to get the first.
      It isn't "string subtraction".
      >
      So after the test, if it succeeds, `str` points to the last `/`
      in `pathname`. Then it gets incremented (and a 0 written over
      the `/`, so that `pathname` points to the string up-to-but-not-
      including the `/`, ie, "/usr/bin"). Now the difference between
      `str` and `pathname` -- notice that `str` points into the same
      object as `pathname` does -- is the number of characters before
      the "foo" in the string. Adding that to `execname` gets a pointer
      to the "foo" in execname's "/usr/bin/foo".
      >
      } else {
      fname=execname;
      *pathname='\0';
      }
      >
      I have checked the ascii values, for the above,
      >
      Why? The code works whatever the character set is. (It might not
      be /useful/ on a machine with a different character set, but that's
      another matter.)
      >
      and turning the
      resulting ascii codes (which seem to be concat'd together), I do not
      produce foo.
      >
      Would appreciate any help on this, as it's frying my peonic brain!
      >
      Ah, well, that's the problem -- you should have asked for a
      /positronic/ brain, not a /peonic/ one.
      >
      --
      "Thereafter , events may roll unheeded." /Foundation/
      >
      Hewlett-Packard Limited registered office: Cain Road, Bracknell,
      registered no: 690597 England Berks RG12 1HN
      Chris - many thanks for your help! Answers my question nicely.

      cheers

      KB.

      Comment

      • Jens Thoms Toerring

        #4
        Re: Subtracting strings

        Krumble Bunk <krumblebunk@gm ail.comwrote:
        I have the following code, which although makes use of the string
        library, and could therefore be labelled as OT, the actual question I
        have relates to the code around it.
        <Reordered the paragraphs of the post a bit>
        I have checked the ascii values, for the above, and turning the
        resulting ascii codes (which seem to be concat'd together), I do not
        produce foo.
        Lookig at ASCII values won't explain the mystery;-) You're not
        "subtracing strings" (whatever this might mean), you're subtracting
        pointers. This is allowed as long as the pointers involved point to
        the same object (as they do here since they both point somewhere
        into the string pointed to by 'pathname') and then return the
        number of elements between the two pointers.
        This is the highly abridged version:
        const char *fname;
        const char *execname="/usr/bin/foo";
        char *pathname;
        char *str;
        ...
        strcpy(pathname ,execname); <- on my platform
        I plan to use strlcpy() :-)
        Whatever you use (I don't know what strlcpy() is supposed to do),
        I hope in the left-out code you allocate enough memory for 'pathname'
        to point to.
        if((str=strrchr (pathname,'/')) != NULL)
        {
        *++str='\0';
        I guess you meant here to have

        *str++ = '\0';

        since I guess you intended to overwrite the '/' and not the 'f'
        following it.

        Given that what 'execname' points to has successfully been copied to
        memory pointed to what 'pathname' points to, 'str' now points to
        the "foo" part in the memory assigned to 'pathname', which contains

        |------ pathname points here
        v
        '/', 'u', 's', 'r', '/', 'b', 'i', 'n', '\0', 'f', 'o', 'o', '\n'
        ^
        str points here --------------|

        'str - pathname' is then the number of characters in between (9).
        fname=execname+ (str - pathname); <- somehow, fname
        'fname' points now to 'execname + 9', i.e. the 10th character in the
        memory pointed to by 'execname'. And, probably not surprising anymore,
        at this position starts the "foo" part of that string.
        ends up being "foo" as intended
        } else {
        fname=execname;
        *pathname='\0';
        }
        Regards, Jensa
        --
        \ Jens Thoms Toerring ___ jt@toerring.de
        \______________ ____________ http://toerring.de

        Comment

        • Krumble Bunk

          #5
          Re: Subtracting strings

          On Apr 17, 3:45 pm, j...@toerring.d e (Jens Thoms Toerring) wrote:
          Krumble Bunk <krumbleb...@gm ail.comwrote:
          I have the following code, which although makes use of the string
          library, and could therefore be labelled as OT, the actual question I
          have relates to the code around it.
          >
          <Reordered the paragraphs of the post a bit>
          >
          I have checked the ascii values, for the above, and turning the
          resulting ascii codes (which seem to be concat'd together), I do not
          produce foo.
          >
          Lookig at ASCII values won't explain the mystery;-) You're not
          "subtracing strings" (whatever this might mean), you're subtracting
          pointers. This is allowed as long as the pointers involved point to
          the same object (as they do here since they both point somewhere
          into the string pointed to by 'pathname') and then return the
          number of elements between the two pointers.
          >
          This is the highly abridged version:
          const char *fname;
          const char *execname="/usr/bin/foo";
          char *pathname;
          char *str;
          ...
          strcpy(pathname ,execname); <- on my platform
          I plan to use strlcpy() :-)
          >
          Whatever you use (I don't know what strlcpy() is supposed to do),
          I hope in the left-out code you allocate enough memory for 'pathname'
          to point to.
          >
          if((str=strrchr (pathname,'/')) != NULL)
          {
          *++str='\0';
          >
          I guess you meant here to have
          >
          *str++ = '\0';
          >
          since I guess you intended to overwrite the '/' and not the 'f'
          following it.
          >
          Given that what 'execname' points to has successfully been copied to
          memory pointed to what 'pathname' points to, 'str' now points to
          the "foo" part in the memory assigned to 'pathname', which contains
          >
          |------ pathname points here
          v
          '/', 'u', 's', 'r', '/', 'b', 'i', 'n', '\0', 'f', 'o', 'o', '\n'
          ^
          str points here --------------|
          >
          'str - pathname' is then the number of characters in between (9).
          >
          fname=execname+ (str - pathname); <- somehow, fname
          >
          'fname' points now to 'execname + 9', i.e. the 10th character in the
          memory pointed to by 'execname'. And, probably not surprising anymore,
          at this position starts the "foo" part of that string.
          >
          ends up being "foo" as intended
          } else {
          fname=execname;
          *pathname='\0';
          }
          >
          Regards, Jensa
          --
          \ Jens Thoms Toerring ___ j...@toerring.d e
          \______________ ____________ http://toerring.de
          Excellent!! Thanks a lot Jens, loved the ascii art - that helped
          hammer it in for me. Thanks to both of you!

          KayBee.

          Comment

          • fred.l.kleinschmidt@boeing.com

            #6
            Re: Subtracting strings

            On Apr 17, 6:51 am, Krumble Bunk <krumbleb...@gm ail.comwrote:
            Hi all,
            >
            I have an elementary question, of which I never grasped, and i'd like
            to finally put it to rest.
            >
            I have the following code, which although makes use of the string
            library, and could therefore be labelled as OT, the actual question I
            have relates to the code around it.  This is the highly abridged
            version:
            >
            const char *fname;
            const char *execname="/usr/bin/foo";
            char *pathname;
            char *str;
            >
            ...
            strcpy(pathname ,execname);
            One hopes that pathname has been set to point to some
            legitimate storage long enough to hold the number of
            characters in execname.
            if((str=strrchr (pathname,'/')) != NULL)
                {
                    *++str='\0';
            This places a NUL character in the position *AFTER the
            last slash (Chris Dollin's comment notwithstanding )
                    fname=execname+ (str - pathname);
                } else {
                    fname=execname;
                    *pathname='\0';
                }
            >
            So the final result is that fname points to the first
            character after the last slash in execname
            (i.e., the file name stripped of the directory component),
            and pathname points to the directory portion,
            INCLUDING the last slash, if there is one.

            All that stuff with subtracting pointers is unnecessary.
            All you need is:

            strcpy(pathname ,execname);
            str = strrchr( pathname, '/');
            if ( str ) {
            *str = '\0';
            fname = str++;
            }
            else {
            fname = execname;
            *pathname = '\0';
            }

            Note that here pathname ends up pointing to a string
            that is the directory portion WITHOUT the trailing slash.

            Under posix, one would use basename() and dirname().

            --
            Fred Kleinschmdit

            Comment

            • Eric Sosman

              #7
              Re: Subtracting strings

              Krumble Bunk wrote:
              Hi all,
              >
              I have an elementary question, of which I never grasped, and i'd like
              to finally put it to rest.
              >
              I have the following code, which although makes use of the string
              library, and could therefore be labelled as OT, the actual question I
              have relates to the code around it. This is the highly abridged
              version:
              >
              const char *fname;
              const char *execname="/usr/bin/foo";
              char *pathname;
              char *str;
              >
              ...
              strcpy(pathname ,execname); <- on my platform
              I plan to use strlcpy() :-)
              if((str=strrchr (pathname,'/')) != NULL)
              {
              *++str='\0';
              fname=execname+ (str - pathname); <- somehow, fname
              ends up being "foo" as intended
              } else {
              fname=execname;
              *pathname='\0';
              }
              >
              I have checked the ascii values, for the above, and turning the
              resulting ascii codes (which seem to be concat'd together), I do not
              produce foo.
              >
              Would appreciate any help on this, as it's frying my peonic brain!
              What you've done is perfectly all right (assuming the
              elisions are filled in correctly). Let's suppose strrchr()
              returns a non-NULL:

              - Then str starts out pointing at the rightmost '/' in
              pathname. In your example this is the ninth character,
              the one at offset 8.

              - You increment str to make it point at the tenth character,
              the 'f' at offset 9.

              - You deposit a zero byte at that position, so that when
              pathname is viewed as a string it contains "/usr/bin/".

              - Here's the part that seems to mystify you: Subtracting
              two pointers gives the number of array elements that lie
              between them. Since str == pathname + 9, it follows that
              str - pathname == 9.

              - ... so fname = execname = (str - pathname) is the same
              as fname = execname + 9, that is, fname now points at
              the tenth character in execname, the 'f'.

              --
              Eric.Sosman@sun .com

              Comment

              • Eric Sosman

                #8
                Re: Subtracting strings

                fred.l.kleinsch midt@boeing.com wrote:
                [...]
                All that stuff with subtracting pointers is unnecessary.
                All you need is:
                >
                strcpy(pathname ,execname);
                str = strrchr( pathname, '/');
                if ( str ) {
                *str = '\0';
                fname = str++;
                }
                else {
                fname = execname;
                *pathname = '\0';
                }
                >
                Note that here pathname ends up pointing to a string
                that is the directory portion WITHOUT the trailing slash.
                ... and fname points to a string of length zero.

                --
                Eric.Sosman@sun .com

                Comment

                • Keith Thompson

                  #9
                  Re: Subtracting strings

                  Krumble Bunk <krumblebunk@gm ail.comwrites:
                  I have an elementary question, of which I never grasped, and i'd like
                  to finally put it to rest.
                  >
                  I have the following code, which although makes use of the string
                  library, and could therefore be labelled as OT, the actual question I
                  have relates to the code around it. This is the highly abridged
                  version:
                  [snip]

                  Others have answered your (implicit) question; I'll just mention a
                  couple of points about posting here.

                  Code that uses the C standard library is absolutely topical here.

                  The FAQ for this newsgroup is an excellent resource; you can find it
                  at <http://www.c-faq.com/>. (I'm not suggesting that the answer to
                  your question could have been found there, just that it's worth
                  knowing about).

                  When you post a followup, please trim the quoted text down to what's
                  necessary for your response to make sense. It's rarely necessary to
                  quote the entire article. Don't quote signatures unless you're
                  actually commenting on them.

                  --
                  Keith Thompson (The_Other_Keit h) <kst-u@mib.org>
                  Nokia
                  "We must do something. This is something. Therefore, we must do this."
                  -- Antony Jay and Jonathan Lynn, "Yes Minister"

                  Comment

                  • Kenneth Brody

                    #10
                    Re: Subtracting strings

                    Krumble Bunk wrote:
                    [...]
                    const char *fname;
                    const char *execname="/usr/bin/foo";
                    char *pathname;
                    char *str;
                    >
                    ...
                    strcpy(pathname ,execname); <- on my platform
                    I plan to use strlcpy() :-)
                    I assume you have allocated space for pathname somewhere?
                    if((str=strrchr (pathname,'/')) != NULL)
                    {
                    *++str='\0';
                    fname=execname+ (str - pathname); <- somehow, fname
                    ends up being "foo" as intended
                    } else {
                    fname=execname;
                    *pathname='\0';
                    }
                    >
                    I have checked the ascii values, for the above, and turning the
                    resulting ascii codes (which seem to be concat'd together), I do not
                    produce foo.
                    >
                    Would appreciate any help on this, as it's frying my peonic brain!
                    Because str points somewhere within pathname, "str-pathname" gives
                    you the offset within pathname. In this case, it points to the
                    character after the last '/', which is the 'f' in "foo".

                    When added to execname, it points to the character in execname at
                    the same offset. Because pathname was a copy of execname, it, too,
                    points to the 'f' in "foo", but within execname.

                    Perhaps it would make more sense to you with something like this:

                    int OffsetToFname;
                    ...
                    *++str = '\0';
                    OffsetToFname = str - pathname;
                    fname = execname + OffsetToFname;

                    Of course, unless you are going to free(pathname), I'm not sure why
                    you don't simply point fname to the return from strrchr().

                    --
                    +-------------------------+--------------------+-----------------------+
                    | Kenneth J. Brody | www.hvcomputer.com | #include |
                    | kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer .h|
                    +-------------------------+--------------------+-----------------------+
                    Don't e-mail me at: <mailto:ThisIsA SpamTrap@gmail. com>

                    Comment

                    • lawrence.jones@siemens.com

                      #11
                      Re: Subtracting strings

                      Krumble Bunk <krumblebunk@gm ail.comwrote:
                      >
                      const char *fname;
                      const char *execname="/usr/bin/foo";
                      char *pathname;
                      char *str;
                      >
                      ...
                      strcpy(pathname ,execname); <- on my platform I plan to use strlcpy() :-)
                      Hopefully, some of the omitted code allocates space and sets pathname to
                      point to it. Otherwise, you're using an ininitialized variable.
                      if((str=strrchr (pathname,'/')) != NULL)
                      {
                      *++str='\0';
                      fname=execname+ (str - pathname); <- somehow, fname ends up being "foo" as intended
                      At this point, str is pointing to the character just past the last "/"
                      in pathname, which is the beginning of "foo". (str - pathname) is the
                      number of characters between pathname and str (9 in this case), so
                      adding that to execname causes fname to point 9 characters past
                      execname, which is the beginning of "foo" in the original string.

                      For pointer subtraction to be valid, both pointers must point to
                      elements of the same array and the result of the subtraction is the
                      number of elements between them (e.g., for pointers to int, you get the
                      number of ints).

                      -Larry Jones

                      Something COULD happen today. And if anything DOES,
                      by golly, I'm going to be ready for it! -- Calvin

                      Comment

                      • CBFalconer

                        #12
                        Re: Subtracting strings

                        Krumble Bunk wrote:
                        >
                        I have an elementary question, of which I never grasped, and i'd
                        like to finally put it to rest.
                        >
                        I have the following code, which although makes use of the string
                        library, and could therefore be labelled as OT, the actual
                        question I have relates to the code around it. This is the
                        highly abridged version:
                        >
                        const char *fname;
                        const char *execname="/usr/bin/foo";
                        char *pathname;
                        ^^^^^^^^
                        This is a pointer to memory. It doesn't point to any memory yet.
                        char *str;
                        >
                        ...
                        strcpy(pathname ,execname); <- on my platform
                        Thus this just blows up.

                        --
                        [mail]: Chuck F (cbfalconer at maineline dot net)
                        [page]: <http://cbfalconer.home .att.net>
                        Try the download section.


                        ** Posted from http://www.teranews.com **

                        Comment

                        • Chris Dollin

                          #13
                          Re: Subtracting strings

                          CBFalconer wrote:
                          Krumble Bunk wrote:
                          >>
                          >I have an elementary question, of which I never grasped, and i'd
                          >like to finally put it to rest.
                          >>
                          >I have the following code, which although makes use of the string
                          >library, and could therefore be labelled as OT, the actual
                          >question I have relates to the code around it. This is the
                          >highly abridged version:
                          >>
                          >const char *fname;
                          >const char *execname="/usr/bin/foo";
                          >char *pathname;
                          ^^^^^^^^
                          This is a pointer to memory. It doesn't point to any memory yet.
                          >
                          >char *str;
                          >>
                          >...
                          >strcpy(pathnam e,execname); <- on my platform
                          >
                          You're making unwarrented assumptions about the contents of `...`.

                          --
                          "It was starting to end, after what seemed most of /Nine Princes in Amber/
                          eternity to me."

                          Hewlett-Packard Limited registered office: Cain Road, Bracknell,
                          registered no: 690597 England Berks RG12 1HN

                          Comment

                          • Chris Dollin

                            #14
                            Re: Subtracting strings

                            fred.l.kleinsch midt@boeing.com wrote:
                            >if((str=strrch r(pathname,'/')) != NULL)
                            >
                            >    {
                            >        *++str='\0';
                            >
                            This places a NUL character in the position *AFTER the
                            last slash (Chris Dollin's comment notwithstanding )
                            Oops. Good catch, thanks.

                            --
                            "There's something about this place," said Peter presently, /Gaudy Night/
                            "that alters one's values."

                            Hewlett-Packard Limited Cain Road, Bracknell, registered no:
                            registered office: Berks RG12 1HN 690597 England

                            Comment

                            • Topi Linkala

                              #15
                              Re: Subtracting strings

                              Kenneth Brody wrote:
                              Krumble Bunk wrote:
                              [...]
                              >
                              >>const char *fname;
                              >>const char *execname="/usr/bin/foo";
                              >>char *pathname;
                              >>char *str;
                              >>
                              >>...
                              >>strcpy(pathna me,execname); <- on my platform
                              >>I plan to use strlcpy() :-)
                              >
                              >
                              I assume you have allocated space for pathname somewhere?
                              >
                              >
                              >>if((str=strrc hr(pathname,'/')) != NULL)
                              > {
                              > *++str='\0';
                              > fname=execname+ (str - pathname); <- somehow, fname
                              >>ends up being "foo" as intended
                              > } else {
                              > fname=execname;
                              > *pathname='\0';
                              > }
                              >>
                              >>I have checked the ascii values, for the above, and turning the
                              >>resulting ascii codes (which seem to be concat'd together), I do not
                              >>produce foo.
                              >>
                              >>Would appreciate any help on this, as it's frying my peonic brain!
                              >
                              >
                              Because str points somewhere within pathname, "str-pathname" gives
                              you the offset within pathname. In this case, it points to the
                              character after the last '/', which is the 'f' in "foo".
                              >
                              When added to execname, it points to the character in execname at
                              the same offset. Because pathname was a copy of execname, it, too,
                              points to the 'f' in "foo", but within execname.
                              >
                              Perhaps it would make more sense to you with something like this:
                              >
                              int OffsetToFname;
                              ...
                              *++str = '\0';
                              OffsetToFname = str - pathname;
                              fname = execname + OffsetToFname;
                              >
                              Of course, unless you are going to free(pathname), I'm not sure why
                              you don't simply point fname to the return from strrchr().
                              See his code. After the code has completed pathname has the following
                              characters: '/', 'u', 's', 'r', '/', 'b', 'i', 'n', '/', '\0', 'o', 'o',
                              '\0'

                              So if he would just set fname to str then fname would point to an empty
                              string.

                              Why he wants to retain the last slash is a question. Normally one would
                              have pathname as "/usr/bin" and fname as "foo" but maybe he wants to
                              have them so that he can later concatenate them without adding the '/'.

                              Topi
                              --
                              "The whole problem with the world is that fools and fanatics are
                              always so certain of themselves, but wiser people so full of doubts."
                              - Bertrand Russell
                              "How come he didn't put 'I think' at the end of it?" - Anonymous

                              Comment

                              Working...