C语言第八天

欢迎使用C语言

C共同体

共同体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。可以定义一个带有多成员的共同体,但是任何时候只能有一个成员带有值。共同体提供了一种使用相同的内存位置的有效方式。
为了定义共用体,必须时使用union语句,方式与定义结构类似。union语句定义了一个新的数据类型,带有多个成员。
union语句的格式如下:

union [union tag]{
member definition;
member definition;
...
member definition;
}[one or more union variables];

union tag是可选的,每个member definition是标准的变量定义,比如int i;或者float f;或者其他有效的变量定义。在共用体定义的末尾,最后一个分号之前,可以指定一个或多个共用体变量,这是可选的。可以根据需要在一个共用体内使用任何内置的或者用户自定义的数据类型。
共用体占用的内存应足够存储共用体中最大的成员。

访问共用体成员

为了访问共用体的成员,使用成员访问运算符(.)。成员访问运算符是共用体变量名称和要访问的共用体成员之间的一个句号。可以使用union关键字来定义共用体类型的变量。

结构体与共用体

结构体变量所占内存长度是其中最长字段大小的整数倍;实际上就是类型最大的倍数刚好大于表面上最大的长度。共用体变量所占点的内存长度等于最长的成员变量的长度。共用体作用就是节省内存。

补充

位:"位(bit)"是电子计算机中最小的数据单位。每一位的状态只能是0或1。
字节:8个二进制位构成1个“字节(Byte)”,它是存储空间的基本计量单位。1个字节可以储存1个引文字母或者半个汉子,1个汉字占据2个字节的存储空间。
字:“字”由若干个字节构成,字多的位数交租字长,不同档次的机器有不同的字长。字是极速三年级进行数据处理和运算的单位。
一般的计算机都已经到了64位机,也就是说一个基本单位就是64位,也就是8字节了。结构体,共用体,位域的定义中,按顺序分配内存,下一个字段所占大小如果超出了上一个字段占的内存单元剩余部分,就会重新申请下一个内存单元,而上一个多出部分将空着。

#include<stdio.h>
#include<string.h>
struct{
    
    
unsigned int widthValidated;
unsigned int heightValidated;
}status1;
struct{
    
    
	unsigned int widthValidated:1;
	unsigned int heightValidated:1;
}status2;//只有2位被用来存储值
void main(){
    
    
	printf("Memory size occupied by status1:%d\n",sizeof(status1));
	printf("Memory size occupied by status2:%d\n",sizeof(status2));
}
//自己找彩蛋

位域声明

在结构内声明位域的形式

struct{
type[member_name]:width;
} 
元素 描述
type 整数类型,决定了如何解释位域的值。类型可以是整型、有符号整型、无符号整型
member_name 位域的名称
width 位域中位的数量。宽度必须小于或等于指定类型的位宽度。

带有预定义宽度的变量被称为位域。位域可以存储多于1位的数。

C typedef

C语言提供了typedef关键字,可以使用它来为类型取一个新的名字;也可以定义一个新的数据类型名字,然后使用这个新的数据类型来直接定义数据变量。
#define仅限于为类型定义符号名称,#define不仅可以类型定义别名,也能为数值定义别名,也能为数值定义别名。
typedef是由编译器执行解释的,#define语句是由预编译器进行处理的。

typedef与#define的区别

(1)#define可以使用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做。
(2)在连续定义几个变量的时候,typedef能够保证定义的所有变量均为同一类型,而#define则无法保证。
1.typedef给出的符号名称仅限于对类型,而不是对值。
2.typedef的解释由编译器,而不是预编译器执行。并不是简单点的文本替换。
3.typedef虽然范围有限,但是在其受限范围内typedef比#define灵活。
typedef是为复杂的声明定义一个新的简单的别名。
typedef在语法上是一个存储类的关键字,虽然它并不真正影响对象的存储特性。

C 输入&输出

输入意味着要向程序填充一些数据。数据可以是以文件的形式或从命令行中进行。C语言提供了一系列内置的函数来读取给定的输入,并根据需要填充到程序中。
输出意味着要在屏幕上、打印机上或任意文件中显示一些数据。C语言提供一系列内置的函数来输出数据到计算机屏幕上和保存数据到文本文件或二进制文件中。

标准文件

C语言把所有的设备都当作文件。设备(显示器)被处理的方式与文件相同。三个文件会在程序执行时自动打开,以便访问键盘和屏幕。

标准文件 文件指针 设备
标准输入 stdin 键盘
标准输出 stdout 屏幕
标准错误 stderr 屏幕

文件指针是访问文件的方式,C语言中的I/O(输入/输出)通常使用printf()和scanf()两个函数。
scanf()函数用于从标准输入(键盘)读取并格式化,printf()函数发送格式化输出到标准输出(屏幕)。

注意事项

1.所有的C语言程序都需要包含main()函数。代码从main()函数开始执行。
2.printf()用于格式化输出到屏幕。printf()函数在"stdio.h"头文件中声明。
3.stdio.h是一个头文件(标准输入输出头文件),#include是一个预处理命令,用来引入头文件。当编译器遇到printf()函数时,如果没有找到stdio.h头文件,会发生编译出错。
4.return 0;语句用于表示退出程序。

getchar()和putchar()函数

int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一时间内只会读取一个单一的字符。可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一时间内只会输出一个单一的字符。可以在循环内使用这个方法,以便在屏幕上输出多个字符。

gets()和puts()函数

char *gets(char *s)函数从stdin读取一行到s所指向的缓冲区,直到一个终止符或EOF。
int puts(const char *s)函数把字符串s和一个尾随的换行符写入到stdout。

gets()和fgets()函数

gets函数原型:char *gets(char *buffer);//读取字符到数组:gets(str);str为数组名。
gets函数功能:从键盘上输入字符,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。读取的换行符被转换为null值,作为字符数组的最后一个字符,来结束字符串。
gets函数由于没有指定输入字符大小,所以会无限读取,一旦输入的字符大于数组长度,就会发生内存越界,从而造成程序崩溃或其他数据的错误。
fgets()函数原型:char *fgets(char *s,int n,FILE *stream); fgets(str,sizeof(str),stdin);其中str为数组首地址,sizeof(str)为数组大小,stdin表示从键盘输入数据。
fgets函数功能:从文件指针stream中读取字符,存放以s为起始地址的空间里,直到读完N-1个字符,或者读完一行。

scanf()和printf()函数

*int scanf(const char format,…) 函数从标准输入流stdin读取输入,并根据提供的format来浏览输入。
*int printf(const char format,…) 函数把输出写入到标准输出流stdout,并根据提供的格式产生输出。
format 可以是一个简单的常量字符串,但是可以分别制定%s、%d、%c、%f等来输出或读取字符串、整数、字符或浮点数。

C文件读写

一个文件,无论它是文本文件还是二进制文件,都是代表一系列的字节。C语言不仅提供了访问顶层的函数,也提供了底层(OS)调用来处理存储设备上的文件。

打开文件

使用fopen()函数来创建一个新的文件或者打开一个已有文件,这个调用会初始化类型FILE的一个对象,类型FILE包含了所有用来控制流的必要的信息。
FILE *fopen(const char *filename,const char *mode);
filename是字符串,用来命名文件

模式 描述
r 打开一个已有的文本文件,允许读取文件
w 打开文本文件,允许写入文件。如果文件不存在;则会创建一个新文件从文件的开头写入内容。如果文件存在,则会被截断为零长度,重新写入
a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。会在已有的文件内容追加内容。
r+ 打开一个文本文件,允许读写文件。
w+ 打开一个文本文件,允许读写文件。如果文件已存在;则文件会被截断为零长度,如果文件不存在,则会创建一个新文件
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入只能是追加模式。

关闭文件

使用fclose()函数。函数的原型:
int fclose(FILE *fp);
如果成功关闭文件,fclose()函数返回零,如果关闭文件时发生错误,函数返回EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF是一个定义在头文件stdio.h中的常量。
C标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。

写入文件

int fputs(int c,FILE *fp);
函数fputs()把函数c的字符值写入到fp所指向的输出流中,如果写入成功,它会返回写入的字符,如果发生错误,则会返回EOF。可以使用下面的函数来把一个以NULL结尾的字符串写入到流中:
int fputs(const char *s,FILE *fp);
函数fputs把字符串s写入到fp所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回EOF。可以使用int fprintf(FILE *fp,const char *format,…)函数来写把一个字符串写入到文件中。

读取文件

从文件读取单个字符的最简单的函数:
int fgetc(FILE *fp);
fgetc()函数从fp所指向的输入文件中读取一个字符。返回值时读取的字符,如果发生错误则返回EOF。
char *fgets(char *buf,int n,FILE *fp);函数允许从流中读取一个字符串。
函数fgets()从fp所指向的输入流中读取n-1个字符。它会把读取的字符串复制到缓冲区buf,并在最后追加一个null字符来终止字符串。
如果这个函数在读取最后一个字符之前就遇到一个换行符’\n’或文件的末尾EOF,则只会返回读取到的字符,包括换行符。可以使用int fscanf(FILE *fp,const char *format,…)函数来从文件中读取字符串,但是在遇到第一个空格字符时,它会停止读取。

fseek

fseek可以移动文件指针到指定位置读;或插入写:
int fseek(FILE *stream,long offset,int whence);
fseek设置当前读写点到offset处,whence可以是SEEK_SET,SEEK_CUR,SEEK_END这写值决定是从文件头、当前点和文件尾计算偏移量offset。
可以定义一个文件指针FILE *fp;当打开一个文件时,文件指针指向开头,要指到多少个字节,只要控制偏移量。例如,相对当前位置往后移动一个字节:fseek(fp,1,SEEK_CUR);中间的值就是偏移量。如果要往前移动一个字节,直接改为负值:fseek(fp,-1,SEEK_CUR)。

C预处理器

C预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。C预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预编译。我们将C预处理器简写为CPP。
所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理指令应从第一列开始。

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 取消已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#else #if的替代方案
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if…#else条件编译块
#error 如果给定条件为真,则编译下面代码
#if 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

预定义宏

在编程中可以使用这些宏,但是不能直接修改这些预定义的宏。

描述
_ DATE _ 当前日期,一个以"MMMDDYYYY"格式表示的字符常量
_ TIME _ 当前时间,一个以"HH:MM:SS"格式表示的字符常量。
_ FILE _ 这会包含当前文件名,一个字符串常量
_ LINE _ 这会包含当前行号,一个十进制常量
_ STDC _ 当编译器以ANSI标准编译时,则定义为1

预处理器运算符

C预处理器提供了下列的运算符来创建宏:
(1)宏延续运算符():一个宏通常写在一个单行。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符()。
(2)字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数参数列表
(3)标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。
(4)defined()运算符
预处理defined运算符是用在常量表达式中的,用来确定一个标识是否已经使用#define定义过。如果指定的标识符已定义,则值为真(非零)。如果指定的标识符未定义,则值为假(零)。

C头文件

头文件是扩展名为.h的文件,包含了C函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:自己编写的头文件和编译器自带的头文件。
在程序中要使用头文件,需要使用C预处理指令#include来引用它。stdio.h头文件,它是编译器自带的头文件。引用头文件相当于复制头文件的内容,但是不能直接在源文件中复制头文件的内容,因为这么做很容易出错,特别在程序是由多个源文件组成的。
建议把所有的常量、宏、系统全局变量和函数原型写在头文件中,在需要时,随时引用这些头文件。

引用C头文件

#include < file >这种形式用于引用编译器的类库路径里面的头文件,一般是系统头文件。它在系统目录的标准列表中搜索名为file的文件。在编译代码时,可以通过-l选项把目录前置在该列表前。
#include "file"这种形式用于引用是程序目录的相对路径中的头文件,如果在程序目录没有找到的头文件,则到编译器的类库路径的目录下找该头文件,一般是用户头文件。它在包含当前文件的目录中搜索名为file的文件。在编译源代码,同样可以通过-l选项把目录前置在该列表前。

引用C头文件的操作

1.#include指令会指示C预处理浏览指定的文件作为输入。
2.预处理器的输出包含了已经生成的输出,被引用文件生成的输出以及#include指令之后的文本输出。

只引用一次引用C头文件

如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将会产生错误。为了防止这种情况,标准的做法是把文件的整个内容放在条件编译语句。
#pragma once
或者
#ifndef HEADER_FILE
#define HEADER_FILE

#endif
这种结构就是通常所说的包装器#ifndef。当再次引用头文件时,条件为假,因为HEADER_FILE已定义。预处理器会跳过文件的整个内容,编译器会忽略它。

有条件引用

预处理器使用宏来定义头文件的名称,就是所谓的有条件引用。它不是用头文件的名称作为#include的直接参数,只需使用宏名称代替:
#define SYSTEM_H “system_1.h”

#include SYSTEM_H

C强制类型转换

强制类型转换是把变量从一种类型转换为另一种数据类型。
强制类型转换运算符的优先级大于乘除法。
类型转换可以是隐式的,由编译器自动执行。也可以是显式的,通过使用强制类型转换运算符来指定。
在编程时,有需要类型转换时,都用上强制类型转换运算符。

char,short
int
unsigned int
long
unsigned long
long long
unsigned long long
float
double

C错误处理

C语言你不提供对错误处理的直接支持,但它以返回值的形式允许访问底层数据。在发生错误时,大多数的C或UNIX函数调用返回1或NULL,同时会设置一个错误代码 errno,该错误代码是全局变量,表示在函数调用器件发生了错误。可以在errno.h头文件中找到所有的出错的代码。
可以通过检测返回值,然后根据返回值决定采取哪种适当的动作。开发人员应该在程序初始化时,把errno设置为0,0值表示是程序中没有错误。

perror()和strerror()

perror()函数显示传给它的字符串,后跟一个冒号、一个空格和当前errno值的文本表示形式。
strerror()函数,返回一个指针,指针指向当前errno值的文本表示形式。
使用stderr文件流来输出所有的错误。

被零出的错误

在进行除法运算时,如果不检查除数是否为零,则会导致一个运行时错误。

程序退出状态

通常情况下,程序成功执行完一个操作正常退出的时候回带有值EXIT_SUCCESS。在这里,EXIT_SUCESS是宏,它被定义为0。
如果程序中存在一种错误情况,当退出程序时,会带有状态值EXIT_FAILURE,被定义为-1。

猜你喜欢

转载自blog.csdn.net/qq_31932681/article/details/95229392