文章标题

来源:互联网 发布:10月中国经济数据 编辑:程序博客网 时间:2024/06/04 01:00

基础

为甚么学C

主要是因为最近在学习nginx
这货是用C写的,如果想自己写一个模块,并编译到nginx当中,那么C必不可少

helloworld

在虚拟机192.168.27.131上的/home/solofeng/study/repos/C下开展国际惯例[solofeng@com C]$ vi helloworld.c==========================#include <stdio.h>int main(){    printf("hello world! \n");    return 0;}==========================[solofeng@com C]$ gcc helloworld.c -o helloworld   /*编译*/ [solofeng@com C]$ lshelloworld  helloworld.c[solofeng@com C]$ ./helloworld     /*执行*/ hello world! 

关于C中的格式化输出的%

    实际上在输出中是作占位符的作用    格式说明由“%”和格式字符组成,如%d%f等。它的作用是将输出的数据转换为指定的格式输出。格式说明总是由“%”字符开始的。 格式字符有d,o,x,u,c,s,f,e,g等。 如%d  digit       整型输出,%ld长整型输出,%o  octonary    以八进制数形式输出整数,%x  hexadecimal 以十六进制数形式输出整数,或输出字符串的地址。%u  unsigned    以十进制数输出unsigned型数据(无符号数)。注意:%d与%u有无符号的数值范围,也就是极限的值,不然数值打印出来会有误。%c  char        用来输出一个字符,%s  string      用来输出一个字符串,%f  float       用来输出实数,以小数形式输出,默认情况下保留小数点6位。%11.9f  float       用来输出实数,总位数11位,小数点后9位%e          以指数形式输出实数,%g          根据大小自动选f格式或e格式,且不输出无意义的零。

变量

1. 数据类型基本类型    int    char    float    double    _Bool(布尔)    enum(枚举)指针类型构造类型空类型2. C的数据类型有点特殊,他的大小会根据不同的环境会有所异同,这就很尴尬了.我只能大概知道他的大小.而且还可以分为有符号signed和无符号unsigned    char 1byte;    short 2byte;    int 4byte;    long 8byte;    emmm,和java其实一样呀3. 进制转换,略4. 反码,补码,略举个栗子:[solofeng@com C]$ vi helloworld.c==========================#include <stdio.h>#define name "guangda"int main(){    printf("hello world! \n");    printf("hello ,i m %s.\n",name);    return 0;}==========================[solofeng@com C]$ rm helloworld[solofeng@com C]$ gcc helloworld.c -o helloworld[solofeng@com C]$ lshelloworld  helloworld.c[solofeng@com C]$ ./helloworld hello world! hello ,i m guangda.上文中的#define name "guangda"注意#在C中不是注释,放在前面表示宏定义(大概可以理解为预定义,在这里就是预定义了一个全局变量name,所有函数皆可用)疑问: int,char,float等类型的字节数是固定的,string实际上是字符数组,那么C如何让cpu知道哪里是数组的结尾回答: 实际上,存储字符串数组时,除了字符串本身以外,在最后一个字符的后一位存储一个"\0"表示数组的结束,当cpu读取到"\0"时,便明白字符串结束了,不再往下读取

运算符&语句&注释

+ - * / %++ -- += -= *= /= %=< > = != >= <= ==>> <<& | ~ && || !sizeof (长度运算符). (对象运算符)-> (指针运算符)pow函数->幂函数(例如求210次方)if(){}if(){}else{}if(){}else if{}else{}switch(){    case num: expression; break;    default: expression; break;}while(){}do{}while();for(){}三元exp1 ? exp2 : exp3;///**/

数组

int list[10] = {1,2,3};sizeof(list)结果是返回数组占的空间的大小,而不是元素的个数char name[] = "guang";char name[] = {'g','u','a','n','g'};

第一步进阶

重点:指针

关于指针变量和普通变量的区别:普通变量有类型(int/long/...),编译器知道该变量对应的内存地址,内存中存放着对应的值;指针变量也有类型,就是指针变量类型,大小为4byte,编译器知道该指针变量对应的内存地址,然后该内存地址中存放指针的地址;    同时,定义指针变量时的类型为具体的值得类型.比如,指针的地址存放着值为字符,则定义:    char *pa格式:类型名 *指针变量名 (定义时需要加上*来表明他是一个指针变量,但是他真正的变量名并不包括*)char *paint *pb取址运算符&举栗子:初始化指针变量char *pa = &a;(获取a变量的内存地址赋值给指针变量*pa)取值运算符*printf("%c\n",*pa);(获取指针变量*pa的值并输出)不好意思,我TM晕指针了;等我搞明白了,再回头过来补

函数

函数声明+函数定义;声明要在定义之前

> 测试一下值传递1 #include <stdio.h>  2   3 void swap(int x,int y);  4 void swap(int x,int y){  5     int temp;  6     printf("in swap function,互换前:x=%d,y=%d\n",x,y);  7     temp = x;  8     x = y;  9     y = temp; 10     printf("in swap function,互换后:x=%d,y=%d\n",x,y); 11 } 12  13 int main(){ 14     int x = 3, y = 5; 15     printf("in main function,互换前:x=%d,y=%d\n",x,y); 16     swap(x,y); 17     printf("in main function,互换前:x=%d,y=%d\n",x,y); 18 } 19  20 /* 21  * 22  * 以下为输出结果: 23 in main function,互换前:x=3,y=5 24 in swap function,互换前:x=3,y=5 25 in swap function,互换后:x=5,y=3 26 in main function,互换前:x=3,y=5 27  * 28  * 29  * 当初学java的时候,有点小迷惑:这是为什么呢 30  * 后来想到了一招,把main中的x,y换成了i,j.emmmm,瞬间理解了 31  * 就是说main中的xy和swap中的xy就不是同一个东西. 32  * 33  * 后面,对方法的作用域理解得深一点了,恍然大悟,emmm,的确不是同一个东西 34  * 35  * 再后来,小明老师提出了一个问题:java有没有引用传递. 36  * 我当时说:没有,java只有值传递,传递对象的引用时也不过是传递了对象的地址,这个地址就是"值". 37  * 好像离题了,上面两行当我没说 38  * 39  * emmm,类比到C.这里的方法调用,作用域等,很明显跟java是同出一辙的 40  * 但是,C之所以叫C,是因为他还有个流弊的东西,叫指针,使用指针就可以实现非值传递-->地址传递 41  * 以上面的两个函数举栗子,具体点来说,就是在swap中修改main中的变量 42  * 哇,有点作用域穿透了的感觉 43  * 瞬间觉得C高大上啊 44  * 具体实现请参考swap_num_sup.c 45  * */ 46  47 
> 测试一下地址传递1 #include <stdio.h>  2   3 void swap(int *px,int *py);  4 void swap(int *px,int *py){  5     int temp;  6     printf("in swap function,互换前:x=%d,y=%d\n",*px,*py);  7     temp = *px;  8     *px = *py;  9     *py = temp; 10     printf("in swap function,互换后:x=%d,y=%d\n",*px,*py); 11 } 12  13 int main(){ 14     int x = 3, y = 5; 15     printf("in main function,互换前:x=%d,y=%d\n",x,y); 16     swap(&x,&y); 17     printf("in main function,互换前:x=%d,y=%d\n",x,y); 18 } 19  20 /* 21  *以下为输出结果 22 in main function,互换前:x=3,y=5 23 in swap function,互换前:x=3,y=5 24 in swap function,互换后:x=5,y=3 25 in main function,互换前:x=5,y=3 26  * 27  * 原理就不解释了,因为无论是main,还是swap,都是在操作&x,&y地址的值. 28  * 29  * */
测试返回字符串#include <stdio.h>  2   3 char *select(char simplename);  4 char *select(char simplename){  5     switch(simplename){  6         case 'A':  7             return "Apple";  8         case 'B':  9             return "Banana"; 10         case 'C': 11             return "Cat"; 12         case 'D': 13             return "Dog"; 14         default: 15             return "None,please input from ABCD"; 16     } 17 } 18  19  20 int main(){ 21     char input; 22     printf("请输入一个选项(ABCD)"); 23     scanf("%c",&input); 24     printf("%s\n",select(input)); 25     return 0; 26 } 27  28 /* 29  * 有一个问题,关于返回值的 30  *  31  * 复杂的比如结构体,先不说,字符串怎么返回? 32  * 因为C中就没有关于字符串的定义啊,只有字符数组 33  * emm,这个时候,指针函数就能帮到你 34  * 一牵扯到指针,自然而然就联想到内存地址,没错,指针函数返回的就是地址 35  * 那上面的代码举栗子:就是返回字符串的第一个字符的地址.聪明的gcc会自动往下读取直到\0,从而获取完整字符串 36  * */

函数指针和指针函数

从名字就能看出来,函数指针是指针;指针函数是函数,其实没有什么好混淆的

不过,这个函数指针有个什么鸟用呢

如果想调用函数,直接调用接好了,传个屁啊,多此一举

实践出真知,下面用代码来一探究竟咯

没有使用函数指针:  1 #include <stdio.h>  2 int add(int i,int j);  3 int sub(int i,int j);  4 int add(int i,int j){  5     return i+j;  6 }  7 int sub(int i,int j){  8     return i-j;  9 } 10 int main(){ 11     int i=10; 12     int j=3; 13     printf("10+3=%d\n",add(i,j)); 14     printf("10-3=%d\n",sub(i,j)); 15 }使用了函数指针:  1 #include <stdio.h>  2 int add(int i,int j);  3 int sub(int i,int j);  4 int calc(int (*pfunction)(int x,int y),int i,int j);  5 int add(int i,int j){  6     return i+j;  7 }  8 int sub(int i,int j){  9     return i-j; 10 } 11 int calc(int (*pfunction)(int x,int y),int i,int j){ 12     return (*pfunction)(i,j); 13 } 14 int main(){ 15     int i=10; 16     int j=3; 17     printf("10+3=%d\n",add(i,j)); 18     printf("10-3=%d\n",sub(i,j)); 19  20     printf("10+3=%d\n",calc(add,i,j)); 21     printf("10-3=%d\n",calc(sub,i,j)); 22  23 }  24  25  26 /* 27  * 对比一下,使用了函数指针和没有使用函数指针的区别,emmm 28  * 好像还真是多此一举啊 29  * 使用了函数指针的功能,我不用也能实现,还更简洁,(╯‵□′)╯︵┻━┻  30  * 31  * 仔细想想的话,这东西好像跟java的泛型有点说不清的联系 32  * 对外暴露一个统一的大范围的接口 33  * 然后具体实现时,调用具体的接口 34  *  35  * 感觉真没有什么卵用 36  * 不管了,使用到了再说 37  * */ 38  39 

链接属性

external(默认):   整个项目内有效     externinternal:   只在本文件内有效    staticnone:       无效的属性所谓的链接属性是相对文件作用域内而言的;拥有链接属性的有比如:函数名,全局变量.所以上面的external,也就是说可以a文件中调用b文件的全局属性或者方法;需要小心的是不要忘了定义链接属性或者重复定义链接属性举个栗子:[solofeng@com utils]$ vi aad_external_internal.c=========================  1 #include <stdio.h>  2 int count = 12;=========================[solofeng@com utils]$ vi aad_external_internal_sup.c =========================  1 #include <stdio.h>  2 extern int count;  3 int main(){  4     printf("external count=%d\n",count);  5 }=========================[solofeng@com utils]$ gcc aad_external_internal.c aad_external_internal_sup.c -o aad_external_internal[solofeng@com utils]$ ./aad_external_internal external count=12总结一下,这几个东西就是java里面的public/protected/default/private

生命周期&存储类型

生命周期:1. 静态存储期(全局变量,函数)2. 动态存储期(局部变量)存储类型:autoregisterstaticexterntypedefauto:局部变量默认类型    int age = 10;    auto int age = 10;    上面两个是等价的    auto这个属性需要声明在代码块作用域中register:寄存器变量    可能会被放到寄存器的变量 ,具体放不放那要看编译器的    register这个属性需要声明在代码块作用域中;且不能使用取址运算符&,因为有可能去了寄存器嘛,而寄存器地址不允许获取static:    修饰全局变量:那么变量链接属性修改为internal,变量的作用域为本文件.    修饰局部变量:变成静态的局部变量,特点是拥有静态存储期,就是说,出了原本的作用域,他占的内存也不释放,与整个项目程序同寿.(跟java中的静态变量类似)    疑问:但是在作用域以外可以访问他吗,如果不能,那他有个屌用?    ==============================      1 #include <stdio.h>      2       3 int main(){      4     int i;      5     for(i=0; i<10; i++){      6         static int result = 0;      7         result += i;      8         printf("this %d trun ,the result is %d\n",i,result);      9     }     10     //printf("at last,the result is %d\n",result);     11      12 }     ==============================     以上是反面例子,的确不能在作用域以外访问,如果一定要问有什么用,暂时想到一个计数器(先不管同不同步什么的)     ==============================      1 #include <stdio.h>      2       3 //计数器方法,每次调用时count自增一次.      4 int counter(){      5     static int count = 0;      6     count++;      7 }      8       9 int main(){     10      11     printf("假设程序段A调用了一次支付操作\n");     12     printf("一共使用了多少次支付操作:%d\n",counter());     13      14     printf("假设程序段B调用了一次支付操作\n");     15     printf("一共使用了多少次支付操作:%d\n",counter());     16      17     printf("假设程序段C调用了一次支付操作\n");     18     printf("一共使用了多少次支付操作:%d\n",counter());     19 }     ==============================     [solofeng@com utils]$ ./aae_static_variable_counter     假设程序段A调用了一次支付操作    一共使用了多少次支付操作:1    假设程序段B调用了一次支付操作    一共使用了多少次支付操作:2    假设程序段C调用了一次支付操作    一共使用了多少次支付操作:3    那我就更有问题了,既然如此,我直接拿一个external全局变量去操作不就ok了.    但是external全局变量可视范围太广了,不安全.一不小心就被别人篡改了数据.extern: 略typedef: 略
原创粉丝点击