4.C语言笔记:递归、函数指针、字符串函数群 1.递归函数一个函数的函数体里直接或间接调用了函数本身。递归函数一定要有结束条件。递推阶段由原问题出发按递归公式递推从未知到已知最终达到递归终止条件。回归阶段按递归终止条件求出结果逆向逐步带入递归公式回归到原问题求解。编写递归函数求阶乘 n!#include stdio.h int fac(int n); int main (){ int n; int ret; scanf(%d,n); ret fac(n); printf(%d!%d\n,n,ret); return 0; } int fac(int n){ if(n 1){ //终止条件 return 1; } return n * fac(n - 1); //递推过程直到发现f(1) 11是结果再逆向代回得到最终结果 }斐波那契数列1、1、2、3、5.。。。。。。第n项前两项的和#include stdio.h int fib(int n); int main (){ int n; int i ; scanf(%d,n); for(i 1; i n; i){ printf(%d\n,fib(n)); } putchar(\n); retunr 0; } int fib(int n){ if(n 1 || n 2){ return 1; } return fib(n - 1) fib(n - 2); }2.函数指针函数指针用来存放函数的地址这个地址是一个函数的入口。函数名代表了函数的入口地址。数据类型*h函数指针名参数说明列表#includestdio.h void print_hello (int a) { printf(hello world %d \n,a); } int main() { void (*p)(int); //声明一个函数指针 print_hello(100); printf(%p\n,print_hello); //函数名会指向函数本身的地址并自动解引用。 p print_hello; //将指针指向一个函数名 p(100); (*p)(100); return 0; }#include stdio.h int add(int a, int b){ return a b;} int mul(int a, int b){ return a * b;} int div(int a, int b){ return a / b;} int sub(int a, int b){ return a - b;} int fun_arith(int a, int b, int (*p)(int,int)){ return p(a,b); } int main(){ int m,n; int (*p)(int,int); scanf(%d%d,m,n); p add; printf( %d\n,p(m,n)); p mul; printf(* %d\n,p(m,n)); p sub; printf(- %d\n,p(m,n)); p div; printf(/ %d\n,p(m,n)); printf(%d\n,fun_arith(m,n,add)); return 0; }函数指针数组如 int (*p[4])(int,int);#includestdio.h int add(int a, int b){ return a b;} int mul(int a, int b){ return a * b;} int div(int a, int b){ return a / b;} int sub(int a, int b){ return a - b;} int main(){ int m,n; int (*p[4])(int,int); scanf(%d%d,m,n); p[0] add; p[1] sub; printf(%d\n,p[1](m,n)) ; return 0; }qsort函数qsort(a,n, sizeof(char),compar); //qsort定义写法需要#includestdlib.hqsort(数组地址元素个数一个元素空间比较函数)#includestdio.h #includestdlib.h int compar(const void *p1,const void *p2); int main() { char a[] {w, z, q, a, y, b}; int n sizeof(a) / sizeof(char); int i; qsort(a,n, sizeof(char),compar); //qsort定义写法 for(i 0; i n; i){ printf(%c, a[i]); } return 0; } int compar(const void *p1,const void *p2){ return *(char *)p1 - *(char *)p2; }#includestdio.h #includestdlib.h int compar(const void *p1,const void *p2); int main() { double a[] {2.5, 1.3, 4.4, 5.7, 7.1}; int n sizeof(a) / sizeof(double); int i; qsort(a,n, sizeof(double),compar); //qsort定义写法 for(i 0; i n; i){ printf(%.1lf, a[i]); } return 0; } int compar(const void *p1,const void *p2){ return *(double *)p1 - *(double *)p2 0 ? 1 : -1; 浮点数比较时有误差这里用条件表达式 } 结果大于0就返回正数1否则返回负数-13.预处理指令#define宏 如 #define N 10终端预处理指令 gcc -E define1.c -o define1.i (文件名叫define1.c)#define M (32) #define Q 32 printf(%d,M * 2); 输出10 printf(%d,Q * 2); 输出7#define M 3.0e-23 #define K 950 printf(%e,5 * K / M);没有值的宏定义#define M (32) #define Q 32 #define _DEBUG_ int main(){ printf(%d,M * 2); 输出10 printf(%d,Q * 2); 输出7 #ifdefine _DEBUG_ printf(%s %s %d, _FUNCTION_, _FILE_, _LINE_); 所在函数名文件名第几行 #endif return 0; }#include预处理命令后面跟和的区别 编译器会到标准库查找头文件 编译器会先去当下目录没找到的话再去标准库squart 是math.h里的函数输出类型为浮点型编译时需要后面加上-lm如 gcc define2.c -Wall -lm#includestdio.h #includemath.h #includehead.h 这里提前建了一个文件叫head.h 里面有int global_a 100; int mian(){ printf(squart(global_a) %lf\n,squart(global_a)); 输出10.000000 return 0; }vi指令 vsp 文件名 在当前vi窗口中分屏打开另一个文件宏可以取消#undef#define M 3.0e-23 #define K 950 printf(%e,5 * K / M); #undef M printf(%e,5 * K / M); 报错 #define M 3.0e-23 printf(%e,5 * K / M); 又可以了宏和const区别const int a 10; #define a 10宏是预处理阶段const是程序运行时。宏是文本替换不占内存const是变量占内存。宏定义可以取消const永久生效typedef用法为数据类型定义新名字 typedef int integer; integer a相当于int atypedef int * PRINT; PRINT a相当于int * atypedef 数组指针 #includestdio.h typedef int (* P)[3]; int main(){ int a[3] {6,8,10}; //int (*p)[3]; P p; int i; p a; for(i 0; i 3; i){ printf(%d %d\n,a[i],(*p)[i]); } return 0; }typedef 函数指针 #include stdio.h typedef int (*FUN_P)(int); int get_sum (int n) { int i,sum 0; for (i 1;i n; i){ sum i; } return sum } int main () { //int (*fun_p)(int); FUN_P fun_p; fun_p get sum; printf(sum %d\n,get_sum(100)); 5050 printf(sum %d\n,(*fun_p)(100)); 5050 return 0; }#define 和typedef 区别typedef int AAA; 是语句结尾需要分号。typedef有类型检查#define是文本替换没有类型检查。typedef int *PTR; PTR a,b; ab都是指针#define PTR int * PTR a,b; a是指针b是整数#define是全局变量放到函数里也是全局作用。typedef如果放到函数里只作用于函数里面。宏函数的使用带参宏#define 宏名形参列表字符串#includestdio.h #define debug(s) printf(%s\n,s) int main(int argc,const char *argv[]) { debug(hello world); return 0; }#includestdio.h #define SQUARE(n) (n)*(n) int main(int argc,const char *argv[]) { scanf(%d,n); printf(%d,SQUARE(n2)); 如果上面的宏不加(n)*(n) 就会变成 n2*n2 return 0; }宏是文本替换、文本展开没有调度开销效率高。#includestdio.h #define MAX(a, b) ((a) (b) ? (a) : (b)) int main(int argc,const char *argv[]) { int m 100; int n 200; printf(max%d\n, MAX(m300,n)); return 0; }宏效率比函数高但宏不能递归函数方便调试。宏函数是无脑替换可能不符合实际情况多个标点或者优先级不对函数更灵活。4.变量存储类型存储类型 数据类型 变量名存储类型auto、register、static、extern变量存储类型决定变量的作用域、生命周期、初始值auto是自动变量使用时可以省略 auto int a;register存储类型经常访问的变量可能放在cpu寄存器里提高访问效率叫寄存器型。 cpu内的高速存储单元数量有限如果申请不到默认auto类型。编译时如果不想编译器做优化可以加-o0 如gcc 1.c -Wall -o0time ./1.c 可以显示执行时间register int a 0;寄存器变量不能有地址只能对内存里的变量取地址所以register int a; int *p a;会报错static存储类型static变量称为静态存储类型的变量。定义全局变量或局部变量表示该变量在程序运行当中一直存在。默认初始值是0只要程序没结束就不会消失再调用该函数存储类型的变量不再重新说明而且保留上一次的值。static可以限制全局变量的作用域让其只能在当前文件中使用。static可以限制局部变量作用域让其只能在某一个函数内部被访问其他函数无法访问。#include stdio.h void fun() { static int a 10; 如果是auto类型输出一直都是11 a; 但static类型不会再定义 a 10 了 printf(%d %p\n,a, a); } int main(){ int i 0; while(i 5){ fun(); i; } return 0; } 输出 11 同一地址 12 同一地址 13 同一地址 14 同一地址 15 同一地址extern存储类型外部参照引用型使用extern的变量是想引用在其他文件中函数体外部说明的变量。编译时不会分配内存而是在链接时将其与实际的定义进行关联。当希望在一个文件中使用另一个文件中定义的全局变量时需要使用extern。编译时 gcc 2.c 1.c -Wall文件1.c 里 int global_a 100; static int global_b 100; 文件2.c #includestdio.h extern int global_a; int main(){ printf(%d,global_a); printf(%d,global_b); 报错 static类型的变量无法被别的文件引用 return 0; }static类型的变量无法被别的文件引用。声明外部函数gcc 2.c 1.c -Wall文件1.c 里 void print_hello(){ printf(hello world\n); } 文件2.c #includestdio.h extern void print_hello(); int main(){ print_hello(); return 0; }5.字符串函数头文件#includestring.h求字符串长度的函数strlen字符串拷贝函数strcpy字符串连接函数strcat字符串比较函数strcmpstrlen函数#includestdio.h #includestring.h int main(){ char s1[10]{A, \0, B, C, \0, D}; char s2[ ]\t\v\\\0; char s3[ ]\x69\141\n; 、\xhh是十六进制 \hhh是八进制 printf(%ld-%ld-%ld\n, strlen(s1), strlen(s2), strlen(s3)); printf(%s\n,s1); printf(%s\n,s2); printf(%s\n,s3); return 0; } 输出1-3-1 A i astrcpy和strcat#includestring.h #includestdio.h int main(){ char destination[25]; char blank[] ,c[] C,turbo[] Turbo; strcpy(destination,turbo); strcat(destination,blank); strcat(destination,c); printf(%s\n,destination); return 0; }strcmp函数从左向右比较直到遇到不同的字符或者\0为止。返回值intASCII的差值#includestdio.h int main(){ char s1[] sdfsdf; char s2[] sdfsdf; printf(%d\n, strcmp(s1,s2)); 输出0 return 1; } char s1[] sd; char s2[] sdasdf; 输出-97 printf(%d\n, strcmp(s1,s2)); char s1[] sddsdf; char s2[] sdfsdf; 输出-2 printf(%d\n, strcmp(s1,s2));isalpha函数#includectype.hint isalpha () 判断是不是字母返回值0不是字母非0是字母#includestdio.h #includectype.h int main(){ printf(%d\n,isalpha(A)); 1024 printf(%d\n,isalpha(a)); 1024 printf(%d\n,isalpha(0)); 0 return 0; }isupper函数#includectype.hint isupper ()判断是不是大写字母返回值0不是大写字母非0是大写字母#includectype.h #includestdio.h int main(){ printf(%d\n,isupper(A)); 256toupper函数#include ctype.hint toupper ( ); 把小写字母转为大写字母返回值为转换后结果#includectype.h #includestdio.h int main (){ char str[] Welcome To Beijing; int i 0; printf(before:%s\n,str); while (str[i] ! \0) { str[i] toupper(str[i]); i; } printf(%s,str); WELCOME TO BEIJINGstrncpy函数char * strncpy (char * dest,const char * src,size_t n)dest: 目标字符串 src:源字符串 n要复制的字符数目标字符串必须足够大以及额外的\0不然容易溢出。如果源字符串的长度大于或等于n不会自动添加\0 需要是手动添加如果源串长度小于n会用\0凑齐。#include stdio.h int main (){ char dest[20]; char *src hello; strncpy(dest, src, 2); dest[2] \0; puts(src); hello puts(dest); he return 0; } char dest[20] ********; char *src hello; strncpy(dest, src, 2); puts(dest); he******* 如果不加 dest[2] \0; 就会he*******编写strncpy函数char * mystrncpy(char *dest, const char *src,int len){ assert(dest ! NULL src ! NULL); char *res dest; int offset 0; if(strlen(src) len ){ //src长度小于len offset len - strlen(src); len strlen(src); } while(len--){ *dest *src; } while(offset--){ *dest \0; } return res; }strncat函数char* strncat(char* dest,const char* src,int n)连接n个src的字符到dest后面追加\0char dest[20] abc; char *src hello; strncat(dest,src,20); abchello strncat(dest,src,2); abche puts(src); puts(dest); return 0; }编写strncat函数 char* my_strncat(char *dest,const char*src,int n){ char* ret dest; assert(dest !NULL src ! NULL); while(*dest ! \0){ dest; } while(n (*dest *src) ! \0){ *dest *src; n--; } *dest \0; return ret; }strncmp函数int strncmp(const char* strl,const char*str2,size_t n)比较前n个字符直到遇到不同的字符返回ASCII的差值char s1[] abcdefgg; char s2[] abcsds; printf(%d\n,strncmp(s1,s2,2)); 0 printf(%d\n,strncmp(s1,s2,5)); -15int my_strncmp(const char* str1,const char* str2,int n) { if(!n){ return 0; } while(--n *str1 *str1 *str2){ str1; str2; } return *str1 - *str2; }strchr函数char *strchr(const char *str,int c)查找一个字符串里的字符c如果以后返回地址如果没有返回null#includestdio.h #includestring.h int main(){ char *s how are you; char *r NULL; r strchr(s, a); printf(%p %ld\n, r, r - s); 地址 4 r strchr(s,z); printf(%p %ld\m, r, r - s); nil 垃圾数 return 0; }#includestdio.h char *mystrchr(char * str,int c){ while(*str! \0){ if(*str c){ return str; } str; } return NULL; }strstr函数char *strstr(const char *str1,const char *str2);返回字符串中首次出现子串的位置如果没有返回空。匹配不包括\0但到\0结束#includestdio.h #includestring.h int main(int argc ,const char *argv[]){ char * s1 how are you; char * s2 ow; char * r NULL; r strstr(s1,s2); printf(%p %ld\n,r, r - s1); 地址 1 return 0; }