If I try to compile
printf("here %u\n", dest->q_head);
gcc complains with
error: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘ngx_uint_t {aka long unsigned int}’
fair enough..
But if I compile with
printf("here %lu\n", dest->q_head);
I get
error: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘ngx_uint_t {aka unsigned int}’
Nothing else in the code changes.
ngx_uint_t is defined as...
typedef unsigned int ngx_uint_t;
dest->head is defined as...
ngx_uint_t q_head;
If I cast it to (unsigned int) it compiles and prints OK.
printf("here %u \n", (unsigned int)dest->q_head);
Any idea what is going on there?
the implementation of printf() does not know the term ngx_uint_t, the typedef does not change this. soprintf() simply does not know what is going on and throws errors
http://code.metager.de/source/xref/gnu/glibc/stdio-common/vfprintf.c from line 270 downwards
( http://code.metager.de/source/xref/gnu/glibc/stdio-common/printf.c printf() calls vfprintf() )
this is also the reason why it works when casted
Related
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 <stdlib.h> 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.
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.
On compiling the following C program, GCC emits a warning message which is somewhat confusing.
Program Source
#include <stdio.h>
typedef struct {
int x;
} dummy_t;
void myfunc (dummy_t *pointer)
{
printf("x = %d\n", pointer->x);
}
int main ()
{
dummy_t d = { 10 };
/* INCORRECT. */
myfunc((struct dummy_t *)&d);
/* Correct. */
// myfunc((dummy_t *)&d);
return 0;
}
Compilation
bash$ gcc c.c
c.c: In function ‘main’:
c.c:17:20: warning: passing argument 1 of ‘myfunc’ from incompatible pointer type
myfunc((struct dummy_t *)&d);
^
c.c:7:6: note: expected ‘struct dummy_t *’ but argument is of type ‘struct dummy_t *’
void myfunc (dummy_t *pointer)
Notice how both the expected type and the argument type are reported to have the same value struct dummy_t *. This is confusing.
Shouldn't the expected type be dummy_t *?
The above program is a simplified version of the actual code where I faced this problem.
GCC Version
bash$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
You're right that the error message is confusing. A newer version gives a much better error message:
note: expected 'dummy_t * {aka struct <anonymous> *}' but argument is
of type 'struct dummy_t *'
As you can see, dummy_t and struct dummy_t are different types. With this declaration:
typedef struct {
int x;
} dummy_t;
You are typedef'ing an anonymous struct. However, later when you do struct dummy_t, you are forward declaring a new struct named dummy_t. Clearly, these are two different types, hence the error.
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).
I have the following c++ source test in MinGW in winx , g++ version is 4.8.1 :
compiled : g++ -std=c++11 int64test.cpp -o int64test.exe
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <errno.h>
#include <atomic>
#include <iostream>
#include <cstdint>
using namespace std ;
int main(int argc, const char *argv[])
{
atomic<unsigned int> uintlocal1(1000) ;
unsigned int uint1,uint2,uint3 ;
uint1 = uintlocal1.fetch_add(1) ;
uint2 = uintlocal1.fetch_add(1) ;
uint3 = uintlocal1.fetch_add(1) ;
printf("(%d)(%d)(%d)(%d)\n",uint1,uint2,uint3,unsigned(uintlocal1)) ;
atomic<uint64_t> uint64local1(1000) ;
uint64_t u1,u2,u3 ;
u1 = uint64local1.fetch_add(1) ;
u2 = uint64local1.fetch_add(1) ;
u3 = uint64local1.fetch_add(1) ;
printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
}
The answer is :
(1000)(1001)(1002)(1003)
(1000)(0)(1001)(0)
Obviously , the atomic uint64_t is wrong , while atomic int is right !!
But I don't know what cause this problem , what should I modify so that I
can use atomic correctly ...thanks !!
You are using incorrect formats for the line printing uint64_t data. When I compile your code my compiler produces the following warnings:
main.cpp:18:33: warning: format specifies type 'int' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]
printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
~~ ^~
%llu
main.cpp:18:36: warning: format specifies type 'int' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]
printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
~~ ^~
%llu
main.cpp:18:39: warning: format specifies type 'int' but the argument has type 'uint64_t' (aka 'unsigned long long') [-Wformat]
printf("(%d)(%d)(%d)(%d)\n",u1,u2,u3,unsigned(uint64local1)) ;
~~ ^~
%llu
Note: You can enable a similar format checking warning in GCC 4.8.1 using the flag -Wformat, or better yet, -Wall.
On my platform the types int and unsigned long long are not layout compatible and so when printf tries to access a vararg specified by %d when the actual passed argument is uint64_t the result will be undefined behavior.
The normal formatters for printf such as %d and %llu are used for built in types such as int or unsigned long long. The types in stdint.h are not built in and may correspond to different built-in types on different platforms, requiring different formatters on each platform.
For example int64_t might be the same as int on one platform and the same as long long on a different platform. Since to use int in printf you use the format specifier %d and to use long long you use the format specifier %lld, you cannot write portable code using stdint types and the normal format specifiers.
Instead, the header inttypes.h is provided with macros that contain the correct format specifiers. The macro for uint64_t is PRIu64. This macro will be defined to be whatever the correct format specifier is on your platform. Use it like this:
printf("(%" PRIu64 ")(%" PRIu64 ")(%" PRIu64 ")(%u)\n",u1,u2,u3,unsigned(uint64local1));
Make sure you get the spaces put in between the macro and the quoted string fragments, otherwise in C++11 the macros will not work correctly.
Here's a useful reference on the normal formatters: http://en.cppreference.com/w/cpp/io/c/fprintf
Here's a reference for the stdint.h types and formatters: http://en.cppreference.com/w/cpp/types/integer
Note: Using incorrect format specifiers with printf an easy mistake to make and leads to undefined behavior. One of the advantages of the iostream library is that this sort of mistake is impossible.