How to make gcc/g++ warn on comparing signed and unsigned char - gcc

I have the following piece of code:
#include <stdlib.h>
#include <stdio.h>
void test(unsigned char * arg) { }
int main() {
char *pc = (char *) malloc(1);
unsigned char *pcu = (unsigned char *) malloc(1);
*pcu = *pc = -1; /* line 10 */
if (*pc == *pcu) puts("equal"); else puts("not equal"); /* line 12 */
pcu = pc; /* line 14 */
if (pcu == pc) { /* line 16 */
test(pc); /* line 18 */
}
return 0;
}
If I compile it with gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) (but it is not limited to this particular version) with options
gcc a.c -pedantic -Wall -Wextra -Wsign-conversion -Wno-unused-parameter; ./a.out
I get the following warnings
test.c: In function ‘main’:
test.c:10:21: warning: conversion to ‘unsigned char’ from ‘char’ may change the sign of the result [-Wsign-conversion]
test.c:14:13: warning: pointer targets in assignment differ in signedness [-Wpointer-sign]
test.c:16:17: warning: comparison of distinct pointer types lacks a cast [enabled by default]
test.c:18:17: warning: pointer targets in passing argument 1 of ‘test’ differ in signedness [-Wpointer-sign]
test.c:4:6: note: expected ‘unsigned char *’ but argument is of type ‘char *’
not equal
g++ warnings/errors are similar. I hope I understand why the comparison on line 12 is evaluated to false, but is there any way to get a warning also in such cases? If not, is there some principial difference between line 12 and the lines which cause warnings? Is there any specific reason why comparison of char and unsigned char shouldn't deserve its warning? Because at least at first glance, line 12 seems to me more "dangerous" than e.g. line 16.
A short "story behind": I have to put together pieces of code from various sources. Some of them use char and some of them use unsigned char. -funsigned-char would work fine, but I am forced to avoid it and rather to add proper type conversions. That's why such a warning would be useful for me, because now, if I forget to add a type conversion in such a case, the program silently fails.
Thanks in advance, P.

I believe this is caused by integer promotion.
When you deal with char or short, what C actually does (and this is defined by the standard, not the implementation) is promote those types to int before doing any operations. The theory, I think, is that int is supposed to be the natural size used by the underlying machine, and therefore the fastest, most efficient size; in fact, most architectures will do this conversion on loading a byte without being asked.
Since both signed char and unsigned char will fit happily within the range of a signed int, the compiler uses that for both, and the comparison becomes a pure signed comparison.
When you have a mismatched type on the left-hand-side of the expression (lines 10 and 14) then it needs to convert that back to the smaller type, but it can't, so you get a warning.
When you compared the mismatching pointers (line 16) and passed the mismatching pointer (line 18), the integer promotion is not in play because you never actually dereference the pointers, and so no integers are ever compared (char is an integer type also, of course).

Related

Why am i getting the followng error when I called getline() in my C code?

I am getting the following error
rudimentary_calc.c: In function ‘main’:
rudimentary_calc.c:9:6: error: conflicting types for ‘getline’
9 | int getline(char line[], int max) ;
| ^~~~~~~
In file included from rudimentary_calc.c:1:
/usr/include/stdio.h:616:18: note: previous declaration of ‘getline’ was here
616 | extern __ssize_t getline (char **__restrict __lineptr,
| ^~~~~~~
when I ran the following code
#include <stdio.h>
#define maxline 100
int main()
{
double sum, atof(char[]);
char line[maxline];
int getline(char line[], int max) ;
sum = 0;
while (getline(line, maxline) > 0)
printf("\t %g \n", sum += atof(line));
return 0;
}
What am I doing wrong? I am very new to C, so I don't know what went wrong.
Generally, you should not have to declare "built-in" functions as long as you #include the appropriate header files (in this case stdio.h). The compiler is complaining that your declaration is not exactly the same as the one in stdio.h.
The venerable K&R book defines a function named getline. The GNU C library also defines a non-standard function named getline. It is not compatible with the function defined in K&R. It is declared in the standard <stdio.h> header. So there is a name conflict (something that every C programmer has do deal with).
You can instruct GCC to ignore non-standard names found in standard headers. You need to supply a compilation flag such as -std=c99 or -std=c11 or any other std=c<year> flag that yout compiler supports.
Live demo
Always use one of these flags, plus at least -Wall, to compile any C code, including code from K&R. You may encounter some compiler warnings or even errors. This is good. Thy will tell you that there are some code constructs that were good in the days of K&R, but are considered problematic now. You want to know about those. The book is rather old and the best practices and the C language itself have evolved since.

The result of using strtol() under stdlib.h and stdio.h is different

When trying to parse a number too big to fit a long, strtol() returns 0 instead of LONG_MAX (stdio.h). If I read the POSIX spec correctly, it should be LONG_MAX. There is a different between stdio.h and stdlib.h
#include "stdio.h"
int main(void){
printf("%ld\n", strtol("99999999999999999999999"));
return 0;
} # 0
#include "stdio.h"
//#include "stdlib.h"
int main(void){
char *end[500];
printf("%ld\n", strtol("99999999999999999999999", end, 10));
return 0;
} # 9223372036854775807
strtol is declared in header <stdlib.h> as
long strtol( const char *restrict str, char **restrict str_end, int base );
// ^^^^^^^^ ^^^^^^^^ since C99
In the first posted snippet, <stdlib.h> is not included and the function is called with one single argument, so that, if compiled with -Wall -Wextra -std=gnu11, gcc produces the following explanatory warnings before outputting 0:
prog.c: In function 'main':
prog.c:5:21: warning: implicit declaration of function 'strtol' [-Wimplicit-function-declaration]
printf("%ld\n", strtol("99999999999999999999999"));
^~~~~~
prog.c:5:15: warning: format '%ld' expects argument of type 'long int', but argument 2 has type 'int' [-Wformat=]
printf("%ld\n", strtol("99999999999999999999999"));
~~^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%d
Which means that the library function is not called at all and an implicitly declared function with the same name is called, an int with value 0 is returned and printed (with the wrong format specifier, which is itself undefined behavior).
Note that the same code fails to compile with clang, which reports the following:
prog.c:4:21: warning: implicitly declaring library function 'strtol' with type 'long (const char *, char **, int)' [-Wimplicit-function-declaration]
printf("%ld\n", strtol("99999999999999999999999"));
^
prog.c:4:21: note: include the header &ltstdlib.h&gt or explicitly provide a declaration for 'strtol'
prog.c:4:53: error: too few arguments to function call, expected 3, have 1
printf("%ld\n", strtol("99999999999999999999999"));
~~~~~~ ^
1 warning and 1 error generated.
In the second snippet, strtol is called with the right number of arguments, but, as posted (with the #include commented out), has the same missing header problem. To produce the expected output, LONG_MAX, header stdlib.h has to be included.

Error installing perl on Win10 using MinGW

If this is the wrong place for this question I apologise and please redirect me to the suitable section.
I'm somewhat rusty on installing from command line, especially on Windows. I decided to install the latest Perl version on my PC, running under Windows 10. I had previously installed it using Strawberry Perl download, but as it was a few versions out of date I decided to remove it and refresh my skills (ha) by installing it manually. I downloaded the lastest Perl release from https://www.perl.org/get.html#win32 and have been reading the README.win32 to make sure I install it correctly.
As I need a compiler, I decided to use Gcc and dmake. I installed and can run them successfully so went back to installing Perl. As per instructions I tried running dmake in the win32 subdirectory in the Perl download folder. Before this I edited makefile.mk, where these variables are uncommented from the Build configuration section:
INST_DRV/INST_TOP (left as is)
INST_VER *= \5.26.0
USE_MULTI *= define
USE_ITHREADS *= define
USE_IMP_SYS *= define
USE_LARGE_FILES *= define
USE_64_BIT_INT *= define
USE_LONG_DOUBLE *= define
DEFAULT_INC_EXCLUDES_DOT *= define
CCTYPE = GCC
GCCWRAPV *=define
CCHOME *= C:\MinGW
(nothing else changed after this)
When I run dmake in the directory, it quickly comes to this error:
gcc -c -I.\include -I. -I.. -DWIN32 -DPERLDLL -DPERL_CORE -s -O2 -
D__USE_MINGW_ANSI_STDIO -fwrapv -fno-strict-aliasing
-DPERL_EXTERNAL_GLOB -DPERL_IS_MINIPERL -omini\toke.o ..\toke.c
In file included from ..\perl.h:3220:0,
from ..\toke.c:40:
./win32.h:417:13: error: conflicting types for 'mkstemp'
extern int mkstemp(const char *path);
^~~~~~~
In file included from ..\perl.h:790:0,
from ..\toke.c:40:
c:\mingw\include\stdlib.h:809:30: note: previous definition of 'mkstemp' was here
__cdecl __MINGW_NOTHROW int mkstemp (char *__filename_template)
^~~~~~~
In file included from ..\toke.c:40:0:
..\toke.c: In function 'Perl_filter_add':
..\perl.h:1756:20: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
#define PTR2nat(p) (PTRV)(p) /* pointer to integer of PTRSIZE */
^
..\perl.h:1769:28: note: in expansion of macro 'PTR2nat'
#define FPTR2DPTR(t,p) ((t)PTR2nat(p)) /* function pointer to data pointer */
^~~~~~~
..\toke.c:4397:21: note: in expansion of macro 'FPTR2DPTR'
IoANY(datasv) = FPTR2DPTR(void *, funcp); /* stash funcp into spare field */
^~~~~~~~~
..\perl.h:1769:25: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
#define FPTR2DPTR(t,p) ((t)PTR2nat(p)) /* function pointer to data pointer */
^
..\toke.c:4397:21: note: in expansion of macro 'FPTR2DPTR'
IoANY(datasv) = FPTR2DPTR(void *, funcp); /* stash funcp into spare field */
^~~~~~~~~
..\toke.c: In function 'Perl_filter_del':
..\perl.h:1756:20: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
#define PTR2nat(p) (PTRV)(p) /* pointer to integer of PTRSIZE */
^
..\perl.h:1769:28: note: in expansion of macro 'PTR2nat'
#define FPTR2DPTR(t,p) ((t)PTR2nat(p)) /* function pointer to data pointer */
^~~~~~~
..\toke.c:4463:26: note: in expansion of macro 'FPTR2DPTR'
if (IoANY(datasv) == FPTR2DPTR(void *, funcp)) {
^~~~~~~~~
..\perl.h:1769:25: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
#define FPTR2DPTR(t,p) ((t)PTR2nat(p)) /* function pointer to data pointer */
^
..\toke.c:4463:26: note: in expansion of macro 'FPTR2DPTR'
if (IoANY(datasv) == FPTR2DPTR(void *, funcp)) {
^~~~~~~~~
..\toke.c: In function 'Perl_filter_read':
..\perl.h:1756:20: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
#define PTR2nat(p) (PTRV)(p) /* pointer to integer of PTRSIZE */
^
..\perl.h:1768:28: note: in expansion of macro 'PTR2nat'
#define DPTR2FPTR(t,p) ((t)PTR2nat(p)) /* data pointer to function pointer */
^~~~~~~
..\toke.c:4554:13: note: in expansion of macro 'DPTR2FPTR'
funcp = DPTR2FPTR(filter_t, IoANY(datasv));
^~~~~~~~~
..\perl.h:1768:25: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
#define DPTR2FPTR(t,p) ((t)PTR2nat(p)) /* data pointer to function pointer */
^
..\toke.c:4554:13: note: in expansion of macro 'DPTR2FPTR'
funcp = DPTR2FPTR(filter_t, IoANY(datasv));
^~~~~~~~~
In file included from ..\perl.h:5644:0,
from ..\toke.c:40:
..\toke.c: In function 'S_pending_ident':
..\perl.h:1734:26: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
# define INT2PTR(any,d) (any)(d)
^
..\embed.h:427:59: note: in definition of macro 'newUNOP_AUX'
#define newUNOP_AUX(a,b,c,d) Perl_newUNOP_AUX(aTHX_ a,b,c,d)
^
..\toke.c:8912:37: note: in expansion of macro 'INT2PTR'
INT2PTR(UNOP_AUX_item *,
^~~~~~~
I get a bunch of warnings beforehand about casting from pointers to integers of different sizes, but this is the bit where it stops and produces an error. Am I missing something obvious? I haven't done this for a while, so I'm hoping it is a silly user error on my part! Thanks.
Try obtaining the MinGW source packages, and installing using makepkg-mingw to build them.
Most if not all have patches applied to customize (or fix) them for the MSYS2/MinGW environment.
Stock source downloaded from its author may not compile directly in that environment the way it would on Linux, or OS X using "configure" and "make".
Instructions are available, and there may be other similar instructions out there associated with Arch Linux.

passing argument 1 of 'strlen' makes pointer from integer without a cast

It is not clear why I get a warning of:
[Warning] passing argument 1 of 'strlen' makes pointer from integer without a cast [enabled by default]
expected 'const char *' but argument is of type 'char'
on two of the 3 statements containing strlen() below.
Even when I attempted to cast *str it still gave the same warning.
bfr is a character buffer. *str points to that char buffer after the call to
gets(). If I use strlen(*str) I get a warning. If I use strlen(bfr) I do not.
But *str should be the equivalent to bfr. Thus the confusion regarding the error.
Now in reality, strlen arg 1 is defined as strlen(const char *string). So I
would have expected strlen(bfr) to also produce an error since bfr[] is a
char string and not a const char either.
And where is the integer that is being made into a pointer?
I am using gcc under wXDev-C++.
void test(){
FILE *fileID = fopen("somefile.txt","r");
char *str, len;
char bfr[16];
str = fgets(bfr,16,fileID); // str will be set equal to &bfr[0]
len = strlen(*str); // This gives a warning
len = strlen((const char)*str); // This gives a warning
len = strlen(bfr); // This does not give a warning
}
Sometimes you just need to take a fresh look in the morning at a problem. I realized that strlen is looking for a pointer to a string and 'str' is defined as a pointer. So *str would be a pointer to a pointer. So the warning was correct. It should read len = strlen(s) not len = strlen(*s). And it is 'str' pointing to 'bfr' not *str;
Answered my own question.

warning: cast from pointer to integer of different size

I'm working on socket programming.. my code executes the way I want it to, I'm able to use it. BUT it gives me a warning on compilation.
I compile using
gcc server1.c -o server1 -lpthread
And I get the warning
warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
This error comes for the following code
int newsockfd;
newsockfd = (int)newsockfdd; //this line
And I'm using newsockfdd (which is int) in the following chunk of code
if (pthread_create(&threadID[i++], NULL, serverThread, (void *)(intptr_t)newsockfdd) != 0)
{
perror("Thread create error");
}
As you can probably tell, the code is not written too well (I am working on making it better). I know that this warning comes because of something to do with the size of int. But I really dunno how to fix it. Before I put in (intptr_t) in the pthread_create statement, it was showing a warning on that line, but that time the warning was
warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
It seems like there should be a simple fix to this? But I can't find it. I'm using Ubuntu 64-bit. Is that a reason for the warning?
As has been established in the comments, the situation is (modulo renaming to avoid confusing occurrences of newsockfdd as passed argument or received parameter)
void *serverThread(void *arg) {
// ...
int newsockfd = (int)arg;
// ...
}
and in main (or a function called from there)
// ...
int newsockfdd = whatever;
// ...
if (pthread_create(&threadID[i++], NULL, serverThread, (void *)(intptr_t)newsockfdd) != 0)
// ..
So when the int newsockfdd is passed as an argument to serverThread, it is cast to a void*. Originally, that cast was direct, but the intermediate cast to intptr_t was inserted to remove the warning about the cast to pointer from integer of different size.
And in serverThread, the received void* is cast to int, resulting in the warning about the cast from pointer to integer of different size.
That warning could probably also be removed by inserting an intermediate cast to intptr_t.
But, while the standard allows casting integers to pointers and vice versa, the results are implementation-defined and there's no guarantee that int -> void* -> int round-trips (although, a footnote in the standard says
The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to
be consistent with the addressing structure of the execution environment.
so probably it will round-trip and work as intended in this case - but it would likely not work [only for values of small enough absolute value] if the size of a void* is smaller than that of the integer type [consider long long -> void* -> long long on 32-bit systems]).
The proper fix is to avoid the casting between integers and pointers,
void *serverThread(void *arg) {
// ... check that arg isn't NULL
int newsockfd = *(int *)arg;
// ...
}
in severThread, cast the received pointer to a pointer of appropriate type, and read the pointed-to value, and in main
if// ...
int newsockfdd = whatever;
// ...
if (pthread_create(&threadID[i++], NULL, serverThread, &newsockfdd) != 0)
pass the address of newsockfdd.
One caveat: if serverThread is called from multiple places, the calls in all these places need to be fixed.

Resources