C语言程序设计-现代方法(笔记2)

第七章 基本类型

1、整数类型(7.1)

  有符号整数如果为正数或零,那么最左边的位(符号位)为0;如果是负数,则符号位为1.

  默认情况下,C语言中的整型变量都是有符号的。

  十进制常量包含0-9的数字,但不能以零开头;八进制常量只包含0-7,必须要以零开头;十六进制常量包含0-9数字和a-f字母,并且总以0x开头。

  为了强制编译器把常量作为长整数来处理,需在后面加字母L(或l),例如:15L,0x7fffL.

  为了指明是无符号常量,可以在常量后面加字母U(或u),例如:15U,0x7fffU.

  有符号整数溢出,程序行为是未定义的;无符号整数溢出,结果是有定义的:一般是0。

2、浮点类型(7.2)

  C语言提供三种浮点类型:float(单精度浮点数),double(双精度浮点数),long double(扩展精度浮点数)。float和double对于大部分程序都够用。

  float精度为6个数字,double精度为15个数字。浮点常量必须包含小数点或指数,例如:57.0,5.7e+1。

3、字符类型(7.3)

  在C中,字符和整数之间的关联非常强,字符常量事实上是int类型而不是char类型。

  有符号(signed)字符取值范围:-128-127;无符号(unsigned)字符取值范围:0-255。

  可移植性技巧:不要假设char类型默认为signed或unsigned。如果有区别,用signed char 或unsigned char代替char.。

  转义序列共有两种:字符转义序列和数字转义序列。字符转义序列,例如:\a,\n,\t等。数字转义序列,例如:\x1b,\033等。

  字符处理函数:小写字符转换成大写字母,ch = toupper(ch),需要包含#include <ctype.h>

  用scanf和printf函数也能读/写字符:

do {
  scanf("%c",&ch);
} while (ch != '\n')

  getchar()返回的是一个int类型值,而不是char类型值。getchar()的惯用法。第一个用于检测是不是换行符,第二个用于检测空格。

while (getchar() != '\n')
    ;

while ((ch = getchar()) == ' ')
    ;

4、类型转换(7.4)

  当算术表达式或逻辑表达式中操作数的类型不相同时;当赋值运算符右侧表达式的类型和左侧变量的类型不匹配时;当函数调用中的实参类型与其对应的形参类型不匹配时;当return语句中表达式的类型和函数返回值的类型不匹配时。

  任一操作数的类型是浮点类型,需要将float提升为double;两个操作数类型都不是浮点类型,需要将int提升为unsigned int或long int。

  C语言会把赋值运算右边的表达式转换成左边变量的类型。

  强制类型转换:(float)dividend / divisior;将变量dividend强制转换为float,编译器也会将divisior转换为float类型。

5、类型定义(7.5)

  使用typedef对类型进行定义,例如,typedef int Bool;

  类型定义更加易于理解,类型定义可以更好的编写可移植的程序。

  可以移植性的技巧:为了更大可移植性,可以考虑使用typedef定义新的整数类型名。

6、sizeof运算符(7.6)

  sizeof (类型名),返回的值是一个无符号整数,代表存储类型名的值所需的字节数。

  sizeof也可以用于常量,变量和表达式,比如sizeof(i+j)

第八章 数组  

1、一维数组(8.1)

  数组是含有多个数据值的数据结构,并且每个数据值具有相同的数据类型。

  数组常见操作 

for (i = 0; i < N; i++)
    a[i] = 0;                    //初始化数组

for (i = 0; i < N; i++)
    scanf("%d", &a[i]);        //将数据读入数组

for (i = 0; i < N; i++)
    sum += a[i];                 //对数组中元素求和

  数组初始化:int a[10] ={0};初始化完全为空是非法的,所以要在大括号内放一个0,其他值会默认初始化为0。

  C99中指定初始化:int a[15] = { [2] = 29, [9] = 7, [14] =48}

  可以用sizeof计算数组元素的大小,比如数组是a[10],用数组的大小除以数组元素的大小:sizeof(a) / sizeof(a[0])

  数组赋值除了用下面的方法,也可以用memcpy函数进行赋值,需要将头文件string.h包含,一般memcpy比较高效。

for (i = 0; i < N; i++)
  a[i] = b[i];

2、多维数组(8.2)

  C语言中的数组是按照行主序存储的,也就是从第0行开始,接着第1行,以此类推。

  多维数组不建议省略掉内层的花括号。

  常量数组(声明前加const)好处:首先,表明程序不会改变数组(方便别人阅读程序),然后,有助于编译器发现错误(告诉编译器不会修改)。

  变长数组优点:程序员不必再构造数组时给定一个长度,程序可以在执行时计算所需元素个数。

  变长数组限制:没有静态存储限制,没有初始化。

第九章 函数

1、函数的定义和调用(9.1)

  函数不能返回数组,但关于返回类型没有其他限制;返回类型是void类型说明函数没有返回值;省略返回类型C89默认为int类型,C99中是不合法的。

  如果返回类型很冗长,可以把返回类型单独放在一行,这很有用。

  C89中变量声明必须出现在语句之前;C99中,变量声明和语句可以混在一起,只要变量在第一次使用之前进行声明就行。

  没有时间完成函数,预留下空间,以待后续完成编写函数体。

  printf函数返回显示的字符个数。

  一个函数中可以声明与另一个函数中的变量同名的变量。因为这两个同名的变量在内存中地址不同,所以给其中一个变量赋新值不会影响另一个变量。

2、函数调用(9.2-9.3)

  在调用前声明每个函数。

  在C语言中,实际参数是通过值传递额:调用函数时,计算出每个实际参数的值并且把它赋值给相应的形式参数。

  在函数执行过程中,对形式参数的改变不会影响实际参数的值,因为形式参数中包含的是实际参数值的副本(copy).

  一维数组型实际参数

//sum_array函数原型
int sum_array(int a[], int n);
//sum_array函数定义
int sum_array(int a[], int n)
{
  int i, sum = 0;
  for (i = 0; i < n; i++)
    sum += a[i];
 
  return sum;      
}

  如果return语句中表达式的类型和函数的返回类型不匹配,系统将会把表达式的类型隐式转换成返回类型。

3、程序终止(9.5)

  return语句和exit函数之间差异:不管哪个函数调用exit函数都会导致程序终止,return语句仅当由main函数调用时才会导致程序终止。

  如果函数调用它本身,那么此函数就是递归的。

第十章 程序结构

1、局部变量(10.1)

  函数体内声明的变量成为该函数的局部变量。局部变量有自动存储期限和块作用域两种性质。

  局部变量的块作用域不能延伸到其他函数,只能在所属函数内部。

  静态局部变量有永久的存储单元(静态存储期限),在整个程序执行期间都会保留变量的值。

  形式参数拥有和局部变量一样的性质,即自动存储期限和块作用域。

2、外部变量(10.2)

  外部变量是声明在任何函数体外的。外部变量有静态存储期限和文件作用域(从变量被声明的点开始一直到所在文件的末尾)。

  使用外部变量的缺点:1.容易造成程序的耦合;2.外部变量赋值错误,很难查找;3.不方便函数的复用。

  使用外部变量时,要确保它们都拥有有意义的名字。

3、程序块和作用域(10.3-10.4)

  程序块包括:{多条声明 多条语句}。声明在程序块中的变量存储期限是自动的。

  当程序块内的声明命名一个标识符时,如果此标识符已经是可见的,新的升明明临时“隐藏”了旧的声明,标识符获得了新的含义。

4、构建C程序(10.5)

  •   单个文件程序的构建:构建程序的编排顺序。
  •   #include指令;
  •   #define指令;
  •   类型定义(typedef int Bool;);
  •   外部变量的声明;
  •   除main函数之外的函数的原型;
  •   main函数的定义;
  •   其他函数的定义。

  建议:在每个函数定义前需要进行注释,已给出函数名、描述函数的目的、讨论每个形式参数的含义、返回值和副作用等信息。

第十一章 指针

1、指针变量(11.1)

  可执行程序由代码和数据两部分组成。程序中每个变量占有一个或多个字节内存,把第一个字节的地址称为是变量的地址。

  指针就是地址,而指针变量就是存储地址的变量。

  C语言要求每个指针变量只能指向一种特定类型的对象。

2、取地址运算符和间接寻址运算符(11.2)

  使用&(取地址)运算符,可以找到变量的地址。使用*(间接寻址)运算符,可以获得指针所指向对象的内容。

  通过使用&运算符把某个变量的地址付给指针变量

int i, *p;
p = &i;

  一旦指针变量指向了对象,可以使用*(间接寻址)运算符访问存储在对象中的内容。

  只要p指向i,*p就是i的别名。*p不仅拥有和i相同的值,而且对*p的改变也会改变i的值。

3、指针赋值(11.3-11.5)

  假设如下声明,对指针初始化后,p=&i。可以进行q=p,这是指针赋值,p和q都指向i。而*q = *p,是赋值语句,将p指向的值复制到q指向的对象中。

int i, j, *p, *q;

  指针可以作为参数,传递地址给实际参数,实际参数和形参通过指针建立联系,改变形参内容即改变实参内容。

  函数可以返回指针,同时函数也可以返回指向外部变量或指向声明为static的局部变量的指针。

  永远不要返回指向局部变量的指针,返回后局部变量就不存在了。

第十二章 指针和数组  

1、指针的算术运算(12.1)

  当指针指向数组元素时,可以通过3中算术运算访问数组内的其他元素:指针加上整数,指针减去整数,两个指针相减。

  指针加上整数:p原先指向的元素向后j个位置。如果p指向元素a[i],则p+j指向a[i+j]。

  指针减去整数:p原先指向的元素向前j个位置。如果p指向数组元素a[i],则p-j指向a[i-j]。

  两个指针相减:指针之间距离。如果p指向a[i],q指向a[j],那么p-q就等于i-j。只有两个指针指向同一个数组时,相减才有意义。

2、指针用于数组处理(12.2)

  *运算符和++运算符的组合:

  a[i++] = j;先将值存入一个数组元素中,然后前进到下一个元素。

  *p++ = j;类似于a[i++] = j。因为++优先级高于*,也可以看成*(p++) = j;因为p++的值是p,所以p只有在表达式计算出来后才可以自增。因此*(p++)的值是*p,即p指向当前对象的值。

  (*p)++:这个表达式返回p指向的对象值,然后对对象进行自增(p本身是不变化的)。

表达式 含义
*p++或*(p++)  
(*p)++  
*++p或*(++p)  
++*p或++(*p)  

  最频繁用到的是*p++。

3、用数组名作为指针(12.3)

  可以用数组的名字作为指向数组第一个元素的指针。

  a[i]等价于*(a+i),可以把数组的取下标操作看成是指针算术运算的一种形式。  

for (p = &a[0]; p < &a[N]; p++)
  sum += *p;
// 简化循环后,用a替换&a[0],同时用a+N替换&a[N]
for (p = a; p < a + N; p++)
  sum += *p;

  许多程序员认为把形式参数声明为*a更准确,因为它会提醒我们传递的仅仅是指针而不是数组的副本。实践中,*a比a[ ]更常用。

4、指针和多维数组

  C语言按行主序存储二维数组。即,先是0行的元素,接着是1行的,以此类推。

        

 

  处理多维数组的行,处理多维数组的列,用多维数组名作为指针。

posted on 2020-01-04 17:37  chengabc  阅读(427)  评论(0编辑  收藏  举报