c语言学习笔记

一、指针

  1. 一个变量的地址就称为该变量的指针,如果有某一变量专门用来存放另一变量的地址,则称它为指针变量,指针变量的值就是地址(即指针),一个指针变量占用内存四个字节,跟它所指向的变量占用内存多少无关。
  2. 如果指针p已经指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p-1指向同一数组中的上一个元素。执行p+1是将p的值加上一个数组元素所占用的字节。
  3. 如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址。a+1的计算方法跟p+1的计算方法一样,也是加上一个数组元素所占用的字节。*(p+i)或*(a+i)就是p+i或a+i指向的数组元素,即a[i]。也就是*(p+i)=*(a+i)=a[i]。实际上,在编译时,对数组元素a[i]就是按*(a+i)处理的,将a[i]按照a+i计算地址,然后找出此地址单元中的值。
  4. 如果指针p1和p2都指向同一数组,如果执行p2-p1,结果是p2-p1的值除以数组元素的长度,也就是p1和p2所指向的数组元素之间的距离。
  5. 若int a[10],*p; for(p=a;a<p+5;a++)scanf("%d",a);这样写是不可以的,因为数组名a代表数组首元素的地址,它是一个指针型常量,对它执行a++是无法实现的。
  6. 指向数组的指针变量也可以带下标,如p[i]。因为在程序编译时,对下标的处理方法是转换为地址的,对p[i]处理成*(p+i),如果p是指向a[0],则p[i]代表a[i],如果p指向a[3],则p[1]代表a[3+1]也就是a[4]。
  7. C编译都是将形参数组名作为指针变量来处理的。fun(int arr[])等价于fun(int *arr)。实参数组名代表一个固定的地址,或者说是指针常量,不可以被赋值,但是形参数组名并不是一个固定的地址,而是按指针变量处理,在函数执行期间,形参数组名可以再被赋值,比如上面的fun函数体内可以执行arr=arr+3。
  8. 从二维数组的角度看,二维数组的每个元素(a[0]、a[1]…)都是一个一维数组,二维数组名a代表二维数组首行的首地址,即第一行(第一个数组)的首地址,a+1代表第二行的首地址。二维数组0行1列的元素的地址应该表示为a[0]+1(因为a[0]是一维数组名),也就是说a[0]+1=&a[0][1]。结合第3点,a[0]等价于*(a+0),所以a[0]+1=*(a+0)+1=&a[0][1]。则a[0][1]=*(a[0]+1)=*(*(a+0)+1)=*(*a+1)。
    一般地,a[i][j]=*(a[i]+j)=*(*(a+i)+j)。
  9. 虽然二维数组名a的值是首行的首地址,其值跟a[0]和*(a+0)是一样的,但是它们的含义却不一样,数组名a的类型是指向一维数组,而a[0]的类型是指向整型变量的指针。a指向一维数组a[0],a[0]指向列元素a[0][0]。也就是说,二维数组名是指向行的,一维数组名是指向列的,在指向行的指针前面加一个*,就转换为列的指针。在指向列的指针前面加&,就称为指向行的指针,比如&a[0]=&*(a+0)=&*a=a,指向第0行。在二维数组中,a+i、a[i]、*(a+i)、&a[i]、&a[i][0]的值相等。
  10. 二维数组的元素在内存中是按行顺序存放的,即存放完序号为0的行中的全部元素后,接着存放序号为1的行中的全部元素,以此类推。a[i][j]在数组中的相对位置的计算公式为i*m+j,如果指针指向a[0][0](即int *p=a[0]),那么a[i][j]的地址就是p+(i*m+j),等价于&a[0][0]+(i*m+j)。
  11. 定义指向一维数组的指针:int (*p)[4],p是指向一个包含4个元素的一维数组,p的值是该一维数组的起始地址。注意区分于 int *p[4],由于[]级别高,p与[4]先结合,p[4]是定义数组的形式,然后再与*结合,*p[4]就是包含四个整型指针的指针数组。
    一维数组指针p指向一维数组a的写法是:p=&a,而不是p=a,因为这样写表示p的值是&a[0],指向了a[0]。通过一维数组指针p取一维数组元素a[i]的值的写法:(*p)[i],将指向行的指针转为指向列的指针。
    一维数组指针p指向二维数组a的写法是:p=a,表示p指向二维数组的第0行。取出a[i][j]的值的写法:*(*(p+i)+j)。
  12. 一个表达式的结果如果是一个指针,那么这个表达式就叫指针表达式。当一个指针表达式的结果指针已经明确具有了指针自身占据的内存的话,这个指针表达式就是一个左值。比如int a; int *p; p = &a则&a是一个指针表达式,但它不是一个左值。又如int **p2; *p2 = &p,则*p2跟&p都是指针表达式,*p2是一个左值,因为*p2就是指针变量p,指针变量p已经在内存中有了自己的位置,所以*p2也就有了自己的位置。

也就是说,判断一个指针表达式是否是左值,即看有没有一个具体的指针变量存储了这个指针表达式代表的指针变量的地址,使这个地址在内存中额外占据了一个空间,像&a、&p这种,都只是通过取址符来取到指针,实际上在内存中根本没有一个指针变量存储这两个地址。

在C语言中表示位于赋值运算符两侧的两个值,左边的就叫左值,右边的就叫右值。左值其实要引用一个对象,而一个对象在我们的程序中又肯定有一个名字或者可以通过一个名字访问到,所以左值又可以归纳为:左值表示程序中必须有一个特定的名字引用到这个值。而右值引用的是地址里的内容,所以相反右值又可以归纳为:右值表示程序中没有一个特定的名字引用到这个值除了用地址。

  1. sizeof函数返回的是否个对象自身类型的大小,比如int arr[40], sizeof(arr)返回的是160,而由于arr+1是一个指针,所以sizeof(arr+1)返回的4。
  2. 如果程序中定义了一个函数,在编译时,编译系统会为函数代码分配一段存储空间,这段存库空间的起始地址称为这个函数的指针。可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。
    (1)例如,int (*p)(int,int);定义p是一个指向函数的指针变量,它可以指向函数的类型为整型且有两个整型参数的函数。p的类型用int(*)(int,int)表示。通过该指针能够调用函数,假设定义了一个函数int max(int,int),让p指向这个函数,int (*p)(int,int);p = max;则通过(*p)(a,b)可以去调用这个函数,作用跟直接调用max是一样的。
    (2)注意*p两边的括号是不能去掉的,去掉则变成int *p(int,int);,这是函数声明的形式,声明了一个p函数,函数带有两个参数,返回值为指向整型变量的指针。让指针变量指向某一个函数,只需要给出函数名而不用给出参数,如上面的p=max,如果写成p=max(a,b);这样是不对的。
    (3)对指向函数的指针进行加减是没有意义的。
    (4)指向函数的指针变量的重要用途就是把函数的地址作为参数传递到其他函数,这样在其他函数中就可以依据不同的需要传入不同的参数,进而调用不同的函数。
  3. 静态存储区分为栈和堆,栈用来存储返回地址、参数和局部变量,而堆用来存储一些临时用的数据,这些数据不用在程序的生命不分定义,也不用等到函数结束时才释放,需要时随时开辟,不需要时随时释放,即动态分配内存。对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc四个函数,这四个函数的声明在stdlib.h头文件中。
    (1)malloc函数,函数原型为void* malloc(unsigned int size),在内存的动态存储区中分配一个长度为size的连续空间。函数返回值是指向所分配区域的第一个字节的指针。指针的基类型为void,即不指向任何类型的数据,只提供一个地址。函数执行失败则返回NULL。
    (2)calloc函数,函数原型为void* calloc(unsigned n, unsigned size),在内存的动态存储区分配n个长度为size的连续空间,函数返回指向所分配区域的起始位置的指针,如果分配失败则返回NULL。用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size,这就是动态数组。
    (3)free函数,函数原型为void free(void *p),释放指针p所指向的动态空间,p应是最近一次调用calloc或malloc函数时所得到的函数返回值。
    (4)realloc函数,函数原型为void *realloc(void *p, unsigned int size),如果已经通过malloc函数或者calloc函数获得了动态空间,如果想改变其大小,可以用realloc函数重新分配,将p所指向的动态空间的大小改变为size,如果重分配不成功则返回NULL。
  4. void指针类型,可以定义一个基类型为void的指针变量,即void*型变量,它不指向任何类型的数据,当把void指针赋值给不同基类型的指针变量(或相反)时,编译系统会自动进行转换。

二、字符串

  1. 可以通过字符指针变量来输出一个字符串。char *string = “abc”;printf(“%s”,string);C语言对字符串常量是按字符数组处理的,在内存中开辟了一个字符数组来存放该字符串常量,然后用字符指针变量指向该字符数组的首地址。
  2. 可以对字符指针变量赋值,但不能对数组名赋值。
  3. 指针变量的值是可以改变的,而数组名代表一个固定的值(数组首元素的地址),不能改变。如char *a = “abc”;a=a+1;这是可以的,如char a[]=”abc”;a=a+1;这是不可以的。
  4. 字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再赋值)。如char a[]=”abc”;a[1]=’d’;这是可以的;如char *a=”abc”;a[1]=’d’;这是不可以的。
  5. 用指针变量或字符数组指向一个格式字符串,可以用它代替printf函数中的格式字符串。char *format(或char format[])=”a=%d”;printf(format,a);相当于printf(”a=%d”,a);
  6. 简单的字符串复制实现:把第一个参数指向的字符串复制到第二个参数所指向的字符串
    void strcpy(char *from, char *to)
    {
    for(int i=0; from[i] != ‘\0’;i++)
    {
    to[i] = from[i];
    }
    to[i] = ‘\0’;
    }
  7. 简单的字符串连接:
    void strcat(char *str1, char *str2)
    {
    while(*str1 != ‘\0’)
    str1++;
    while(*str2 !=’\0’)
    *str1++ = *str2++;
    *str2 = ‘\0’;
    }
  8. 简单的字符串查找,如果找到则返回指向该字符的指针
    char *strchr(char *str, char ch)
    {
    while(*str != ‘\0’)
    {
    if(*str == ch)
    return str;
    str++;
    }
    return NULL;
    }
  9. 简单的字符串比较
    int strcmp(char *str1, char *str2)
    {
    int result;
    while (*str1 != ‘\0’)
    {
    result = *str1++ - *str2++;
    if(result && *str2)
    break;
    }
    if(result < 0)
    return -1;
    else if(result == 0)
    return 0;
    return 1;
    }
  10. puts函数,调用形式为puts(字符数组),使用puts函数输出会将字符串结束标志‘/0’转换成’\n’,也就是说输出完字符串后换行。
  11. gets函数,调用形式为gets(字符数组),从转动输入一个字符串到字符数组,并且得到一个函数值,函数值是字符数组的起始地址。
  12. strlen函数,调用形式为strlen(字符数组),返回字符串长度,结束符不计算在内。
  13. 定义一个指定长度的字符数组去接收字符串,未被赋值的数组元素的初值为空,也就是’\0’,如char [10]=”abc”;或char [10] = {‘a’,’b’,’c’};则从char[4]开始都是’\0’

三、字符常量

  1. ‘\101’,’\x41’,都代表‘A’,ASCII码为65,‘a’为97。
  2. 常用的字符的ASCII码,标准ASCII码从0-127,共128个字符,扩展ASCII码从0-255,共256个字符,128-255是图形符号。
字符ASCII码
空格32
480
A65
a97
048

四、位运算

  1. &按位与,0&0=0,0&1=0,1&0=0,1&1=1,参加运算的两个数据按二进制位进行与运算。按位与的特殊用途:(1)把原数所处的单元清0,只需要用一个新数二进制位跟原数相反的数进行与运算即可。(2)要想保留哪一位,就与一个数进行与运算,此数在该位取1。
  2. |按位或,0|0=1,0|1=1,1|0=1,1|1=1。
  3. ʌ异或(XOR)运算符。0ʌ0 = 0,0ʌ1=1,1ʌ0=1,1ʌ1=0。异或的意思是判断两个相应的位值是否为异,为异值为1(真)。异或运算的应用:(1)使特定位翻转,要使哪几位翻转就将与其进行异或运算的数在该位的值为1,其他位为0即可。(2)与0异或,保留原值。(3)交换两个数的值,可以通过a=a ʌ b,b=b ʌ a,a=a ʌ b实现。异或运算满足结合律、交换律。一个数与自身进行异或运算得0。
  4. 取反,按位取反,将0变成1,1变成0。将某个数的最后一个二进制位变为0,则可以通过a=a&1。
  5. <<左移运算符,将一个数的二进制数左移n位,右补0,溢出的高位舍弃。在被溢出舍弃的高位中不含1的情况下,左移n位相当于乘以2n次方。左移运算比乘法运算快的多。
  6. 右移运算符,将一个数的二进制数右移n位,移到右端的低位被舍弃,对于无符号数,高位补0。右移n位相当于除以2n。右移时需要注意符号位问题,符号位1为负数,0为整数。如果原来符号位为0,右移时高位也补0。如果原来符号位为1,右移时高位补1还是0取决于系统,补0的称为逻辑右移,补1的称为算术右移。

  7. 位运算符可以跟赋值运算符组成复合赋值运算符,跟+=、-=一样。

五、输入输出

  1. f格式符指定数据宽度和小数位数,用%m.nf。m数据宽度,n小数位数。如果小数位数指定为0,不仅不输出小数,而且小数点也不输出。float型数据只能保证6位有效数字,double行数据只能保证15位有效数字。
  2. 输出的数据向左对齐,用%-m.nf。当数据长度不超过m时,数据向左靠,右端补空格。
  3. e格式符,用格式声明%e指定以指数形式输出实数,如果不指定输出数据所占的宽度和数字部分的小数位数,一般自动给数字部分的小数位数为6位,指数部分(e占1列,指数符号1列,指数3列)占5列。可以用%m.ne的形式来指定数据宽度和数字部分的小数位数。e也可以写成E,此时输出的数据中的e就会被替换为E。
  4. i格式符,作用与d格式符相同,按十进制整型数据的实际长度输出。
  5. o格式符,以八进制整数形式输出、 将内存单元中的二进制数按3位一组构成八进制数形式输出,输出的数值不带符号。
  6. x/X格式符,以十六进制数形式输出整数,用X时,则以大写字符输出。
  7. u格式符。用来输出无符号型数据,以十进制整数形式输出。
  8. g/G格式符,用来输出浮点数,系统自动选f格式或者g格式输出,选择其中长度较短的格式,不输出无意义的0。用G时,若以指数形式输出,则指数以大写表示。
  9. putchar(c)输出字符的函数,其中的c可以是字符常量、整型常量、字符变量和整型变量(其值在ASCII代码范围内)。可以用putchar输出转义字符。

六、结构体

  1. 声明结构体类型的一般形式为struct 结构体名{成员表列};(注意分号不能省)
  2. 定义结构体变量时可以对它的某一成员进行初始化。比如struct Sutdent b = {.name=”zhangsan”}。其他未被指定初始化的数值型成员被系统初始化为0,字符型成员被系统初始化为’\0’,指针型被成员为系统初始化为NULL。当然也可以对全部成员进行初始化,这时就不需要加(.成员名=)了,按顺序写下来即可。
  3. 结构体变量的地址主要用作函数参数,传递结构体变量的地址,不能用scanf函数整体读入结构体变量;如scanf("%d,%s,%d,%c",&student);是不行的;scanf("%d",&student.num);是可以的。
  4. 如果p指向一个结构体变量stu,则stu.成员名=(*p).成员名=p->成员名

七、文件操作

  1. 文件标识(文件名)包括三部分:(1)文件路径(2)文件名主干(3)文件后缀

  2. 数据文件可分为ASCII文件和二进制文件。数据在内存中是以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件,可以认为它就是存储在内存的数据的映像,所以也称之为映像文件。如果要求在外存上以ASCII代码形式存储,则需要在存储前进行转换。ASCII码文件又称文本文件,每一个字节放一个字符的ASCII码。

  3. 数据在磁盘上存储,字符一律以ASCII码形式存储,数值型数据既可以用ASCII形式存储,也可以用二进制形式存储。

  4. 用ASCII码形式输出时字节与字符一一对应,一个字节代表一个字符,便于对字符进行逐个处理,也便于输出字符,但一般存储空间较多,而且需要花费转换时间(二进制形式与ASCII码间的转换)。

  5. 用二进制形式输出数值,可以节省外存空间和转换时间,把内存中的存储单元的内容原封不动地输出到磁盘上,此时每一个字节并不一定代表一个字符。如果程序运行过程中有的中间数据需要保存在外部介质上,以便在需要时再输入到内存,一般用二进制文件比较方便。在事务管理中,常有大批数据存放在磁盘上,随时调入计算机进行查询或处理,然后又把修改过的信息在存回磁盘,这时也常用二进制文件。

  6. 缓冲文件系统是指系统自动在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲后才一起送到磁盘;从磁盘向计算机读入数据,装满缓冲区才从缓冲区逐个地将数据送到程序数据区(给程序变量)。

  7. 每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、状态及位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名为FILE。声明FILE结构体类型的信息包含在头文件stdio.h中。打开文件时系统会自动创建一个FILE类型变量,根据文件的情况将信息自动放入。一般通过设置一个指向FILE类型变量的指针变量来引用FILE类型变量。指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头。

  8. fopen函数,调用方式为fopen(文件名,使用文件方式),返回值是一个指向文件的指针变量。如果不能实现打开文件,fopen返回NULL。检查文件打开是否出错:
    if((fp = fopen(“file”,“r”)) == NULL)
    {
    printf(“cannot open this file\n”
    exit(0);
    }

文件使用方式含义如果指定的文件不存在
r只读,为了输入数据,打开一个文本文件出错
w只写,为了输出数据,打开一个文本文件(如果指定的文件存在,则先将该文件删去,再重新创建一个新文件)建立新文件
a向文本文件末尾添加数据出错
rb只读,为了输入数据,打开一个二进制文件出错
wb只写,为了输出数据,打开一个二进制文件(如果指定的文件存在,则先将该文件删去,再重新创建一个新文件)建立新文件
ab向二进制文件尾添加数据出错
r+(rw)读写,为了读写,打开一个文本文件,写数据时只覆盖新数据所需空间的数据出错
w+(wr)读写,为了读写,建立一个文本文件(如果指定的文件存在,则先将该文件删去,再重新创建一个新文件)建立新文件
a+(ar)读写,为了读写,打开一个文本文件出错
rb+读写,为了读写,打开一个二进制文件,写数据时只覆盖新数据所需空间的数据出错
wb+读写,为了读写,建立一个新的二进制文件(如果指定的文件存在,则先将该文件删去,再重新创建一个新文件)
建立新文件
ab+
读写,为了读写,打开一个二进制文件出错
  1. 计算机从ASCII文件读入字符时,遇到回车换行符,系统把它转换为一个换行符,在输出时把换行符转换成为回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到外部文件中的数据形式完全一致,一一对应。
  2. 程序中可以使用3个标准的流文件—标准输入流、标准输出流、标准出错输出流。标准输入流就是从终端的输入,标准输出流是向终端的输出,标准出错输出流是当程序出错将出错信息发送到终端。程序开始运行时系统自动打开这3个标准流文件,系统定义了3个文件指针变量stdin,stdout和stderr,分别指向标准输入流、标准输出流和标准出错输出流,可以通过这3个指针变量对以上3种流进行操作,它们都以终端作为输入输出对象,如果程序中指定要从stdin所指的文件输入数据,就是指从终端键盘输入数据。
  3. fclose函数,调用形式为fclose(文件指针)。向文件写数据时,是先将数据输出到缓冲区,等缓冲区满了之后才正式输出给文件,如果数据未充满缓冲区而程序结束运行,可能会使缓冲区的数据丢失,因此要用fclose函数关闭文件,关闭文件时会将缓冲区的数据输出到磁盘文件然后再撤销文件信息区。fclose返回一个值,关闭成功则返回0,关闭失败则返回EOF(-1)。
  4. fgetc函数,调用形式为fgetc(文件指针),从文件指针指向的文件读入一个字符,成功则返回所读的字符,失败则返回EOF(-1)。C系统将fgetc函数定义为宏名getc,因此fgetc函数和getc函数是等价的。
  5. fputc函数,调用形式为fputc(字符,文件指针),把字符写到文件指针所指向的文件中,成功则返回输出的字符,失败则返回EOF(-1)。C系统将fputc函数定义为宏名putc,因此fputc函数和putc函数是等价的。
  6. 访问磁盘文件时,是逐个字符(字节)进行的,系统用“文件读写位置标记”来表示当前所访问的位置,开始时文件读写位置标记指向第一个字节,每访问一个字节标记就自动后移到下一个字节。用feof(文件指针)函数可以检查文件读写位置标记是否移到了文件的末尾,如果是则返回1,不是则返回0。EOF可以作为文本文件的结束标志,但不能成为二进制文件的结束符,feof函数既可以判断二进制文件,又可以判断文本文件。
    使用rewind函数可以使文件位置标记指向文件开头,调用形式为rewind(文件指针),没有返回值。
    使用fseek函数可以改变文件位置标记,fseek函数一般用于二进制文件,调用形式为fseek(文件类型指针,位移量,起始点),起始点用0(常量SEEK_SET)、1(SEEK_CUR)、2(SEEK_END)代替,0代表文件开始位置,1代表当前位置,2代表文件末尾位置。位移量是指以起始点为基点,镶嵌移动的字节数,位移量是long型数据(在数字的末尾加一个字母L就表示是long型)。对于文本文件,位移量必须是0。
    使用ftell函数可以测定文件位置标记的当前位置,调用形式为ftell(文件指针),如果调用函数出错,如fp指向的文件不存在,则ftell函数返回-1L。
  7. fgets函数,调用形式为char *fgets(char *str,int n,FILE *fp),从文件指针所指向的文件中读入n-1个字符,然后在读入的字符串后面加上一个’\0’,然后把它们放到字符数组中,成功则返回字符指针str,失败则返回NULL。如果在读完n-1个字符前遇到换行符或文件结束符EOF,读入则结束,但将遇到的换行符也作为一个字符读入。
  8. fputs函数,调用形式为int puts(char *str,FILE *fp),将str所指向的字符串输出到文件指针所指向的文件中。字符串末尾的’\0’不输出,如果输出成功,则返回0,失败则返回EOF(-1)。
  9. fgets和fputs这两个函数类似于gets和puts函数,gets和puts函数以终端作为读写对象,而fgets和fputs以文件作为读写对象。
  10. fprintf函数,调用方式为fprintf(文件指针,格式字符串,输出表列),将输出表列的变量按格式字符串的格式输出到文件指针指向的文件,用法跟printf相似。如如fprintf(fp,"%d",a),fprintf(stdout,"%d",a)等价于printf("%d",a)。
  11. fscanf函数,调用方式为fscanf(文件指针,格式字符串,输入表列),从文件指针所指向的文件读入ASCII字符。如fscanf(fp,"%d",&a),fscanf(stdin,"%d",&a)等价于scanf("%d",&a)。
  12. fread函数,调用格式为fread(buffer,size,count,fp),以二进制形式从文件中读取一个数据块,其中buffer是一个地址,用来存放从文件读入的数据的存储区的地址,size是要读写的字节数,count是要读写的数据项数,每个数据项长度为size,fp是文件类型指针,指向要读取的文件,函数执行成功则返回count值。可以用来读取结构体,比如
    for(0;i<40;i++) fread(&stud[i],sizeof(Student),1,fp),每次从fp所指的文件读入一个结构体并放入定义好的结构体数组元素中。
  13. fwrite函数,调用格式为fwrite(buffer,size,count,fp),以二进制形式向文件写入一个数据块,其中buffer是一个地址,指要把从该地址开始的存储区中的数据向文件输出,size是要读写的字节数,count是要读写的数据项数,每个数据项长度为size,fp是文件类型指针,指向要写入的文件,函数执行成功则返回count值。
  14. 在调用各种输入输出函数时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查,调用形式为ferror(fp),返回值为0表示未出错,返回值非0表示出错,对同一个文件每一次调用输入输出函数,都会产生一个新的ferror函数值,因此,在应当在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。
  15. clearer函数,调用形式为clearerr(fp),使文件错误标志(ferror函数值)和文件结束标志置为0。

八、变量

  1. 在一个函数内部定义的变量只能在本函数范围内有效;在复合语句内定义的变量只能在本复合语句范围内有效(这种复合语句也称为分程序或程序块)。这些变量被称为局部变量。
  2. 函数之外定义的变量称为外部变量,外部变量是全局变量(全程变量)。全局变量在程序的全部执行过程中都占用存储单元。当全局变量和局部变量同名,在局部变量的作用范围内,局部变量有效,全局变量被屏蔽即不起作用。
  3. 变量的存储有两种不同的方式:静态存储方式和动态存储方式,静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式,而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。
  4. 内存中供用户使用的存储空间可以分为3个部分(1)程序区(2)静态存储区(3)动态存储区。数据分别存储在静态存储区和动态存储区中。
  5. 全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。
  6. 动态存储区存放(1)函数形参(2)自动变量(3)函数调用时的现场保护和返回地址等。对这些数据来说,在函数调用开始时分配动态存储空间,函数结束时释放这些空间,这种分配和释放是动态的。
  7. 在C语言中,每一个变量和函数都有两个属性:数据类型和数据的存储类别。存储类别指的是数据在内存中存储的方式(如静态存储和动态存储)。C的存储类别包括4种:
    (1)自动的(auto)(2)静态的(static)(3)寄存器的(register)(4)外部的(extern)。根据变量的存储类别,可以知道变量的作用域和生存期。前三个属于局部变量的存储类别,最后一个属于全局变量的存储类别。
  8. 自动变量:函数中的局部变量,如果不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中,函数中的形参和函数中定义的局部变量(包括在复合语句中定义的局部变量),都属于自动变量。在调用函数时,系统会给这些变量分配存储空间,在函数调用结束时就自动释放这些存储空间。自动变量用关键字auto作存储类别的声明,如果不写的话默认为auto。
  9. 静态局部变量。如果希望函数中的局部变量的值在函数调用结束后不消失而继续保留原值,则可以用关键字static进行声明。静态局部变量属于静态存储类别,在静态存储区内分存储单元,在程序整个运行期间都不释放。静态局部变量是在编译时完成赋初值的,而且只赋初值一次,每次调用函数时不再重新赋初值而是保留上次函数调用结束时的值。如果在定义静态局部变量不赋初值的话,则对静态局部变量来说。编译时自动赋初值0(对数值型变量)或空字符’\0’(对字符变量)。虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不用引用它的,因为它是局部变量。
  10. 寄存器变量。一般情况下,变量的值是存放在内存中的,如果某个变量使用频繁,为了提高提高执行效率,允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必到内存中去存取,寄存器的存取熟读远高于内存的存取速度,这种变量叫做寄存器变量,用register声明。寄存器变量属于动态存储方式。
  11. 一般来说,外部变量是在函数的外部定义的全部变量,它的作用域是从变量的定义处开始到程序文件的末尾,在此作用域内,全部变量可以被程序的各个函数引用,但有时候需要扩展外部变量的作用域,包括以下三种情况:
    (1)在文件内扩展外部变量作用域。如果在外部变量定义点之前的函数想要引用这个变量,则可以通过加上关键字extern对该变量作外部变量声明,表示把该外部变量的作用域扩展到此处,然后就可以使用了。比如在某个外部变量A定义点前的函数中要使用该外部变量A,可以在函数的开头加上声明,extern int A;,也可以不写类型,即也可以这样声明,extern A。
    (2)将外部变量作用域扩展到其他文件。如果程序由多个源文件组成,在一个文件中需要用到另一个文件中定义的外部变量,可以在该文件用extern声明其他文件定义的外部变量,这样在编译和连接时系统就会知道声明的变量存在着外部链接,可以从别处找到已经定义的外部变量。在编译时如果遇到extern时,系统会先在本文件中找外部变量的定义,如果找到,就在本文件中扩展作用域,如果找不到,就在连接时从其他文件中找外部变量的定义,如果找到了就将作用域扩展到本文件,如果找不到就报错。
    (3)将外部变量的作用域限制在本文件中。如果希望某些外部变量只能被本文件引用,可以在定义外部变量时加一个static,这种变量称为静态外部变量。注意加不加static全局变量都采取静态存储方式,存放在静态存储区中。
    在这里插入图片描述

九、基础

  1. 在逻辑表达式的求解中,并不是所有的逻辑运算符都被执行,只是在必须执行下一个逻辑运算符才能求出表达式的解时,才执行运算符,比如a++||b++,如果a++的值为0的时候,b++则不会执行。(短路现象)
  2. switch后面括号内的表达式的值类型应该为整型或字符型,case后面表达式不能有变量,多个case标号可以共用一组执行语句,比如
    case ‘A’:
    case ‘B’:
    case ‘C’: printf(“abc”);break;
  3. 数组如果有进行初始化的话,没有赋值的元素的值为0,如果没有初始化的话,数组元素值都为随机数。
  4. 绝对值函数,double fabs(double x);幂函数double pow(double x,double y),计算xy的值;正弦函数double sin(double x);平方根函数double sqrt(double x);
  5. 函数声明可以不加参数名,比如int max(int a,int b);可以写成int max(int,int);。
  6. 宏定义语句不是C语句,不用加分号,不带参数的宏定义一般形式为#define 标识符 字符串,带参数的宏定义的一般形式为#define 宏名(参数表) 字符串。
    (1)宏替换就是直接替换。
    (2)可以用#undef 宏名来终止宏定义的作用域。
    (3)宏定义与定义变量的含义不同,不分配存储空间。
    (4)利用宏定义可以得到多个结果,比如#define CRICLE(R,L,S,V) L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*R
  7. 运算符优先级和结合性

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值