c语言学习笔记(一)

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

c程序结构:

#include <stdio>              /*引用的链接库

 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。

浮点数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)

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

    


函数原型是一个函数的声明,它描述了函数的参数类型、个数以及函数的返回值类型。函数原型中声明的一个参数就创建了一个形式参量(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(initialize;test;update)        也是一个入口条件循环。

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只能跳出一个循环)。


重定向运算符:

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

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

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


递归的基本原理:

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、局部变量作用域:只在被定义的函数花括号内区域起作用

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

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

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


链接:

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

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

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

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