c程序设计语言里提到的例程
1、getline / copy 1.9节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int getline(char s[], int lim) { int c, i; for(i = 0; i < lim-1 && (c=getchar())!=EOF && c != '\0'; ++i) s[i] = c; if (c == '\n') { s[i] = c; ++i; } s[i] = '\0'; return i; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void copy(char to[], char from[]) { int i; i = 0; while((to[i] = from[i]) != '\0') ++i; }
2、reverse 1.9节1.19题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> #define MAXLINE 1000 int getline(char line[], int maxline); void reverse(cahr s[]); main() { char line[MAXLINE]; while(getline(line, MAXLINE) > 0){ reverse(line); printf("%s", line); } } void reverse(char s[]) { int i, j; char temp; i = 0; while(s[i] != '\0') ++i; --i; if (s[i] == '\n') --i; j = 0; while(j < i){ temp = s[j]; s[j] = s[i]; s[i] = temp; --i; ++j; } }
3.atoi 2.7节/3.5节/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int atoi(char s[]) { int i, n; n = 0; for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i) n = 10 * n + (s[i] - '0'); return n; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<ctype.h> int atoi(char s[]) { int i, n, sign; for(i = 0; isspace(s[i]); i++) ; sign = (s[i] == '-') > -1 : 1; if(s[i] == '+' || s[i] == '-') i++; for(n = 0; isdigit(s[i]); i++) n = 10 * n + (s[i] - '0'); return sign * n; }
4.htoi 2.7节 练习2.3
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#define YES 1 #define NO 0 //convert hex string s to integer int htoi(char s[]) { int hexdigit, i, inhex, n; i = 0; if(s[i] == '0'){ ++i; if(s[i] == 'x' || s[i] == 'X'){ ++i; } } n = 0; inhex = YES; for( ; inhex == YES; ++i){ if(s[i] >= '0' && s[i] <= '9') hexdigit = s[i] - '0'; else if(s[i] >= 'a' && s[i] <= 'z') hexdigit = s[i] - 'a' + 10; else if(s[i] >= 'A' && s[i] <= 'Z') hexdigit = s[i] - 'A' + 10; else inhex = NO; if(inhex == YES) n = 16 * n + hexdigit; } return n; }
5.squeeze 2.8节 练习2.4。将s1中与s2中字符匹配的字符都删除
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void squeeze(char s1[], char s2[]) { int i, j, k; for(i = k = 0; s1[i] != '\0'; i++){//k表示待匹配位置。 for(j = 0; s2[j] != '\0' && s2[j] != s1[j]; j++) ; if(s2[j] == '\0') // 没匹配,则将s1[i]这个字符复制到k表示的匹配后的位置。 s1[k++] = s1[i]; } s1[k] = '\0'; }
6.any 2.8节 练习 2.5。 返回s2中任意字符在s1中第一次出现的位置,不存在则返回-1。标准库中strpbrk函数完成同样功能,但是返回指针
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int any(cha s1[], char s2[]) { int i, j; for(i = 0; s1[i] != '\0'; i++) for(j = 0; s2[j] != '\0'; j++) if(s1[i] == s2[j]) return i; return -1; }
7.getbits 2.9节。 返回x中从右边数第p位开始(包括第p位)向右数共n位的字段
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
unsigned getbits(unsigned x, int p, int n) { return (x >> (p+1-n)) & ~(~0 << n); } (x >> (p+1-n))将相应字段移动到最右端 ~0获得一组1,<<n后取反获得一组掩码
8.setbits 2.9节 练习2.6。 将x中从右边第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
unsigned setbits(unsigned x, int p, int n, unsigned y) { return x & ~(~(~0 << n) << (p+1-n)) | (y & ~(~0 << n)) << (p+1-n); } 原始情况如下 xxx...xnnnx...xxx x yyy..........ynnn y 我们只要把x中的n清零,把y中除了n的位清零并移动到第p位处,然后或操作 xxx...x000x...xxx x 000...0nnn0...000 y ------------------- xxx...xnnnx...xxx x
9.invert 2.9节 练习2.7。 将x中从右边第p位开始的n个位求反,x的其余各位保持不变
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
unsigned invert(unsigned x, int p, int n) { return x ^ (~(~0 << n) << (p+1-n)); } 类似之前的技巧,将右边n位设置为1,然后移动到位置p
10.rightrot 2.9节 练习2.8。 将x向右循环位移n位(即移出的位会到最左端)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
unsigned rightrot(unsigned x, int n) { int wordlength(void); int rbit; while (n-- > 0){ rbit = (x & 1) << (wordlength() - 1); //rbit将x的最右位移动到最左端 x = x >> 1; x = x | rbit; //完成循环位移 } return x; } //计算出所使用的平台的字长 int wordlength(void) { int i; unsigned v = (unsigned) ~0; for(i = 1; (v = v >> 1) > 0; i++) ; return i; } //另一种解法,不使用循环 unsigned rightrot(unsigned x, int n) { int wordlength(void); unsigned rbits; if((n = n % wordlength()) > 0){ rbits = ~(~0 << n) & x; rbits = rbits << (wordlength() - n); x = x >> n; x = x | rbits; } return x; }
11.bitcount 2.10节 练习2.9。 统计1的数量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int bitcount(unsigned x) { int b; for(b = 0; x != 0; x &= x-1) ++b; return b; }
12.binsearch 3.3节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int binsearch(int x, int v[], int n) { int low, high, mid; low = 0; high = n - 1; while(low <= high){ mid = low + (high-low)/2; if(x < v[mid]) high = mid - 1; else if(x > v[mid]) low = mid + 1; else return mid; } return -1; }
13.escape/unescape 3.4节 练习3.2。 将字符串t复制到字符串s中,并将换行符、制表符等不可见字符换为转义字符;完成功能相反的unescape函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void escape(char s[], char t[]) { int i , j; for(i = j = 0; t[i] != '\0'; i++) { switch(t[i]){ case '\n': s[j++] = '\\'; s[j++] = 'n'; break; case '\t': s[j++] = '\\'; s[j++] = 't'; break; default: s[j++] = t[i]; break; } } s[j] = '\0'; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void unescape(char s[], char t[]) { int i, j; for(i = j = 0; t[i] != '\0'; i++) switch(t[i]){ case '\\': switch(t[++i]){ case 'n': s[j++] = '\n'; break; case 't': s[j++] = '\t'; break; default: s[j++] = '\\'; s[j++] = t[i]; break; } break; default: s[j++] = t[i]; break; } s[j] = '\0'; }
14.shellsort 3.5节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void shellsort(int v[], int n) { int gap, i, j, temp; for(gap = n/2; gap > 0; gap /= 2) for(i = gap; i < n; i++) for(j = i - gap; j >= 0 && v[j] > v[j+gap]; j -= gap){ temp = v[j]; v[j] = v[j+gap]; v[j+gap] = temp; } } 这种实现很紧凑,但是每次v[j] > v[j+gap]都做一次完整的交换,效率不高,可以参考一般的插入排序实现,使用一个temp保存待排序数,直到最后再将temp放入数组中。 如下 void shell_sort(ElementType A[], int N) { int P, D, i, temp; for (D = N/2 ; D>0; D/=2) { for (P = D; P < N; P++) { temp = A[P]; for (i = P; i >= D && A[i-D] > temp; i-=D) { A[i] = A[i-D]; } A[i] = temp; } } }
15.reverse 3.5节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void reverse(char s[]) { int c, i, j; for(i = 0; j = strlen(s) - 1; i < j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; } }
16.expand 3.5节 练习3.3。 把字符串s1中类似a-z的速记符号拓展为abc...xyz的完整列表,要求拓展顺序为ascii顺序。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void expand(char s1[], char s2[]) { char c; int i, j; i = j = 0; while((c = s1[i++]) != '\0'){ //fetch a char from s1[] if(s1[i] == '-' && s1[i+1] >= c){ i++; while(c < s1[i]) s2[j++] = c++; } else s2[j++] = c; } s2[j] = '\0'; }
17.itoa 3.6节/3.6节 练习3.4
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void itoa(int n, char s[]) { int i, sign; if((sign = n) < 0) n = -n; i = 0; do{ s[i++] = n % 10 + '0'; } while((n /= 10) > 0); if(sign < 0) s[i++] = '-'; reverse(s); } 取数字的方向和填字符串数组的方向刚好相反。 因为n = -n,这个实现可能导致溢出。 解决溢出问题之后的版本: #define abs(x) ((x) < 0? -(x) : (x)) void itoa(int n, char s[]) { int i, sign; sign = n; i = 0; do{ s[i++] = abs(n % 10) + '0'; } while((n /= 10) != 0); if(sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); }
18.itob 3.6节 练习3.5
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void itob(int n, char s[], int b) { int i, j, sign; if((sign = n) < 0) n = -n; i = 0; do{ j = n % b; s[i++] = (j <= 9) ? j+'0' : j+'a'-10; }while((n /= b) > 0); if(sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); }
19.strindex 4.1节。 返回字符串t在s中出现位置的索引(类似strstr)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int strindex(char s[], char t[]) { int i, j, k; for(i = 0; s[i] != '\0'; i++){ for(j = i; k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++) ; if(k > 0 && t[k] == '\0') return i; } return -1; }
20.strrindex 4.1节 练习4.1。 返回字符串t在s中最右边出现位置的索引
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int strrindex(char s[], char t[]) { int i, j, k; for(i = strlen(s) - strlen(t); i >= 0; i--) { for(j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++) ; if(k > 0 && t[k] == '\0') return i; } return -1; }
21.qsort 4.10节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void qsort(int v[], int left, int right) { int i, last; if(left >= right) return; swap(v, left, left+(right-left)/2); last = left; for(i = left+1; i <= right; i++) if(v[i] < v[left]) swap(v, ++last, i); swap(v, left, last); qsort(v, left, last-1); qsort(last+1, right); }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void qsort(void *v[], int left, int right, int(*comp)(void *, void *)) { int i, last; void swap(void *v[], int, int); if (left >= right) return; swap(v, left, (left+right)/2); last = left; for (i = left + 1; i <= right; i++) if ((*comp)(v[i], v[left]) < 0) swap(v, ++last, i); swap(v, left, last); qsort(v, left, last-1, comp); qsort(v, last+1, right, comp); }
22.strcpy 5.5节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void strcpy(char *s, char *t) { while(*s++ == *t++) ; return; }
1 应该使用的完善的版本 2 char *strcpy(char *strDest, const char *strSrc) 3 { 4 assert((strDest!=NULL) && (strSrc !=NULL)); 5 char *address = strDest; 6 while( (*strDest++ = * strSrc++) != ‘\0’ ) 7 ; 8 return address ; 9 }
23.strcmp 5.5节
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int strcmp(char *s. char *t) { for( ; *s == *t; s++, t++) if(*s == '\0') return 0; return *s - *t; }
24.strend 5.5节 练习5.3。 如果字符串t出现在字符串s的尾部,函数返回1;否则返回0
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int strend(char *s, char *t) { char *begin_s = s; char *begin_t = t; for( ; *s; s++) ; for( ; *t; t++) ; for( ; *s == *t; s--, t--) if(t == begin_t || s == begin_s) break; if(*s == *t && t == begin_t && *s != '\0') return 1; else return 0; }
25.find 5.10节。 对命令<find -x -n 模式>返回与x不匹配的行,-x表示除此之外,-n表示显示行号
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> #include<string.h> #define MAXLINE 1000 int getline(char *line, int max); int main(int argc, char *argv[]) { char line[MAXLINE]; long lineno = 0; int c, except = 0, number = 0, found = 0; while(--argc > 0 && (*++argv)[0] == '-') while(c = *++argv[0]) switch(c){ case 'x': except = 1; break; case 'n': number = 1; break; default: printf("find: illegal option %c\n", c); argc = 0; found = -1; break; } if(argc != 1) printf("Usage: find -x -n pattern\n"); else while(getline(line, MAXLINE) > 0){ lineno++; if((strstr(line, *argv) != NULL) != except){ if(number) printf("%ld:", lineno); printf("%s", line); found++; } } return found; }
26.tail 5.10节 练习5.13。 使用命令tail -n将输入中的后n行打印出来
5.11节 练习5.14~5.17 命令行参数解析
练习5.20 语义解析6.4节 词频统计
6.6节 表查找核心代码
tip:
1:文件描述符0、1、2对应stdin、stdout、stderr
2:read/write()返回实际传输字节数,在读文件时,函数的返回值可能小于请求的字节数;在写文件时,如果返回值与请求值不同则可能发生了错误。返回0表示到达文件结尾,返回-1表示发生某种错误。
3:
1、printf / scanf
知识点:变长参数函数使用:
头文件#include<stdarg.h>
使用va_list ap; 来声明一个变长参数类型的变量ap,即argument pointer。
使用va_start(ap, fmt); fmt为最后一个有名参数(函数原型void print(char *fmt, ...)),ap将被设置为指向第一个无名参数的指针。
使用ival = va_arg(ap, int);来获得一个参数,类型由va_arg的第二个参数决定,并且ap指向下一个参数。
va_end(ap); 在函数返回前调用,完成相关清理工作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> #include<stdarg.h> int main() { print("%d %d",1,2); return 0; } void print(char *fmt, ...) { va_list ap; char *p, *sval; int ival; double dval; va_start(ap, fmt); for(p = fmt; *p; p++){ if(*p != '%'){ putchar(*p); continue; } switch (*++p) { //略过% case 'd': ival = va_arg(ap, int); printf("%d", ival); break; case 'f': dval = va_arg(ap, double); printf("%f", dval); break; case 's': for(sval = va_arg(ap, char *); *sval; sval++) putchar(*sval); break; default: putchar(*p); break; } } va_end(ap); }
因为类型提升的问题,未被声明的参数会被提升为int和double,所以var_arg(ap, char)和var_arg(ap,float)错误的。