% cat tmp.c void foo(void) { signed char *ps = "signed?"; unsigned char *pu = "unsigned?"; } % gcc --version gcc (GCC) 4.0.0 Copyright (C) 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % gcc -c tmp.c tmp.c: In function 'foo': tmp.c:3: warning: pointer targets in initialization differ in signedness tmp.c:4: warning: pointer targets in initialization differ in signedness Plain char is either signed or unsigned, depending on the system configuration. Both initializations call for warnings, since plain char is a distinct type from both signed char and unsigned char, but in one of the two cases it doesn't actually "differ in signedness".
I don't think the warning is misleading as strings are only ever "const char*"
String literals in C are char*, not const char*, though writing to a string literal invokes undefined behavior. But that's not the point. Assuming plain char is signed, the warning "pointer targets in initialization differ in signedness" for signed char *ps = "signed?"; is misleading because the pointer targets don't differ in signedness. They do differ in type, so a warning is appropriate, but not that warning.
(In reply to comment #2) > String literals in C are char*, not const char*, though writing to a > string literal invokes undefined behavior. But that's not the point. Actually as string literals are defined as specifying an array of character constants, a "const char *" type seems most appropriate, which is why a write to a string literal is undefined. But agree that GCC should not generate a warning that the types differ in signness when initializing a signed or unsigned char * with a string literal, as the types are defined as being compatible; but should generate a warning that a "const char *" is being assigned to a non-const "char *".
Oh, I agree completely that making string literals const (as they are in C++) would make more sense. The reason they aren't defined that way in C is that by the time "const" was added to the language, there was too much code that would be broken by the change. For example, given void func(char *s); the call func("hello"); would be a constraint violation if string literals were const. (As it is, it's merely dangerous, causing undiagnosed undefined behavior if func attempts to modify the string.) But all this is beside the point. I get the same warning messages with this: void foo(void) { const signed char *ps = "signed?"; const unsigned char *pu = "unsigned?"; } Plain char, unsigned char, and signed char are all compatible, in the sense that you can use a value of any of the three types to initialize an object of any of the three types (possibly with an implicit conversion). But because they are all distinct types, pointers to those types are distinct and incompatible types. The correct warning message should be "initialization from incompatible pointer type", the same message produced for the initialization of lptr in the following: void bar(void) { int i = 42; long l = i; /* ok, implicit conversion */ int *iptr = &i; long *lptr = iptr; /* incompatible types */ } even though int and long happen to have the same representation on the system I'm using, just as char and signed char happen to have the same representation. The initializations of ps and pu in foo() are invalid, but this has nothing to do with signedness or constness; it's just because the types are incompatible.
(In reply to comment #4) > Oh, I agree completely that making string literals const > (as they are in C++) would make more sense. The reason they > aren't defined that way in C is that by the time "const" was > added to the language, there was too much code that would be > broken by the change. For example, given > void func(char *s); > the call > func("hello"); > would be a constraint violation if string literals were const. - yes, and would have been at least been made visable if a warning that a "const char *" was being assigned to a "char *" were arguably properly generated when passed as an argument. (although I may be blind, I can find reference to the elements of a string literal being const char's, I can't find any specifying albeit this, a string literal is defined as a "char *", as opposed to more properly a "const char *"?) > But all this is beside the point. I get the same warning > messages with this: > > void foo(void) > { > const signed char *ps = "signed?"; > const unsigned char *pu = "unsigned?"; > } > > Plain char, unsigned char, and signed char are all compatible, > in the sense that you can use a value of any of the three > types to initialize an object of any of the three types > (possibly with an implicit conversion). > > But because they are all distinct types, pointers to those types > are distinct and incompatible types. - understood, but as the types pointed to are "compatible" is seem like a mistake to interpret thier respective pointers as being otherwise. (as their referenced object's values may be assigned freely to each other, so it would seem that arguably pointers to them should be treated similarly. Which would also be consistent with the standard's apparent aliasing guidline: 6.3 Expressions [#7] An object shall have its stored value accessed only by an lvalue expression that has one of the following types:59 - a type compatible with the effective type of the object, - a qualified version of a type compatible with the effective type of the object, - a type that is the signed or unsigned type corresponding to the effective type of the object, - a type that is the signed or unsigned type corresponding to a qualified version of the effective __________ 59. The intent of this list is to specify those circumstances in which an object may or may not be aliased. Which seems to imply stongly that compatible types which only differ in qualification and/or signness should be considred as being potentially aliased by such coresponding pointers, as they reference strinctly compaible types?
I misused the term "compatible" above (and I think the standard itself is sometimes a bit loose about the term). All references are to the C99 standard. I think the C90 rules are the same or very similar. 6.7.8p11: The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type. 6.5.16.1p1 (Simple assignment): One of the following shall hold: [...] -- both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right; 6.7.5.1p2: For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types. 6.2.5p14: The type char, the signed and unsigned integer types, and the floating types are collectively called the basic types. Even if the implementation defines two or more basic types to have the same representation, they are nevertheless different types. (34) 6.2.5p15: The three types char, signed char, and unsigned char are collectively called the _character types_. The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char. (35) Footnote 35: CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the two options. Irrespective of the choice made, char is a separate type from the other two and is not compatible with either. For an initialization of a char object, there's an implicit conversion, even though types char and signed char are not compatible. signed char sc = 'x'; char c = sc; /* implicit conversion */ There is no implicit conversion for pointer types other than void*: signed char *psc = ≻ char *pc = psc; /* illegal, incompatible types */
But it's platform-independent.
There's also the following issue, which seem related. $ cat test.c void nil_uch(unsigned char *uch) { *uch = 0; } void nil_sch(signed char *sch) { *sch = 0; } int main(void) { char ch = 0; nil_uch(&ch); nil_sch(&ch); return 0; } $ gcc --version powerpc-apple-darwin8-gcc-4.0.1 (GCC) 4.0.1 (Apple Computer, Inc. build 5250) Copyright (C) 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc -o test test.c test.c: In function 'main': test.c:14: warning: pointer targets in passing argument 1 of 'nil_uch' differ in signedness test.c:15: warning: pointer targets in passing argument 1 of 'nil_sch' differ in signedness I'd expect the warning to be muted in one of the calls, depending on -f{un}signed-char.
>I'd expect the warning to be muted in one of the calls, depending on -f{un}signed-char. No, char is a seperate type from signed char and unsigned char so they are always incompatiable when it comes to pointers to them. Closing as invalid.
(In reply to comment #9) > >I'd expect the warning to be muted in one of the calls, depending on > -f{un}signed-char. > > No, char is a seperate type from signed char and unsigned char so they are > always incompatiable when it comes to pointers to them. > > Closing as invalid. Yes, they're incompatible -- but that's not what the warning says. I'm now using gcc 4.1.3. (Note that the warning doesn't appear without "-pedantic".) Here's the current behavior: ======================================== % cat tmp.c void foo(void) { signed char *ps = "signed?"; unsigned char *pu = "unsigned?"; } % gcc --version gcc (GCC) 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. % gcc -c -pedantic tmp.c tmp.c: In function `foo': tmp.c:3: warning: pointer targets in initialization differ in signedness tmp.c:4: warning: pointer targets in initialization differ in signedness ======================================== On my system plain char is signed, so for the initialization of ps, the pointer targets *don't* differ in signedness. A warning is necessary; the warning that's currently printed is incorrect. Here's what it should do: ======================================== [...] % gcc -c -pedantic -c tmp.c tmp.c: In function `foo': tmp.c:3: warning: initialization from incompatible pointer type tmp.c:4: warning: initialization from incompatible pointer type ========================================
Actually as a user I would find clearer a warning such: warning: initialization of 'signed char *' from incompatible pointer type 'char *' so CONFIRMED.
*** Bug 260998 has been marked as a duplicate of this bug. *** Seen from the domain http://volichat.com Page where seen: http://volichat.com/adult-chat-rooms Marked for reference. Resolved as fixed @bugzilla.
This problem still exists in gcc 5.3.0. Here's a perhaps clearer example that doesn't depend on string literals, and that demonstrates the problem both when plain char is signed and when it's unsigned. $ cat tmp.c #include <limits.h> void foo(void) { char *pc = 0; #if CHAR_MIN < 0 /* plain char is signed but incompatible with signed char */ signed char *psc = pc; #else /* plain char is unsigned but incompatible with unsigned char */ unsigned char *puc = pc; #endif } $ gcc --version gcc (GCC) 5.3.0 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc -std=c11 -pedantic-errors -c tmp.c tmp.c: In function 'foo': tmp.c:6:24: error: pointer targets in initialization differ in signedness [-Wpointer-sign] signed char *psc = pc; ^ $ gcc -std=c11 -pedantic-errors -c -funsigned-char tmp.c tmp.c: In function 'foo': tmp.c:9:26: error: pointer targets in initialization differ in signedness [-Wpointer-sign] unsigned char *puc = pc; ^ $
I think I might have a fix, but there's this thing about e.g. void foo (void) { char *p = ""; signed char *ps = ""; unsigned char *pu = ""; } with this issue fixed, we'd warn like this $ xgcc z.c -c z.c: In function ‘foo’: z.c:5:21: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types] signed char *ps = ""; ^~ z.c:6:23: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types] unsigned char *pu = ""; ^~ whereas older gcc would be silent on that. I.e., -Wincompatible-pointer-types is a warning that is enabled by default, but -Wpointer-sign is enabled by -Wpedantic || -Wall. I.e., we'd be more verbose. Maybe this is a non-issue as most of the people are using -Wall anyway. In any case I agree that the message about signedness is wrong.
Joseph, based on gcc-patches discussion, should we close this as invalid, or do you think there is a beneficial change that could be made?
Well, an additional note: 'char' and 'signed char' are different types (or similar in the unsigned case) could be added in the case where the types have the same representation, one is char and the other is signed or unsigned char. Or the wording of the original warning could be refined in that case. But I don't think the less-specific wording about incompatible types would be appropriate here, or that the warning should be enabled more widely than the general signedness case (if anything, this case is less serious than that one).
I just took a quick look at the discussion on the gcc-patches mailing list. It's true that the standard doesn't classify plain "char" either as a signed integer type or as an unsigned integer type. But I think that 99% of users think of plain "char" as either signed or unsigned, not some third kind of signedness. If plain "char" has the same range as "signed char", saying that they "differ in signedness" is just confusing. (Even to me, and I read ISO language standards for fun.) And really, the signedness is not the issue. The issue is that they're incompatible types. I'll also note the documentation of the "-fsigned-char" option: Let the type 'char' be signed, like 'signed char'. It's difficult to tell uses that plain char is not signed when gcc's own documentation says it is. I understand that you might not want to warn about assigning a "signed char*" value to a "char* object in the same circumstances where you'd warn about assigning an "int*" to a "long*". (Both are constraint violations, but that's another kettle of fish.) Perhaps char* vs. signed char* or char* vs. unsigned char* needs to be treated as a special case. But I still say that the existing warning is misleading and at least needs to be rephrased.
(In reply to Bernd Schmidt from comment #15) > Joseph, based on gcc-patches discussion, should we close this as invalid, or > do you think there is a beneficial change that could be made? (In reply to Keith Thompson from comment #17) > I just took a quick look at the discussion on the gcc-patches mailing > list. > Link to the gcc-patches archives where this discussion took place?
(In reply to Keith Thompson from comment #17) > > I'll also note the documentation of the "-fsigned-char" option: > > Let the type 'char' be signed, like 'signed char'. > > It's difficult to tell uses that plain char is not signed when gcc's > own documentation says it is. > I guess this is a documentation issue, too, then
*** Bug 71202 has been marked as a duplicate of this bug. ***