c语言学习笔记(一)

c语言中的五类语句(statement):declaration:声明;assignment赋值;function:函数;control:控制;null:空

c程序结构:

#include               /*引用的链接库

 function declaration;        函数声明

int main(void)                  定义主函数返回值,int说明返回一个整数;以及参数,括号里的void说明main()函数不接受任何参数,main函数也能被其他函数调用。

{                                          主函数体开始

int num   ;                          声明整数变量num,句末的分号表明这一行是C语言的一个语句或指令。声明多个类型相同的变量时,可以用逗号分隔他们,但函数参数变量的声明
                                                必须在每个变量前面加上数据类型再用逗号分隔他们,而函数的局部变量需要在大括号中被声明,同时可以采用逗号分隔。

num=1        ;                       为变量赋值

printf("i am a number %d.\n",num)  ; 调用函数,符号\n表示换行。%d表示输出位置和形式(此位置以十进制输出)。%.2f.表示输出带两位小数的浮点数。%O/%X表示八/十六进制

return 0      ;                          返回语句

}                                          主 函数体结束*/.            /*和*/之间的语句都不会被执行,而//则只对当前行做注释

c中,所有的变量都必须在使用之前被声明,变量名字最长可以有63个字符,但外部标识符只能有31个字符。第一个字符必须是字母或下划线。但下划线打头的名字通常被认为是库和操作系统标识符。另外名字区分大小写。另外一个注意的问题是,不能使用关键字和保留标识符做变量名。注意,程序头的函数声明需要以分号结束,而程序体的函数定义则不需要以分号结束。


声明变量为变量分配内存,赋值则为内存赋值,赋值语句赋值的顺序从右到左。初始化变量就是为变量赋一个初始值。

call调用指函数向下调用比自己低的子程序;而invoke请求,则是指函数向上请求比自己高的子程序,如内核函数。

如何使程序具有良好的可读性:对变量进行注释;使用空行分割一个函数功能的不同部分。

程序状态(program state)指程序执行过程中的给定时间点上所有变量值的集合,是当前计算状态的一个快照。


几种数据类型:INT声明有符号整数类型;long/short/signed/unsigned用来声明字节长度和符号;char用来声明字符和小的整数(1Byte);float/double声明浮点数;_bool声明布尔变量(true/false);_complex/_Imaginary用来声明复数和虚数。确切长度类型(exact width type)uint32_t表示一个32位的无符号整数类型,_t表示type;最小长度类型(minimum width type)int_least8_t表示某个系统没有定义8位数据类型,但它的最小数据类型却是8位;最快最小长度类型(fastest minimum width type)int_fast8_t表示该系统中最小最快的数据类型;最大无符号整数类型uintmax_t。FILE *文件指针类型,用于对文件的读写操作。

浮点数3.16E7表示3.16乘以10的7次方,浮点数与整数在内存中的存储方式不同,浮点数采用小数部分+指数部分的存储方式,如3.16E将被存储为.316 8。因为在任意两个整数之间,哪怕差值为1的两个整数之间,也存在无穷的可能小数值,所以浮点数不能表示该区域内的所有值,而只能以近似值表示这两个整数,例如7.0可能以浮点值6.9999存储,至于几个9,涉及到精度问题。两个相差较大的浮点数进行减运算,会损失更多的精度。float类型数据至少能表示6位有效数字,取值范围在10的-37次方到37方,系统通常采用32位存储一个浮点数,前24位用于存储有效数字(尾数)和符号,后8位用于存储指数和符号.而double类型则和float类型的最小取值范围相同,只不过它的有效数字至少为10位(双精度),系统使用64位来存储double类型,多出的32位,要么全部用于尾数部分来增加精度,要么其中一部分被用于指数部分来增加取值范围,但每种方法都使

double类型至少表示13位有效数字,成为真正的双精度。还有一类特殊的浮点值NaN(not-a-number),例如asin()函数返回反正铉值,而正炫值不能大于1,即asin()函数的输入参数不能大于1,否则函数返回NaN值

_Bool布尔类型。习惯上为布尔变量取一个表明真或假值的名字。

整数溢出:有符号数和无符号数溢出时,都会返回到当前位数的最小值。有符号数溢出返回值跟位数有关,假设是8位,则返回-128d(d表示十进制),而16位,则返回-8192d;无符号溢出:因为最小值是0,所以任何位数都返回0。需要特别注意的是:溢出的时候,系统不会产生异常中断,给你提示,这样,程序中很可能存在漏洞,比如本来你希望使用的变量是一个大于0的无符号数,但它溢出时,就会出现错误。而你完全不知道程序已经脱离了你的控制期望。

浮点数溢出:与整数不同,包括上溢和下溢,上溢表示计算结果是一个大的不能用现有位数表示的数,这表示代表有效数字部分的位数,以及代表指数部分的位数都已经达到最大值,这时再大一点的数值,就会溢出,将返回一个无穷大的值,inf或infinity。下溢表示计算结果是一个小的不能用现有位数表示的数,这时所有位数应该都是二进制值1,这时你对它进行除以2操作,那么将右移一位,从而损失一位有效数字,如果你除以一个很大的值,也就是右移一定的位数将使所有的位都为0。


一个字符T,将被视为变量名;使用单引号‘T’,则被视为一个字符常量,即T的ASCII值;而使用双引号“T”,则被视为一个字符串,实际上这时时由‘T’和‘\0’(结束符)两个字符组成。


定义符号常量的格式:#define 常量的符号名 value;注意常量名(通常全大写)和常量值之间没有=号,这与赋值语句不同。另一种定义符号常量的方法:const int 常量符号名=value,这里使用const关键字将变量转换为常量


C的一些运算符:

      间接运算符(indertection或dereference运算符):*注意与乘号的区别,乘号是水平分割X,而这里是垂直分割X。来取得指针变量指向内存中的值ptr=&bah;val=*ptr;这两句
                                                    语句相当于val=bah;可以看出,使用地址运算符和间接运算符可以完成上述语句的功能,这也只是间接运算符名字的由来。

                                                  int * pi:pi是一个指针,而且* pi是int类型的。对指针加1,等价于对指针的值加上它指向对象的字节大小。

                                        简单地说,&运算符获得变量的地址,*运算符获得指针变量指向变量的数值。当确实需要在一个函数中访问其它函数中的数据时,可以使用指针参数。

                                       间接运算符的优先级高于算术运算符+,因此*(dates+2)表示数组dates的第3个元素的值;而*dates+2则表示第一个元素和值加上2

      算术运算符:+ - * / % ++ --;          增量运算符++:后缀a++,使用a的值后再改变a;前缀++a,使用a的值前改变a。增量运算符比乘除运算拥有更高的优先级,只有圆括号比
                                   他们的优先级高。如x*y++代表(x)*(y++)。下面两种情况下不要对变量使用增量运算符:同一个函数的多个参数都使用该变量;一个表达式里多次使用该变量

      其它运算符:Sizeof,给出其右边操作数的字节大小,操作数可以是带圆括号的类型名如sizeof(float),或一个具体的变量名sizeoffish;          

      指派运算符:(类型名)   类型转换规则:1、在包含两种数据类型的运算中,两个值都被转换为两种类型中较高的级别;2、赋值语句中,计算的最后结果被转换为被赋值
                                                    变量的数据类型;3、在变量被作为函数的参数传递时,将转换为函数原型中的类型。可以使用指派运算符(cast operator)来人工指定类型换,
                                                    其格式为:(类型名)。如x=1.1+1.9和 x=(int)1.1+(int)1.9第一个得到3,而第二个得到2

      关系运算符:< <=  > >= != ==从左到右优先级逐渐降低。 不要混淆于赋值=,通常用于判断表达式中。      小心:浮点数比较只能使用<和>,因为舍入误差可能造成两个逻辑
                             上应该相等的数不等。   如 3×1/3应该是1.0,但如你使用6位小数来表示1/3,那么结果就是0.999999而不等于1。

      逻辑运算符:! && || 从左到右优先级逐渐降低。逻辑运算符优先级低于关系运算符。含有逻辑运算符的表达式一定是从左向右计算,并且只要表达式生效,则停止右边的
                               相关计算。注意与位逻辑运算符的区别,常规逻辑运算符是对整个值而不是每个位进行操作。

                                 ~二进制按位取反;&二进制按位与运算;|二进制按位或运算;^二进制按位异或运算

      赋值运算符:=;    +=这样的运算符表示,赋值语句左边的变量根据右边的表达式运算后被赋予一个新值。优先级低于算术运算符。如x*=3*y+12表示x=x*(3*y+12)

                                         逻辑运算符也有这种形式,如&=   ; val &=0377; 等同于 val= val & 0377;

      地址运算符:&用来指示变量的指针,但数组和结构构类型的首元素变量则不需要这个符号,因为编译程序会自动设置首元素的指针。

    


函数原型是一个函数的声明,它描述了函数的参数类型、个数以及函数的返回值类型。函数原型中声明的一个参数就创建了一个形式参量(formal parameter)的变量。在函数被调用时,被调用函数会接受调用函数传递来的参数值,这个值被称为被调用函数的实际参数(actual argument),然后被调用函数会把实际参数赋给形式参量,并执行函数体。因为被调用函数使用的实际参数值是从调用函数中复制的,所以不管被调用函数对该值如何操作,都不会影响调用函数中的原数值。形式参量是局部变量。而参数是由调用函数提供给被调用函数的值,将赋给被调用函数相对应的参量。调用函数通过使用被调用函数名后跟圆括号和分号的格式来调用。

函数原型中圆括号内的形参声明,可以只声明数据类型并省略变量名。函数声明中的变量名只是虚设的名字,函数定义中的变量名可以不与函数声明中的变量名匹配。函数不接受参数时,应当在圆括号内使用void,当参数个数不能确定时,应当在圆括号内使用...。当函数体中声明的局部变量类型与返回值的类型不同时,将返回与函数头定义返回值类型相同类型的值。函数声明只是将函数类型告诉编译器,其后要用分号,而函数定义则是函数的实际实现代码,其后不跟分号。

函数语句如果只包括一个简单语句(简单语句以一个分号结束),则不需要使用大括号;如果使用复合语句(有一个或多个语句构成,且这些语句本身也可能是复合语句),则使用大括号。


把对函数原型声明和对常量的定义,放在一个头文件中,然后在每个源代码文件中使用#include语句引用该头文件,是一个很好的编程习惯。

函数的典型定义形式:

name(parameter declaration list)

function body

参数声明列表是由逗号隔开的一系列变量的声明,非参数的局部变量只能在有花括号界定的函数体内部声明。


while循环是使用入口条件的条件循环种类,即要进入循环体,必须满足条件判断。其一般格式:

while(expression)

         statement;          //注意语句结束处的分号,上面的while结尾处并没有分号

C中,单独的分号代表空语句(null statement),有时候,可以有意使用带有空语句的while语句,这样所有的程序都在判断语句中进行。如

while(scanf("%d",&num==1)

;           //应该把分号单独一行,以表明这是一个空语句。

就实现了跳过不是空格或数字的输入,因为只有在输入一个数字时判断条件才为真。

while(goats!=0)和while(goats)是相同作用的语句,但后者更常用,因为它们都在goats的值为0时才终止循环,即判断值为假(0).


for(initialization;test-expression;update-expression)        也是一个入口条件循环。

statement;

第一个表达式用来初始化计数器,只在循环体执行前被执行一次;第二个表达式表明判断条件,每次循环都被执行。如果为空语句则判断结果为真,即死循环。;第三个表达式用来更新计数器,每次循环都被执行。可以采用递增或其它任何合法的表达 式。

逗号运算符:可以在一个for循环中使用多个初始化或更新表达式。也可以在for循环以外的语句使用逗号运算符。有两个规则:1、被逗号分开的表达式按照从左到右的次序进行计算。2、整个逗号表达式的值是逗号右边成员的值。如x=(y=3,(z=++y+2)+5),根据上述二规则,x=11。同时要注意与声明语句中的逗号分隔符区别


do while是出口(退出)循环种类, 在判断条件为假时重复循环,而while和for则是判断条件为真时循环。一般格式:

do

     statement;

while(expression);           //注意这里有一个分号,因为do while循环本身是一个语句。


使用一个函数需要三个步骤:

1、使用函数原型声明该函数

2、在程序中通过函数调用来使用该函数

3、定义函数


if语句,被称为分支语句(branching statement)或选择语句(selection statement)

if(expression)

     statement1; 

else                                             /else语句不是必需的。而且如果没有大括号分割,则else与它最近的一个if相匹配

     statement2;

条件表达式的格式:使用条件运算符? :

expression1?expression2:expression3;如果表达式1为真,整个条件表达式的值为表达式2;否则表达式值为表达式3


循环辅助语句:continue和break

continue语句,可以用于3种类型的循环语句中,运行到该语句时,忽略剩余部分的循环体语句部分,直接进入下一次循环;当continue语句处于嵌套结构中时,它只影响包含它的那部分结构的循环体。

break语句,结束包含它的循环体循环,并继续执行程序的其余非循环部分

多重选择:利用switch语句和break

switch判断表达式应该是整数值(包括char类型)。case标签必须是整型常量(包括char类型)或仅包含整数常量的表达式,不能用变量做case标签。所以如果选择是基于浮点型变量或表达式的值,那么只能使用if else结构。

switch(integer expression)

{

   case constant1:                  \注意这里的冒号

              statements;               \可选

               break;                      \break中断switch语句的执行,跳到大括号外面的程序部分继续执行

   case constant1:

              statements;              \可选

              break;

   default:

              statements;             \可选

}


goto语句:由goto和一个标签名称构成。标签的命名遵循变量名命名约定。 如goto part2.因为goto语句可用其它语句代替。所以goto只被用来跳出嵌套结构的循环(break只能跳出一个循环)。


重定向运算符:

<和>,该运算符没有运算顺序。运算符只能用于执行程序对数据文件的重定向,不能用于都是数据文件或者都是执行程序的连接;不能用于一个以上输入和输出文件的定向;操作符左右最好都有空格。progfile2

>>向一个文件的末尾追加数据;

|是管道运算符,可以将一个程序的输出与另一个程序的输入连接起来。


递归的基本原理:

1、虽然每一级递归都使用相同的变量名,但这些变量的值并不相同,即各级递归拥有自己独立的局部变量。

2、每一级递归调用都会有一次返回。当程序流执行到某一级递归的结尾处时,会转移到上一级递归的调用语句后的语句继续执行。

3、递归函数中,位于次级递归调用前的语句,和各级被调用函数具有相同的执行顺序。

4、递归函数中,位于次级递归调用后的语句,和各级被调用函数具有相反的执行顺序。

5、除了为每级递归调用传递参数外,递归函数每次都是从头开始执行递归函数,因此可以把递归看作一个循环语句。

6、递归函数中必须包含可以终止递归调用的语句。

尾递归(tail recurSion或end recursion):次级递归调用语句位于函数结尾即return语句之前。尾递归的作用相当于一条循环语句,这是最简单的递归形式。


数组:数组声明中包括数组元素的数目和元素的类型。变量名后面的【】表明这是一个数组变量,方括号内的数字表示数组元素的数目。如name【40】,为防止数组越界,应在数组声明中使用代表数组元素数目的符号常量,然后在程序中需要使用数组大小的地方都直接饮用该符号常量。

数组的初始化,如果数组元素较少,可以使用花括号括起来的一系列数值来初始化数组,数值之间用逗号分隔。

数组名同时也是该数组首元素的地址,即数组f==&f[0],仅在在函数原型声明或函数定义头时,可以用int f[]代替int *f,如int sum(int f[],int n);所以,使用数组名作为实际参数时,实际上并不是把整个数组传递给被调用函数,而是传递了数组首地址,因此相应的被调用函数的形参是一个指针。

一维数组传递数组参数时,只能使用指针参量。两种传递数组参数的方法:

1、用一个指针参量来确定数组的开始地址;用一个整型参量来确定数组的元素个数;

2、用一个指针参量来确定数组的开始地址;用另一个指针参量来确定数组的结束地址。这里的结束地址的值是将开始地址的值加上数组的大小来获得的,因此实际上是指向数组最后一个元素之后的下一个元素,所以在读取数组元素循环时,判断条件应当是<号

二维数组的参数传递:

例如 double ar[5] [12];

传统的传递多维数组的方法是把数组名(首地址)传递给相应类型的指针参量,指针的声明需要指定各维的大小(最左边的一维不需要),第一维的大小作为第二个参数来传递。

void display(double ar[ ] [12],int rows);

display(sales,5);

或者使用变长数组,这样各维大小都可以作为参数被传递给函数,注意参数传递次序为先从左到右传递各维大小,然后传递整个数组格式。

void display(int rows,int cols,double ar[rows ] [cols]);

display(5,12,sales);


如果调用函数不需要修改原始数组的内容,那么,在调用函数原型声明或定义中,声明形式参量时,需要加上关键字const。

total +=*start ++; 这是一个循环语句,因为*比++优先级高,这样相当于total +=(*start )++,整个语句是将首指针指向的值加给total,然后指针自增1,

可对指针变量进行的基本操作:

1、赋值:把一个地址赋给指针,通常使用数组名或&运算符来进行赋值。

2、求值:使用*运算符,取得指针变量指向地址的值。

3、取指针地址:同样可以使用&运算符,取得存储指针变量的内存地址

4、指针值加或减一个整数:结果是将该整数和指针所指类型的字节数相乘,然后加上初始指针值。

5、增加或减小指针值:可以使指针指向该数组的下一个元素。

6、求指针差值:即求数组中二元素元素值之间的差值,即差值乘上指针所指类型的字节数。

7、比较:对两个相同类型的指针变量进行比较运算

注意:不要对未初始化的指针赋值,如int *pt; *pt=5;将导致5被赋给一个随机内存地址,有可能改写其它程序的数据。当创建一个指针变量时,系统只是分配用来存储该指针变量的内存地址,这时该内存地址中的值在没有被初始化前是随机的。


变长数组:VLA,int quarters=4; int regions=5; double sales[regions][quarters];变长数组的大小并不会变化,变是指创建数组时,各维大小可以用变量来指定。变长数组的大小在其被创建后就是固定的。


字符串是以空字符(\0)结尾的char数组。字符串常量属于静态存储类(static storage)。指如果在一个函数中使用字符串常量,即使多次调用该函数,该字符串在程序的整个运行过程中只存储一份。

指定字符串数组大小时,一定要确保数组元素数比字符串长度至少多1,因为多出来一个元素用于储存空字符。

字符串的参数传递:实际参数传递的都是第一个字符的地址,一般不需要传递字符串的长度,因为有空字符决定长度。

比较字符串时,应使用strcmp( )函数而不是关系运算符;使用strcpy( )或strncpy( )函数来赋值字符串,而不是用赋值运算符。


存储时期(storage duration):变量在内存中保留的时间

1、静态存储时期(static storage duration):所有的具有文件作用域的变量都具有静态存储时期,即程序执行期间一直存在,这里注意与链接类型中的static区别

2、自动存储时期(automatic storage duration):局部变量作用域变量一般具有自动存储时期,即只当程序进入这些变量的函数时,这些变量才会被分配内存。


作用域:函数变量的使用区域。

1、局部变量作用域:只在被定义的函数花括号内区域起作用

2、函数原型作用域:从变量定义处一直到原型声明的末尾

3、文件作用域:从它被定义处到文件结尾处都起作用。通常又被称为全局变量(global variable)

4、函数作用域(function scope):只适用于goto语句使用的标签,意味着goto标签对任何函数都起作用,即任何函数都可以转移到goto标签处执行。


链接:变量可被那些文件链接使用

1、外部链接:具有文件作用域的变量被多文件程序使用,如果变量前面没有使用存储类说明符static

2、内部链接:具有文件作用域的变量被一个文件使用。如果变量前面使用了存储类说明符static

3:空链接:具有局部变量作用域或函数原型作用域的变量具有空链接,即它们都是私有的。


五种存储类:函数和变量都具有存储类。

c语言学习笔记(一)_第1张图片

1、自动(局部变量):auto

2、寄存器:register

3、具有外部链接的静态:extern关键字来引用

4、具有内部链接的静态:static

5、空链接的静态(局部变量):static

typedef,extern,register,auto和static一样,都是存储类说明符,可以加在变量声明的最前面来改变存储类。不可以在一个声明中使用一个以上的存储类说明符。

static用于局部变量声明时,使该变量具有静态存储时期,从而在整个程序运行期间(即使包含该变量的函数没有被调用时),该变量都存在并保留其值;当static用于全局变量的声明时,表明该变量具有内部链接。

静态变量(static variable)并不代表变量的数值不变,而是指变量在内存的位置固定。当一个局部变量被声明为静态时,虽然作用域为局部,但是,却有静态存储时期,也就是当局部函数调用返回时,它们并不消失,此时也许它们并不被分配在栈中。静态变量和全局变量在程序被载入内存时就已经被分配内存并存储,而局部变量只在函数被调用时才被分配栈内存。注意:对函数的参数不能声明为static,因为参数是使用栈的局部变量

把变量的声明放在所有函数的外面,就可以创建一个外部变量。

外部变量的初始化:可以被显式初始化,而且只可以用常量表达式来初始化文件作用域变量。如果不初始化,外部变量将被自动赋值为0.

假设某个变量在程序中被声明了两次,那么第一次是定义声明,将为其在内存留出存储空间;第二次是引用声明。当用extern声明外部变量时,编译器会认为该变量已经被定义过,而不对其进行内存分配,所以不应用extern来进行外部定义,而只用它来引用一个已经存在的外部变量。


内存分配函数:malloc( )和free()

malloc( )函数接受一个所需内存字节数的参数,然后在堆内存中找到一块合适的块,但该函数原型返回值为viod,所以为将该块内存的首地址返回,要采用返回值的强制类型转换。

创建一个数组的3种方法:

1、声明一个数组,用常量表达式来指定数组维数,然后通过数组名来访问数组元素。

2、声明一个变长数组,用变量表达式来指定数组维数,然后用数组名来访问数组元素。

3、声明一个指针,调用malloc( )函数,然后使用该指针来访问数组元素。

后面两种方法可以用来创建一个动态数组,即在程序运行时才被分配内存并可在程序运行时选择数组大小的数组。

free()函数接受malloc( )函数的返回值,释放先前分配的内存。不能用free()函数来释放通过其它方式分配的内存。


类型限定词const:如果变量声明中带有关键字const,则不能通过赋值,增量或减量运算来修改该变量的值。一个位于*左边任意位置的const使指针指向的数据成为常量;而一个位于*右边位置的const使指针自身成为常量。

类型限定词volatile,告诉编译器该变量可被程序和/或其它一些应用改变,例如一个内存地址中保存当前时钟时间,不管程序是否修改它,它总是会被修改。

一个变量值可以同时是const和volatile,如硬件时钟一般不能由程序修改,对程序而言,它是const的,但硬件却不断改变这个值,对硬件而言,它又是volatile的。

所以,volatile const int loc这样的声明是正确的。

类型限定词restrict,只可被用于指针变量,并表明该指针变量是访问某个数据对象的唯一(没有其它变量可以访问该数据),初始(没有其它变量可以改变该数据)方式。restrict告诉编译器指针所指向的数据不会被其他方式修改,因而可以对代码进行优化


exit()函数,无论哪个函数调用exit()函数,都将终止整个程序,这与return语句的逐级返回是不同的。

fopen()函数,有两个参数,第一个参数是要到开文件的文件名,确切地说,是该文件名字符串的首地址。第二个参数用于指定打开文件模式的一个字符串,包括可读/写等模式。程序成功打开一个文件后,该函数返回一个文件指针(file pointer),其它I/O函数通过该指针来指定该文件,这里需要注意的是,fp并不指向实际的文件,而是指向一个文件相关信息的数据包,如使用的缓冲区信息等。

fclose()函数,如果只是刷新了文件缓冲区,将返回EOF,而如果同时关闭了文件,则返回0

fseek ()函数和ftell()函数:

fseek ()函数有3个参数,第一个参数是指向被搜索文件的指针fp;第二个参数是表示相对于文件起点(文件头或尾)的偏移量(offset),这个参数必须是long类型;第三个参数是起点模式,包含三种模式常量:seek_set 代表文件头,seek_end代表文件尾, seek _cur代表当前位置。

fseek (fp,-10L,seek_end);//从文件结尾处退回10个字节。如果一切正常,该函数返回0,否则返回-1


ftell()函数返回值为long类型,返回值为文件的当前位置,因为文件开始处为0,所以该函数实际上返回的是距离文件开始处的字节数(offset)。该函数在文本模式和二进制模式下的工作方式不同,对于文本模式,该函数返回值可以作为fseek ()函数中的第二个参数使用。

fseek (fp,0L,seek_end);         //将当前位置设置为从文件结尾处偏移0字节,也就是设置为文件结尾处

last=ftell(fp);                                      //将文件开头(fp指针处)到当前位置(文件结尾处)的字节数赋给last

for (count = 1L;  count <= last; count++);           

{

fseek (fp,-count,seek_end);        //回退

ch = getc(fp);

}

第一次循环将程序定位于文件结尾前的第一个字符,也就是文件的最后一个字符,然后打印这个字符。


fgetpos() 函数和 fsetpos()函数

这两个函数解决了fseek ()函数和ftell()函数对文件大小只能是long类型的限制,即支持对更大字节的文件操作。这两个函数采用fpos_t(file position type文件定位类型)类型值来代表文件中某字节的位置。fpos_t不是一种基本类型,它需要通过别的类型来定义,它可以使除外数组类型的任意类型,甚至结构类型都可以。

fgetpos() 函数原型:int fgetpos(FILE * restrict stream, fpos_t * restrict pos);被调用时,该函数在pos所指的位置上放置一个fpos_t类型值。


fwrite()函数原型:size_t fwrite(const viod * restrict ptr, size_t size, size_t nmeb, FILE * restrict fp);

size_t类型是sizeof运算符返回的类型,通常是unsigned int 类型。指针ptr是要将要写入的源数据块的地址,size表示要写入的源数据块的大小(以字节为单位)。nmemb表示数据块的数目。fp指定要写入的文件。

char buffer[256];   fwrite(buffer, 256, 1, fp);将256字节大小的数据从buffer指向的内存缓冲区写入fp指向的文件。


建立结构声明格式

struct struct_name{

结构成员列表;

}

结构声明何以放在任何函数的外面,这样它就能在声明后,被本文件中所有函数使用;如果在一个函数内部被声明,那该结构只能在该函数内部使用

定义结构类型的变量

结构变量声明中,struct struct_name的作用就像int或float所起的作用一样。

struct book{

char title [MAXTITL];

chat author[MAXAUTL];

float value;

};

strut book library;

上面的代码段可简写为

struct book{

char title [MAXTITL];

chat author[MAXAUTL];

float value;

} library;

更进一步简写为

struct {

char title [MAXTITL];

chat author[MAXAUTL];

float value;

} library;

初始化结构

对花括号内的各个结构成员进行初始化,注意初始化数值应该与结构成员的声明类型匹配,同时各个成员初始化值之间用逗号而不是用分号分隔。注意,对静态结构的初始化需要使用常量值。

访问结构成员

使用结构成员运算符.实现对结构成员的访问,如library.value

访问结构数组中的结构成员

结构数组:数组元素是结构类型的元素。对某个数组元素结构成员的访问可以这样:在数组元素名后加一个点运算符,然后是结构成员名。如library[0].title对一个结构数组元素的title成员的标识。可以看出,点运算符左边的下标用于结构数组,右边的下标用于结构成员


结构嵌套

一个结构中含有另一个结构。即某个结构成员的类型为结构。

结构指针的声明

struct book * him; \\声明一个book结构类型的指针变量him,还记得int * him么?

结构指针的初始化

him = &library[0];   \\注意,和数组不同,一个结构的名字不代表该结构的地址,必须使用&,这里library是一个结构数组,这样him指向数组的第一个元素

注意,某些系统中,结构的大小有可能大于它内部成员的大小总和,因为系统对数据的对齐存储会导致空白内存。

使用指针访问结构成员

->运算符,如him->value等同于library[0].value

因为&和 *是一对互逆运算符,所以library[0].value==(*him).value        \\必须有圆括号,因为 . 运算符的优先级高于*


向函数传递结构成员

只要结构成员的类型和函数的参数类型一致,那么,就可以将结构成员传递给函数。

函数使用结构地址指针作为参数

函数使用唯一参数,一个指向结构的指针,然后通过->运算符对该结构的成员进行访问。这里要注意,函数要先对要访问的结构类型进行参数声明。

函数使用结构变量作为参数

将函数的参数声明为一个结构类型的变量,这样通过.运算符实现对变量名.结构成员的访问,这里函数使用的是原结构的一个副本,与上面的地址指针参数使用原结构的方式不同。这种方式不能实现对原结构的数据改写,但能更好地保护原结构数据。


结构的相互赋值

与数组不同,结构可以将自己的结构值赋给同样类型的另一结构。如 data1=data2; //将data2中的成员值赋给data1。这同样可用于对结构的初始化。


联合的声明类似于结构,但是联合的各成员共享同一个存储空间,即一个时间点只有一个成员可以存在于联合中。联合允许你创建一个类型不定的值的变量。

枚举类型(enumerated type)声明代表整数常量的符号名称。关键字enum

enum spectrum{red,orange,yellow,green,blue,cilet};

enum spectrum color;

第一个声明建立一个枚举类型spectrum,第二个声明声明一个枚举类型spectrum的变量color。花括号中枚举了color可能有的值。花括号中的枚举列表中的常量可以是默认值,0、1、2、3、............例如上例中,green的值为3,当然也可以为枚举列表中的常量指定某个整数值


typedef和#define的区别

1、typedef给出的符号名称仅限于类型,而不是对值

2、typedef的解释由编译器,而不是由预处理语句执行

3、typedef更灵活

typedef char * string;

没有关键字typedef,上述语句将string声明为一个char类型的指针变量。有了关键字,则使string成为char指针类型的标识符

因此 string name,sign;等同于char * name ,*sign;

c语言学习笔记(一)_第2张图片

1、数组的[ ]和函数的()具有相同的优先级,并且都高于间接运算符*

2、数组的[ ]和函数的()从左到右进行结合


要声明一个指向特定函数类型的指针,首先应该声明一个该函数原型,然后用(* pf)代替函数名称,pf就成为可指向那种类型函数的指针了。

void ToUpper(char *);

void ToLower(char *);

void (* pf)(char *);   \\ 这里(* pf)的圆括号是必须的,去掉的话void * pf(char *);就成了一个返回void *值的pf(char *)函数了

pf=ToUpper;      \\这是正确的,ToUpper是函数ToUpper( )的的地址。

pf=ToLower( ); \\这是错误的,因为ToLower( )不是地址。

函数指针最普遍的用法也是作为另一个函数的参数,如

void show(void(* fp)(char *),char *str);

fp是一个函数指针,这个函数的参量是char *,并将viod类型返回值作为show()函数的参量,而str则是一个数据指针。

带有返回值的函数有两种不同的方式向其他函数传递参数:

1、function1(sqrt);         \\将sqrt()函数的地址作为参数传递,因为function1可能会在自己的函数体中使用sqrt()函数

2、function1(sqrt(4,0));    \\\将sqrt()函数的返回值作为参数传递

不能创建一个元素为函数的数组,但可以创建一个元素是函数指针的数组。


函数名的五种用法:

1、原型声明中的函数名:int comp(int , int );

2、函数调用中的函数名:status=comp(q, r);

3、函数定义中的函数名:int comp(int x, int y);

4、赋值语句中的函数名:pfunct=comp;

5、用作指针参数的函数名:slowsort(arr,n,comp);



掩码用法:

1、取相应有效位:ch &=0XFF;      \\保留ch的最低8位数据,而将其它位设为0。这里不管ch原始数据是多少位,最终将是一个字节的有效数据

2、打开某个特定位:flags |=mask;

3、关闭某个特定位:flags &= ~mask;       \\假设mask除了位1其它位都是0,将所有位取反,然后与目标数进行与运算。结果只有1位数据被设为0其它位保持不变。

4、将某个特定位结果反置:flags ^=mask;

5、查看某个位的值:if ((flag & mask)==mask) \\ 因为flag的其它位会影响比较结果,所以必须先用mask屏蔽其它位,然后再与mask比较。因为位运算符优先级低于==所以要加上圆括号


移位运算符:>> <<,左移和右移分别代表乘以2和除以2操作。移位运算符也可以实现复合运算符,如>>=。例子:int s=16;s>>=3; \\s将被赋值为2



你可能感兴趣的:(c语言学习笔记(一))