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.在栈中分配的空间的生命期与这个变量所在的函数和类相关。
如果是函数中定义的局部变量,那么它的生命期就是函数被调用时,如果函数运行结束,那么这块内存就会被回收。
如果是类中的成员变