When I ran the APUE sample code shown below on my Mac, it throws a EXC_BAD_ACCESS exception. I have checked the file cursor, and it is in the right position 12. I even try to replace the fprintf with fputc. After that, it works fine and the exception is gone. But I want to know what happened out there and why.
#include "apue.h"
#define BSZ 48
int main(){
FILE *fp;
char buf[BSZ];
memset(buf,'a',BSZ-2);
buf[BSZ-2]='\0';
buf[BSZ-1]='X';
if ((fp = fmemopen(buf,BSZ,"w+")) == NULL)
err_sys("fmemopen failed");
printf("initial buffer contents: %s\n",buf);
fprintf(fp, "hello, world");
printf("before flush: %s\n", buf);
fflush(fp);
printf("after fflush: %s\n",buf);
printf("len of string in buf = %ld\n",(long)strlen(buf));
memset(buf,'b', BSZ-2);
buf[BSZ-2]='\0';
buf[BSZ-1]='X';
fprintf(buf, "hello, world");
// fputc('a',fp);
fseek(fp,0,SEEK_SET);
printf("after fseek: %s\n",buf);
printf("len of string in buf = %ld\n", (long)strlen(buf));
}
Console out put as below:
/Users/heping/Documents/APUE-Example-Code/stdio/cmake-build-debug/memstr
initial buffer contents:
before flush: hello, world
after fflush: hello, world
len of string in buf = 12
Exception: EXC_BAD_ACCESS (code=1, address=0x8)
Process finished with exit code 9
Last stack frame like this: it seems like that something is wrong with the file lock, I think.
As already discussed in the comments, you passed the buffer buf to fprintf() and not the FILE pointer fp, which causes UB.
Every reasonable compiler will print a warning when you trying to do something like that you enabled the warning flags. You can avoid a lot of debugging when you enable all compiler warnings and only disable specific warnings when you are sure you do not want a warning for some type of error.
As mentioned here How to enable all compiler warnings in CLion?, you can enable warnings by adding the compiler flags to CMakeLists.txt. For all warnings for C code, adding this line:
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Werror")
-Wall will enable all normal warnings, -Wextra will enable additional warnings, -Wpedantic will warn about not strictly following the C standard you specified and -Werror will turn every warning into a error, so you have to fix the error before you can compile the program. In case you get warnings you do not want, you can disable them the same way, like this:
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -Werror -Wno-cast-function-type")
-Wno-cast-function-type will disable warnings about casting function pointers to different function pointers. You can read about all the warning options here
Related
gcc 7.1 has introduced a new warning that tells you if you use functions such as snprintf and your arguments would result in output truncation.
The documentation implies that it is only raised if you don't check and act upon the return value:
Level 1 of -Wformat-truncation enabled by -Wformat employs a
conservative approach that warns only about calls to bounded functions
whose return value is unused and that will most likely result in
output truncation.
Here's a sample compilation unit, compiled with version 7.3.0 that illustrates the issue.
#include <stdio.h>
#include <stdlib.h>
int main() {
char w;
int size = snprintf(&w, 1, "%s", "hello world");
if(size<0) {
abort();
}
char *buffer = malloc(size+1);
snprintf(buffer, size+1, "%s", "hello world");
printf("Wrote %d characters: %s\n", size, buffer);
return 0;
}
Compiled like this:
$ gcc -Wformat-truncation=1 test.c
test.c: In function ‘main’:
test.c:8:31: warning: ‘%s’ directive output truncated writing 11 bytes into a region of size 1 [-Wformat-truncation=]
int size = snprintf(&w, 1, "%s", "hello world");
^~ ~~~~~~~~~~~~~
test.c:8:7: note: ‘snprintf’ output 12 bytes into a destination of size 1
int size = snprintf(&w, 1, "%s", "hello world");
Am I misinterpreting the documentation? I can't see how I could check the return value more than I am doing already.
Reference: previous SO question that implies the warning should not be raised. I really don't like disabling warnings and usually compile with -Wall -Werror so I'd appreciate some guidance here.
Thanks , this has been solved , please see updated code and output in the answer . Thanks Jonathan and everyone else.
I have written below code to read the file present in same directory.
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
int main(){
FILE *fptr;
/*Tried putting different combinations like filename with
quotes|filename without quotes|complete path with quotes|complete path
without quotes*/
if((fptr=fopen("TestFile.txt","r"))==NULL){
printf("\nfopen() returning NULL: %d , %s \n",errno,strerror(errno));
}else{
printf("\nfopen() returning something else: %d , %s
\n",errno,strerror(errno));
}
int c;
while((c=fgetc(fptr))!=EOF){
printf("%c",c);
}}
And i am was getting below output :
./a.out
Segmentation fault (core dumped)
And a GDB core analysis had the following :
(gdb) run
Starting program: /home/astitva/Documents/Coding/a.out
Dwarf Error: wrong version in compilation unit header (is 0, should be 2,
3, or 4) [in module /usr/lib/debug/.build-
id/12/5dab90a4cfa8edc5d532f583e08e810c232cd5.debug]
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Dwarf Error: wrong version in compilation unit header (is 0, should be 2,
3, or 4) [in module /usr/lib/debug/.build-
id/c0/5201cc642f6b800835e811d7cb28f103aeb191.debug]
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7abc496 in strlen () from /lib/x86_64-linux-gnu/libc.so.6
and my text file TestFile.txt was :
DATA ENETERD AT RUN INSTANCE 1 ------> BLABLABLA
DATA ENETERD AT RUN INSTANCE 2 ------> YADAYADAYADA
DATA ENETERD AT RUN INSTANCE 3 ------> FOOBARFOOBAR
To avoid the warning, you need to #include <string.h> in your code. Add an exit(1) in the error-handling if block :
if((fptr=fopen("TestFile.txt","r"))==NULL){
printf("\nfopen() returning NULL: %d %s\n",errno, strerror(errno));
exit(1);
}
The program needs to exit "gracefully" if the file doesn't exist. So, if there is no valid file present, the program will simply exit and not print anything on the stdout.
EDIT : Just adding on Jonathan's helpful comment on ignoring compiler's warnings :
"If you ignored a compiler warning — don't. If the compiler didn't warn you about the undeclared function strerror(), you need to find the options that make it report such problems (if you use gcc, you would use gcc -Wall -Wextra -Werror — and I'd add -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration too, though clang doesn't like -Wold-style-declaration)."
I am currently studying C implementations of Linux terminal commands in class but I can't seem to get the following example to run on my machine. I'm using the latest distro of Ubuntu. It will compile but with a warning. "assignment makes pointer from integer without a cast" it refers to the line with a=ctime(&n->ut_time); I found this code online. It is suppose to replicate the terminal command "Who" to display system users. I'm simply trying to study how it works but I cant seem to get it to run. Any help or explanations would be appreciated.
#include<stdio.h>
#include<sys/utsname.h>
#include<utmp.h>
int main(void)
{
struct utmp *n;
char *a;
int i;
setutent();
n=getutent();
while(n!=NULL)
{
if(n->ut_type==7)
{
printf("%-9s",n->ut_user);
printf("%-12s",n->ut_line);
a=ctime(&n->ut_time);
printf(" ");
for(i=4;i<16;i++)
{
printf("%c",a[i]);
}
printf(" (");
printf("%s",n->ut_host);
printf(")\n");
}
n=getutent();
}
}
Transferring comment to answer
The compiler is telling you you've not got #include <time.h> so ctime() is assumed to return an int and not a char *. All hell breaks loose (segmentation faults, etc) because you are not paying attention to the compiler warnings.
Remember, while you're learning C, the compiler knows a lot more about C than you do. Its warnings should be heeded. (When you know C reasonably well, you still pay attention to the compiler warnings - and make the code compile cleanly without warnings. I use gcc -Wall -Wextra -Werror and some extra options — usually -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration; sometimes -Wshadow, -pedantic; and occasionally a few others.)
I'm writing some C code for an embedded linux system using an open_memstream and I don't understand why I am getting a compile warning: assignment makes pointer from integer without a cast
To make things simple, rather than pasting all my code I reproduced the problem with the small example from here:
#include <stdio.h>
#include <stdlib.h>
int
main (void)
{
FILE *stream;
char *buf;
size_t len;
off_t eob;
stream = open_memstream (&buf, &len);
if (stream == NULL)
/* handle error */ ;
fprintf (stream, "hello my world");
fflush (stream);
printf ("buf=%s, len=%zu\n", buf, len);
eob = ftello(stream);
fseeko (stream, 0, SEEK_SET);
fprintf (stream, "good-bye");
fseeko (stream, eob, SEEK_SET);
fclose (stream);
printf ("buf=%s, len=%zu\n", buf, len);
free (buf);
return 0;
}
The code works, but the compiler complains about the line stream = open_memstream (&buf, &len);
What integer is it talking about? We're passing in a pointer to a size_t as required by the function prototype.
FILE *open_memstream(char **bufp, size_t *sizep);
Is there a problem with this code, or do I need to take a look at my compiler? I want to get rid of this warning the right way.
UPDATE:
Using gcc 4.3.2, glibc 2.9
UPDATE 2:
Tried the following:
powerpc-860-linux-gnu-gcc -std=c99 -Wall -D_XOPEN_SOURCE=700 -c source.c
Result:
source.c: In function 'main':
source.c:12: warning: implicit declaration of function 'open_memstream'
source.c:12: warning: assignment makes pointer from integer without a cast
According to this, it seems that _XOPEN_SOURCE=700 is available since glibc 2.10.
Since I'm using glibc 2.9, what other alternatives do I have (other than upgrading glibc)?
UPDATE 3:
Adding the following got rid of the warning:
extern FILE *open_memstream(char **bufp, size_t *sizep);
Is there anything wrong with this solution?
UPDATE 4:
This worked instead of the extern:
powerpc-860-linux-gnu-gcc -std=c99 -Wall -D_GNU_SOURCE -c ops_cmds.c
So according to the manpage, need to use _GNU_SOURCE if glibc pre-2.10 (in my case) and _XOPEN_SOURCE=700 if 2.10+
Define:
#define _POSIX_C_SOURCE 200809L
or
#define _XOPEN_SOURCE 700
in your source code before including stdio.h. Or with gcc you can define and pass the macro value to the source file with -D option:
gcc -std=c99 -Wall -D_XOPEN_SOURCE=700 -c source.c
open_memstream is a POSIX function and its declaration is not visible in your program without this define.
The compiler is complaining about the return value of open_memstream, not about the arguments you pass in.
Your open_memstream is not declared, i.e. the compiler does not see the prototype. So the compiler (apparently pre-C99) assumes that it returns an int. You are forcing that int into stream pointer, which is what triggers the warning about "making pointer form integer".
Make sure open_memstream is declared before you attempt to use it. The prototype is supposed to reside in stdio.h, but it is only available in POSIX.1-2008. You have to enable it explicitly (see other answers).
I have an error reporting functionality in my little C library I'm writing. I want to provide an errorf function in addition to the plain error function to allow embedding information in error messages easily.
/*
* Prints a formatted error message. Use it as you would use 'printf'. See the
* 'sio_error' function.
*/
void sio_errorf(const char *format, ...) {
// Print the error prefix
if (g_input == STDIN) fputs("error: ", stderr);
else fprintf(stderr, "%s: ", g_progname);
// Pass on the varargs on to 'vfprintf'.
va_list arglist;
va_start(arglist, format);
// This may produce the following warning -- ignore it:
// warning: format string is not a string literal
vfprintf(stderr, format, arglist);
va_end(arglist);
fputc('\n', stderr);
}
The problem is, I get this warning (compiling with clang 4.0 with the -Weverything switch):
warning: format string is not a string literal
I understand why doing this would be bad. Is there any way I can get rid of this warning? Can I somehow enforce that the format argument sio_errorf be a string literal, so that the compiler knows that it always will be, and that I'm simply passing it on?
I know I can use -Wno-format-nonliteral, but only if other people are going to manually compile it too, they won't do that. I'd rather something in the source code that silences the warning.
Ideally I would still get the warning if the string I passed to sio_errorf actually isn't a literal, but I'm not sure if that's possible.
If you're using GCC or one of its relatives, try an attribute on the declaration:
void sio_errorf(const char *format, ...) __attribute__((format(printf, 1, 2)));
To add the attribute to a definition, you can use this:
__attribute__((format(printf, 1, 2)))
static void sio_errorf(const char *format, ...) {
....
Many compilers will allow you to set warning levels in one way or another. For example, gcc allows the control via -W flags on the command line when invoking the compiler.
Hopefully that's the compiler you're using since a program like this:
#include <stdio.h>
int main (void) {
char *s = "xyzzy\n";
printf (s);
return 0;
}
generates the exact message you describe (assuming you've enabled the warnings with -Wformat and -Wformat-nonliteral).
The particular command line argument you would be looking for is:
-Wno-format-nonliteral
which will prevent complaints about the use of non-literal strings in those functions.
However, you may be looking for something more fine-grained so it also allows you to specify the disposition of certain diagnostic messages on the fly within your code with pragmas:
#include <stdio.h>
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int main (void) {
char *s = "xyzzy\n";
printf (s);
return 0;
}
#pragma GCC diagnostic warning "-Wformat-nonliteral"
If you compile that with -Wformat -Wformat-nonliteral, you won't see the warning, because you've told gcc to ignore that particular warning for the main function.
Later versions of gcc than the one I run have the following options:
#pragma GCC diagnostic push
#pragma GCC diagnostic pop
which will push and pop the state of the diagnostics. This gets around the problems in my code above where you might configure that warning as an error - my second pragma would change it into a warning.
The use of push/pop would allow restoration to its original disposition with something like:
#include <stdio.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int main (void) {
char *s = "xyzzy\n";
printf (s);
return 0;
}
#pragma GCC diagnostic pop