C语言第四讲

1、首先,我们来看一下关于循环语句的问题,在C或C++循环语句中,for语句的使用频率最高,while语句其次,do语句很少用。

在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。以下面两个代码为例:

A:

int row;
	for (row = 0; row < 100; row++)
	{
		for (col = 0; col < 5; col++)
		{
			sum = sum + a[row][col];
		}
	}
B:

for (col = 0; col < 5; col++)
	{
		for (row = 0; row < 100; row++)
		{
			sum = sum + a[row][col];
		}
	}
B的效率高于A。

如果循环体内存在逻辑判断,并且循环次数很大,应该将逻辑判断移动到循环体外面,以下面两个代码为例:

C:

for (i = 0; i < N; ++i)
	{
		if (condition)
		{
			DoSomething();
		}
		else
		{
			DoSomething();
		}
	}

D:

if (condition)
		{
			for(i=0;i<N;++i)
			DoSomething();
		}
		else
		{
			for(i=0;i<N;++i)
			DoSomething();
		}

C效率低但是程序简洁,D效率高程序不简洁。

C的程序比D的多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。

如果N非常大,最好采用D的写法,可以提高效率。

如果N非常小,则两者效率差别并不明显,采用C的写法比较好,因为程序更加简洁。


再来看看for和do while语句的区别:


两者的结构在这里不再赘述,直接看他们有什么不同,for和do while语句都是判断条件是否满足的语句,不满足时跳出循环。

但是do while语句是先做一次循环再判断,因此至少执行一次,而for语句是先判断再循环。


让我们再来看看break和continue的作用简介:


break出现在循环中时直接跳出循环,而continue出现在循环中时,结束本趟循环,进入下一趟循环。


2、简单介绍assert(断言)的用法:


断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。如果assert的参数为假,那么程序就会中止,在使用assert时要引入头文件#include<assert.h>下面用一个例子来介绍assert的使用:


例如:

构造一个单链表时需要判断该链表是否为空,假设该单链表的头结点为phead,返回值类型为指针类型,那么在继续函数体功能之前应该先判断phead是否为空,一般用if语句判断,这里把if语句和assert语句都给出来,读者可自行比较选择。


A:

if (phead == NULL)
	{
		return NULL;
		}

B:

assert(phead!=NULL);


在函数的入口处,使用断言检查参数的有效性(合法性)。


3、简介引用和指针的比较:


一些规则如下:


A:引用被创建的时候必须初始化,指针则可以在任何时候被初始化。


B:引用必须与合法的存储单元关联,不能有NULL引用,而指针则可以是NULL.


C:一旦引用被初始化就不能改变引用的关系,而指针可以随时改变所指的对象。


引用的主要功能是传递函数的参数和返回值,在C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。


举几个例子:


A:

void Swap1(int a,int b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
B:

void Swap2(int *p,int *q)
{
	int *tmp = NULL;
	*tmp = *p;
	*p = *q;
	*q = *tmp;
}
C:

void Swap3(int *p,int *q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;
}
D:

void Swap4(int *p,int *q)
{
	int *tmp = p;
	p = q;
	q = tmp;
}

以上四个函数究竟哪一个能实现主函数中a和b的调换呢?

int main()
{
	int a = 10;
	int b = 20;
	Swap1(a,b);
	printf("%d,%d\n",a,b);
	return 0;
}
答案是函数C,

函数A只进行了值传递,函数体内的a,b是外部变量a,b的一份拷贝,改变的a,b的值不会影响主函数中a,b的值,因此函数A并没有实现对主函数a,b的调换。


函数B中存在野指针,程序崩溃,野指针指向的是一个随机的地址。


函数C是正确的,由于函数体的p,q是指向外部变量a,b的指针,改变该指针的内容将导致a,b的值改变,因此成功的在主函数中将a,b的值调换了。


函数D与函数A的性质一样,都是只进行了值传递,并没有影响主函数中a,b的值,因此没有发生调换。


4、指针和数组的对比:


数组名对应着一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变,指针可以随时指向任意类型的内存块,他的特征是可变,所以经常用指针来操作动态内存。指针比数组灵活但也更危险。


不能对数组名进行直接复制或比较。


数组名是一个数组首元素的首地址。


sizeof(arr)/sizeof(arr[0])代表什么?

arr是整个数组,arr[0]是数组中第一个元素,
用整个数组所占空间除一个元素所占得空间,结果就是数组元素的个数。


如何遍历数组?


对于一个a[n]数组,用for循环来实现,定义i=0为数组下标,在循环体中输出数组中的元素,当i<n时停止循环,即为遍历数组。


对于一个数组arr,&arr和arr的含义:


&arr指的是数组arr的地址,而arr指的是数组首元素的首地址即&arr[0]


5、野指针:


野指针不是NULL指针,是指向“垃圾”内存的指针,野指针很危险,if语句的判断对他没有用。野指针的形成原因有:


A:指针变量没有被初始化,任何指针变量在刚被创建的时候不会自动成为NULL指针,其值是随机的,所以在创建指针变更量的时候一定要对他进行初始化,让他指向一个合法的内存。


B:指针被free或delete之后,没有置为NULL,让人误以为p是个合法的指针。


C:指针操作超越了变量的作用范围。


6、字符数组:


字符数组的打印需要注意:用%s打印的时候要有'\0'作为终止标志,遇到它时才会停止打印。


字符串用双引号引起来,单个字符用单引号。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值