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'作为终止标志,遇到它时才会停止打印。
字符串用双引号引起来,单个字符用单引号。