[comp.lang.c] Pointer arithmetic

kdq@demott.com (Kevin D. Quitt) (01/05/91)

    I originally posted to comp.lang.c++ (don't know how it got there):

>    What about the subtraction of pointers to get a constant?  I discovered
>that gcc does not allow:
>
>	int = strchr( string, char ) - &string;
>
>to determine the position of char within string.  Is there a (good) reason
>for disallowing this?  BTW, Microsoft C *does* accept this.


    I rapidly received two responses indicating that the & was the problem.
(Nobody picked on using int and char as variables :-)


    The & was "artistic" in my example.  Microsoft C does allow &string,
but provides a warning that the ampersand is ignored.

    The actual code is:


const unsigned char *chars   = "some string"
unsigned char pass[];

    c   = strchr( chars, toupper( pass[ i ]) ) - chars + 1;


    gcc would not accept this no matter what I did.

-- 
 _
Kevin D. Quitt         demott!kdq   kdq@demott.com
DeMott Electronics Co. 14707 Keswick St.   Van Nuys, CA 91405-1266
VOICE (818) 988-4975   FAX (818) 997-1190  MODEM (818) 997-4496 PEP last

tim@proton.amd.com (Tim Olson) (01/06/91)

In article <1991Jan5.001607.5915@demott.com> kdq@demott.COM (Kevin D. Quitt) writes:
|     The actual code is:
| 
| 
| const unsigned char *chars   = "some string"
| unsigned char pass[];
| 
|     c   = strchr( chars, toupper( pass[ i ]) ) - chars + 1;
| 
| 
|     gcc would not accept this no matter what I did.

The problem is the "unsigned" type specifier in the declaration for
chars.  Section 3.5.4.1 (Pointer declarators) states that

	For two pointer types to be compatible, both shall be
	identically qualified and both shall be pointers to
	compatible types.

The function "strchr" is defined to return a type "char *"; it is
not compatible with "chars", which is of type "unsigned char *".  Try
removing the "unsigned" specifier from the declaration for "chars", or
explicitly cast the result of the strchr() function to (unsigned char *).


--
	-- Tim Olson
	Advanced Micro Devices
	(tim@amd.com)

darcy@druid.uucp (D'Arcy J.M. Cain) (01/06/91)

[I removed comp.lang.c++ from the Newsgroups header as it seems to be 
only a C question]

In article <1991Jan5.001607.5915@demott.com> Kevin D. Quitt writes:
>const unsigned char *chars   = "some string"
>unsigned char pass[];
>    c   = strchr( chars, toupper( pass[ i ]) ) - chars + 1;
>    gcc would not accept this no matter what I did.

I too have noticed this difference between gcc and Turbo-C.  The problem
seems to be the "unsigned" part of the declaration of chars.  I tried
the following:

#include	<stdio.h>
#include	<string.h>

int		main(void)
{
	const char *chars = "some string";
	int c;

	c = strchr(chars, 'e') - chars + 1;
	return(c);
}

which compiled without a peep with "gcc -O -Wall tst.c -o tst" but
failed when I declared chars as "const unsigned char *chars ...".
(it didn't seem to matter whether or not const was used.)  The error
message is:
tst.c: In function main:
tst.c:6: warning: initialization between incompatible pointer types
tst.c:9: warning: argument passing between incompatible pointer types
tst.c:9: invalid operands to binary -
tst.c:7: warning: `c' may be used uninitialized in this function

So gcc differentiates unsigned char from char.  String literals are
composed of char objects so their addresses can't be assigned to an
unsigned char pointer, strchr() takes a char pointer so an uncasted
unsigned char doesn't match the parameter list and char and unsigned
char can't be subtracted.  So the question is what does ANSI say on
the issue.  K&R2 seems to support gcc in this.  (A2.6, page 194 and
A6.6, page 198.)  In any case, the snippet you provide doesn't exhibit
any reason for using unsigned char over char so perhaps you can just
change the declaration.

-- 
D'Arcy J.M. Cain (darcy@druid)     |
D'Arcy Cain Consulting             |   There's no government
West Hill, Ontario, Canada         |   like no government!
+1 416 281 6094                    |

chip@tct.uucp (Chip Salzenberg) (01/08/91)

According to kdq@demott.COM (Kevin D. Quitt):
>    The actual code is:
>
>
>const unsigned char *chars   = "some string"
>unsigned char pass[];
>
>    c   = strchr( chars, toupper( pass[ i ]) ) - chars + 1;
>
>
>    gcc would not accept this no matter what I did.


If strchr() wasn't declared properly, gcc would complain.

But a more serious complaint is the lack of a check on the return
value of strchr().  It might be NULL, after all, in which case the
subtraction will send the resulting value into deep space.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
       "If Usenet exists, then what is its mailing address?"  -- me
             "c/o The Daily Planet, Metropolis."  -- Jeff Daiell

rsargent@alias.UUCP (Richard Sargent) (01/08/91)

In article <1991Jan5.001607.5915@demott.com> kdq@demott.COM (Kevin D. Quitt) writes:
>
>const unsigned char *chars   = "some string"
>unsigned char pass[];
>
>    c   = strchr( chars, toupper( pass[ i ]) ) - chars + 1;
>
>

One thing that comes to mind is that a *very* fussy compiler, such
as gcc with -pedantic (?) turned on, would complain that the
difference could be between strings in two different address spaces.
This would result in a meaningless difference.

Now, by definition, strchr() returns a pointer to within the string
(or NULL, I think). But, the compiler may be ignoring what the
standard says the function does, and may just consider it as any
function returning a pointer to char. In the event that the 
character is NOT in the string, a NULL return value will result
in a meaningless difference.

Checking the return value and only taking the difference when
non-NULL may be enough. Or maybe the compiler is just too fussy.
Or maybe the compiler has a bug. Or ...

gwyn@smoke.brl.mil (Doug Gwyn) (01/09/91)

In article <1991Jan7.173726.1003@alias.uucp> rsargent@alias.UUCP (Richard Sargent) writes:
>>    c   = strchr( chars, toupper( pass[ i ]) ) - chars + 1;
>One thing that comes to mind is that a *very* fussy compiler, such
>as gcc with -pedantic (?) turned on, would complain that the
>difference could be between strings in two different address spaces.

The actual error in this code is the mixing of pointers to char and
to unsigned char.  If the compiler still complains after that error
is remedied, then it has a problem.  Ensuring that pointer arithmetic
involves valid operands in this situation is entirely the programmer's
responsibility, not the compiler's.  Assuming that everything is declared
properly and that the chars string contains all possible runtime values
of toupper(...), the above pointer arithmetic (with types fixed) is
strictly conforming and thus the C implementation should NOT issue a
diagnostic.

volpe@camelback.crd.ge.com (Christopher R Volpe) (01/10/91)

In article <14794@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
|>The actual error in this code is the mixing of pointers to char and
|>to unsigned char.  If the compiler still complains after that error
|>is remedied, then it has a problem.  Ensuring that pointer arithmetic
|>involves valid operands in this situation is entirely the programmer's
|>responsibility, not the compiler's.  Assuming that everything is declared
|>properly and that the chars string contains all possible runtime values
|>of toupper(...), the above pointer arithmetic (with types fixed) is
|>strictly conforming and thus the C implementation should NOT issue a
|>diagnostic.

I assume you mean "should NOT fail to generate correct code", right?
It can, of course, issue a diagnostic if it's raining outside or if
it's past 5:00pm on a Friday.               
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

kdq@demott.com (Kevin D. Quitt) (01/10/91)

In article <2788A63D.A86@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>If strchr() wasn't declared properly, gcc would complain.
>
>But a more serious complaint is the lack of a check on the return
>value of strchr().  It might be NULL, after all, in which case the
>subtraction will send the resulting value into deep space.

    As I indicated, strchr was not being declared; it's not declared in
the same include file as with Microsoft C.  I'm used to compilers that
complain when things are declared, so I'm used to silence as being a
sign that everything's OK.  And you're right about checking the return
value. 

    This code is a fragment from an encryption package, which is why I
had to change it to ask my question.  When I get to the line with
strchr, I have already guaranteed that the search will not fail.

-- 
 _
Kevin D. Quitt         demott!kdq   kdq@demott.com
DeMott Electronics Co. 14707 Keswick St.   Van Nuys, CA 91405-1266
VOICE (818) 988-4975   FAX (818) 997-1190  MODEM (818) 997-4496 PEP last

gwyn@smoke.brl.mil (Doug Gwyn) (01/10/91)

In article <15486@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes:
>In article <14794@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
>|>strictly conforming and thus the C implementation should NOT issue a
>|>diagnostic.
>I assume you mean "should NOT fail to generate correct code", right?
>It can, of course, issue a diagnostic if it's raining outside or if
>it's past 5:00pm on a Friday.               

No, I used the term "should not" advisedly.  A quality ANSI C implementation
should not generate "diagnostics" for a strictly conforming program that are
indistinguishable from the ones required by the standard to be generated for
violations of syntax rules and constraints.  Note that I did not say "must
not" (as in the sense that the implementation would not be standard
conforming if it did); however, customers have a reasonable right to expect
diagnostics to be useful, and spurious ones that announce errors where there
are none are an indication of poor product quality.  Some people might want
the option to generate "warnings" that are readily distinguishable from
"error" diagnostics, for example a la UNIX "lint -p"; in the absence of
other lint-like support this would be a helpful feature.  However, I would
hope that such warnings could be easily suppressed, since many legitimate
applications of C involve deliberate exploitation of specific features of an
implementation.  While I strongly discourage unnecessary implementation
dependency in application code, there are times when it really is needed.