Something about setbuf
When I worked on Exercise 5.1 of APUE.2e, I made some discoveries about function setbuf : void setbuf(FILE *restrict fp, char *restrict buf).
First, setbuf doesn’t check whether buf has a size of BUFSIZ. It leaves the work to users to make sure that buf has a size of BUFSIZ. I thought it would check!!!
Second, when the stream to be set is associated with a terminal device, such as stdin, stdout and stderr, setbuf doesn’t set them to line buffered (when buf is not NULL), but fully buffered. My system is Ubuntu 10.04 (2.6.32-33-generic).
Below is the program I worked out. A litter messy.
If you can’t view it, here is the link to download the source file: Click Me
?Download Exercise5-1.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /* * exercise5-1.c * * Created on: Sep 1, 2011 * Author: zhuhuang */ //How to build apueerr.h? I put the code of error.c from Figure B.3 and errorlog.c from Figure B.4 into a file named //"apueerr.h". Line "#include <apue.h>" is added to the beginning of apueerr.h. Then place both apueerr.h and apue.h into //folder /usr/include. Now you can used "#include <apueerr.h>" to replace "#include <apue.h>" in all programs from the book. #include <apueerr.h> //use isatty function to decide whether the file is a terminal device //pr_stdio from fig5-11.c void pr_stdio(const char *name, FILE *fp) { printf("stream = %s, ", name); /* * The following is nonportable. */ if(fp->_IO_file_flags & _IO_UNBUFFERED) printf("unbuffered"); else if(fp->_IO_file_flags & _IO_LINE_BUF) printf("line buffered"); else printf("fully buffered"); printf(", buffer size = %dn", fp->_IO_buf_end - fp->_IO_buf_base); } //Based on testing, when buffer size if small than BUFSIZ, system-provided setbuf still works and will expand the size of //buffer to BUFSIZ without moving buf. And even when the stream (such as stdin, stdout, stderr)is associated with a //terminal device, it is not set to be line buffered, but fully buffered. //My setbuf can do the same things, except that we have to move the buffer when its size is smaller than BUFSIZ. //Realloc gives a different starting address when it is used to expand an existing buffer. //The APUE2e book said "buf must point to a buffer of length BUFSIZ" (Page 137). However, if buf doesn't point to a buffer //of BUFSIZ, setbuf still works. The only explanation I can come up with is that: setbuf assumes the provided buf is of size //BUFSIZ and doesn't check it (It seems that there is no way to check the size of dynamically allocated buffer). It then //simply make 'buf' to be buffer for fp. It leaves the work to the user to make sure the allocated size of buf is BUFSIZ. /* void setbuf(FILE *fp, char *buf) { //char *newbuf; printf("buf address before realloc: %#xn", buf); if(buf == NULL) { if(setvbuf(fp, NULL, _IONBF, 0) != 0) //Disable buffering err_sys("setvbuf error 1"); } else { //sizeof(buf) doesn't give the size that buf has been allocated to. //No way to check the size. if(sizeof(buf) < BUFSIZ) if((buf = realloc(buf, BUFSIZ)) == NULL) err_sys("realloc error"); printf("buf address after realloc: %#xn", buf); if(setvbuf(fp, buf, _IOFBF, BUFSIZ) != 0) //Realloc succeed. Make it fully buffered. err_sys("setvbuf error 2"); } } */ //Another version of setbuf, which don't check the size of buf. //This one works exactly like the system-provided setbuf, judged from the output. void setbuf(FILE *fp, char *buf) { //char *newbuf; printf("buf address before realloc: %#xn", buf); if(buf == NULL) { if(setvbuf(fp, NULL, _IONBF, 0) != 0) //Disable buffering err_sys("setvbuf error 1"); } else { if(setvbuf(fp, buf, _IOFBF, BUFSIZ) != 0) //Realloc succeed. Make it fully buffered. err_sys("setvbuf error 2"); } } int main(void) { //char buffer1[20]; //Using this form will cause realloc to fail on buffer1. //char buffer2[20]; //Using this form will cause realloc to fail on buffer2. char *buffer1 = malloc(20); char *buffer2 = malloc(20); //For testing //char *buffer3 = malloc(20); //printf("size of buffer3; %dn", sizeof(buffer3)); //gives 4. //buffer3 = realloc(buffer3, BUFSIZ); //printf("size of buffer3; %dn", sizeof(buffer3)); //gives 4. printf("Starting address of char array buffer1: %#xn", buffer1); //printf("Ending address of char array buffer1: %#xn", &buffer1[19]); printf("Starting address of char array buffer2: %#xn", buffer2); //printf("Ending address of char array buffer2: %#xn", &buffer2[19]); //Perform I/O on stream: stdin, stdout, stderr fputs("enter any charactern", stdout); if(getchar() == EOF) err_sys("getchar error"); fputs("one line to standard errorn", stderr); printf("Default size for setbuf (BUFSIZ): %dn", BUFSIZ); printf("Before calling setbuf to stdin, stdout and stderr: n"); pr_stdio("stdin", stdin); pr_stdio("stdout", stdout); pr_stdio("stdout", stderr); setbuf(stdin, buffer1); //it doesn't matter that the buffer size is not BUFSIZ //printf("Starting address of char array buffer1: %#xn", buffer1); //printf("Ending address of char array buffer1: %#xn", &buffer1[BUFSIZ-1]); printf("Starting address of the buffer made by setbuf for stdin: %#xn", stdin->_IO_buf_base); printf("Ending address of the buffer made by setbuf for stdin: %#xn", stdin->_IO_buf_end); setbuf(stdout, NULL); setbuf(stderr, buffer2); //printf("Starting address of char array buffer2: %#xn", buffer2); //printf("Ending address of char array buffer2: %#xn", &buffer2[BUFSIZ-1]); printf("Starting address of the buffer made by setbuf for stderr: %#xn", stderr->_IO_buf_base); printf("Ending address of the buffer made by setbuf for stderr: %#xn", stderr->_IO_buf_end); printf("After calling setbuf to stdin, stdout and stderr: n"); pr_stdio("stdin", stdin); pr_stdio("stdout", stdout); pr_stdio("stdout", stderr); } |
底层的东西,看不懂。
搞IT的,果然是大半夜码代码。
@ygc 这个应该算系统层的,系统调用这些,还不算太底层。