第六节 函数与程序结构
6-1
例1 将带有某个子串的语句输出出来
编写一个程序,将带有某个子串(“ould”)的语句输出出来
// output the line with a certain string
#include <stdio.h>
#include <string.h>
#define MAXLEN 10000
int main(){
int mygetline(char s[]);
void withsentence(char s[],char sentence[],int n);
char s[MAXLEN];
char sentence[]="ould";
int i=0;
i=mygetline(s);
while(i!=0){
withsentence(s,sentence,i);
i=mygetline(s);
}
return 0;
}
int mygetline(char s[]){
char c;
int i=0;
while((c=getchar())&&c!=EOF&&c!='\n'){
s[i++]=c;
}
if(c=='\n'){
s[i]='\0';
}
return i;
}
void withsentence(char s[],char sentence[],int n){
// 这里可以用检测当前到达的字符是否为'\0'来避免使用strlen函数
int slen=strlen(sentence);
int sign;
for(int i=0;i<n;i++){
sign=0;
for(int j=0;j<slen&&s[j+i]!='\0';j++){
if(s[j+i]!=sentence[j])
sign=1;
}
// 也可以通过让withsentence函数返回某个值(例如对应上的字符串长度,
// 然后在main程序里printf,这样可以避免使用sign变量)
if(sign==0){
printf("%s\n",s);
}
}
}
将上文中的注释付诸实践后的另一个代码:
// output the line with a certain string
#include <stdio.h>
#include <string.h>
#define MAXLEN 10000
int main(){
int mygetline(char s[]);
int withsentence2(char s[],char senten[]);
char s[MAXLEN];
char sentence[]="ould";
int i=0;
i=mygetline(s);
while(i!=0){
if(withsentence2(s,sentence)>0)
printf("%s\n",s);
i=mygetline(s);
}
return 0;
}
int mygetline(char s[]){
char c;
int i=0;
while((c=getchar())&&c!=EOF&&c!='\n'){
s[i++]=c;
}
if(c=='\n'){
s[i]='\0';
}
return i;
}
int withsentence2(char s[],char sentence[]){
int i,j,slen; // slen="similiar_len"
for(i=0;s[i]!='\0';i++){
for(j=0;sentence[j]!='\0';j++){
if(s[i+j]==sentence[j]){
slen++;
}
else
break;
}
if(sentence[j]=='\0') // 这里一开始没想到,要注意一下
return slen;
}
return 0;
}
例2 atof
将字符串s转换为相应的双精度浮点数
#include <stdio.h>
#include <ctype.h>
#define MAXLEN 10000
int main(){
double atof();
double answer;
answer=atof();
if(answer!=-1)
printf("%lf\n",answer);
return 0;
}
double atof(){
char c;
double number=0;
int sign=0,power=1;
while(isspace(c=getchar()))
;
sign=(c=='-')?-1:1;
while(isdigit(c=getchar())){
number=number*10+(c-'0');
}
while(isdigit(c=getchar())){
number=number*10+(c-'0');
// 这里一开始使用的是计算小数点后面数字的位数,但是那样的话后面还会多加一个循环,
// 因此用power更好
power*=10;
}
number/=power;
number=number*sign;
return number;
}
6-2
例5 逆波兰表达式
#include <stdio.h>
#include <ctype.h>
#define MAX 10000
#define NUMBER '0'
#define BUFSIZE 100
double stack[MAX];
int bufp=0;
char buf[BUFSIZE];
// k是push和pop的栈要用到的,代表当前栈用到哪一位了
int k=0;
char getch();
void ungetch(char c);
int main(){
void push(double n);
double pop();
char getop(char s[]);
double atof(char s[]);
int temp;
char s[MAX];
char type;
while((type=getop(s))!=EOF){
switch(type){
case(NUMBER):
push(atof(s)); break;
case('+'):
push(pop()+pop()); break;
case('-'):
push(-pop()!=pop()); break;
case('*'):
push(pop()*pop()); break;
case('/'):
temp=pop();
if(temp!='0')
push(pop()/temp);
else
printf("Error! Dividing zero!\n");
break;
case('\n'):
printf("%f\n",pop()); break;
default:
printf("Error!\n"); break;
}
}
return 0;
}
char getop(char s[]){
char c;
// i用来存储当前往s里存储到了第几位,由于每次的s[]都是全新的,因此要重置i
int i=0;
s[0]='\0';
while((c=getch())==' '||c=='\t')
;
if(c==EOF||c=='+'||c=='-'||c=='*'||c=='/'||c=='\n'){
return c;
}
else if(isdigit(c)){
s[i++]=c;
while(isdigit(c=getch()))
s[i++]=c;
if(c=='.'){
s[i++]='.';
while(isdigit(c=getch()))
s[i++]=c;
}
ungetch(c);
s[i]='\0';
return NUMBER;
}
else
return 'e';
}
void push(double n){
stack[k++]=n;
}
double pop(){
return stack[--k];
}
char getch(){
return (bufp>0)?buf[--bufp]:getchar();
}
void ungetch(char c){
if(bufp>=BUFSIZE)
printf("Already full!\n");
else
buf[bufp++]=c;
}
double atof(char s[]){
double number;
// j是当前atof转换到s的哪一位了,每次都要转换一个全新的s,因此要充值
int power=1,j=0;
while(isspace(s[j]))
j++;
while(isdigit(s[j])){
number=number*10+s[j]-'0';
j++;
}
if(s[j]=='.')
j++;
while(isdigit(s[j])){
number=number*10+s[j]-'0';
power*=10;
j++;
}
number/=power;
return number;
}
几个在编写代码时遇到的问题:
1. 每次要读入一个数,而不是一个字符,我们想尽量用不同函数把功能拆开,因此这里使用getop专门读取下一个基团并识别其类型。
2. 除了在for循环里定义,其它时候不建议使用i,j,k等变量,因为这些变量里的值如果需要保留,很容易设置重复导致混乱。
3. 注意getch和ungetch中的退回字符是通过缓冲区实现的。
6-3
作业讲解 itoh
编写itoh(int x, char s[]),将x中的整型转为16进制数并存在s中,x可能为负数。
第一种方法:未考虑负数的短除法
#include <stdio.h>
#include <string.h>
#define MAXLEN 10000
int main(){
int x;
char s[MAXLEN];
void itoh(int x, char s[]);
void reverse(char s[]);
scanf("%d\n",&x);
itoh(x,s);
reverse(s);
printf("%s\n",s);
return 0;
}
void itoh(int x,char s[]){
int index=0;
do{
if(x%16<10)
s[index++]=x%16+'0';
else
s[index++]=x%16-10+'a';
x/=16;
} while(x!=0);
s[index++]='\0';
}
void reverse(char s[]){
int temp;
int i=0,j=strlen(s)-1;
while(j>=i){
temp=s[i];
s[i]=s[j];
s[j]=temp;
i++,j--;
}
}
第二种方法:考虑负数,使用取二进制最后四位+右移
#include <stdio.h>
#include <string.h>
#define MAXLEN 10000
int main(){
void itohplus(int x,char s[]);
void reverse(char s[]);
// 如果这里x就是unsigned int,那么如果输入负数会出现segmentation fault的报错
int x;
char s[MAXLEN];
scanf("%d\n",&x);
itohplus(x,s);
reverse(s);
printf("%s\n",s);
return 0;
}
void itohplus(int x,char s[]){
int index=0;
unsigned int k;
k=x;
while(k!=0){
// 注意如果(k&15)不使用括号,会先计算15<10产生错误
s[index++]=((k&15)<10)?(k&15)+'0':(k&15)-10+'a';
k>>=4;
}
s[index]='\0';
}
void reverse(char s[]){
int temp,i=0,j=strlen(s)-1;
while(i<=j){
temp=s[i];
s[i]=s[j];
s[j]=temp;
i++,j--;
}
}
两个注意点:
1. 因为有负数,使用unsigned int转化,但是获取输入值的仍为int,需另设一个unsigned int变量k使其等于x,否则会出现segmentation fault的报错。
2. (k&15)<10注意优先级,不使用括号会先计算<
例9 printd
#include <stdio.h>
int main(){
void printd(int n);
int n;
scanf("%d\n",&n);
printd(n);
putchar('\n');
return 0;
}
void printd(int n){
if(n<0){
n=-n;
putchar('-');
}
if((n/10)!=0)
printd(n/10);
putchar(n%10+'0');
}
注意递归就不需要使用while了,只需要在函数里调用函数,就可以起到循环效果
例10 快速排序
#include <stdio.h>
int main(){
int s[16]={11,8,31,9,33,19,13,1,5,34,65,13,32,23,17,41};
void quicksort(int left,int right,int s[]);
quicksort(0,15,s);
for(int i=0;i<16;i++)
printf("%d ",s[i]);
printf("\n");
return 0;
}
void quicksort(int left,int right,int s[]){
if(left<right){
int flagloc=(left+right)/2;
int flag=s[flagloc];
void swap(int i,int j,int s[]);
swap(left,flagloc,s);
flagloc=left;
for(int i=left+1;i<=right;i++){
if(s[i]<flag){
flagloc++;
swap(i,flagloc,s);
}
}
swap(left,flagloc,s);
quicksort(left,flagloc-1,s);
quicksort(flagloc+1,right,s);
}
}
void swap(int i,int j,int s[]){
int temp=s[i];
s[i]=s[j];
s[j]=temp;
}
注意设置终止条件if(left<right)
作业 仿照printd,用递归实现itoa
先转为字符串,然后输出字符串
#include <stdio.h>
#define MAXLEN 10000
int i=0;
int main(){
int n;
char s[MAXLEN];
void itoa(int n,char s[]);
scanf("%d\n",&n);
itoa(n,s);
s[i]='\0';
printf("%s\n",s);
}
void itoa(int n,char s[]){
if(n<0){
n=-n;
s[i++]='-';
}
if((n/10)!=0){
itoa(n/10,s);
}
s[i++]=n%10+'0';
}
6-4
例11 汉诺塔
#include <stdio.h>
int main(){
void hanoi(int n,char a,char b,char c);
int n;
scanf("%d\n",&n);
hanoi(n,'A','B','C');
return 0;
}
void hanoi(int n,char a,char b,char c){
if(n==1)
printf("%c -> %c\n",a,c);
else{
hanoi(n-1,a,c,b);
printf("%c -> %c\n",a,c);
hanoi(n-1,b,a,c);
}
}
第七节 指针与数组
7-1
例6 用指针完成swap操作
#include <stdio.h>
int main(){
void swap(int *pa,int *pb);
int a=1,b=2;
int *pa,*pb;
pa=&a,pb=&b;
printf("before:%d %d\n",a,b);
swap(pa,pb);
printf("after:%d %d\n",a,b);
return 0;
}
void swap(int *pa,int *pb){
int temp=*pa;
*pa=*pb;
*pb=temp;
}
7-2
例7 getint函数
样例输入输出如下图,array长度为定值10,要求多读的要退回
#include <stdio.h>
#include <ctype.h>
#define LEN 10
#define BUFSIZE 100
int bufp=0;
int buf[BUFSIZE];
int getch();
void ungetch(int c);
int main(){
int getint(int *p);
int array[LEN]={0};
for(int i=0;i<LEN&&(getint(&array[i]))!=EOF;i++)
;
for(int i=0;i<LEN;i++)
printf("array[%d]=%d\n",i,array[i]);
return 0;
}
int getint(int *p){
char c;
int sign;
while(isspace(c=getch()))
;
sign=(c=='-')?-1:1;
if(c=='+'||c=='-')
;
else{
ungetch(c);
}
while(isdigit(c=getch())){
*p=10* *p +c-'0';
}
*p*=sign;
if(c!=EOF)
ungetch(c);
return c;
}
int getch(){
return (bufp>0)?buf[--bufp]:getchar();
}
void ungetch(int c){
if(bufp>=BUFSIZE)
printf("No more place!");
else
buf[bufp++]=c;
}
例11 用指针实现不完善的内存分配器
#define ALLOCSIZE 100
static char allocbuf[ALLOCSIZE];
static char *allocp=allocbuf;
// 函数类型也可以是指针(即返回值的类型为指针)
char *alloc(int n){
// 指针可以和常数做加减法,或与另一个指针做减法
if((ALLOCSIZE-allocp+allocbuf)>=n)
return allocp-n;
else
// 指针可以赋值为0,即空指针
return 0;
}
void free(char *p){
if(p>=allocbuf&&p<=allocbuf+ALLOCSIZE)
allocp=p;
}
例12 用指针计算字符串长度
#include <stdio.h>
int main(){
int mystrlen(char *p);
char s[]="Hello friends!";
int n=mystrlen(s);
printf("%d\n",n);
}
int mystrlen(char *p){
char *s=p;
while(*s!='\0')
s++;
// 两个指针间可以做减法
return s-p;
}
例14 strcopy(char *s, char *t)函数
#include <stdio.h>
#define MAXLEN 1000
int main(){
void strcopy(char *s,char *t);
char s[]="asdf";
char t[MAXLEN];
strcopy(s,t);
printf("%s\n",t);
return 0;
}
void strcopy(char *s,char *t){
while(*s!='\0'){
*(t++)=*(s++);
}
// 这里要不就单独令t最后为'\0',要不就把*t=*s放在while后面的括号里,
// 不然的话t字符串就不是以'\0'结尾了
*t='\0';
}