C语言入门

来源:互联网 发布:不败传说冰川网络 编辑:程序博客网 时间:2024/06/07 19:32

一、编程基础

1.编程语言是用来控制计算机的一系列指令(Instruction),它有固定的格式和词汇(不同编程语言的格式和词汇不一样),必须遵守,否则就会出错,达不到我们的目的。CPU、内存、硬盘等部件不能各自为战,它们通过主板连接在一起,由CPU控制,协调工作。
1. 载入内存,让程序运行起来,双击QQ图标,操作系统会在硬盘上找到安装软件的数据复制到内存中运行,这个过程叫做载入内存,这个程序叫做加载器。
2. 在C语言中,八进制通常以“0”开头,十六进制通常以“0x”开头,十进制转换成二进制用的是辗除法,直到商为0,余数倒序排列,美国制定了一套英文字符与二进制位的对应关系,称为ASCII码,Unicode编码可以容纳100多万个符号。

二、C语言初探

  1. CPU只认识几百个二进制形式的指令,编译器将C语言代码转换成CPU能识别的二进制指令,也就是把代码加工成.exe程序,Windows下常用编译器是微软开发的cl.exe,Linux下常用编译器是GUN组织开发的GCC。除了编译器等必须的工具,我们往往还需要很多其他的辅助软件,例如编辑器、调试器等,这些工具通常被打包在一起,统一发布和安装,例如visual studio2013,它们统称为IDE——集成开发环境。
  2. \后面加8进制数称为转义字符,在ASCII码中,从0-31的字符为控制字符,它们都是看不见的字符,不能在显示器上显示,只能以转义字符的形式来表示,不过直接使用ASCII码值记忆不方便,针对常用的控制字符,C语言又定义了简写方式,完整列表如下:
    \a响铃,\b退格,\f换页,\n换行,\r回车,\t水平制表,\v垂直制表。同时,字符串中出现‘’\时必须要转义。

三、变量和数据类型

1.char字符型1, short短整型2, int整型4, long长整型4, float单精度浮点型4, double双精度浮点型8
5. 在计算机内存中,数值一律采用补码表示,正数的补码是其本身,负数的是原码的符号位不变其他位相反变为反码,然后反码加1变为补码。
6. float输出%f默认6位小数,超过6位四舍五入,%.2f输出2位小数。
7. char变量在内存中存储的是字符对应的ASCII码值,int变量在内存中存储的是整数本身对应的ASCII码值。
8. 全局变量的默认初始值是0,局部变量的初始值随机
9. ANSI C标准=ISO C=C89=C90,C99标准进行了一些优化,但是微软等并不感冒
10. 光标定位需要用到Windows.h中的setconsolecursorposition函数。

四、输入输出

1.printf(“%-9d”,a),左对齐,宽带9个字节,十进制输出
11. puts字符串 putchar字符 printf各种 gets字符串 getchar字符 scanf各种
12. 在vs中输入三个字符就提示时可以按“Ctrl+enter”进行确认快速输入
13. 在vs中有边界检查,scanf_s(“%s”,str,5),gets_s(str,30),gets会读空格,scanf遇到空格后结束。scanf_s最后一个参数是缓冲区的大小,最多可以输入n-1个字符。
14. 当缓冲区中有数据时,scanf会直接读取,匹配失败后重头开始,控制字符不是以%d开头,回车符起作用。
15. 清空缓冲区,fflush(stdin);
16. getch()无回显 无缓冲区,getche()有回显 无缓冲区
17. 在定义数组或者和sizeof、&运算符一起使用时数组名才表示整个数组,表达式中的数组名会被转换为一个指向数组的指针。数组定义char pwd[20],函数中定义数组getpwd(char*pwd,int pwdlen),pwd为保存密码的内存的首地址,pwdlen为密码的最大长度。在使用时就调用getpwd(pwd,20)。
18. char*p=”123”,char p[]=”123”,都可以表示字符串,但是后者更好,可以读取以及写入。
19. 赋值<&&和||<关系运算符<非!,switch选择结构,代替简单的有多个分支的if else。
20. continue是跳过此次循环,进入下一次循环。
21. char c[10]={0},剩余的元素会自动初始化为0,所以只需给第一个元素赋值为0,里面都是字符‘\0’。
22. C语言规定,可以将字符串直接赋值给字符数组,char str[30]=”c.biancheng.net”。没有专门的字符串常量,没有string类型,通常就用字符数组来存放。
23. 字符串函数stdio.h printf,puts,scanf,gets;string.h:strlen,strcat,strcpy,strcmp;strcat_s(str1,40,str2),40为str1的容量。gets_s(str,15)遇到空格不停。
24. while(ch=getch()){}getch后面的内容都被阻塞了,while(1){if(kbhit()){ch=getch();if(ch==27)break;}printf(“%d”,++i);sleep(1000);}kbhit()检测缓冲区中是否有字符,不用等待输入字符。
25. srand((unsigned)time(NULL))-播种,属于time.h;int a=rand()%51+13-13~63的随机数。
26. 函数一旦遇到return就会停止运行。在所有函数之外进行加减乘除、if…else、调用函数都是没有意义的,永远不会被执行。
27. 1!+2!+…+10!,调用过程-main>sum>factorial
28. stdio.h中未包含函数定义,都是函数声明,函数定义都在系统库中。合格程序员:stdio.h/ctype.h/stdlib.h/string.h; 熟练程序员:assert.h/limits.h/stddef.h/time.h;优秀程序员:float.h/math.h/error.h/locale.h/setjmp.h/signal.h/stdarg.h
29. 编译:针对单个源文件;链接:针对多个文件,将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序。#称为连接符,将宏参数或其他串联起来,LINE:当前源代码行号;FILE:源文件名称;DATE:编译日期;TIME:编译时间
30. 条件编译:预处理程序的功能。

#if 整型常量表达式            #ifdef  宏名#elif                      #else#else                      #endif#endif

22.#include:包含一个源代码文件;#define:定义宏;#undef:取消已定义宏;#if:如果条件为真,编译下面代码;#ifdef:如果宏已定义,编译下面代码;#ifndef:如果宏未定义,编译下面代码。
23.在编写代码的过程中,我们认为变量名代表的是数据本身,而函数名、字符串名、数组名表示代码块或数据块的首地址。
24.int p=&arr[0];int *p=arr; arr,p,&arr[0]等价,如果一个指针指向数组,则称为数组指针。引入数组指针后,访问数组元素有两种方法,使用下标:arr[i],p[i];使用指针:(p+i),*(arr+i),arr被转换成了一个指针。
25.字符与整数运算时,先转换为整数(ASCII码)。char str[20]=”cc”;char *s1=str;char*s2=str+2;char c1=str[4];char c2=*str。
26.指针数组是一个数组,只是每个元素保存的都是指针,以p1=int*(p1[5])为例子,在32位环境下占用4*5=20个字节;二维数组指针是一个指针,它指向一个二维数组,以p2=int(*p2)[5]为例,它占用4个字节内存。
27.int (pmax)(int a,int b)=max;调用是maxval=(*pmax)(a,b),pmax是一个函数指针,在前面加号表示对他指向的函数进行调用。
28.优先级顺序,定义中被括号括起来的那部分》后缀操作符:()表示函数;【】表示这是一个数组》前缀操作符:*表示指向…的指针。

四、C语言指针的总结

指针(pointer)就是内存的地址,C语言允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组、函数以及其他指针变量的地址。
程序在运行过程中需要的是数据和指令的地址,变量名、函数名、字符串名和数组名在本质上是一样的,都是地址的助记符:在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、字符串名、数组名表示的是代码块或者数据块的首地址;程序被编译和链接后,这些名字都会消失,取而代之的是他们的地址。

定义 含义 int *p p可以指向int类型的数据,也可以指向类似int arr[n]的数组 int **p p为二级指针,指向int *类型的数据 int *p[n] p为指针数组,[]的优先级高于* int (*p)[n] p为二维数组指针 int *p() p是一个函数,它的返回值类型是int* int (*p)() p是一个函数指针,指向原型为int function()的函数

1.指针变量可以进行加减运算,例如p++,指针变量的加减运算并不是简单的加上或减去一个整数,而是跟指针指向的数据类型有关。
2.给指针变量赋值时,要将一份数据的地址赋给它,不能直接赋一个整数
3.使用指针变量之前一定要初始化,否则就不能确定指针指向哪里,如果它指向的内存没有使用权限,程序就崩溃了。对于暂时没有指向的指针,建议赋值NULL。
4.两个指针变量可以相减,如果两个指针变量指向同一个数组中的某个元素,那么相减的结果是两个指针之间相差的元素个数。
5.数组也是有类型的,数组名的本义是表示一组类型相同的数据。在定义数组时,或者和sizeof等运算符一起使用时数组名才表示整个数组,表达式中的数组名会被转换为一个指向数组的指针。
C语言规定,对于一个符号的定义,编译器总是从它的名字开始读取,然后按照优先级顺序依次解析。
char ( c[10])(int **p);
先看符号,再看左右两边优先级,(* c[10])说明c就是一个指针数组,只是指针指向的数据类型尚未明确。跳出括号看优先级,()说明是一个函数,int *p是函数参数。再看左边,char是函数的返回值类型。合起来就是:c 是一个拥有 10 个元素的指针数组,每个指针指向一个原型为char *func(int **p);的函数。
int (((pfunc)(int ))[5])(int *);
(pfunc)说明pfunc是一个指针,(int )表明这是一个函数,int*是函数的参数,左边的表示函数的返回值是一个指针,只是指针指向的数据类型不确定。((pfunc)(int ))合起来表示pfunc是一个指向函数的指针,现在函数的参数列表知道,函数的返回值是一个指针,但是不知道指针指向的数据类型。
(((pfunc)(int ))[5])表示这是一个指针数组,函数返回值的指针指向这样一个数组。那么指针数组中的指针又指向什么类型的数据呢?
int (((pfunc)(int ))[5])(int )表示指向一个原型为intfunc(int )的函数。
将上面三部分合起来就是,pfunc是一个指针,他是一个函数指针,这个函数指针指向一个指针数组,该指针数组中的指针指向原型为intfunc(int *)的函数。

五、结构体

  1. 结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是集合本身,要想取得结构体变量的地址,必须在前面加&,所以给pstu赋值:struct stu*pstu=&stu1;
  2. 结构体是一种数据类型,同于int等,结构体变量才包含实实在在的数据,才需要内存来存储。
  3. 通过结构体指针获取结构体成员,(*pointer).membername或pointer->membername
  4. enum week{mon=1,tus,wen,thu,fri,satu,sun}day;day是枚举类型的变量,为整型
  5. typedef可以为类型起一个新的别名,语法格式是typedef char array20[20];表示array20是类型char[20]的别名,他是一个长度为20的数组类型。
  6. typedef char(*PTR_TO_ARR)[30]; 数组指针类型

六、调试

当我们运行我们编写的程序发现运行结果与我们预想的不同的时候,我们可以先用即时窗口,使用一些比较简单的数据来测试我们的各个函数运行结果是否符合我们的预期,如果都符合的话,我们可以使用程序中产生的一些比较复杂的数据来进一步测试我们的各个函数,直至找到可能导致错误的函数。

找到可能导致错误的函数之后,我们就可以使用逐语句调试来一步步跟踪运行程序了,渐渐的的我们就可以缩小范围直至定位错误(无关代码可以考虑暂时注释掉),在这期间,我们要仔细观察程序运行过程中各个数据的变化情况,观察的仔细与否直接与我们能否找到错误直接挂钩。

如果上一步运行的数据一直是正常的,我们就可以排除这个函数的嫌疑了(减少对他的调试次数)。此时,我们就应该考虑问题是否出现在之前的函数上了,可能因为偶然性,我们第一次测试函数的时候并没有发现其错误,导致范围锁定产生偏差,此时我们需要再次耐心的对所有未排除嫌疑的进行调试,直至再次找到出错的函数。再重复上一步,直至找到错误。

可以看到,调试其实是一项比较复杂的活,需要大量的操作,所以在我们编写代码的时候要万分谨慎!因为很多时候,BUG都是因为我们的粗心大意导致的笔误引起的!
这里写图片描述
这里写图片描述