c 语言一些示例
2019年03月02日
/1-导言/1.6.c
1#include <stdio.h> 2 3// 1-6 4 5main() { 6 int c; 7 while (c = getchar() != EOF) 8 printf("%d\n", c); 9 printf("%d - at EOF\n", c); 10} 11
/1-导言/1.7.c
1#include <stdio.h> 2 3// 1-7 4 5main() 6{ 7 printf("EOF is %d\n", EOF); 8} 9
/1-导言/a.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5 6main() 7{ 8 int fahr, celsius; 9 int lower, upper, step; 10 11 lower = 0; 12 upper = 300; 13 step = 20; 14 15 fahr = lower; 16 while (fahr <= upper) { 17 celsius = 5 * (fahr-32) / 9; 18 printf("%d\t%d\n", fahr, celsius); 19 fahr = fahr + step; 20 } 21} 22
/1-导言/b.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5// 优化版本 6 7main() 8{ 9 // 浮点数 10 float fahr, celsius; 11 int lower, upper, step; 12 13 lower = 0; 14 upper = 300; 15 step = 20; 16 17 fahr = lower; 18 while (fahr <= upper) 19 { 20 celsius = (5.0/9.0) * (fahr-32.0); 21 // 输出美观 22 // %3.0f 至少占3字符宽,不带小数点和小数部分 23 // %6.1f 至少占6字符宽,小数点后面有1位数字 24 printf("%3.0f %6.1f\n", fahr, celsius); 25 fahr = fahr + step; 26 } 27} 28
/1-导言/c.c
1# include <stdio.h> 2 3// 摄氏温度转华氏温度 4// f = 9*c / 5 + 32 5 6main() 7{ 8 float fahr, celsius; 9 int lower, upper, step; 10 11 lower = 0; 12 upper = 300; 13 step = 20; 14 15 printf("Celsius Fahr\n"); 16 celsius = lower; 17 while (celsius <= upper) { 18 fahr = 9.0 * celsius / 5.0 + 32.0; 19 printf("%3.0f %6.1f\n", celsius, fahr); 20 celsius = celsius + step; 21 } 22} 23
/1-导言/copy.c
1#include <stdio.h> 2 3main() { 4 int c; 5 c = getchar(); 6 while(c != EOF){ 7 putchar(c); 8 c = getchar(); 9 } 10} 11
/1-导言/copy2.c
1#include <stdio.h> 2 3main() 4{ 5 int c; 6 7 while ((c = getchar()) != EOF) 8 putchar(c); 9} 10
/1-导言/d.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5// 优化版本 6// for 7 8main() 9{ 10 float fahr; 11 12 for (fahr = 0; fahr <= 300; fahr = fahr + 20) { 13 printf("%3.0f %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0)); 14 } 15} 16
/1-导言/e.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5// 优化版本 6// for 7// 逆序 8 9main() 10{ 11 float fahr; 12 13 for (fahr = 300; fahr >= 0; fahr = fahr - 20) 14 { 15 printf("%3.0f %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0)); 16 } 17} 18
/1-导言/f.c
1#include <stdio.h> 2 3#define LOWER 0 4#define UPPER 300 5#define STEP 20 6 7// 打印华氏温度和摄氏温度对照表 8// c = (5/9) * (f-32) 9// 优化版本 10// for 11 12main() 13{ 14 float fahr; 15 16 for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) 17 { 18 printf("%3.0f %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0)); 19 } 20} 21
/1-导言/hello.c
1#include <stdio.h> 2 3main() 4{ 5 printf("hello, world\n"); 6} 7
/deep/10/cpstdin.c
1#include <unistd.h> 2 3// 使用 read 和 write 调用一次一个字节地从标准输入复制到标准输出 4 5int main() { 6 char c; 7 8 while (read(STDIN_FILENO, &c, 1) != 0) { 9 write(STDOUT_FILENO, &c, 1); 10 } 11 12 return 0; 13} 14
/deep/10/csapp.c
1#include <unistd.h> 2#define RIO_BUFSIZE 8192 3 4typedef struct { 5 int rio_fd; // Descriptor for this internal buf 6 int rio_cnt; // Unread bytes in internal buf 7 char *rio_bufptr; // Next unread byte in internal buf 8 char rio_buf[RIO_BUFSIZE]; // Internal buffer 9} rio_t; 10 11ssize_t rio_readn(int fd, void *usrbuf, size_t n) { 12 size_t nleft = n; 13 ssize_t nread; 14 char *bufp = usrbuf; 15 16 while (nleft > 0) { 17 if ((nread = read(fd, bufp, nleft)) < 0) { 18 // Interrupted by sig handler return and call read() again 19 // 被应用信号处理程序的返回终端,手动重启 20 if (errno == EINTR) { 21 nread = 0; 22 } else { 23 // errno set by read() 24 return -1; 25 } 26 } 27 else if (nread == 0) { 28 // EOF 29 break; 30 } 31 32 nleft -= nread; 33 bufp += nread; 34 } 35 36 // Return >= 0 37 return (n - nleft); 38} 39 40ssize_t rio_writen(int fd, void *usrbuf, size_t n) { 41 size_t nleft = n; 42 ssize_t nwritten; 43 char *bufp = usrbuf; 44 45 while (nleft > 0) { 46 if ((nwritten = write(fd, bufp, nleft)) <= 0) { 47 if (errno == EINTR) { 48 nwritten = 0; 49 } else { 50 return -1; 51 } 52 } 53 54 nleft -= nwritten; 55 bufp += nwritten; 56 } 57 58 return n; 59} 60 61void rio_readinitb(rio_t *rp, int fd) { 62 rp->rio_fd = fd; 63 rp->rio_cnt = 0; 64 rp->rio_bufptr = rp->rio_buf; 65} 66 67// 内部的 rio_read 函数 68static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { 69 int cnt; 70 71 while (rp->rio_cnt <= 0) { // Refill if buf is empty 72 rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 73 if (rp->rio_cnt < 0) { 74 if (errno != EINTR) { 75 return -1; 76 } 77 else if (rp->rio_cnt == 0) { // EOF 78 return 0; 79 } 80 else { 81 rp->rio_bufptr = rp->rio_buf; // Reset buffer ptr 82 } 83 } 84 } 85 86 // Copy min(n, rp->rio_cnt) bytes from internal buf to user buf 87 cnt = n; 88 if (rp->rio_cnt < n) 89 cnt = rp->rio_cnt; 90 memcpy(usrbuf, rp->rio_bufptr, cnt); 91 rp->rio_bufptr += cnt; 92 rp->rio_cnt -= cnt; 93 return cnt; 94} 95 96ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { 97 int n, rc; 98 char c, *bufp = usrbuf; 99 100 for (n = 1; n < maxlen; n++) { 101 if ((rc = rio_read(rp, &c, 1)) == 1) { 102 *bufp++ = c; 103 if (c == '\n') { 104 n++; 105 break; 106 } 107 } else if (rc == 0) { 108 if (n == 1) 109 return 0; // EOF, no data read 110 else 111 break; // EOF, some data was read 112 } else { 113 return -1; // Error 114 } 115 } 116 *bufp = 0; 117 return n-1; 118} 119 120ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { 121 size_t nleft = n; 122 ssize_t nread; 123 char *bufp = usrbuf; 124 125 while (nleft > 0) { 126 if ((nread = rio_read(rp, bufp, nleft)) < 0) 127 return -1; 128 else if (nread == 0) 129 break; 130 nleft -= nread; 131 bufp += nread; 132 } 133 return (n - nleft); 134} 135
/deep/10/readme.md
打开或关闭文件
1#include <sys/types.h> 2#include <sys/stat.h> 3#include <fcntl.h> 4 5int open(char *filename, int flags, mode_t mode); 6// 返回:若成功则为新文件描述符,若出错为 -1
1#include <unistd.h> 2 3int close(int fd); 4// 返回:若成功则为 0,若出错则为 -1
读和写文件
size_t --- unsigned long ssize_t --- long
1#include <unistd.h> 2 3ssize_t read(int fd, void *buf, size_t n); 4// 返回:若成功则为读的字节数,若 EOF 则为 0,若出错为 -1 5 6ssize_t write(int fd, const void *buf, size_t n); 7// 返回:若成功则为写的字节数,若出错则为 -1
RIO Robust I/O
RIO 的无缓冲的输入输出函数
1ssize_t rio_readn(int fd, void *usrbuf, size_t n); 2ssize_t rio_writen(int fd, void *usrbuf, size_t n); 3// 返回:若成功则为传送的字节数,若 EOF 则为 0,若出错则为 -1
RIO 的带缓冲的输入函数
1void rio_readinitb(rio_t *rp, int fd); 2 3ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); 4ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n); 5// 返回:若成功则为读的字节数,若 EOF 则为 0,若出错则为 -1
/deep/11/csapp.c
1#include "csapp.h" 2#include <unistd.h> 3#define RIO_BUFSIZE 8192 4 5typedef struct { 6 int rio_fd; // Descriptor for this internal buf 7 int rio_cnt; // Unread bytes in internal buf 8 char *rio_bufptr; // Next unread byte in internal buf 9 char rio_buf[RIO_BUFSIZE]; // Internal buffer 10} rio_t; 11 12ssize_t rio_readn(int fd, void *usrbuf, size_t n) { 13 size_t nleft = n; 14 ssize_t nread; 15 char *bufp = usrbuf; 16 17 while (nleft > 0) { 18 if ((nread = read(fd, bufp, nleft)) < 0) { 19 // Interrupted by sig handler return and call read() again 20 // 被应用信号处理程序的返回终端,手动重启 21 if (errno == EINTR) { 22 nread = 0; 23 } else { 24 // errno set by read() 25 return -1; 26 } 27 } 28 else if (nread == 0) { 29 // EOF 30 break; 31 } 32 33 nleft -= nread; 34 bufp += nread; 35 } 36 37 // Return >= 0 38 return (n - nleft); 39} 40 41ssize_t rio_writen(int fd, void *usrbuf, size_t n) { 42 size_t nleft = n; 43 ssize_t nwritten; 44 char *bufp = usrbuf; 45 46 while (nleft > 0) { 47 if ((nwritten = write(fd, bufp, nleft)) <= 0) { 48 if (errno == EINTR) { 49 nwritten = 0; 50 } else { 51 return -1; 52 } 53 } 54 55 nleft -= nwritten; 56 bufp += nwritten; 57 } 58 59 return n; 60} 61 62void rio_readinitb(rio_t *rp, int fd) { 63 rp->rio_fd = fd; 64 rp->rio_cnt = 0; 65 rp->rio_bufptr = rp->rio_buf; 66} 67 68// 内部的 rio_read 函数 69static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { 70 int cnt; 71 72 while (rp->rio_cnt <= 0) { // Refill if buf is empty 73 rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 74 if (rp->rio_cnt < 0) { 75 if (errno != EINTR) { 76 return -1; 77 } 78 else if (rp->rio_cnt == 0) { // EOF 79 return 0; 80 } 81 else { 82 rp->rio_bufptr = rp->rio_buf; // Reset buffer ptr 83 } 84 } 85 } 86 87 // Copy min(n, rp->rio_cnt) bytes from internal buf to user buf 88 cnt = n; 89 if (rp->rio_cnt < n) 90 cnt = rp->rio_cnt; 91 memcpy(usrbuf, rp->rio_bufptr, cnt); 92 rp->rio_bufptr += cnt; 93 rp->rio_cnt -= cnt; 94 return cnt; 95} 96 97ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { 98 int n, rc; 99 char c, *bufp = usrbuf; 100 101 for (n = 1; n < maxlen; n++) { 102 if ((rc = rio_read(rp, &c, 1)) == 1) { 103 *bufp++ = c; 104 if (c == '\n') { 105 n++; 106 break; 107 } 108 } else if (rc == 0) { 109 if (n == 1) 110 return 0; // EOF, no data read 111 else 112 break; // EOF, some data was read 113 } else { 114 return -1; // Error 115 } 116 } 117 *bufp = 0; 118 return n-1; 119} 120 121ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { 122 size_t nleft = n; 123 ssize_t nread; 124 char *bufp = usrbuf; 125 126 while (nleft > 0) { 127 if ((nread = rio_read(rp, bufp, nleft)) < 0) 128 return -1; 129 else if (nread == 0) 130 break; 131 nleft -= nread; 132 bufp += nread; 133 } 134 return (n - nleft); 135} 136 137 138// 11 139 140int open_clientfd(char *hostname, char *port) { 141 int clientfd; 142 struct addrinfo hints, *listp, *p; 143 144 // get a list of potential server addresses 145 memset(&hints, 0, sizeof(struct addrinfo)); 146 hints.ai_socktype = SOCK_STREAM; // open a connection 147 hints.ai_flags = AI_NUMERICSERV; // using a numeric port arg 148 hints.ai_flags |= AI_ADDRCONFIG; // recommender for connections 149 getaddrinfo(hostname, port, &hints, &listp); 150 151 // walk the list for one that we can successfully connect to 152 for (p = listp; p; p = p->ai_next) { 153 // create a socket descriptor 154 if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 155 continue; // socket failed, try the next 156 157 // connect to the server 158 if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 159 break; // success 160 161 close(clientfd); // connect failed, try another 162 } 163 164 // clean up 165 freeaddrinfo(listp); 166 if (!p) // all connects failed 167 return -1; 168 else // the last connect succeeded 169 return clientfd; 170} 171 172int open_listenfd(char *port) { 173 struct addrinfo hints, *listp, *p; 174 int listenfd, optval = 1; 175 176 // get a list of potential server addresses 177 memset(&hints, 0, sizeof(struct addrinfo)); 178 hints.ai_socktype = SOCK_STREAM; // accept connection 179 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; // on any IP address 180 hints.ai_flags |= AI_NUMERICSERV; // using port number 181 getaddrinfo(NULL, port, &hints, &listp); 182 183 // walk the list for one that we can bind to 184 for (p = listp ; p; p = p->ai_next) { 185 // create a socket descriptor 186 if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 187 continue; // socket failed, try the next 188 189 // eliminates "address already in use" error from bind 190 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)); 191 192 // bind the descriptor to the address 193 if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) 194 break; // success 195 196 close(listenfd); // bind failed, try the next 197 } 198 199 // clean up 200 freeaddrinfo(listp); 201 if (!p) // no address worked 202 return -1; 203 204 // make it a listening socket ready to accept connection requests 205 if (listen(listenfd, LISTENQ) < 0) { 206 close(listenfd); 207 return -1; 208 } 209 return listenfd; 210} 211
/deep/11/csapp.h
1#include <stdio.h> 2#include <string.h> 3#include <errno.h> 4#include <stdlib.h> 5#include <unistd.h> 6#include <signal.h> 7#include <sys/types.h> 8#include <sys/socket.h> 9#include <netdb.h> 10 11#define RIO_BUFSIZE 8192 12#define MAXLINE 100 13#define LISTENQ 1024 14 15// Unix-style error 16void unix_error(char *msg) { 17 fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 18 exit(0); 19} 20 21pid_t Fork(void) { 22 pid_t pid; 23 if ((pid = fork()) < 0) 24 unix_error("Fork error"); 25 return pid; 26} 27 28typedef struct { 29 int rio_fd; // Descriptor for this internal buf 30 int rio_cnt; // Unread bytes in internal buf 31 char *rio_bufptr; // Next unread byte in internal buf 32 char rio_buf[RIO_BUFSIZE]; // Internal buffer 33} rio_t; 34 35ssize_t rio_readn(int fd, void *usrbuf, size_t n) { 36 size_t nleft = n; 37 ssize_t nread; 38 char *bufp = usrbuf; 39 40 while (nleft > 0) { 41 if ((nread = read(fd, bufp, nleft)) < 0) { 42 // Interrupted by sig handler return and call read() again 43 // 被应用信号处理程序的返回终端,手动重启 44 if (errno == EINTR) { 45 nread = 0; 46 } else { 47 // errno set by read() 48 return -1; 49 } 50 } 51 else if (nread == 0) { 52 // EOF 53 break; 54 } 55 56 nleft -= nread; 57 bufp += nread; 58 } 59 60 // Return >= 0 61 return (n - nleft); 62} 63 64ssize_t rio_writen(int fd, void *usrbuf, size_t n) { 65 size_t nleft = n; 66 ssize_t nwritten; 67 char *bufp = usrbuf; 68 69 while (nleft > 0) { 70 if ((nwritten = write(fd, bufp, nleft)) <= 0) { 71 if (errno == EINTR) { 72 nwritten = 0; 73 } else { 74 return -1; 75 } 76 } 77 78 nleft -= nwritten; 79 bufp += nwritten; 80 } 81 82 return n; 83} 84 85void rio_readinitb(rio_t *rp, int fd) { 86 rp->rio_fd = fd; 87 rp->rio_cnt = 0; 88 rp->rio_bufptr = rp->rio_buf; 89} 90 91// 内部的 rio_read 函数 92static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { 93 int cnt; 94 95 while (rp->rio_cnt <= 0) { // Refill if buf is empty 96 rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 97 if (rp->rio_cnt < 0) { 98 if (errno != EINTR) { 99 return -1; 100 } 101 else if (rp->rio_cnt == 0) { // EOF 102 return 0; 103 } 104 else { 105 rp->rio_bufptr = rp->rio_buf; // Reset buffer ptr 106 } 107 } 108 } 109 110 // Copy min(n, rp->rio_cnt) bytes from internal buf to user buf 111 cnt = n; 112 if (rp->rio_cnt < n) 113 cnt = rp->rio_cnt; 114 memcpy(usrbuf, rp->rio_bufptr, cnt); 115 rp->rio_bufptr += cnt; 116 rp->rio_cnt -= cnt; 117 return cnt; 118} 119 120ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { 121 int n, rc; 122 char c, *bufp = usrbuf; 123 124 for (n = 1; n < maxlen; n++) { 125 if ((rc = rio_read(rp, &c, 1)) == 1) { 126 *bufp++ = c; 127 if (c == '\n') { 128 n++; 129 break; 130 } 131 } else if (rc == 0) { 132 if (n == 1) 133 return 0; // EOF, no data read 134 else 135 break; // EOF, some data was read 136 } else { 137 return -1; // Error 138 } 139 } 140 *bufp = 0; 141 return n-1; 142} 143 144ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { 145 size_t nleft = n; 146 ssize_t nread; 147 char *bufp = usrbuf; 148 149 while (nleft > 0) { 150 if ((nread = rio_read(rp, bufp, nleft)) < 0) 151 return -1; 152 else if (nread == 0) 153 break; 154 nleft -= nread; 155 bufp += nread; 156 } 157 return (n - nleft); 158} 159 160 161// 11 162 163int open_clientfd(char *hostname, char *port) { 164 int clientfd; 165 struct addrinfo hints, *listp, *p; 166 167 // get a list of potential server addresses 168 memset(&hints, 0, sizeof(struct addrinfo)); 169 hints.ai_socktype = SOCK_STREAM; // open a connection 170 hints.ai_flags = AI_NUMERICSERV; // using a numeric port arg 171 hints.ai_flags |= AI_ADDRCONFIG; // recommender for connections 172 getaddrinfo(hostname, port, &hints, &listp); 173 174 // walk the list for one that we can successfully connect to 175 for (p = listp; p; p = p->ai_next) { 176 // create a socket descriptor 177 if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 178 continue; // socket failed, try the next 179 180 // connect to the server 181 if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 182 break; // success 183 184 close(clientfd); // connect failed, try another 185 } 186 187 // clean up 188 freeaddrinfo(listp); 189 if (!p) // all connects failed 190 return -1; 191 else // the last connect succeeded 192 return clientfd; 193} 194 195int open_listenfd(char *port) { 196 struct addrinfo hints, *listp, *p; 197 int listenfd, optval = 1; 198 199 // get a list of potential server addresses 200 memset(&hints, 0, sizeof(struct addrinfo)); 201 hints.ai_socktype = SOCK_STREAM; // accept connection 202 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; // on any IP address 203 hints.ai_flags |= AI_NUMERICSERV; // using port number 204 getaddrinfo(NULL, port, &hints, &listp); 205 206 // walk the list for one that we can bind to 207 for (p = listp ; p; p = p->ai_next) { 208 // create a socket descriptor 209 if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 210 continue; // socket failed, try the next 211 212 // eliminates "address already in use" error from bind 213 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)); 214 215 // bind the descriptor to the address 216 if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) 217 break; // success 218 219 close(listenfd); // bind failed, try the next 220 } 221 222 // clean up 223 freeaddrinfo(listp); 224 if (!p) // no address worked 225 return -1; 226 227 // make it a listening socket ready to accept connection requests 228 if (listen(listenfd, LISTENQ) < 0) { 229 close(listenfd); 230 return -1; 231 } 232 return listenfd; 233} 234 235 236void echo(int connfd) { 237 size_t n; 238 char buf[MAXLINE]; 239 rio_t rio; 240 241 rio_readinitb(&rio, connfd); 242 while((n = rio_readlineb(&rio, buf, MAXLINE)) != 0) { 243 printf("server received %d bytes\n", (int)n); 244 rio_writen(connfd, buf, n); 245 } 246} 247
/deep/11/echo.c
1#include "csapp.h" 2 3void echo(int connfd) { 4 size_t n; 5 char buf[MAXLINE]; 6 rio_t rio; 7 8 rio_readinitb(&rio, connfd); 9 while((n = rio_readlineb(&rio, buf, MAXLINE)) != 0) { 10 printf("server received %d bytes\n", (int)n); 11 rio_writen(connfd, buf, n); 12 } 13} 14
/deep/11/echoclient.c
1#include "csapp.h" 2 3int main(int argc, char **argv) { 4 int clientfd; 5 char *host, *port, buf[MAXLINE]; 6 rio_t rio; 7 8 if (argc != 3) { 9 fprintf(stderr, "usage: %s <host> <port>\n", argv[0]); 10 exit(0); 11 } 12 host = argv[1]; 13 port = argv[2]; 14 15 clientfd = open_clientfd(host, port); 16 rio_readinitb(&rio, clientfd); 17 18 while (fgets(buf, MAXLINE, stdin) != NULL) { 19 rio_writen(clientfd, buf, strlen(buf)); 20 rio_readlineb(&rio, buf, MAXLINE); 21 fputs(buf, stdout); 22 } 23 close(clientfd); 24 exit(0); 25} 26
/deep/11/echoserveri.c
1#include "csapp.h" 2 3void echo(int connfd); 4 5int main(int argc, char **argv) { 6 int listenfd, connfd; 7 socklen_t clientlen; 8 struct sockaddr_storage clientaddr; // enough space for any address 9 char client_hostname[MAXLINE], client_port[MAXLINE]; 10 11 if (argc != 2) { 12 fprintf(stderr, "usage: %s <port>\n", argv[0]); 13 exit(0); 14 } 15 16 listenfd = open_listenfd(argv[1]); 17 while (1) { 18 clientlen = sizeof(struct sockaddr_storage); 19 connfd = accept(listenfd, (struct sockaddr *) (&clientaddr), &clientlen); 20 getnameinfo((struct sockaddr *) (&clientaddr), clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0); 21 printf("Connected to (%s %s)\n", client_hostname, client_port); 22 echo(connfd); 23 close(connfd); 24 } 25 exit(0); 26} 27
/deep/11/hostinfo.c
1#include "csapp.h" 2 3int main(int argc, char **argv) { 4 struct addrinfo *p, *listp, hints; 5 char buf[MAXLINE]; 6 int rc, flags; 7 8 if (argc != 2) { 9 fprintf(stderr, "usage: %s <domain name>\n", argv[0]); 10 exit(0); 11 } 12 13 // get a list of addrinfo records 14 memset(&hints, 0, sizeof(struct addrinfo)); 15 hints.ai_family = AF_INET; // IPv4 only 16 hints.ai_socktype = SOCK_STREAM; // Connections only 17 if ((rc = getaddrinfo(argv[1], NULL, &hints, &listp)) != 0) { 18 fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc)); 19 exit(1); 20 } 21 22 // Walk the list and display each IP address 23 flags = NI_NUMERICHOST; // Display address string instead of domain name 24 for (p = listp; p; p = p->ai_next) { 25 getnameinfo(p->ai_addr, p->ai_addrlen, buf, MAXLINE, NULL, 0, flags); 26 printf("%s\n", buf); 27 } 28 29 // Clean up 30 freeaddrinfo(listp); 31 32 exit(0); 33} 34
/deep/11/readme.md
IP 地址
1// IP address structure 2struct in_addr { 3 uint32_t s_addr; // Address in netword byte order (big-endian) 4};
Unix 提供了下面这样的函数在网络和主机字节顺序间实现转换。
1#include <arpa/inet.h> 2 3uint32_t htonl(uint32_t hostlong); 4uint16_t htons(uint16_t hostshort); 5// 返回:按照网络字节顺序的值 6 7uint32_t ntohl(uint32_t netlong); 8uint16_t ntohs(uint16_t netshort); 9// 返回:按照主机字节顺序的值
套接字地址结构
1// IP socket address structure 2struct sockaddr_in { 3 uint16_t sin_family; 4 uint16_t sin_port; 5 struct in_addr sin_addr; 6 unsigned char sin_zero[8]; 7}; 8 9// Generic socket address structure 10struct sockaddr { 11 uint16_t sa_family; 12 char sa_data[14]; 13};
socket 函数
客户端和服务器使用 socket 函数来创建一个套接字描述符。
1#include <sys/types.h> 2#include <sys/socket.h> 3 4int socket(int domain, int type, int protocol); 5// 返回:若成功则为非负描述符,若出错则为 -1
connect 函数
客户端通过调用 connect 函数来建立和服务器的连接。
1#include <sys/socket.h> 2 3int connect(int clientfd, const struct sockaddr *addr, socklen_t addrlen); 4// 返回:若成功则为 0,若出错则为 -1
connect 函数会阻塞
服务器:bind、listen 和 accept
1#include <sys/socket.h> 2 3int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 4// 返回:若成功则为 0,若出错则为 -1
1#include <sys/socket.h> 2 3int listen(int sockfd, int backlog); 4// 返回:若成功则为 0,若出错则为 -1
1#include <sys/socket.h> 2 3int accept(int listenfd, struct sockaddr *addr, int *addrlen); 4// 返回:若成功则为非负连接描述符,若出错则为 -1
getaddrinfo 函数
将主机名、主机地址、服务名和端口号的字符串表示转化成套接字地址结构。
1#include <sys/types.h> 2#include <sys/socket.h> 3#include <netdb.h> 4 5int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct addrinfo **result); 6// 返回:如果成功则为 0,如果错误则为非零的错误代码 7 8void freeaddrinfo(struct addrinfo *result); 9// 返回: 无 10 11const char *gai_strerror(int errcode); 12// 返回:错误消息
addrinfo 结构
1struct addrinfo { 2 int ai_flags; 3 int ai_family; 4 int ai_socktype; 5 int ai_protocol; 6 char *ai_canonname; 7 size_t ai_addrlen; 8 struct sockaddr *ai_addr; 9 struct addrinfo *ai_next; 10};
getnameinfo 与 getaddrinfo 相反
1#include <sys/socket.h> 2#include <netdb.h> 3 4int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char * service, size_t servlen, int flags); 5// 返回:如果成功则为 0,如果错误则为非零的错误代码
套接字接口辅助函数
1int open_clientfd(char *hostname, char *port); 2int open_listenfd(char *port); 3// 返回:若成功则为描述符,若出错则为 -1
/deep/7-link/main.c
1int sum(int *a, int n); 2 3int array[2] = {1, 2}; 4 5int main() 6{ 7 int val = sum(array, 2); 8 return val; 9} 10
/deep/7-link/readme.md
gcc -Og -o prog main.c sum.c
/deep/7-link/sum.c
1int sum(int *a, int n) 2{ 3 int i, s = 0; 4 5 for (i = 0; i < n; i++) { 6 s += a[i]; 7 } 8 9 return s; 10} 11
/deep/8/csapp.h
1#include <stdio.h> 2#include <string.h> 3#include <errno.h> 4#include <stdlib.h> 5#include <unistd.h> 6#include <signal.h> 7 8// Unix-style error 9void unix_error(char *msg) { 10 fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 11 exit(0); 12} 13 14pid_t Fork(void) { 15 pid_t pid; 16 if ((pid = fork()) < 0) 17 unix_error("Fork error"); 18 return pid; 19} 20
/deep/8/fork.c
1#include <stdlib.h> 2#include <sys/types.h> 3#include <unistd.h> 4#include <stdio.h> 5 6int main() 7{ 8 pid_t pid; 9 int x = 1; 10 11 pid = fork(); 12 // 子进程 13 if (pid == 0) { 14 printf("child : x=%d\n", ++x); 15 exit(0); 16 } 17 18 // 父进程 19 printf("parent: x=%d\n", --x); 20 exit(0); 21} 22
/deep/8/get_pid.c
1#include <stdio.h> 2#include <sys/types.h> 3#include <unistd.h> 4 5int main() { 6 // 当前进程 7 pid_t p = getpid(); 8 printf("pid: %d\n", p); 9 10 // 父进程 11 p = getppid(); 12 printf("ppid: %d\n", p); 13 14 // 进程组 15 p = getpgrp(); 16 printf("pgrp: %d\n", p); 17 18 return 0; 19} 20
/deep/8/hello.c
1int main() 2{ 3 write(1, "hello, world\n", 13); 4 _exit(0); 5} 6
/deep/8/kill.c
1#include "csapp.h" 2 3int main() { 4 pid_t pid; 5 6 // Child sleeps until SIGKILL signal received, then dies 7 if ((pid = Fork()) == 0) { 8 // Wait for a signal to arrive 9 pause(); 10 printf("control should never reach here!\n"); 11 exit(0); 12 } 13 14 // Parent sends a SIGKILL signal to a child 15 kill(pid, SIGKILL); 16 exit(0); 17} 18
/deep/8/sigint.c
1#include "csapp.h" 2 3// 捕获键盘 Ctrl+C 发送的 SIGINT 信号 4 5// SIGINT handler 6void sigint_handler(int sig) { 7 printf("Caught SIGINT!\n"); 8 exit(0); 9} 10 11int main() { 12 // Install the SIGINT handler 13 if (signal(SIGINT, sigint_handler) == SIG_ERR) 14 unix_error("signal error"); 15 16 // Wait for the receipt of a signal 17 pause(); 18 19 return 0; 20} 21
/deep/8/waitpid1.c
1#include "csapp.h" 2#define N 10 3 4int main() 5{ 6 int status, i; 7 pid_t pid; 8 9 // 创建 N 个子进程 10 for (i = 0; i < N; i++) { 11 if ((pid = Fork()) == 0) { 12 // 子进程退出 13 exit(100+i); 14 } 15 } 16 17 // 回收子进程,顺序不固定 18 while ((pid = waitpid(-1, &status, 0)) > 0) { 19 if (WIFEXITED(status)) { 20 printf("child %d terminated normally with exit status=%d\n", pid, WEXITSTATUS(status)); 21 } else { 22 printf("child %d terminated abnormally\n", pid); 23 } 24 } 25 26 // 最后一次调用 waitpid 返回 -1,并设置 errno 为 ECHILD 27 if (errno != ECHILD) { 28 unix_error("waitpid error"); 29 } 30 31 exit(0); 32} 33
/deep/8/waitpid2.c
1#include "csapp.h" 2#define N 10 3 4int main() 5{ 6 int status, i; 7 pid_t pid[N], retpid; 8 9 // 创建 N 个子进程 10 for (i = 0; i < N; i++) { 11 if ((pid[i] = Fork()) == 0) { 12 // 子进程退出 13 exit(100+i); 14 } 15 } 16 17 // 回收子进程,顺序固定 18 i = 0; 19 while ((retpid = waitpid(pid[i++], &status, 0)) > 0) { 20 if (WIFEXITED(status)) { 21 printf("child %d terminated normally with exit status=%d\n", retpid, WEXITSTATUS(status)); 22 } else { 23 printf("child %d terminated abnormally\n", retpid); 24 } 25 } 26 27 // 最后一次调用 waitpid 返回 -1,并设置 errno 为 ECHILD 28 if (errno != ECHILD) { 29 unix_error("waitpid error"); 30 } 31 32 exit(0); 33} 34
/note.md
C 程序设计语言
1 - 导言
编译:cc hello.c
执行:./a.out
include <stdio.h>
包含标准库的信息,告诉编译器在本程序中包含标准输入/输出库的信息。
程序的起点为 main 函数
main 函数不接收参数值
用双引号括起来的字符序列称为字符串或字符常量。
printf 函数不是 C 语言本身的一部分,仅是标准库函数中的一个函数。
-
%3.0f 至少占 3 字符宽,不带小数点和小数部分
-
%6.1f 至少占 6 字符宽,小数点后面有 1 位数字
-
%d 十进制
-
%f 浮点数
-
%o 八进制
-
%x 十六进制
-
%c 字符
-
%s 字符串
-
%% %
#define
指令可以把符号名(符号变量)定义为一个特定的字符串:#define 名字 替换文本
,指令末尾无分号。
getchar() 和 putchar()
- getchar 从文本流中读入下一个输入字符,作为结果返回
- putchar 将打印一个字符
EOF end of file 定义在头文件<stdio.h>
中,是一个整型数。与任何 char 类型的值都不相同。
/tmp/main.c
1#include <stdio.h> 2 3void multstore(long, long, long *); 4 5int main() { 6 long d; 7 multstore(2, 3, &d); 8 printf("2 * 3 --> %ld\n", d); 9 return 0; 10} 11 12long mult2(long a, long b) { 13 long s = a * b; 14 return s; 15} 16
/tmp/mstore.c
1long mult2(long, long); 2 3void multstore(long x, long y, long *dest) { 4 long t = mult2(x, y); 5 *dest = t; 6} 7
/tmp/show_bytes.c
1#include <stdio.h> 2 3// 使用强制类型转换来访问和打印不同程序对象的字节表示 4 5typedef unsigned char *byte_pointer; 6 7void show_bytes(byte_pointer start, size_t len) { 8 // 因为 char 类型的指针每次是按照字节加 1 9 // 所以可以打印出存储在相应地址的字节 10 11 // pa[i] 与 *(pa+i) 是等价的 12 // 一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现 13 for (size_t i = 0; i < len; i++) { 14 // 数组形式 15 // start[i] 表示读取以 start 指向的位置为起始的第 i 个位置处的字节 16 printf(" %.2x", start[i]); 17 18 // 指针形式 19 // printf(" %.2x", *start++); 20 } 21 printf("\n"); 22} 23 24void show_int(int x) { 25 // 强制类型转换 26 // 并不会改变真实的指针,而是告诉编译器以新的数据类型来看待被指向的数据 27 show_bytes((byte_pointer) &x, sizeof(int)); 28} 29 30void show_float(float x) { 31 show_bytes((byte_pointer) &x, sizeof(float)); 32} 33 34// 指向任何对象的指针都可以转换为 void * 类型,且不会丢失信息 35// 如果将结果再转换为初始指针类型,则可以恢复初始指针 36// ANSI C 使用类型 void * 代替 char * 作为通用指针的类型 37void show_pointer(void *x) { 38 show_bytes((byte_pointer) &x, sizeof(void *)); 39} 40 41void test_show_bytes(int val) { 42 int ival = val; 43 // 39 30 00 00 => 小端法 44 show_int(ival); 45 show_float((float)ival); 46 show_pointer(&ival); 47} 48 49int main() { 50 // 0x00003039 51 int val = 12345; 52 test_show_bytes(val); 53 show_bytes((byte_pointer) &"12345", 6); 54 return 0; 55} 56
/tmp.md
/1-导言/1.6.c
1#include <stdio.h> 2 3// 1-6 4 5main() { 6 int c; 7 while (c = getchar() != EOF) 8 printf("%d\n", c); 9 printf("%d - at EOF\n", c); 10} 11
/1-导言/1.7.c
1#include <stdio.h> 2 3// 1-7 4 5main() 6{ 7 printf("EOF is %d\n", EOF); 8} 9
/1-导言/a.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5 6main() 7{ 8 int fahr, celsius; 9 int lower, upper, step; 10 11 lower = 0; 12 upper = 300; 13 step = 20; 14 15 fahr = lower; 16 while (fahr <= upper) { 17 celsius = 5 * (fahr-32) / 9; 18 printf("%d\t%d\n", fahr, celsius); 19 fahr = fahr + step; 20 } 21} 22
/1-导言/b.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5// 优化版本 6 7main() 8{ 9 // 浮点数 10 float fahr, celsius; 11 int lower, upper, step; 12 13 lower = 0; 14 upper = 300; 15 step = 20; 16 17 fahr = lower; 18 while (fahr <= upper) 19 { 20 celsius = (5.0/9.0) * (fahr-32.0); 21 // 输出美观 22 // %3.0f 至少占3字符宽,不带小数点和小数部分 23 // %6.1f 至少占6字符宽,小数点后面有1位数字 24 printf("%3.0f %6.1f\n", fahr, celsius); 25 fahr = fahr + step; 26 } 27} 28
/1-导言/c.c
1# include <stdio.h> 2 3// 摄氏温度转华氏温度 4// f = 9*c / 5 + 32 5 6main() 7{ 8 float fahr, celsius; 9 int lower, upper, step; 10 11 lower = 0; 12 upper = 300; 13 step = 20; 14 15 printf("Celsius Fahr\n"); 16 celsius = lower; 17 while (celsius <= upper) { 18 fahr = 9.0 * celsius / 5.0 + 32.0; 19 printf("%3.0f %6.1f\n", celsius, fahr); 20 celsius = celsius + step; 21 } 22} 23
/1-导言/copy.c
1#include <stdio.h> 2 3main() { 4 int c; 5 c = getchar(); 6 while(c != EOF){ 7 putchar(c); 8 c = getchar(); 9 } 10} 11
/1-导言/copy2.c
1#include <stdio.h> 2 3main() 4{ 5 int c; 6 7 while ((c = getchar()) != EOF) 8 putchar(c); 9} 10
/1-导言/d.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5// 优化版本 6// for 7 8main() 9{ 10 float fahr; 11 12 for (fahr = 0; fahr <= 300; fahr = fahr + 20) { 13 printf("%3.0f %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0)); 14 } 15} 16
/1-导言/e.c
1#include <stdio.h> 2 3// 打印华氏温度和摄氏温度对照表 4// c = (5/9) * (f-32) 5// 优化版本 6// for 7// 逆序 8 9main() 10{ 11 float fahr; 12 13 for (fahr = 300; fahr >= 0; fahr = fahr - 20) 14 { 15 printf("%3.0f %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0)); 16 } 17} 18
/1-导言/f.c
1#include <stdio.h> 2 3#define LOWER 0 4#define UPPER 300 5#define STEP 20 6 7// 打印华氏温度和摄氏温度对照表 8// c = (5/9) * (f-32) 9// 优化版本 10// for 11 12main() 13{ 14 float fahr; 15 16 for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) 17 { 18 printf("%3.0f %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0)); 19 } 20} 21
/1-导言/hello.c
1#include <stdio.h> 2 3main() 4{ 5 printf("hello, world\n"); 6} 7
/deep/10/cpstdin.c
1#include <unistd.h> 2 3// 使用 read 和 write 调用一次一个字节地从标准输入复制到标准输出 4 5int main() { 6 char c; 7 8 while (read(STDIN_FILENO, &c, 1) != 0) { 9 write(STDOUT_FILENO, &c, 1); 10 } 11 12 return 0; 13} 14
/deep/10/csapp.c
1#include <unistd.h> 2#define RIO_BUFSIZE 8192 3 4typedef struct { 5 int rio_fd; // Descriptor for this internal buf 6 int rio_cnt; // Unread bytes in internal buf 7 char *rio_bufptr; // Next unread byte in internal buf 8 char rio_buf[RIO_BUFSIZE]; // Internal buffer 9} rio_t; 10 11ssize_t rio_readn(int fd, void *usrbuf, size_t n) { 12 size_t nleft = n; 13 ssize_t nread; 14 char *bufp = usrbuf; 15 16 while (nleft > 0) { 17 if ((nread = read(fd, bufp, nleft)) < 0) { 18 // Interrupted by sig handler return and call read() again 19 // 被应用信号处理程序的返回终端,手动重启 20 if (errno == EINTR) { 21 nread = 0; 22 } else { 23 // errno set by read() 24 return -1; 25 } 26 } 27 else if (nread == 0) { 28 // EOF 29 break; 30 } 31 32 nleft -= nread; 33 bufp += nread; 34 } 35 36 // Return >= 0 37 return (n - nleft); 38} 39 40ssize_t rio_writen(int fd, void *usrbuf, size_t n) { 41 size_t nleft = n; 42 ssize_t nwritten; 43 char *bufp = usrbuf; 44 45 while (nleft > 0) { 46 if ((nwritten = write(fd, bufp, nleft)) <= 0) { 47 if (errno == EINTR) { 48 nwritten = 0; 49 } else { 50 return -1; 51 } 52 } 53 54 nleft -= nwritten; 55 bufp += nwritten; 56 } 57 58 return n; 59} 60 61void rio_readinitb(rio_t *rp, int fd) { 62 rp->rio_fd = fd; 63 rp->rio_cnt = 0; 64 rp->rio_bufptr = rp->rio_buf; 65} 66 67// 内部的 rio_read 函数 68static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { 69 int cnt; 70 71 while (rp->rio_cnt <= 0) { // Refill if buf is empty 72 rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 73 if (rp->rio_cnt < 0) { 74 if (errno != EINTR) { 75 return -1; 76 } 77 else if (rp->rio_cnt == 0) { // EOF 78 return 0; 79 } 80 else { 81 rp->rio_bufptr = rp->rio_buf; // Reset buffer ptr 82 } 83 } 84 } 85 86 // Copy min(n, rp->rio_cnt) bytes from internal buf to user buf 87 cnt = n; 88 if (rp->rio_cnt < n) 89 cnt = rp->rio_cnt; 90 memcpy(usrbuf, rp->rio_bufptr, cnt); 91 rp->rio_bufptr += cnt; 92 rp->rio_cnt -= cnt; 93 return cnt; 94} 95 96ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { 97 int n, rc; 98 char c, *bufp = usrbuf; 99 100 for (n = 1; n < maxlen; n++) { 101 if ((rc = rio_read(rp, &c, 1)) == 1) { 102 *bufp++ = c; 103 if (c == '\n') { 104 n++; 105 break; 106 } 107 } else if (rc == 0) { 108 if (n == 1) 109 return 0; // EOF, no data read 110 else 111 break; // EOF, some data was read 112 } else { 113 return -1; // Error 114 } 115 } 116 *bufp = 0; 117 return n-1; 118} 119 120ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { 121 size_t nleft = n; 122 ssize_t nread; 123 char *bufp = usrbuf; 124 125 while (nleft > 0) { 126 if ((nread = rio_read(rp, bufp, nleft)) < 0) 127 return -1; 128 else if (nread == 0) 129 break; 130 nleft -= nread; 131 bufp += nread; 132 } 133 return (n - nleft); 134} 135
/deep/10/readme.md
打开或关闭文件
1#include <sys/types.h> 2#include <sys/stat.h> 3#include <fcntl.h> 4 5int open(char *filename, int flags, mode_t mode); 6// 返回:若成功则为新文件描述符,若出错为 -1
1#include <unistd.h> 2 3int close(int fd); 4// 返回:若成功则为 0,若出错则为 -1
读和写文件
size_t --- unsigned long ssize_t --- long
1#include <unistd.h> 2 3ssize_t read(int fd, void *buf, size_t n); 4// 返回:若成功则为读的字节数,若 EOF 则为 0,若出错为 -1 5 6ssize_t write(int fd, const void *buf, size_t n); 7// 返回:若成功则为写的字节数,若出错则为 -1
RIO Robust I/O
RIO 的无缓冲的输入输出函数
1ssize_t rio_readn(int fd, void *usrbuf, size_t n); 2ssize_t rio_writen(int fd, void *usrbuf, size_t n); 3// 返回:若成功则为传送的字节数,若 EOF 则为 0,若出错则为 -1
RIO 的带缓冲的输入函数
1void rio_readinitb(rio_t *rp, int fd); 2 3ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); 4ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n); 5// 返回:若成功则为读的字节数,若 EOF 则为 0,若出错则为 -1
/deep/11/csapp.c
1#include "csapp.h" 2#include <unistd.h> 3#define RIO_BUFSIZE 8192 4 5typedef struct { 6 int rio_fd; // Descriptor for this internal buf 7 int rio_cnt; // Unread bytes in internal buf 8 char *rio_bufptr; // Next unread byte in internal buf 9 char rio_buf[RIO_BUFSIZE]; // Internal buffer 10} rio_t; 11 12ssize_t rio_readn(int fd, void *usrbuf, size_t n) { 13 size_t nleft = n; 14 ssize_t nread; 15 char *bufp = usrbuf; 16 17 while (nleft > 0) { 18 if ((nread = read(fd, bufp, nleft)) < 0) { 19 // Interrupted by sig handler return and call read() again 20 // 被应用信号处理程序的返回终端,手动重启 21 if (errno == EINTR) { 22 nread = 0; 23 } else { 24 // errno set by read() 25 return -1; 26 } 27 } 28 else if (nread == 0) { 29 // EOF 30 break; 31 } 32 33 nleft -= nread; 34 bufp += nread; 35 } 36 37 // Return >= 0 38 return (n - nleft); 39} 40 41ssize_t rio_writen(int fd, void *usrbuf, size_t n) { 42 size_t nleft = n; 43 ssize_t nwritten; 44 char *bufp = usrbuf; 45 46 while (nleft > 0) { 47 if ((nwritten = write(fd, bufp, nleft)) <= 0) { 48 if (errno == EINTR) { 49 nwritten = 0; 50 } else { 51 return -1; 52 } 53 } 54 55 nleft -= nwritten; 56 bufp += nwritten; 57 } 58 59 return n; 60} 61 62void rio_readinitb(rio_t *rp, int fd) { 63 rp->rio_fd = fd; 64 rp->rio_cnt = 0; 65 rp->rio_bufptr = rp->rio_buf; 66} 67 68// 内部的 rio_read 函数 69static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { 70 int cnt; 71 72 while (rp->rio_cnt <= 0) { // Refill if buf is empty 73 rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 74 if (rp->rio_cnt < 0) { 75 if (errno != EINTR) { 76 return -1; 77 } 78 else if (rp->rio_cnt == 0) { // EOF 79 return 0; 80 } 81 else { 82 rp->rio_bufptr = rp->rio_buf; // Reset buffer ptr 83 } 84 } 85 } 86 87 // Copy min(n, rp->rio_cnt) bytes from internal buf to user buf 88 cnt = n; 89 if (rp->rio_cnt < n) 90 cnt = rp->rio_cnt; 91 memcpy(usrbuf, rp->rio_bufptr, cnt); 92 rp->rio_bufptr += cnt; 93 rp->rio_cnt -= cnt; 94 return cnt; 95} 96 97ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { 98 int n, rc; 99 char c, *bufp = usrbuf; 100 101 for (n = 1; n < maxlen; n++) { 102 if ((rc = rio_read(rp, &c, 1)) == 1) { 103 *bufp++ = c; 104 if (c == '\n') { 105 n++; 106 break; 107 } 108 } else if (rc == 0) { 109 if (n == 1) 110 return 0; // EOF, no data read 111 else 112 break; // EOF, some data was read 113 } else { 114 return -1; // Error 115 } 116 } 117 *bufp = 0; 118 return n-1; 119} 120 121ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { 122 size_t nleft = n; 123 ssize_t nread; 124 char *bufp = usrbuf; 125 126 while (nleft > 0) { 127 if ((nread = rio_read(rp, bufp, nleft)) < 0) 128 return -1; 129 else if (nread == 0) 130 break; 131 nleft -= nread; 132 bufp += nread; 133 } 134 return (n - nleft); 135} 136 137 138// 11 139 140int open_clientfd(char *hostname, char *port) { 141 int clientfd; 142 struct addrinfo hints, *listp, *p; 143 144 // get a list of potential server addresses 145 memset(&hints, 0, sizeof(struct addrinfo)); 146 hints.ai_socktype = SOCK_STREAM; // open a connection 147 hints.ai_flags = AI_NUMERICSERV; // using a numeric port arg 148 hints.ai_flags |= AI_ADDRCONFIG; // recommender for connections 149 getaddrinfo(hostname, port, &hints, &listp); 150 151 // walk the list for one that we can successfully connect to 152 for (p = listp; p; p = p->ai_next) { 153 // create a socket descriptor 154 if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 155 continue; // socket failed, try the next 156 157 // connect to the server 158 if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 159 break; // success 160 161 close(clientfd); // connect failed, try another 162 } 163 164 // clean up 165 freeaddrinfo(listp); 166 if (!p) // all connects failed 167 return -1; 168 else // the last connect succeeded 169 return clientfd; 170} 171 172int open_listenfd(char *port) { 173 struct addrinfo hints, *listp, *p; 174 int listenfd, optval = 1; 175 176 // get a list of potential server addresses 177 memset(&hints, 0, sizeof(struct addrinfo)); 178 hints.ai_socktype = SOCK_STREAM; // accept connection 179 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; // on any IP address 180 hints.ai_flags |= AI_NUMERICSERV; // using port number 181 getaddrinfo(NULL, port, &hints, &listp); 182 183 // walk the list for one that we can bind to 184 for (p = listp ; p; p = p->ai_next) { 185 // create a socket descriptor 186 if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 187 continue; // socket failed, try the next 188 189 // eliminates "address already in use" error from bind 190 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)); 191 192 // bind the descriptor to the address 193 if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) 194 break; // success 195 196 close(listenfd); // bind failed, try the next 197 } 198 199 // clean up 200 freeaddrinfo(listp); 201 if (!p) // no address worked 202 return -1; 203 204 // make it a listening socket ready to accept connection requests 205 if (listen(listenfd, LISTENQ) < 0) { 206 close(listenfd); 207 return -1; 208 } 209 return listenfd; 210} 211
/deep/11/csapp.h
1#include <stdio.h> 2#include <string.h> 3#include <errno.h> 4#include <stdlib.h> 5#include <unistd.h> 6#include <signal.h> 7#include <sys/types.h> 8#include <sys/socket.h> 9#include <netdb.h> 10 11#define RIO_BUFSIZE 8192 12#define MAXLINE 100 13#define LISTENQ 1024 14 15// Unix-style error 16void unix_error(char *msg) { 17 fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 18 exit(0); 19} 20 21pid_t Fork(void) { 22 pid_t pid; 23 if ((pid = fork()) < 0) 24 unix_error("Fork error"); 25 return pid; 26} 27 28typedef struct { 29 int rio_fd; // Descriptor for this internal buf 30 int rio_cnt; // Unread bytes in internal buf 31 char *rio_bufptr; // Next unread byte in internal buf 32 char rio_buf[RIO_BUFSIZE]; // Internal buffer 33} rio_t; 34 35ssize_t rio_readn(int fd, void *usrbuf, size_t n) { 36 size_t nleft = n; 37 ssize_t nread; 38 char *bufp = usrbuf; 39 40 while (nleft > 0) { 41 if ((nread = read(fd, bufp, nleft)) < 0) { 42 // Interrupted by sig handler return and call read() again 43 // 被应用信号处理程序的返回终端,手动重启 44 if (errno == EINTR) { 45 nread = 0; 46 } else { 47 // errno set by read() 48 return -1; 49 } 50 } 51 else if (nread == 0) { 52 // EOF 53 break; 54 } 55 56 nleft -= nread; 57 bufp += nread; 58 } 59 60 // Return >= 0 61 return (n - nleft); 62} 63 64ssize_t rio_writen(int fd, void *usrbuf, size_t n) { 65 size_t nleft = n; 66 ssize_t nwritten; 67 char *bufp = usrbuf; 68 69 while (nleft > 0) { 70 if ((nwritten = write(fd, bufp, nleft)) <= 0) { 71 if (errno == EINTR) { 72 nwritten = 0; 73 } else { 74 return -1; 75 } 76 } 77 78 nleft -= nwritten; 79 bufp += nwritten; 80 } 81 82 return n; 83} 84 85void rio_readinitb(rio_t *rp, int fd) { 86 rp->rio_fd = fd; 87 rp->rio_cnt = 0; 88 rp->rio_bufptr = rp->rio_buf; 89} 90 91// 内部的 rio_read 函数 92static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) { 93 int cnt; 94 95 while (rp->rio_cnt <= 0) { // Refill if buf is empty 96 rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); 97 if (rp->rio_cnt < 0) { 98 if (errno != EINTR) { 99 return -1; 100 } 101 else if (rp->rio_cnt == 0) { // EOF 102 return 0; 103 } 104 else { 105 rp->rio_bufptr = rp->rio_buf; // Reset buffer ptr 106 } 107 } 108 } 109 110 // Copy min(n, rp->rio_cnt) bytes from internal buf to user buf 111 cnt = n; 112 if (rp->rio_cnt < n) 113 cnt = rp->rio_cnt; 114 memcpy(usrbuf, rp->rio_bufptr, cnt); 115 rp->rio_bufptr += cnt; 116 rp->rio_cnt -= cnt; 117 return cnt; 118} 119 120ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { 121 int n, rc; 122 char c, *bufp = usrbuf; 123 124 for (n = 1; n < maxlen; n++) { 125 if ((rc = rio_read(rp, &c, 1)) == 1) { 126 *bufp++ = c; 127 if (c == '\n') { 128 n++; 129 break; 130 } 131 } else if (rc == 0) { 132 if (n == 1) 133 return 0; // EOF, no data read 134 else 135 break; // EOF, some data was read 136 } else { 137 return -1; // Error 138 } 139 } 140 *bufp = 0; 141 return n-1; 142} 143 144ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) { 145 size_t nleft = n; 146 ssize_t nread; 147 char *bufp = usrbuf; 148 149 while (nleft > 0) { 150 if ((nread = rio_read(rp, bufp, nleft)) < 0) 151 return -1; 152 else if (nread == 0) 153 break; 154 nleft -= nread; 155 bufp += nread; 156 } 157 return (n - nleft); 158} 159 160 161// 11 162 163int open_clientfd(char *hostname, char *port) { 164 int clientfd; 165 struct addrinfo hints, *listp, *p; 166 167 // get a list of potential server addresses 168 memset(&hints, 0, sizeof(struct addrinfo)); 169 hints.ai_socktype = SOCK_STREAM; // open a connection 170 hints.ai_flags = AI_NUMERICSERV; // using a numeric port arg 171 hints.ai_flags |= AI_ADDRCONFIG; // recommender for connections 172 getaddrinfo(hostname, port, &hints, &listp); 173 174 // walk the list for one that we can successfully connect to 175 for (p = listp; p; p = p->ai_next) { 176 // create a socket descriptor 177 if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 178 continue; // socket failed, try the next 179 180 // connect to the server 181 if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 182 break; // success 183 184 close(clientfd); // connect failed, try another 185 } 186 187 // clean up 188 freeaddrinfo(listp); 189 if (!p) // all connects failed 190 return -1; 191 else // the last connect succeeded 192 return clientfd; 193} 194 195int open_listenfd(char *port) { 196 struct addrinfo hints, *listp, *p; 197 int listenfd, optval = 1; 198 199 // get a list of potential server addresses 200 memset(&hints, 0, sizeof(struct addrinfo)); 201 hints.ai_socktype = SOCK_STREAM; // accept connection 202 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; // on any IP address 203 hints.ai_flags |= AI_NUMERICSERV; // using port number 204 getaddrinfo(NULL, port, &hints, &listp); 205 206 // walk the list for one that we can bind to 207 for (p = listp ; p; p = p->ai_next) { 208 // create a socket descriptor 209 if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 210 continue; // socket failed, try the next 211 212 // eliminates "address already in use" error from bind 213 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)); 214 215 // bind the descriptor to the address 216 if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) 217 break; // success 218 219 close(listenfd); // bind failed, try the next 220 } 221 222 // clean up 223 freeaddrinfo(listp); 224 if (!p) // no address worked 225 return -1; 226 227 // make it a listening socket ready to accept connection requests 228 if (listen(listenfd, LISTENQ) < 0) { 229 close(listenfd); 230 return -1; 231 } 232 return listenfd; 233} 234 235 236void echo(int connfd) { 237 size_t n; 238 char buf[MAXLINE]; 239 rio_t rio; 240 241 rio_readinitb(&rio, connfd); 242 while((n = rio_readlineb(&rio, buf, MAXLINE)) != 0) { 243 printf("server received %d bytes\n", (int)n); 244 rio_writen(connfd, buf, n); 245 } 246} 247
/deep/11/echo.c
1#include "csapp.h" 2 3void echo(int connfd) { 4 size_t n; 5 char buf[MAXLINE]; 6 rio_t rio; 7 8 rio_readinitb(&rio, connfd); 9 while((n = rio_readlineb(&rio, buf, MAXLINE)) != 0) { 10 printf("server received %d bytes\n", (int)n); 11 rio_writen(connfd, buf, n); 12 } 13} 14
/deep/11/echoclient.c
1#include "csapp.h" 2 3int main(int argc, char **argv) { 4 int clientfd; 5 char *host, *port, buf[MAXLINE]; 6 rio_t rio; 7 8 if (argc != 3) { 9 fprintf(stderr, "usage: %s <host> <port>\n", argv[0]); 10 exit(0); 11 } 12 host = argv[1]; 13 port = argv[2]; 14 15 clientfd = open_clientfd(host, port); 16 rio_readinitb(&rio, clientfd); 17 18 while (fgets(buf, MAXLINE, stdin) != NULL) { 19 rio_writen(clientfd, buf, strlen(buf)); 20 rio_readlineb(&rio, buf, MAXLINE); 21 fputs(buf, stdout); 22 } 23 close(clientfd); 24 exit(0); 25} 26
/deep/11/echoserveri.c
1#include "csapp.h" 2 3void echo(int connfd); 4 5int main(int argc, char **argv) { 6 int listenfd, connfd; 7 socklen_t clientlen; 8 struct sockaddr_storage clientaddr; // enough space for any address 9 char client_hostname[MAXLINE], client_port[MAXLINE]; 10 11 if (argc != 2) { 12 fprintf(stderr, "usage: %s <port>\n", argv[0]); 13 exit(0); 14 } 15 16 listenfd = open_listenfd(argv[1]); 17 while (1) { 18 clientlen = sizeof(struct sockaddr_storage); 19 connfd = accept(listenfd, (struct sockaddr *) (&clientaddr), &clientlen); 20 getnameinfo((struct sockaddr *) (&clientaddr), clientlen, client_hostname, MAXLINE, client_port, MAXLINE, 0); 21 printf("Connected to (%s %s)\n", client_hostname, client_port); 22 echo(connfd); 23 close(connfd); 24 } 25 exit(0); 26} 27
/deep/11/hostinfo.c
1#include "csapp.h" 2 3int main(int argc, char **argv) { 4 struct addrinfo *p, *listp, hints; 5 char buf[MAXLINE]; 6 int rc, flags; 7 8 if (argc != 2) { 9 fprintf(stderr, "usage: %s <domain name>\n", argv[0]); 10 exit(0); 11 } 12 13 // get a list of addrinfo records 14 memset(&hints, 0, sizeof(struct addrinfo)); 15 hints.ai_family = AF_INET; // IPv4 only 16 hints.ai_socktype = SOCK_STREAM; // Connections only 17 if ((rc = getaddrinfo(argv[1], NULL, &hints, &listp)) != 0) { 18 fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(rc)); 19 exit(1); 20 } 21 22 // Walk the list and display each IP address 23 flags = NI_NUMERICHOST; // Display address string instead of domain name 24 for (p = listp; p; p = p->ai_next) { 25 getnameinfo(p->ai_addr, p->ai_addrlen, buf, MAXLINE, NULL, 0, flags); 26 printf("%s\n", buf); 27 } 28 29 // Clean up 30 freeaddrinfo(listp); 31 32 exit(0); 33} 34
/deep/11/readme.md
IP 地址
1// IP address structure 2struct in_addr { 3 uint32_t s_addr; // Address in netword byte order (big-endian) 4};
Unix 提供了下面这样的函数在网络和主机字节顺序间实现转换。
1#include <arpa/inet.h> 2 3uint32_t htonl(uint32_t hostlong); 4uint16_t htons(uint16_t hostshort); 5// 返回:按照网络字节顺序的值 6 7uint32_t ntohl(uint32_t netlong); 8uint16_t ntohs(uint16_t netshort); 9// 返回:按照主机字节顺序的值
套接字地址结构
1// IP socket address structure 2struct sockaddr_in { 3 uint16_t sin_family; 4 uint16_t sin_port; 5 struct in_addr sin_addr; 6 unsigned char sin_zero[8]; 7}; 8 9// Generic socket address structure 10struct sockaddr { 11 uint16_t sa_family; 12 char sa_data[14]; 13};
socket 函数
客户端和服务器使用 socket 函数来创建一个套接字描述符。
1#include <sys/types.h> 2#include <sys/socket.h> 3 4int socket(int domain, int type, int protocol); 5// 返回:若成功则为非负描述符,若出错则为 -1
connect 函数
客户端通过调用 connect 函数来建立和服务器的连接。
1#include <sys/socket.h> 2 3int connect(int clientfd, const struct sockaddr *addr, socklen_t addrlen); 4// 返回:若成功则为 0,若出错则为 -1
connect 函数会阻塞
服务器:bind、listen 和 accept
1#include <sys/socket.h> 2 3int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 4// 返回:若成功则为 0,若出错则为 -1
1#include <sys/socket.h> 2 3int listen(int sockfd, int backlog); 4// 返回:若成功则为 0,若出错则为 -1
1#include <sys/socket.h> 2 3int accept(int listenfd, struct sockaddr *addr, int *addrlen); 4// 返回:若成功则为非负连接描述符,若出错则为 -1
getaddrinfo 函数
将主机名、主机地址、服务名和端口号的字符串表示转化成套接字地址结构。
1#include <sys/types.h> 2#include <sys/socket.h> 3#include <netdb.h> 4 5int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct addrinfo **result); 6// 返回:如果成功则为 0,如果错误则为非零的错误代码 7 8void freeaddrinfo(struct addrinfo *result); 9// 返回: 无 10 11const char *gai_strerror(int errcode); 12// 返回:错误消息
addrinfo 结构
1struct addrinfo { 2 int ai_flags; 3 int ai_family; 4 int ai_socktype; 5 int ai_protocol; 6 char *ai_canonname; 7 size_t ai_addrlen; 8 struct sockaddr *ai_addr; 9 struct addrinfo *ai_next; 10};
getnameinfo 与 getaddrinfo 相反
1#include <sys/socket.h> 2#include <netdb.h> 3 4int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char * service, size_t servlen, int flags); 5// 返回:如果成功则为 0,如果错误则为非零的错误代码
套接字接口辅助函数
1int open_clientfd(char *hostname, char *port); 2int open_listenfd(char *port); 3// 返回:若成功则为描述符,若出错则为 -1
/deep/7-link/main.c
1int sum(int *a, int n); 2 3int array[2] = {1, 2}; 4 5int main() 6{ 7 int val = sum(array, 2); 8 return val; 9} 10
/deep/7-link/readme.md
gcc -Og -o prog main.c sum.c
/deep/7-link/sum.c
1int sum(int *a, int n) 2{ 3 int i, s = 0; 4 5 for (i = 0; i < n; i++) { 6 s += a[i]; 7 } 8 9 return s; 10} 11
/deep/8/csapp.h
1#include <stdio.h> 2#include <string.h> 3#include <errno.h> 4#include <stdlib.h> 5#include <unistd.h> 6#include <signal.h> 7 8// Unix-style error 9void unix_error(char *msg) { 10 fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 11 exit(0); 12} 13 14pid_t Fork(void) { 15 pid_t pid; 16 if ((pid = fork()) < 0) 17 unix_error("Fork error"); 18 return pid; 19} 20
/deep/8/fork.c
1#include <stdlib.h> 2#include <sys/types.h> 3#include <unistd.h> 4#include <stdio.h> 5 6int main() 7{ 8 pid_t pid; 9 int x = 1; 10 11 pid = fork(); 12 // 子进程 13 if (pid == 0) { 14 printf("child : x=%d\n", ++x); 15 exit(0); 16 } 17 18 // 父进程 19 printf("parent: x=%d\n", --x); 20 exit(0); 21} 22
/deep/8/get_pid.c
1#include <stdio.h> 2#include <sys/types.h> 3#include <unistd.h> 4 5int main() { 6 // 当前进程 7 pid_t p = getpid(); 8 printf("pid: %d\n", p); 9 10 // 父进程 11 p = getppid(); 12 printf("ppid: %d\n", p); 13 14 // 进程组 15 p = getpgrp(); 16 printf("pgrp: %d\n", p); 17 18 return 0; 19} 20
/deep/8/hello.c
1int main() 2{ 3 write(1, "hello, world\n", 13); 4 _exit(0); 5} 6
/deep/8/kill.c
1#include "csapp.h" 2 3int main() { 4 pid_t pid; 5 6 // Child sleeps until SIGKILL signal received, then dies 7 if ((pid = Fork()) == 0) { 8 // Wait for a signal to arrive 9 pause(); 10 printf("control should never reach here!\n"); 11 exit(0); 12 } 13 14 // Parent sends a SIGKILL signal to a child 15 kill(pid, SIGKILL); 16 exit(0); 17} 18
/deep/8/sigint.c
1#include "csapp.h" 2 3// 捕获键盘 Ctrl+C 发送的 SIGINT 信号 4 5// SIGINT handler 6void sigint_handler(int sig) { 7 printf("Caught SIGINT!\n"); 8 exit(0); 9} 10 11int main() { 12 // Install the SIGINT handler 13 if (signal(SIGINT, sigint_handler) == SIG_ERR) 14 unix_error("signal error"); 15 16 // Wait for the receipt of a signal 17 pause(); 18 19 return 0; 20} 21
/deep/8/waitpid1.c
1#include "csapp.h" 2#define N 10 3 4int main() 5{ 6 int status, i; 7 pid_t pid; 8 9 // 创建 N 个子进程 10 for (i = 0; i < N; i++) { 11 if ((pid = Fork()) == 0) { 12 // 子进程退出 13 exit(100+i); 14 } 15 } 16 17 // 回收子进程,顺序不固定 18 while ((pid = waitpid(-1, &status, 0)) > 0) { 19 if (WIFEXITED(status)) { 20 printf("child %d terminated normally with exit status=%d\n", pid, WEXITSTATUS(status)); 21 } else { 22 printf("child %d terminated abnormally\n", pid); 23 } 24 } 25 26 // 最后一次调用 waitpid 返回 -1,并设置 errno 为 ECHILD 27 if (errno != ECHILD) { 28 unix_error("waitpid error"); 29 } 30 31 exit(0); 32} 33
/deep/8/waitpid2.c
1#include "csapp.h" 2#define N 10 3 4int main() 5{ 6 int status, i; 7 pid_t pid[N], retpid; 8 9 // 创建 N 个子进程 10 for (i = 0; i < N; i++) { 11 if ((pid[i] = Fork()) == 0) { 12 // 子进程退出 13 exit(100+i); 14 } 15 } 16 17 // 回收子进程,顺序固定 18 i = 0; 19 while ((retpid = waitpid(pid[i++], &status, 0)) > 0) { 20 if (WIFEXITED(status)) { 21 printf("child %d terminated normally with exit status=%d\n", retpid, WEXITSTATUS(status)); 22 } else { 23 printf("child %d terminated abnormally\n", retpid); 24 } 25 } 26 27 // 最后一次调用 waitpid 返回 -1,并设置 errno 为 ECHILD 28 if (errno != ECHILD) { 29 unix_error("waitpid error"); 30 } 31 32 exit(0); 33} 34
/note.md
C 程序设计语言
1 - 导言
编译:cc hello.c
执行:./a.out
include <stdio.h>
包含标准库的信息,告诉编译器在本程序中包含标准输入/输出库的信息。
程序的起点为 main 函数
main 函数不接收参数值
用双引号括起来的字符序列称为字符串或字符常量。
printf 函数不是 C 语言本身的一部分,仅是标准库函数中的一个函数。
-
%3.0f 至少占 3 字符宽,不带小数点和小数部分
-
%6.1f 至少占 6 字符宽,小数点后面有 1 位数字
-
%d 十进制
-
%f 浮点数
-
%o 八进制
-
%x 十六进制
-
%c 字符
-
%s 字符串
-
%% %
#define
指令可以把符号名(符号变量)定义为一个特定的字符串:#define 名字 替换文本
,指令末尾无分号。
getchar() 和 putchar()
- getchar 从文本流中读入下一个输入字符,作为结果返回
- putchar 将打印一个字符
EOF end of file 定义在头文件<stdio.h>
中,是一个整型数。与任何 char 类型的值都不相同。
/tmp/main.c
1#include <stdio.h> 2 3void multstore(long, long, long *); 4 5int main() { 6 long d; 7 multstore(2, 3, &d); 8 printf("2 * 3 --> %ld\n", d); 9 return 0; 10} 11 12long mult2(long a, long b) { 13 long s = a * b; 14 return s; 15} 16
/tmp/mstore.c
1long mult2(long, long); 2 3void multstore(long x, long y, long *dest) { 4 long t = mult2(x, y); 5 *dest = t; 6} 7
/tmp/show_bytes.c
1#include <stdio.h> 2 3// 使用强制类型转换来访问和打印不同程序对象的字节表示 4 5typedef unsigned char *byte_pointer; 6 7void show_bytes(byte_pointer start, size_t len) { 8 // 因为 char 类型的指针每次是按照字节加 1 9 // 所以可以打印出存储在相应地址的字节 10 11 // pa[i] 与 *(pa+i) 是等价的 12 // 一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现 13 for (size_t i = 0; i < len; i++) { 14 // 数组形式 15 // start[i] 表示读取以 start 指向的位置为起始的第 i 个位置处的字节 16 printf(" %.2x", start[i]); 17 18 // 指针形式 19 // printf(" %.2x", *start++); 20 } 21 printf("\n"); 22} 23 24void show_int(int x) { 25 // 强制类型转换 26 // 并不会改变真实的指针,而是告诉编译器以新的数据类型来看待被指向的数据 27 show_bytes((byte_pointer) &x, sizeof(int)); 28} 29 30void show_float(float x) { 31 show_bytes((byte_pointer) &x, sizeof(float)); 32} 33 34// 指向任何对象的指针都可以转换为 void * 类型,且不会丢失信息 35// 如果将结果再转换为初始指针类型,则可以恢复初始指针 36// ANSI C 使用类型 void * 代替 char * 作为通用指针的类型 37void show_pointer(void *x) { 38 show_bytes((byte_pointer) &x, sizeof(void *)); 39} 40 41void test_show_bytes(int val) { 42 int ival = val; 43 // 39 30 00 00 => 小端法 44 show_int(ival); 45 show_float((float)ival); 46 show_pointer(&ival); 47} 48 49int main() { 50 // 0x00003039 51 int val = 12345; 52 test_show_bytes(val); 53 show_bytes((byte_pointer) &"12345", 6); 54 return 0; 55} 56