C语言基础的复习.docx

上传人:b****1 文档编号:1990606 上传时间:2023-05-02 格式:DOCX 页数:21 大小:60.30KB
下载 相关 举报
C语言基础的复习.docx_第1页
第1页 / 共21页
C语言基础的复习.docx_第2页
第2页 / 共21页
C语言基础的复习.docx_第3页
第3页 / 共21页
C语言基础的复习.docx_第4页
第4页 / 共21页
C语言基础的复习.docx_第5页
第5页 / 共21页
C语言基础的复习.docx_第6页
第6页 / 共21页
C语言基础的复习.docx_第7页
第7页 / 共21页
C语言基础的复习.docx_第8页
第8页 / 共21页
C语言基础的复习.docx_第9页
第9页 / 共21页
C语言基础的复习.docx_第10页
第10页 / 共21页
C语言基础的复习.docx_第11页
第11页 / 共21页
C语言基础的复习.docx_第12页
第12页 / 共21页
C语言基础的复习.docx_第13页
第13页 / 共21页
C语言基础的复习.docx_第14页
第14页 / 共21页
C语言基础的复习.docx_第15页
第15页 / 共21页
C语言基础的复习.docx_第16页
第16页 / 共21页
C语言基础的复习.docx_第17页
第17页 / 共21页
C语言基础的复习.docx_第18页
第18页 / 共21页
C语言基础的复习.docx_第19页
第19页 / 共21页
C语言基础的复习.docx_第20页
第20页 / 共21页
亲,该文档总共21页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

C语言基础的复习.docx

《C语言基础的复习.docx》由会员分享,可在线阅读,更多相关《C语言基础的复习.docx(21页珍藏版)》请在冰点文库上搜索。

C语言基础的复习.docx

C语言基础的复习

3013/3/11

数组的学习

1,C语言中对于数组的定义方法:

其中数组分为数值数组,字符数组,指针数组,结构数组等。

其中数值数组的定义是指存储一般数值的数组:

下面是一般数值数组的定义方法:

Inta[]={1,2,3,4,5,6,7,8,9};

字符数组的定义方法:

Charst[]={‘h’,’e’,’l’,’l’};charc[10]={‘i’,’,’,’a’,’m’,’h’,’a’,’p’,’p’,’y’};

Charst[]=”hello”;两种定义的方法,可以完成对于数组的定义。

Chara[10]=”Iamhappy”;等价。

指针数组的定义方法:

指针数组是指数组元素全为指针的数组称为指针数组。

一维指针数组的定义形式是:

“类型名*数组标识符[数组长度]”。

例如:

一维指针数组的定义形式:

int*ptr_array[10];

指针数组的每个元素都为指针,因此指针数组的每个值都可指向一个数值,*ptr_array[i];便可指向一个数组的值。

定义如:

char*n[3]={“gain”,”much”,”strong”};形如下图;

他们在用法上有些不同,下面的n[]数组不能够对数组内的每个元素进行更改或赋值,但是a[3]数组可以对数组的值进行更改。

 

结构体数组的定义方法:

首先定义结构体structstudent

{

Intnum

Charname[20];

Floatscore1,score2,sum,average;

}

Voidmain()

{

Structstudentstu[5];

}

以此方法便可完成结构体数组的定义,在进行结构体数组的访问

2,二维数组的学习

二维数组只不过是在一维数组的基础上添加了一个空间量,如:

inta[3][4]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};

其中a是一个数组名。

a数组包含3行,即是三个一位数组;既有三个一位数组组成的。

从二维数组的角度来看,a代表二位数组首元素的地址,a[0],a[1],a[2]都是一位数组名。

数组名代表数组首元素的地址,所以a[0],a[1],a[2],分别是a[0][0],a[1][0],a[2][0]的地址。

要是要访问a[0][1],那么他的地址就是(a[0]+1),其值就是*(a[0]+1),a[0]+0,a[0]+1,a[0]+2,a[0]+3分别是a[0][0],a[0][1],a[0][2],a[0][3]元素的地址。

如果要得到a[0][1]的值,用地址法怎么表示呢?

a[0][1]元素的值就是*(a[0]+1),而a[0]又是和*(a+0)无条件等价的,因此可以用*(*(a+0)+1)表示a[0][1]的值。

依此类推,*(a[i]+j)或*(*(a+i)+j)是a[i][j]的值。

定义指针数组元素的指针变量

Int*p:

是指在指针p指向二位数组的第一元素的首地址,通过增加地址值来遍历整个二维数组,因为二维数组在存储的时候是按照顺序存放的。

指向由M个元素组成的一维数组的指针变量:

如果指针变量p指向a[0]即(p=&a[0]),则p+1不是指向a[0][1],而是指向a[1],p的增值是指一维数组的长度为单位。

(易错知识点)

面试的时候常遇见的问题:

A,有一个一维数组inta1[10],定义一个指针,指向一维数组名。

B,有一个一维数组inta2[10],定义一个指针,指向一维数组名的地址。

C,有一个二维数组inta3[3][4],定义一个指针,指向二维数组名。

D,有一个二维数组inta4[3][4],定义一个指针,指向二维数组名的地址。

那我们先看第一个题目,

a1本身就是连续空间的首地址,那么我们就只需要定义一个地址变量p,让p=a就可以了,既然要用“=”,那么等号两边就需要的类型一样,

毫无疑问,p应该为<数据类型>*p了.现在不确定的就是数据类型了.根据指针的定义,应该有p[0]到p[1]的偏移和a[0]到a[1]的偏移一致,我们很容易看出,a[0]和a[1]的偏移是int类型.那么对于第一个题目的数据类型就应该为int了,第一题的答案为int*p;

第二问题,这个问题的难点在,什么叫数组名的地址,我们都知道C语言中的变量都有地址,但数组名是什么,他是不是也在内存中实实在在的存在哪,回答是否定的(感兴趣的同学,可以试着证明下,a的值和&a的值有什么特点).a是数组空间的一个标号,那对标号取地址,是什么那,我们可以试着把&a和&a1的地址都打印出来,然后看下他们的关系,我们会发现,&a和&a1之间的偏移实际上是10个int的单位.那么我们也可以推出数组名的地址原来标示了整个数组的空间.也就是说&a[0]和&a[1]的偏移实际上是一个int[10]的空间.那么对于<数据类型>*p来说,数据类型就是指int[10]这个空间了.第二题的答案为int(*p)[10];

3,二维数组在面试种常见的问题

A,对于数组进行定义、地址空间的判断及书写,完成地址空间的所有定义。

B,对于数组进行与算法及其他一些数据结构进行综合到一起进行考察。

重点:

二维数组作为参数是的函数调用

附加:

二维数组在函数中的调用:

用指针来实现,

#include

usingnamespacestd;

int  fun(inta[][3],intn)    // 其中二维数组形参必须确定数组的第二维的长度,第一维长度可以不定

//intfun(int(*a)[3],intn)  // 和 int  fun(inta[][3],intn) 功能和用法完全相同

{

   intsum=0;

   for(inti=0;i

   {

       for(intj=0;j<3;j++)

      sum=sum+a[i][j];

   }

   returnsum;

}

voidfun1(inta[2])

{

   intn=sizeof(a);// 因为在实际函数调用时,传递来的实参都是指针,所有这里是对指针求sizeof,

                     //而指针变量是int的整型变量。

在vc++6.0中int类型变量占4字节,所以n肯定是4

   printf("%d\n",n);

}

intfun2(inta[],intn)  //形参中可以不指定一维数组的大小。

{

   intsum=0;

   for(inti=0;i

      sum=sum+a[i];

   returnsum;

}

intmain()

{

   intb[2][3]={{1,2,3},{4,5,6}};

   inta[3]={1,2,3};

   intnum=0;

   intnum2=0;

   inti=2;

   int*p=&i;

       num=fun(&b[0],2);   //ok

   // num=fun(b,2); //ok

   // num=fun(&b[0][0],2); // errorC2664:

'fun':

cannotconvertparameter1from'int*'to'int[][3]'

   // num=fun(&b,2);       // errorC2664:

'fun':

cannotconvertparameter1from'int(*)[2][3]'to'int[][3]'

   // num=fun(b[0],2);     // errorC2664:

'fun':

cannotconvertparameter1from'int[3]'to'int[][3]'

  

       num2=fun2(&a[0],3);//ok

   //   num2=fun2(a,3); //ok

   // printf("%d\n",num2);

   // printf("%d%d%d%d%d%d\n",b[0],&b[0],b[0][0],&b[0][0],b,&b);//只有b[0][0]是数组元素,其他均为地址

   // 结果:

124503212450321124503212450321245032 

   //  结论:

虽然b[0],&b[0],&b[0][0],b,&b的值都相同,但是它们所代表(指向)的类型可能就不一样,其中&b[0]和b所指 向的类型是相同的,b[0]和&b[0][0]的类型可以是认为是相当(注意是相当,不是相同)。

  //  C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针,

 //  当N维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素(这里的首元素是个N-1维数组)首地址的指针,

   // printf("%d\n",num);

   //   printf("%d%d%d\n",sizeof(*p),sizeof(p),sizeof(b)); //sizeof(b)是求数组里所有元素所占的内存总和

   // printf("%d%d%d%d\n",*p,p,sizeof(&b[0]),sizeof(b[0])); 

         //sizeof(&b[0])是对指针,即数组b的第一个元素(当然它是个长度是3的一维数组)的地址作用

       //sizeof(b[0])是对数组b的第一个元素(当然它是个长度是3的一维数组)的作用,相当于求长度是3的一维数组所有

        //元素所占内存和

   // 结果:

21245008412//其中1245008是p的值,即*p的地址 

   //   fun1(a);//ok

   //   fun1(&a[0]);//ok

   //   fun1(&a);// errorC2664:

'fun1':

cannotconvertparameter1from'int(*)[3]'to'int[]'

      

   //   printf("%d\n",sizeof(a));

       return  0;

}

//*****************************************************************************

//二维数组的引用作形参时,注意事项。

看例子

#include

usingnamespacestd;

   int  fun(int (&a)[2][3])        //ok,引用作形参,数组作引用形参,必须指定清楚所有维数

// int fun(int (&a)[][3],intn)    //errorC2265:

'':

referencetoazero-sizedarrayisillegal

//int fun(int &a[2][3])            // errorC2234:

'':

arraysofreferencesareillegal

{

   intsum=0;

   for(inti=0;i<2;i++)

   {

        for(intj=0;j<3;j++)

        sum=sum+a[i][j];

   }

   returnsum;

}

intmain()

{

   intb[2][3]={{1,2,3},{4,5,6}};

   intnum=0;

   num=fun(b);

   printf("%d\n",num);

   return0;

}

 

4,本小结主要完成对于数组的基本定义和数组的基本编程。

 

2013/3/13

常用变量的定义及基本c语言语法的学习

变量的定义:

变量可分为整形,浮点型,字符型等

整形变量一般用int定义;浮点型用float和double定义,其中有单精度型和双精度型;

 

完成了变量的基本定义就可对变量完成引用关系。

一般的定义形式为{类型变量名。

}

有一种变量定义前加上static便可将该变量定义为静态全局变量,其有在全局数据区分配内存,未初始化的静态全局变量被系统自动初始化为0,在定义好之后在整个文件是可见的,但是在文件之外是不可见的。

另外,一般变量的存储有三个地方,分别是全局变量的内存分配空间内、堆区、栈区。

一般的程序把动态分配的动态数据存放在堆区内,函数内部的自动变量存放在栈内,自动变量会随着函数的推出而释放空间,静态变量也存放在全局数据内,全局数据也随着函数的退出而释放内存空间。

例如:

定义staticintn;//定义静态全局变量

而intn;//定义全局变量

程序看上去基本一样,但是定义全局变量便可以实现变量在文件中的共享,但定义静态全局变量使变量不能被其他文件所使用,其他文件同时也可以定义相同的变量名字,是不会发生冲突的。

除此之外:

全局变量和全局静态变量的区别

全局变量是不显示用static修饰的全局变量,作用于整个工程文件,另外全局静态变量只作用于该个函数的作用,静态局部变量之前必须加上static关键字。

在程序运行时,在函数体内定义一个变量,每当程序运行到该变量时变分配栈内存空间,当函数被调用完成时,该变量便被收回,局部变量也就失去了相应的效果。

但采用全局变量时,通过全局变量完成进程间函数的通信给程序的维护带来了极大的不便。

而静态局部变量解决了此个问题,静态局部变量保存在全局数据内,而不是在栈内,每此的值保存到下一次被调用,知道下一次赋予新的变量值。

 

基本数据类型;整形,字符型,单精度浮点型,双精度浮点型。

另外还有长整型,短整型,无符号整形

类型定义typedef原类型名新类型名typedefintcount

 

运算符的优先级

运算符包括(算术运算符,关系运算符,逻辑运算符,位运算符,赋值运算符,条件运算符,指针运算符,逗号运算符,求字节运算符,强制类型转换运算符,分量运算符,线标运算符)

其中逗号运算符的优先级最低。

有关自增自减运算符的说明:

(i++)+(i++)+(i++)其中给i取初值3,则此式的计算结果是9,

原因:

由于系统先扫描运算符的优先级发现其中有括号,也就是说明括号优先级最高先执行括号的式子,得到每个括号都是3,在将3个3相加到一起,最后在计算i++.

(++i)+(++i)+(++i)对于该式子为6+6+6得到18,原因++i的自加是在整个表达式求解开始就进行的,即对表达式进行了三次自加然后进行扫描得到6,最后完成表达式的运算。

 

强制类型转换运算符:

inti=1:

(float)i;此过程完成了对i的强制类型转换。

基本程序的语句结构:

一般程序包括三种语句结构:

顺序结构,选择结构,循环结构(while结构和do—while结构)。

顺序结构一般只需要对于语句进行分号处理即可。

选择结构中注意何时用分号,何时不用分号。

(要是加大括号的话,不需要在末端添加分号),另外switch语句进行学习时,应注意case变量后为冒号,而不是分号,语句执行完成后要在后添加分号,在最后一个条件判断时default不可缺少。

除此之外while和do-while语句的异同点需要说明:

相同点:

都是进行循环判断的

不同点:

do-while是先执行后判断,因此do-while至少要执行一次循环体。

而while是先判断后执行,如果条件不满足,则一次循环体语句也不执行。

for(表达式1;表达式2;表达式3)第一步,计算表达式1的值。

第二步,计算表达式2的值。

若值为真(非0)则执行循环体一次,否则跳出循环。

第三步,计算表达式3的值,转回第二步重复执行

 

变量的不使用第三方变量交换函数的两个参数

主要有三个算法:

1)算术运算;2)指针地址操作;3)位运算。

1)算术运算

简单来说,就是通过普通的+和-运算来实现。

代码如下:

inta,b;

a=10;b=12;

a=b-a;//a=2;b=12

b=b-a;//a=2;b=10

a=b+a;//a=10;b=10

通过以上运算,a和b中的值就进行了交换。

表面上看起来很简单,但是不容易想到,尤其是在习惯标准算法之后。

它的原理是:

把a、b看做数轴上的点,围绕两点间的距离来进行计算。

具体过程:

第一句“a=b-a”求出ab两点的距离,并且将其保存在a中;第二句“b=b-a”求出a到原点的距离(b到原点的距离与ab两点距离之差),并且将其保存在b中;第三句“a=b+a”求出b到原点的距离(a到原点距离与ab两点距离之和),并且将其保存在a中。

完成交换。

此算法与标准算法相比,多了三个计算的过程,但是没有借助临时变量。

(以下称为算术算法)

2)指针地址操作

因为对地址的操作实际上进行的是整数运算,比如:

两个地址相减得到一个整数,表示两个变量在内存中的储存位置隔了多少个字节;地址和一个整数相加即“a+10”表示以a为基地址的在a后10个a类数据单元的地址。

所以理论上可以通过和算术算法类似的运算来完成地址的交换,从而达到交换变量的目的。

即:

int*a,*b;//假设

*a=newint(10);

*b=newint(20);//&a=0x00001000h,&b=0x00001200h

a=(int*)(b-a);//&a=0x00000200h,&b=0x00001200h

b=(int*)(b-a);//&a=0x00000200h,&b=0x00001000h

a=(int*)(b+int(a));//&a=0x00001200h,&b=0x00001000h

通过以上运算a、b的地址真的已经完成了交换,且a指向了原先b指向的值,b指向原先a指向的值了吗?

上面的代码可以通过编译,但是执行结果却令人匪夷所思!

原因何在?

首先必须了解,操作系统把内存分为几个区域:

系统代码/数据区、应用程序代码/数据区、堆栈区、全局数据区等等。

在编译源程序时,常量、全局变量等都放入全局数据区,局部变量、动态变量则放入堆栈区。

这样当算法执行到“a=(int*)(b-a)”时,a的值并不是0x00000200h,而是要加上变量a所在内存区的基地址,实际的结果是:

0x008f0200h,其中0x008f即为基地址,0200即为a在该内存区的位移。

它是由编译器自动添加的。

因此导致以后的地址计算均不正确,使得a,b指向所在区的其他内存单元。

再次,地址运算不能出现负数,即当a的地址大于b的地址时,b-a<0,系统自动采用补码的形式表示负的位移,由此会产生错误,导致与前面同样的结果。

有办法解决吗?

当然!

以下是改进的算法:

if(a

{

a=(int*)(b-a);

b=(int*)(b-(int(a)&0x0000ffff));

a=(int*)(b+(int(a)&0x0000ffff));

}

else

{

b=(int*)(a-b);

a=(int*)(a-(int(b)&0x0000ffff));

b=(int*)(a+(int(b)&0x0000ffff));

}

算法做的最大改进就是采用位运算中的与运算“int(a)&0x0000ffff”,因为地址中高16位为段地址,后16位为位移地址,将它和0x0000ffff进行与运算后,段地址被屏蔽,只保留位移地址。

这样就原始算法吻合,从而得到正确的结果。

此算法同样没有使用第三变量就完成了值的交换,与算术算法比较它显得不好理解,但是它有它的优点即在交换很大的数据类型时,它的执行速度比算术算法快。

因为它交换的时地址,而变量值在内存中是没有移动过的。

(以下称为地址算法)

3)位运算

通过异或运算也能实现变量的交换,这也许是最为神奇的,请看以下代码:

inta=10,b=12;//a=1010^b=1100;

a=a^b;//a=0110^b=1100;

b=a^b;//a=0110^b=1010;

a=a^b;//a=1100=12;b=1010;

此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。

这就意味着任意一个数与任意一个给定的值连续异或两次,值不变。

即:

a^b^b=a。

将a=a^b代入b=a^b则得b=a^b^b=a;同理可以得到a=b^a^a=b;轻松完成交换。

以上三个算法均实现了不借助其他变量来完成两个变量值的交换,相比较而言算术算法和位算法计算量相当,地址算法中计算较复杂,却可以很轻松的实现大类型(比如自定义的类或结构)的交换,而前两种只能进行整形数据的交换(理论上重载“^”运算符,也可以实现任意结构的交换)。

介绍这三种算法并不是要应用到实践当中,而是为了探讨技术,展示程序设计的魅力。

从中可以看出,数学中的小技巧对程序设计而言具有相当的影响力,运用得当会有意想不到的神奇效果。

而从实际的软件开发看,标准算法无疑是最好的,能够解决任意类型的交换问题。

内存分配的几种方式:

一般来说,内存的分配方式有三种:

1.从静态存储区域分配。

内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。

例如全局变量,static变量。

2.在栈上创建。

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。

栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

3.从堆上分配,亦称动态内存分配。

程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。

动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

以上三种分配方式,我们要注意内存生命期的问题:

1.静态分配的区域的生命期是整个软件运行期,就是说从软件运行开始到软件终止退出。

只有软件终止运行后,这块内存才会被系统回收

2.在栈中分配的空间的生命期与这个变量所在的函数和类相关。

如果是函数中定义的局部变量,那么它的生命期就是函数被调用时,如果函数运行结束,那么这块内存就会被回收。

如果是类中的成员变

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2