luosuo 发表于 2010-7-29 11:32:07

《C语言开发入门与编程实践》读书笔记

  工作之余暇,无所事事,虑光阴似金,不如习一技傍身,乃自当当网购得一书,曰《C语言开发入门与编程实践》,愿徐徐图之。
  此志。

第一章 C语言与程序设计简介
1 预处理指令不是真正的C程序指令,因此不需要在指令最后加上分号作为结束。
2 头文件的两种加载方式(两者的不同之处在于搜索的路径不同):
  #include<头文件> //加载C程序提供的头文件
  #include“头文件” //加载自定义的头文件
3 程序注释的两种方式: 
  /*可跨行使用*/ 
  //只可在一行之内
4 C语言是区分大小写的。
5 宏展开?
6 函数的return指令之前,可加system(\"pause\");以使程序暂停,观察运行结果。
7 函数的定义

luosuo 发表于 2010-7-29 11:36:01

第二章 基本数据处理
1 变量(Variable)和常量(Constant)都是具有名称的一块内存空间。
2 通常常量的命名以大写字母开头,变量与函数名以小写字母开头。
3 静态内存分配——P29。
4 常量的两种声明方法:
  const 数据类型 常量名称=常量值 //一直到程序结束都不可改变初始值
  #define 常量名称 常量值 //#define为宏指令,并非指令语句。
5 sizeof()函数用来查看各种数据类型或变量长度,用法为“sizeof(数据类型)或sizeof(变量名)”。调用它的预处理指令为#include<stdlib.h>。
6 浮点数的存储方法?
7 浮点数的两种表示方法:小数点方式、科学计数法。
8 C语言中的浮点数可分单精度(Float)与双精度(Double)。
9 如果想在声明float变量时直接赋值,需要在数值字尾加“F”或“f”,否则系统会预设double类型来存储。ex. float PI=3.14159F
10 ASCII采用8位来制定计算机中的内码,不过最左边为校验位,所以实际上只用到7位。字符在内存中是以整数类型存储的。
11 转义符
12 表达式由运算符(Operator)和操作数(Operand)组成。
13 操作数包括常量、变量、函数调用或其他表达式。
14 运算符包括赋值运算符、算术运算符、比较运算符、逻辑运算符、自增自减运算符、位运算符6种。
15 整数的取模运算符%和浮点数的取模函数fmod(a,b)。
16 =为赋值运算符,==为比较运算符。
17 !运算符(NOT)为一元运算符,由右至左;&&、||为二元逻辑运算符,由左至右。
18 自增(++)、自减(--)运算符前缀表达与后缀表达的区别:
  ++变量名 //先将变量值进行+1运算,再输出变量值
  变量名++ //先输出变量值,再进行+1运算
19 位运算符?操作数只能为整数。
20 复合赋值运算符:由右至左。
21 数据类型转换:double>float>unsigned long>long>unsigned int>int。
22 若赋值运算符左边数据类型的定义小于右边时,会发生部分数据被舍掉的情况,影响数据精度。
23 表达式使用char类型时,在计算表达式的值时,编译器会自动把char转换为int类型。不同的编译器在转换char类型时,会产生unsigned或signed两种情况,所以当char变量的值超过127(27)时,最好在声明时指明char类型是有符号还是无符号。
24 强制类型转换:avg=(float)(a+b) //赋值运算符左边的变量不能进行强制数据类型转换,(float)avg=a+b是不合法的。
25 浮点数转换为整数时,不会四舍五入,而是直接省掉小数部分。

luosuo 发表于 2010-7-29 11:40:09

第三章 基本输入/输出函数
1 %+标志设置+栏宽设置+.精度设置+数据长度修饰词+格式化字符
2 若栏宽设置<数据长度,则数据仍会按原本长度靠左显示。否则,以栏宽值为该数据长度靠右显示。
3 scanf()函数的取址运算符&,ex.:
  scanf("%d%f",&a,&b); //输入数据时,利用空格键或回车键或Tab隔开a、b
  scanf("%d,%f",&a,&b); //输入数据时,利用逗号隔开a、b
4 C语言中没有基本数据类型代表字符串,而是通过字符数组来组成,并且以NULL字符(&#39;\0&#39;,字符串结束字符)结尾。声明方式为:
  char 字符串变量[字符串长度];
5 字符串长度代表此字符串可供存储的字符数,但是扣掉结尾的NULL字符,实际可存储的字符会比字符串少一位。
6 用简单的一维字符数组表达字符串:
  char str[]={&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39;\0&#39;};
  char str[]="hello";
7 getchar()会将用户输入的字符响应到标准输出设备上,按下回车键后,才去读取第一个字符,并返回该字符变量。ex.:
  #include<stdio.h>
  int main()
  {
  char a; a=getchar(); printf("%c",a);
  return 0; 
  } //运行程序后,输入“Hello”,屏幕上显示“H”。
8 putchar()用来输出指定的单一字符。因为一个汉字的存储需要两个字节,所以读取一个汉字需要使用两次putchar()。
9 getche()通常用于程序中只需要用户输入一个字符即可继续执行的情况。getch()与getche()用法相同,只是不会把输入的字符显示在屏幕上。
10 gets()与scanf()在输入字符串时相比,可以输入包含空格或者Tab字符在内的字符串而不会结束。

luosuo 发表于 2010-7-29 11:47:13

第四章 流程控制
1 P97:判断是否闰年的程序中引出:格式化设置时的%起什么作用?
2 三种基本流程控制结构:顺序结构、选择结构、循环结构。
3 选择结构的五种控制语句:if,if…else,if …else if,条件运算符(?:),switch。
4 条件运算符是C语言中唯一一个三元运算符,只允许单行表达式。
5 事实上,C语言中并没有if…else if这种语法,它只是将if…else接在else之后。
6 switch语句的“失败经过”现象,通常加break;来避免。
7 switch条件表达式的结果必须是整数或者字符。
8 循环结构有三种控制语句:for,while,do…while。
9 for语句的嵌套。
10 while语句必须自行加入变量初始值及设置一个变量作为计数器。
11 do…while后面需加上分号;此语句会先执行循环语句,后判断条件是否成立。
12 break语句会立刻跳出最近的一层循环体,并将控制权交给循环体外的下一行程序;continue语句则只是忽略之后未执行的语句,但并不跳离循环。
13 goto语句尽量不用,注意标签语句的设置。
14 kbhit()函数。
15 判断奇偶数的方法:
  int x;
  if(x&1)
  printf("x为奇数");
  else
  printf("x为偶数");
16 课后题第二大题第7题,注意答案中printf("%*s",i," ");的用法。意为:若i=5,则该语句为printf("%5s"," ");。
  但是程序:
  #include<stdio.h>
  int main()
  {
  int i;
  scanf("%d",i);  //此处应为&i。
  printf("%*s\n",i,"*"); 
  return 0;
  }
  运行后,程序会无限循环。为何?原因又找到了,又把&忘了!
17 条件运算符(?:)的用法。
18 程序(1):
  #include<stdio.h>
  int main()
  {
  int a,b,t;
  scanf("%d%d",a,b);  //此处忽略了&a,&b!
  a<=b?t=a:t=b;
  printf("%d\n",t);
  return 0;
  }
  为何不能输出正确结果?原因找到了!而程序(2):
  #include<stdio.h>
  int main()
  {
  int a=1,b=2,t;
  a<=b?t=a:t=b; //或t=a<=b?a:b;
  printf("%d\n",t);
  return 0;
  }
19 如果用的是scanf("%d,%d\n",&i,&j);输入参数时得在i,j之间用","号;如果用的是scanf("%d%d\n",&i,&j);就得在i,j之间用空格。
20 这个最大公因数的问题,我考虑了一天多,最终写成下面这样了。目前除了只能解决正整数以外,在输入两个数完毕之后,必须再输入一个字符如“d”,才能求出最大公约数。为何?明白了,原来如此。
  #include<stdio.h>
  #include<stdlib.h>
  int main()
  {
  int a,b,i;
  printf("只能输入正整数:");
  scanf("%d,%d\n",&a,&b); //%d后多加了一个\n。

  if(a<=b)
  i=a;
  else
  i=b; //本段可利用条件运算符简化为:i=a<=b?a:b;

  for(;i>=1;i--)
  {
  if(a%i==0&&b%i==0)
  break;
  }
  printf("%d\n",i);

  return 0;
  }
21 需要学习数论中的辗转相除法求最大公因子。
22 课后第27题,解答:
  #include<stdio.h>
  int main()
  {
  int n,i;
  scanf("%d",&n);
  while(n!=0)
  {
   i=n%10;
   printf("%d",i);
   n=n/10; //while循环用于不知道循环次数的情况。此句为计数器。
  }
  return 0;
  }

luosuo 发表于 2010-7-29 11:51:03


第五章 数组与字符串
1 P138例5-5第22行,老看不明白,于是设计了一个程序:
  #include<stdio.h>
  #include<stdlib`.h>
  #include<conio.h>
  int main()
  {
  char choice;
  choice=getche();
  printf("\n");
  choice=choice-&#39;0&#39;; //查0、1、q的ASCII码可知,此语句将字符转换为了十进制数字。
  printf("%d",choice);

  return 0;
  } //该程序输入1打印1,输入q打印65。
2 我目前的知识薄弱点:进制转换、线性代数、数论。
3 C语言中并没有字符串的基本数据类型,其存储是通过数组实现的。
4 数组名指向数组的第一个元素索引值所在的内存地址,数组的第一个元素索引值为0。
5 一维数组只有一个索引值。其声明语法有纯声明和声明并初始化两种:
  数据类型 数组名[数组大小];
  数据类型 数组名[数组大小]={初始值1,初始值2,…};
6 两个数组不能直接用=互相指定(字符串数组亦然),只能通过数组元素;当设置初始值时,若初始值的个数少于定义的元素个数,则其余元素的值会是之前遗留在该内存上不可预测的残余值;在定义一维数组时,若不指定元素个数,数组的长度会由编译器根据初始值个数自动确定。
7 对多维数组初始化时,只允许第一维的长度可省略不去定义,其他维数则不允许。
8 目前编译器最多可以声明到12维数组。
9 字符串声明最重要的特点是必须使用NULL字符(即&#39;\0&#39;)来标志每一个字符串的结束。
10 字符串有两种声明方式:
  char 字符串变量[字符串长度]=“初始字符串”;
  char 字符串变量[字符串长度]={‘字符1’,‘字符2’,…‘字符n’,&#39;\0&#39;};
11 若未设置初始值,必须设置字符串长度,数组好像没这要求。
12 字符串数组可视为二维数组。
13 对字符串数组和二维数组进行理解,字符串在内存中的排列方式见下图:

  #include<stdio.h>
  int main()
  {
  char str={{"OHSHIT"},{"MYWORLD"}}; //P137的声明方式似乎有问题。
  printf("%c",str); //注意:字符串元素的基本数据类型为%c而非%s。
  return 0;
  }
14 以下语法不合法:
  char str_1[]="hello";
  char str_2;
  str_2=str_1;
15 字符串的连接功能、比较功能、搜索功能

luosuo 发表于 2010-7-29 12:00:00


第六章 指针
1 指针就是地址。指针变量是一种专门存放其它变量在内存中的地址的特殊变量,它的值是变量的地址(而非变量的值!)。指针中只能存放地址,不能将一个整型量或者其他非地址类型的数据赋给一个指针!
2 取址运算符&。
3 变量名称、变量值与内存地址之间的关系。
4 %p是指针的格式化字符,见P67。
5 指针变量的声明(若在声明时,未指定初始值,经常会使指针指向未知的内存地址):
  数据类型* 指针名称;
  数据类型 *指针名称;
6 一旦指定指针的数据类型就不能再做更改,并且不能指向其他数据类型的指针变量。
7 赋予指针初始值的两种方法(注意两者用法上的区别):
  数据类型* 指针名称; //(第一种方法)
  指针变量=&变量名称; //变量名称已定义或声明
            //或者写为数据类型* 指针名称=&变量名称;   
  数据类型* 指针名称=0; //(第二种方法)或者数据类型* 指针名称=NULL;
                //此处0并非数值0,而是代表NULL。
  指针变量=&变量名称;
8 注意以下两种不合法定义,会使指针指向不合法地址,从而造成不可预料错误:
  int *pival=10; //第一种
  int *pival; //第二种
  *pival=10;
9 指针运算时,只针对常量值(如+1或-1)操作,不能对两个指针进行互加互减操作,因为指针变量只是存放地址,它们之间的运算没有意义,且容易让指针指向非法地址。不过对相同类型的指针可以利用比较运算符比较地址间先后次序。
10 多重指针
11 声明数组时,数组时就是指向数组中第一个元素的内存地址,亦即该数组在内存中的起始地址,索引值就是其他元素相对于第一个元素内存地址的偏移量。
12 对于已定义的数组,可以通过&来取得该数组元素的地址,并以指针方式直接存取数组内的元素值,两种语法:
  数组名称[索引值]=*数组名称(+索引值);
  数组名称[索引值]=*(&数组名称[索引值]);
13 数组和指针最大的不同就是数组的地址是只读的,其值不能改变。
14 *是指针运算符,其优先级高于+。在定义语句中,星号*声明其后的变量为一指针(地址)变量;在非定义语句中“*变量名”表示指针变量指向的地址单元内的值。
15 数组长度可由sizeof()取得:
  数组长度=sizeof(数组名)/sizeof(数组名);
16 指针变量取得一维数组地址的两种方法:
  数据类型* 指针变量=数组名;
  数据类型* 指针变量=&数组名;
17 以指针取得arr[ i ]的内存地址:*(arr+i)+j;以双重指针表示法取出arr[ i ]的元素值:*(*(arr+i)+j);以一重指针取出其元素值:*(arr+i*n+j),其中n是指第一维的元素个数。
18 通过指针取得二维数组元素值的方法:
  数据类型* 指针变量=&数组名称; //数组已定义,并且其类型与指针相同
19 基本上,字符数组与指针建立字符串没有太大差异。若使用字符数组,此字符数组的值是指向此字符串的第一个字符的起始地址,且为常量,无法修改也不能进行运算;若使用指针,则可以进行运算。
20 一维指针数组的声明:
  数据类型 *数组名[元素名称];
21 注意使用一维指针数组与二维字符串数组存储字符串的不同。
22 动态内存分配是指在程序执行过程中才提出配置内存的要求,主要目的是让内存的使用更具有弹性。
23 动态内存分配后,必须在程序结束前,完成释放内存的动作。
24 内存泄漏
25 分别使用malloc()、free()在程序执行期间动态分配并释放内存空间:
  数据类型 *指针名称=(数据类型*)malloc(sizeof(数据类型)); //动态分配变量
  free(指针名称);
  数据类型 *指针名称=(数据类型*)malloc(数组长度*sizeof(数据类型));
                             //动态分配一维数组
  free(指针名称);
  数据类型 **指针名称=(数据类型**)malloc(数组长度n*sizeof(数据类型*));
                         //动态分配一个n×m二维数组
  free(指针名称);
  free(指针名称); //第二维数组内存释放完毕
  free(指针名称); //第一维数组内存释放完毕

luosuo 发表于 2010-7-29 12:08:06

第七章 函数
1 自定义函数语法:
  返回值数据类型 函数名称(参数列表)
  {
  程序语句块;
  return 返回值;
  }
2 任何自定义函数在被调用与使用前,都必须先被声明。如果直接将其定义放在了主程序之前,就同时具备了声明与定义的功能,不必再声明。
3 自定义函数的两种声明格式:
  返回数据类型 函数名称(数据类型 参数1,数据类型 参数2,…);
  返回数据类型 函数名称(数据类型,数据类型,…);
4 函数调用的语法格式:
  函数名称(自变量1,自变量2,…);
5 函数参数分为形式参数与实际参数两种(其区别见firefox捕捉的网页)。
6 传递参数的方式根据传递和接收的是参数数值还是地址分为传值调用和传址调用两种,两者的函数声明分别如下:
  返回值数据类型 函数名称(数据类型 参数1,数据类型 参数2,…);
  //以上传值调用,可为:返回值数据类型 函数名称(数据类型,数据类型,…);
  返回值数据类型 函数名称(数据类型 *参数1,数据类型 *参数2,…);
  //以上传址调用,可为:返回值数据类型 函数名称(数据类型 *,数据类型 *,…);
  其调用分别为:
  函数名称(自变量1,自变量2,…);
  函数名称(&自变量1,&自变量2,…);
7 C语言没有提供真正的传址调用功能,而是以配置指针变量的形参来存放实参所传入的变量地址,也就是一种传递指针变量的功能。
8 同一函数可以同时拥有传值与传址两种参数传递方式。
9 一维数组参数传递的函数声明:
  返回值类型or void 函数名称(数据类型 数组名[],数据类型 数组大小…);
  返回值类型or void 函数名称(数据类型 *数组名,数据类型 数组大小…);
  其调用为:
  函数名称(数据类型 数组名,数据类型 数组大小…);
10 冒泡排序法
11 二维数组参数传递的函数声明:
  返回值类型 函数名称(数据类型 数组名[][行],数据类型 列,数据类型 行…);
  其调用为:
  函数名称(数据类型 数组名,数据类型 列数,数据类型 行数…);
12 指针返回值函数原型声明如下:
  返回值数据类型 *函数名称(数据类型 参数1,数据类型 参数2,…);
13 函数指针的声明格式如下:
  返回值数据类型 (*函数指针名称)(参数1数据类型,参数2数据类型,…);
  将其指向函数地址有两种方式:
  返回值数据类型 (*函数指针名称)(参数1数据类型,…)=函数名称;
  返回值数据类型 (*函数指针名称)(参数1数据类型,参数2数据类型,…);
  函数指针名称=函数名称;
14 参数型函数指针:可使函数成为另一函数的参数。其声明与一般函数指针相同,不过位置不同,直接声明在函数的参数列表中。
15 函数可以像整数一样放在数组的连续内存中,通过索引值读取,这就是函数指针数组。函数指针可以如同一般变量,声明成数组类型,主要是可以作为相同类型函数地址的存储与应用。
16 函数指针数组,其原型声明如下: 
  数据类型 (*函数指针名称[])(参数1数据类型,参数2数据类型,…);
17 命令行参数就是程序在MS-DOS系统中执行时所传递的自变量(实际参数)。
18 命令行参数argc与argv,声明:
  int main(int argc,char *argv[])
19 argc:数据类型为整数,表示命令行参数的个数。
20 argv[]:数据类型为不确定长度的字符串指针数组,所传递的的数据都是字符串格式。命令行参数字符串是以空格或Tab字符作为分隔的。
21 main()自变量传递
22 变量的作用域及生命周期
23 全局变量(外部变量)与局部变量:如果程序中,局部变量与全局变量拥有相同的名称,当程序编译时,在程序块内的局部变量设置值会暂时优先盖过全局变量,一旦局部变量所在的程序块执行结束,全局变量又会恢复到原来的设置值。
24 变量的五种存储类型:auto、static、extern、static extern、register。
25 自动变量,一种局部变量,函数执行完毕后立刻释放占用的内存空间,允许不同函数使用同名自动变量:
  auto 数据类型 变量名称;
26 静态局部变量,一种局部变量,函数执行完毕后依然占用的内存空间直到程序完全结束,若未给初始值,会默认为0,与一般变量不同:
  static 数据类型 变量名称;
27 外部变量,即全局变量,是在函数或程序块外所声明的变量,声明时可省略extern,若不指定初始值,默认为0。
  extern 数据类型 变量名称;
28 extern修饰词的功能是可将声明在函数或程序块后方的外部变量,引用到函数内使用。但是若在函数内利用extern声明一个变量时,并不会实际分配内存,必须在函数外部有一个同名变量存在,才会分配。除此之外,extern更能使外部变量跨越不同的程序文件使用。
29 静态外部变量,与extern最大的不同,是只限同一程序文件中使用:
  static extern 数据类型 变量名称;
30 寄存器变量,是指使用CPU的寄存器来存储变量,通常用于存储十分频繁的变量,但是若声明变量超过CPU寄存器的限量时,系统会自动将超过的寄存器变量转换为一般变量。其生命周期随变量所声明的程序块与函数结束而结束:
  register 数据类型 变量名称;
31 递归函数:假如一个函数或程序块是由自身所定义或调用,则称为递归函数。通常一个递归函数有两个必备条件:一个可以反复执行的过程;一个跳出反复执行过程的缺口。
32 基本上,递归函数的应用是为了增加函数的可读性,但是因为需要“暂存堆栈”,自然要花费额外的内存空间,容易造成内存的浪费,影响执行效率。
33 河内塔问题
34 尾归递归就是程序的最后一个指令为递归调用。因为每次调用后,再回到前一次调用的第一行指令就是return,所以不需要再进行任何计算工作,因此也不必保存原来的环境信息。

luosuo 发表于 2010-7-29 16:16:16


第八章 预处理器与宏
1 以#开头的预处理指令并不属于C语法,并不需要以;结束,但仍被编译器接受。
2 #include语法的两种指定方式,在第一章即涉及到。
3 #define是一种替换指令,用来定义宏的名称,并取代程序中的数值、字符串、程序语句或者函数。一旦完成宏的定义后,只要遇到程序中的宏名称,预处理器就会将其展开成所定义的字符串、数值、程序语句或函数等——这应该就是第一章的宏展开吧。
4 当利用#define来定义宏时,其名称通常是大写英文。声明语法如下:
  #define 宏名称 常量值
  #define 宏名称 "字符串
  #define 宏名称 程序语句
  #define 宏名称 函数名称 
5 取消宏的语句:#undef 宏名称
6 宏函数是一种可以传递自变量来取代简单函数功能的宏。对那些简单而又经常调用的函数,以宏函数取代,可以减少调用函数及等待函数返回的时间,提高执行效率。不过由于宏函数被展开成为程序代码的一部分,编译完成后的程序文件容量会比原来的函数文件大,宏函数的声明如下:
  #define 宏函数名(参数列表) (函数表达式)
  //参数列表不需设数据类型;
  //函数表达式若太长,可分行表示并在行尾加“\”,其中空格不会被忽略。
7 注意例题8-5中printf("上底=",&r1);的用法。
8 六种条件编译指令:#if、#else、#elif、#endif、#ifdef、#ifndef,其功能类似流程控制语法,只是不需要加{}和;。
9 条件编译指令的使用:
  #if 条件表达式 //第一种
    程序语句块
  #endif

  #if 条件表达式 //第二种
    程序语句块一
  #else 条件表达式
    程序语句块二
  #endif

  #if 条件表达式 //第三种
    程序语句块一
  #elif 条件表达式一
    程序语句块二
  #elif 条件表达式二
    程序语句块 
  …
  #endif

  #ifdef 宏名称 //第四种:若#define定义了宏名称,编译程序语句块
    程序语句块
  #endif 

  #ifndef 宏名称 //第五种:若#define未定义宏名称,编译程序语句块
    程序语句块
  #endif
10 无论初学者还是老手,在使用scanf()时经常会遗忘&运算符,可以使用宏替换来避免。

houhuaibin 发表于 2011-1-18 14:47:41

看看,了解一下
页: [1]
查看完整版本: 《C语言开发入门与编程实践》读书笔记