第七章:函数、第八章:数组

第七章函数

1、函数的定义:

void fun(...){

...;

}

2、函数的声明,一般写在一个.h文件被包含或者在使用这个函数的前方例如void fun(...);这样的形式。

如果函数的原型没办法见到,编译器便认为这个函数返回一个整型值,这时候如果这个函数返回的是float,那么返回一个整型值再转化为float会出错。

3、对于函数的参数的传递,非常简单,就是按值传递,传指针也是一样,只不过是传递了指向这个地址的指针的拷贝,能够对地址进行一个操作,如果改变指针的指向,原来指针的指向也是不会变的。

最典型的就是swap(int a,int b)与swap(int* a,int* b)这两个函数的例子。

同时提出了,函数的参数如果为数组参数时,不指定它的长度是合法的,因为函数并不为数组元素分配内存。

4、ADT(抽象数据类型)其实就是一个封装好的模块,用户只能调用提供给用户的接口说明(函数)和功能说明,并不知道里面是怎么运作的,也就是所谓的黑箱。

5、递归

递归就是直接或者间接调用自身的函数,但是在每一次调用后都必须越来越接近自己的限制条件也就是目标。我们要相信递归函数会完成它的任务,如果每一个步骤准确,限制条件设置正确,并且每次调用之后会接近限制条件,递归函数总是能够正确完成任务。

但是递归的缺点也非常明显,并不会记录重复的数据,如果使用迭代的话,人为就可以设定变量去利用这些变量,会少了很多计算,例如计算斐波那契数列,使用递归的话,中间值会计算很多次,冗余量很大。

6、可变参数列表,一个函数里面有很多个参数,可能输入的参数没那么多,不过必须按列表顺序访问,可变参数列表通过宏来实现,定义于stdarg.h头文件,声明了一个类型va_list和三个宏——va_start、va_arg和va_end。

第八章:数组

1、数组声明非常简单,一个数组名,一个长度如int a[10].

数组名在表达式中使用的时候,编译器才会为他产生一个指针常量,不是变量,是不可以修改的。

2、下标引用

a[3] 和*(a+3)是代表同一个地址里的数.

例如现在cp = a+3;

cp[-1]是多少,这时候是a[2],但是如果a[-1]是多少,就越界了,此时编译器不知道那个地址里的值是多少。

一个非常奇怪的现象就是2[a]是多少,通常我们一般不会这么用,但是编译器会解释为*(2+a)

3、讨论一下下标和指针的效率。

如果二者都准确的情况下,下标绝不会比指针更有效率,有时候指针会比下标有效率

例如把一个数组的元素置0

int array[10],a;
for(a = 0;a<10;a++)
    array[a]=0;

int array[10],*ap;
for(ap = array;ap<array+10;ap++)
    *ap=0;
对下标表达式求值的时候,编译器在程序中插入指令,取得a的值,并把它与整形的长度也就是4相乘,每次都要,但是在指针的时候,会直接+4,乘法只在编译时执行一次。

对于指针的效率上看,如果2个指针相减与一个整数比较大小来判断是否退出

p1-x<size,会在每次比较时除以4,增加代码,利用一个计数器会更好。

*p1++=*p2++这样的语句,首先p1和p2还是在内存中,每一次进行运算的时候要把它放入寄存器然后进行运算,可以选择register声明,这样p1,p2都在寄存器内,可以省去这个步骤。

当然我们还可以省去计数器,利用2个地址直接去比较。比如上式改为p1<&x[size];

做一个小结就是使用

a、指针比数组下标要有效率

b、把指针放入寄存器,比起在静态内存读更有效率

c、必须在运行时求值比如p1-x<size比起&x[size]代价更高,因为&x[size]只用算一个,并且在编译的时候就可以实现。

4、数组和指针在函数当中。

现在大多数编译器比程序员更懂怎么分配寄存器,所以自己声明register可能会降低效率。

int strlen(char *string)和int strlen(char string[])传递的都是指针。

多维数组的初始化:

声明一个指向整型数组的指针 int (*p)[10];

如果声明了一个数组  Int matrix[3][10],一个Int* p = &matrix[0][0];或者p = matrix[0];

多维数组的函数原型 void func2[int (*mat)[]10];      void func2[int mat[][10]];

但是**mat是不正确的,这是指向整型指针的指针。

三维数组的初始化,最好用多个{}隔开,方便初始化也方便程序员读数组的值。

5、指针数组

int *api[10]。

课后题举例:


这里我们需要输入income,当然很简单的,我们利用if也可以实现,但是如果表很长的话,if会显得程序非常长,逻辑不清晰。如果采用表的形式如下:

static double income_limits[] = {0,23350,56550,117950,256500};
static double basic_tax[] = {0,3502.50,12798.50,31832.50,81710.50};
static double percentage[] = {0.15,0.28,0.31,0.36,0.396};
我们只要知道income在哪个段位的,然后基础的税加上超过的税乘以百分比就能轻松得到。

程序如下:

int main(){
	int flag = 0;
	int income;
	scanf("%d",&income);
	for (int i = 0; income>income_limits[i]; i++)
	{
		flag = i;
	}
	double uper = 1;
	uper = percentage[flag]*(income-income_limits[flag])+basic_tax[flag];
	printf("what should uper is %lf",uper);
}

计算一个矩阵的乘法,有二种想法,第一是传入一个一维数组,那么主要程序就如下所示

for(i=0;i<row;i++)//row是x,col是y
{
    for(j=0;j<col;j++)
    {
        m1p=m1+i*y;
        m2p=m2+col;
        //上面是需要计算多少个数,下面是如何计算
        for(k=0;k<y;k++)
        {
            *r+=*m1p+*m2p;
            m1p+=1;
            m2p+=z;
        }
    }
}
如果传入的是二维数组,那么就更简单了
for (row = 0; row < x; row++)
{
	for (col = 0; col < z; col++)
	{
		for (k= 0; k< y; k++)
		{
			*r += m1[row][k]*m2[k][col];
		}
		r++;
	}
}
直接进行操作就可以。那么现在看看怎么传入这2种类型

第一种:

void matrix_multiply(int *m1,int* m2,int* r,int x,int y,int z);
第二种:

void matrix_multiply(int (*m1)[2],int (*m2)[4],int *r,int x,int y,int z);
我们发现第二种有点不好的是,缺少了可移植性。虽然简化了代码。



接着是一个8皇后问题,根据提示,我们一边一边的去测试这个皇后放入位置是不是准确,那么就需要一个judge函数:

bool judge(int a[][8],int b,int c)
{
	for (int i = 0; i < 8; i++)//检测是不是这一行已经有皇后
	{
		if(a[b][i] == 1)
			return false;
	}
	for (int i = 0; i < 8; i++)//检测该列是不是有皇后
	{
		if(a[i][c] == 1)
			return false;
	}
	for (int i = 0; i < 8; i++)
	{
		for (int j = 0; j < 8; j++)
		{
			if(abs(b-i)==abs(c-j) && a[i][j] == 1)//检测对角线是不是有皇后
				return false;
		}
	}
	return true;
}
接着非常简单就是一个DFS操作:

void DFS(int a[][8],int n)
{
	if(n >= 8)
		ans++;
		//for (int i = 0; i < 8; i++)
		//{
		//	printf("第%d行",i);
		//	for (int j = 0; j < 8; j++)
		//	{
		//		printf("%d  ",a[i][j]);
		//	}
		//	printf("\n");
		//}
	for (int i = 0; i < 8; i++)
	{
		if(judge(a,n,i))
		{
			a[n][i] = 1;
			DFS(a,n+1);
			a[n][i] = 0;
		}
	}
}
每一次深搜进入的n表示行数,对该行的各个点进行测试,可以的话放入皇后,进行下一行测试,如果不行退回来,就需要还原参数,所以由a[n][i]=0;

当n>=8的时候就完成了一次填充,可以选择打印或者选择记录次数。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值