C项目案例实践(0)-语言基础

1.C语言数据类型

所谓数据类型是按被定义变量的性质、表示形式、占据存储空间的多少、构造特点来划分的。C中数据类型可分为基本数据类型、构造数据类型。指针类型、空类型4大类,结构如图:

image

(1)基本数据类型的主要特点:其值不可再分解为其它类型。如整型(int , short int  / short , long int /long) , 实型(单精度:float型,双精度:double型,长双精度:long double型),字符型(char)等。

(2)构造数据类型是根据已定义的一个或多个数据类型用构造的方法类定义。

(3)指针类型是一种特殊的具有重要作用的数据类型。其值用来表示某个变量在内存存储器中的地址。虽然指针变量的取值类型类似于整型值,但这是两个类型完全不同,指针类型在WIN32上的大小是4字节。

(4)空类型是指在定义的时候不确定数据类型,而在使用的时候通过强制转换来确定的数据类型。空类型一般用关键字void修饰。如:void *p;

2.C语言数组

C语言数组大致有一维数组、多维数组、字符数组等。

数组是具有相同数据类型的数据的有序集合,并且使用唯一的名字来标识,其元素可以通过下标来引用。

二维数组中元素排序的顺序: 按行存放,即在内存中先顺序存放第一行的元素,再存放第二行的元素。

字符数组中'字符串结束标志'以字符'\0'代表。有了结束标志'\0'后,字符数组的长度就显得不是那么重要了。因为在程序中,往往依靠检测'\0'的位置来判定字符串是否结束,而不是根据数组的长度来判定字符串长度。因此,在定义存储字符串的字符数组时,总需要将数组的长度定义为字符串中包含的字符个数加1,这里多加的这个1的字节是用来存储附加上的结束标志字符'\0'。

注:'\0' 、 0 和 '0' 的区别: 字符'\0'就是ASCII表中的第一个字符,它的值为整数0,故字符'\0'相当于整数0。但字符'0'是一个数字字符,ASCII码值为48, 即字符'0'相当于整数48.

3形参和实参

一般而言,在调用函数时,主调函数和被调函数之间有数据传递关系。在定义函数时,函数名后面括弧中的变量名称为“形参

,在主调函数中调用一个函数时,函数名后面括弧中的参数为"实参".

void func ( int a ,int b ,float c); 这里的a, b,c是形参

func( 5,6,x+y*5); 这里传入的5,6和x+y*5是实参

在定义函数中指定的形参,在无出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数的形参才分配内存单元。 如a,b在func()未被调用时,是不占内存空间的。 需要注意的是: 实参和形参的类型应相同或值兼容。 在内存中,实参和形参单元是不同的单元。

"值传递"是单向传递,只由实参传递给形参,而不能由形参传递给实参。

"参数值传递"分两种:值传递和传地址(传地址一般是指指针变量作为形参,但是也可以是引用传递, 引用传递是变量前加了&引用符)

4.C语言指针

在C语言中,将地址形象化地称为"指针",意思是通过它能找到以它为地址的内存单元,一个变量的地址称为该变量的"指针"。变量的指针就是变量的地址,存放变量地址的变量是指针变量,用来指向另一个变量。为了表示指针变量和它所指向的变量之间的联系,在程序中用" * "  符号来表示"  指向 "。

定义指针变量的一般形式: 基类型       *指针变量名;

与指针相关的两个运算:

1.&: 取地址运算符

2.*: 指针运算符(或称"间接访问"运算符)

注: 前提:int a ; int * ptr_a;

(1)如果已执行了语句 " ptr_a = &a ", 若有 &*ptr_a, 它的含义是什么呢?"  & " 和  "  * " 运算符的优先级相同,但按自右向左结合,因此,先进行*ptr_a 运算,它就是变量a , 再执行&运算。 因此, &*ptr_a 与 & a相同,即变量a的地址。

(2)*&a的含义又是什么呢? 先进行&a 运算,得到a的地址,再进行*运算。即&a所指向的变量,*&a 和 *ptr_a 的作用是一样的, 它们等价于变量a,即*&a与a等价。

(3)(*ptr_a)++ 相当于 a++. 注意括号是必须的,如果没有括号,就成了*ptr_a++.由于++在ptr_a的右侧,是"后加",因此,先对ptr_a的原值进行*运算,得到a的值,然后使ptr_a的值改变,这样ptr_a就不再指向a了。

 

2.数组与指针

(1)指向数组元素的指针

int a[10]; int *p;

对指针变量赋值(引用): p = &a[0];

表示把a[0]元素的地址赋值给指针变量p,也就是说,p指向了a数组的第0号元素。

C语言规定数组名代表数组的首地址(指针),也就是第0号元素的地址。因此,下面两个语句等价:

p =&a[0];  p = a;

注:数组a 不代表整个数组,上述' p  = a ' 的作用是'把数组的首地址赋值给指针变量p ',而不是'把数组a各元素的值赋给p'.

(2) 通过指针引用数组元素

C语言规定:如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素(而不是p值简单地加1)。例如,数组元素是实型,每个元素占4个字节,则p+1意味着使p的值(地址)加4个字节,以使它指向下一个元素。p+1所代表的地址实际上是p+1*d,d是一个数组元素所占的字节数。

如果p的初值为&a[0], 则:

(1)p+i 和 a+i 就是a[i]的地址,或者说,它们指向a数组的第i个元素。a 代表数组首地址,a+i也是地址。

(2)*(p+i)或*(a+i)就是p+i或a+i所指向的数组元素,即a[i]. 例如:*(p+5) 或 *(a+5)就是a[5].

即*(p+5) = *(a+5) = a[5] . 所以[] 实际上是变址运算符,即a[i]按a+i计算地址,然后找出此地址单元中的地址。

(3)指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价。所以引用一个数组元素,可以用以下两种形式:

1.小标法:如a[i]形式;

2.指针法:如*(a+i)或*(p+i).其中a是数组名,p是指向数组的指针变量,其初值为p = a .

3. 指针与多维数组

这里以二维数组为例,假设已定义 int [3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};, a是数组名。 从二维数组的角度来看,a代表整个二维数组的首地址,也就是第0行的首地址。a+1代表第1行的首地址。二维数组元素的各种地址的表示如下(假设a数组的首地址为2000):

表示形式

含义

地址

a

指向一维数组a[0] , 即第0行首地址

2000

a[0], *(a+0) , *a

0行第0列元素地址

2000

a+1 , &a[1]

第一行首地址

2008

a[1] , *(a+1)

1行第0列元素的地址

2008

a[1]+2 , *(a+1)+ 2 , &a[1][2]

1行第2列元素的地址

2012

*(a[1]+2), *(*(a+1)+2),a[1][2]

1行第2列元素的值

元素值为13

注:a是二维数组名,代表数组首地址,但不能企图用*a来得到a[0][0]的值。 *a相当于*(a+0), 即a[0], 它是0行0列元素的地址。

4字符串与指针

(1)字符串的表示形式在C语言中,可以有两种方法访问一个字符串:

1.用字符数组存放一个字符串,然后输出该字符串;

2.用字符指针指向一个字符串。例如: char *str = "I  am  good ";  相当于定义了一个字符指针变量str时把字符串首地址赋值给了str ,上面的语句等价于:

char *str;

str = " I am good ";  但不等价于: char *str ;  * str = "I am good " ;

(2) 字符指针变量和字符数组,虽然字符数组和字符指针变量都能实现字符串的存储和运算,但它们之间是有区别的,不应混为一谈,主要区别有:

1. 字符数组由若干个元素组成,每个元素中存放一个字符。字符指针变量中存放的地址(字符串的首地址),而不是将字符串放到字符指针变量中。

2.赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。

char str [15];

str = "I am good";

而对于字符指针变量,则可以采用下面的方法赋值:

char *str;

str \ "I am good";

3.对字符指针变量赋初值: char *str = "I am good " ;

等价于  char *str; str \ "I am good ";

而对字符数组的初始化  char str[15] = {"I am good "};

不能等价: char str[15] ; str [] = "I am good " ;

即数组可以在定义时整体赋初值,但不能在赋初值语句中整体赋值。

4.如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个地址值, 即,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。

5.指针变量的值是可以改变的。

6.用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。

函数与指针

一个函数在编译时被分配了一个入口地址。这个入口地址就称为函数的指针。 可以用一个指针变量指向函数,然后通过这个指针变量来调用这个函数。 需要注意:

指向函数的指针变量的一般定义形式: 数据类型  (*指针变量名)();  这里的"数据类型" 是指函数返回值的类型。

(*p)() 表示定义一个指向湖南省的指针变量,它不是固定指向哪一个函数,而只是表示定义了这样一个类型的变量,它专门用来存放函数的入口地址。

在给函数指针变量赋值时,只需要给出函数名而不必给出参数,如 p = max ; 因为是将函数入口地址赋给p , 而不牵涉到实参和形参的结合问题。 如 写成p = max (a, b) ; 则是不对的。

用函数指针变量调用函数时,只需将(*p)代替函数名即可(p 为指针变量名),在(*P)之后的括弧中根据需要写上实参。如语句 c = (*p)(a,b);"  , 表示"调用由p指向的函数, 实参为a, b  得到的函数值赋给c "

对指向函数的指针变量,进行如下p + n , p++ , p--运算是无意义的。

返回指针值的函数

函数的返回类型可以是指针类型,即返回一个地址。这种带回指针值的函数,其定义形式为:类型名 *函数名(参数列表)

例如 :int  *a(int x , int y)

a 是函数名, 调用它以后能得到一个指向整型数据的指针(地址)。 x,y是函数a的形参,为整型。请注意在*a 的两侧没有括号,在a的两侧分别为*运算符和()运算符。而()优先级高于*,因此,a先和()结合。显示这是函数形式。这个函数前面有一个*, 表示此函数是指针型函数(函数值是指针)。最前面的int表示返回的指针值指向整型变量。

指针数组和指向指针的指针

(1)指针数组

一个数组,其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的没一个元素都相当于一个指针变量。一维指针数组的定义形式为:类型名 *数组名[数组长度];  例如:int *p[4]; 由于[]比*优先级高,因此p先与[4]结合,形成p[4]形式,这显然是数组形式,它有4个元素。然后再与p前面的*结合。 *表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可以指向一个整型变量。

它特别适合用于指向若干个字符串,使字符串处理更加方便灵活。

数组指针与指针数组的区别:

1.指针数组 , 例如: int * a[SIZE];

特点: 1. 指针数组是一个数组,共有SIZE个元素,其中每个元素都是指针类型的,并且每个元素的基类型是整数;2.sizeof(a) 的大小为 SIZE*sizeof(int *), 即SIZE个指针变量所占的空间。事实上win32所有类型的指针占用大小为4个字节, 所以 sizeof(int *)  = sizeof(char *)  = 4

当一个程序中需要同时用到多个指针时,最好定义一个指针数组来处理。

2.数组指针(指向数组的指针)

例如: int     (*p)[SIZE];

特点:1.数组指针是一个指针变量,这个指针变量的基类型是一个有SIZE个元素的整型数组;2.sizeof( p ) 的大小为sizeof(int * ) ,即一个指针变量所占的空间。

当需要用指针来指向一个多维数组时,就应该使用数组指针。

指向指针的指针

定义指向指针的指针变量一般形式:类型名 **变量名;

例如  插入  **p ;

p 前面有两个*号, *运算符的结合性为从右向左,因此**p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量的(即指向字符型数据的指针变量)。

C语言的结构体:

struct 结构体名

{

数据类型, 变量名;

}结构体变量名1,结构体变量名2,……,结构体变量名n;

C语言共同体

有时需要使几种不同类型的变量存放到同一段内存单元中。 例如,可把一个整型变量、一个字符型变量、一个实型变量放在同一个地址开始的内存单元中。也就是使用覆盖技术、几个变量相互覆盖。这种使几个不同的变量共占用同一段内存的结构,称为"共用体"类型结构。

(1)共同体定义:

union 共同体名称

{

成员列表;

}共同体变量名列表;

结构体变量所占内存长度是各成员占的内存长度之和(注不是各成员长度的简单相加,还要考虑对齐)。每个成员分别占有其自己的内存单元。共同体变量所占的内存长度等于最长的成员长度。

union T1

{

int x;

char y;

}L;

sizeof(L); // 输出为4

union T2

{

int x ;

char y[6];

}L;

sizeof( L ); //输出为 8 这里要考虑字符对齐, 最小为int型的倍数

union T3

int x;

char y;

double z;

}L;

sizeof(L ) ; // 输出为8

union T4

{

int x ;

char y;

double z;

char g[9];

}L;

sizeof(L ) ; // 输出为16 , 这里也是考虑了对齐的原因,但是这里对齐的参照为double ,也就是最终要是8的倍数, 也就是说每次对齐都是数据成员中最长的为基准,然后是它的多少倍。

共用体类型数据的特点:

1.同一个内存段可以用来存放几种不同类型的成员,但在每一瞬间只能存放其中一种,而不是同时存放几种。即,每次只有一个成员其作用,其它成员不起作用,而不是同时存在和起作用。

2.共同体中的变量中起作用的成员是最后一次存放的成员。

3.共同体变量的地址和它的各成员的地址都是同一地址。

4.不能把共用体变量作为函数参数,也不能使函数返回共用体变量。

C语言文件操作

文件通常是驻留在外部介质(磁盘等)中,在使用时才调入内存中。

C语言把文件看作是一个字符(字节)的序列,即由一个个字符(字节)的数据顺序组成的。根据数据的组织形式,可分为ASCII文件和二进制文件。

1.文件类型指针

指向文件的指针变量即为文件类型指针,定义:

FILE *指针变量标识符;

FILE是系统函数库定义好的一个文件结构,所以必须大写。例如: FILE *fp;  fp表示一个指向FILE结构的指针变量,可以对文件进行各种操作。

2.文件的打开

fopen函数用来打开一个文件, 其调用的一般形式为:

文件指针名 =  fopen (文件名, 使用文件方式);

其中 "文件指针名" 必须是被说明为FILE类型的指针变量; "文件名" 是被打开文件的文件名; "使用文件方式" 是指文件的类型和操作要求。 "文件名"是字符串常量或字符串数组。 例如: FILE *fp;

fp = ("file a " ,"r");

其意义是在当前目录下打开文件file a , 只充许进行 "读" 操作, 并使fp指向该文件。 常用的文件读写方式:

文件使用方式

备注

r

以只读方式打开一个文本文件

w

以只写方式打开一个文本文件

a

向文本文件尾部增加数据,若文件不存在就创建一个文件

rb

以只读方式打开一个二进制文件

wb

以只写方式打开一个二进制文件

ab

向二进制文件尾增加数据,若不存在则创建

r+

以读/写方式打开一个文本文件

w+

以读/写方式建立一个新的文本文件,若文件存在则将文件长度截为0

a+

以读/写增加方式打开一个文本文件,若不存在则创建一个文件

rb+

以读/写方式打开一个二进制文件

wb+

以读/写方式建立一个新的二进制文件,若文件存在则将其长度截为0

ab+

以读/写方式打开一个二进制文件,若文件不存在则创建一个

如果不能打开某一文件,即打开文件失败,则fopen()函数将返回一个空指针NULL。

3.文件关闭

在使用完一个文件之后应该关闭它,以防止它被误用。用fclose函数关闭文件。fclose函数调用的一般形式为:fclose(文件指针)

例如:fclose(fp);

fclose函数也带回一个值,当顺利地执行了关闭操作,则返回值为0; 否则返回EOF (即-1)。 E0F 是在stdio.h 文件中定义的符号常量, 值为-1.

4文件的读/写

文件打开之后,就可以对其进行读/写操作。在C中提供了多种文件读/写函数。

(1) fputc()和fgetc()函数

1.fputc函数的功能是把一个字符写到磁盘文件上去。

调用的一般形式:fputc(ch,fp); 其中, ch 是要输出的字符,它可以是一个字符常量,也可是一个字符变量。fp是文件指针变量。其作用是将字符ch输出到fp所指向的文件中去。fputc函数也带回一个值:如果输出成功,则返回值就是输出的字符,否则返回EOF.

2.fgetc()函数的功能是从指定文件读入一个字符,该文件必须是以读或读/写方式打开,一般调用形式为:ch = fgetc(fp);

其中fp 为文件型指针变量,ch为字符变量。 fgetc函数带回一个字符,赋给ch。 如果在执行fgetc函数读字符时遇到文件结束符,函数返回一个文件结束标识符EOF(即-1)。

(2)fputs()和fgets()函数

1.fputs函数的功能是向指定的文件输出一个字符串。其调用的一般形式:fputs(*str, fp); 其中 str是指向某个字符串的指针,它可以是一个字符串常量,也可是一个字符型指针变量。 fp是文件型指针变量。其作用是将str 所指向的字符串输出到fp所指向的文件中去。 fputs函数也带回一个值:如果输出成功,则返回值就是输出的字符,否则输出EOF

2.fget函数的功能是从指定的文件读入一个字符串。 调用的一般形式: fgets(str, n, fp);

str : 是字符串数组

fp :文件型指针变量

n 为要求得到的字符个数,当只从fp指向的文件输入n-1个字符,然后在最后加一个'\0'字符,因此,得到字符串共有n个字符。如果读完n-1个字符之前遇到换行符或EOF,则读入就结束。fgets函数返回值为str的首地址。

(3)fread()和fwrite()函数

1.fread()函数的功能是读入一个数据块。 fread(buffer , size ,count , fp);

2.fwrite函数的功能是写入一个数据块。fwrite(buffer, size, coutn ,fp);

buffer是一个指针,对fread来说,它是读入数据的存放地址,对fwrite来说,是要输出数据的地址(以上均指起始地址)。size是要读/写的字节数,count是要进行读/写多少个size字节的数据项,fp是文件型指针。

如果fread 或fwrite调用成功,则函数返回值为count的值,即输入/输出数据项的完整个数。

(4)fscanf()和fprintf()函数

fprintf()函数的功能是将内存中的数据转换为对应的字符,并以ASCII码形式输出到文本文件中。

fprintf(文件指针,格式控制字符串,输出列表);

fscanf()函数的功能是根据文本文件中的格式进行输入数据。

fscanf(文件指针,格式控制字符串,输入列表);

5 文件定位

文件中有一个读/写位置指针,指向当前的读/写位置,每次读/写1个(或1组)数据后,系统自动将位置指针移动到下一个读/写位置上。如果想改变系统这种读/写规律,可使用有关文件定位的函数。

(1)rewind函数

rewind函数的功能是使位置指针重新返回文件的开头,此函数无返回值。 rewind(fp);

(2)fseek函数可以实现改变文件的位置指针。 fseek(pf , offset, orgin);

(3)ftell函数 获得文件的当前位置指针的位置。 long t = ftell(fp);

6文件状态

1.feof函数,它可判断文件是否结束。  feof(fp); 若到文件尾部, 函数值为真。

2.ferror函数,可检测在调用各种输入/输出函数时,该函数是否出错。 ferror(fp);

若ferror返回值为0 , 表示未出错。在执行fopen函数时,ferror函数的初始值自动置为0.

3.clearer()函数,可使文件错误标志和文件结束标志置为0. clearer(fp);

C 中把文件当作为一个“流”,按字节进行处理。C文件按编码方式分为二进制文件或ASCII文件。

posted @ 2013-10-21 22:11  AI Algorithms  阅读(571)  评论(0编辑  收藏  举报