在《编程范式》13中老师给出了几个有趣的例子,这里简单实现一下做个备忘。
缓冲区溢出VS隐形无限递归
下面是两个由数组溢出导致的宕机,但是出现的原因是不同的。
#include <iostream>
#include <cstdio>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
/*
g()一种缓冲区溢出
程序在 for循环部分会陷入死循环
*/
void g(){
int j = 0;
int i;
int array[4];
#i=4的时候array[4]对应的是i的地址,将i改成了0
for(i = 0;i<=4;i++){
array[i] = 0;
j++;
printf("%d\n",j);
if(j>100)break;
}
}
/*
g2()程序陷入无限递归
i输出 0、1、2、3
*/
void g2(){
printf("call g2");
int array[4];
int i;
#array[4] 对应的是SP(一个汇编概念),会让SP-4,陷入递归
for(i = 0;i<=4;i++){
array[i] -= 4;
printf("%d\n",i);
}
}
int main(int argc, char** argv) {
g();
g2();
}
C++接受可变参数...
C接受可变参数的一个典型例子是printf函数,scanf()函数,其函数原型为:
int printf(const char* format,…)
,int scanf(const char *format,…)
。
实现可变参数需要借助stdarg.h(这里不讨论varargs.h)中提供的宏。
- 原型:
void va_start(va_list arg_ptr)
,
功能:以固定参数的地址为起点确定变参的内存起始地址,获取第一个参数的首地址
返回值:无- 原型:
va_list
类型的变量,
va_list arg_ptr ,这个变量是指向参数地址的指针,因为得到参数的地址之后,再结合参数的类型,才能得到参数的值。- 原型:
type va_arg(va_list arg_ptr,type)
功能:获取下一个参数的
返回值:根据传入参数类型决定返回值- 原型:
void va_end(va_list arg_ptr)
;
功能:将arg_ptr指针置0
返回值:无
使用va_arg需要注意的是:
va_arg(ap,type)
取出一个参数的时候,type不能为下面的数据类型:
——char、signed char、unsigned char
——short、unsigned short
——signed short、short int、signed short int、unsigned short int
——float
原因在于:
在C语言中,调用一个不带原型声明的函数时,调用者会对每个参数执行“默认实际参数提升(default argument promotions)”。
同时,对可变长参数列表超出最后一个有类型声明的形式参数之后的每一个实际参数,也将执行上述提升工作。然后,调用者将提升后的参数传递给被调用者。
提升工作如下:
——float类型的实际参数将提升到double
——char、short和相应的signed、unsigned类型的实际参数提升到int
——如果int不能存储原值,则提升到unsigned int
#include <iostream>
#include <cstdio>
#include <cstdarg>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
/*
types:指示后面的参数是0:int还是1:doubel型,
num:指示后面参数的个数
*/
float t(int types,int num,...){
va_list ap;
va_start(ap,num);
float sum = 0;
if(types==0){
for (int i = 0; i<num;i++){
sum += va_arg(ap,int);
}
}
else if(types==1){
for (int i = 0; i<num;i++){
sum += (float)va_arg(ap,double);
}
}
va_end(ap);
return sum;
}
int main(int argc, char** argv) {
printf("%lf-----\n",t(0, 4, 4, 6, 2, 8));
printf("%lf+++++\n",t(1, 2, 40.2, 30.1));
return 0;
}
段错误VS总线错误
- seg fault:通常在对一个错误的指针进行解引用时发出。比如NULL指针。
bus errors:总线错误,计算机需要内存对齐,int型变量的首地址需要是4的倍数, short类型变量的首地址需要是2的倍数。