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语言本身的一部分,仅是标准库函数中的一个函数。

#define 指令可以把符号名(符号变量)定义为一个特定的字符串:#define 名字 替换文本,指令末尾无分号。

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语言本身的一部分,仅是标准库函数中的一个函数。

#define 指令可以把符号名(符号变量)定义为一个特定的字符串:#define 名字 替换文本,指令末尾无分号。

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