调试的错误就是编程给你最好的东西,因为在每个错误上面都标志着前进的一步。
—-By CSDN
在练习写一个简单版scanf函数,测试时出现意外错误,检查了一下,可能是对头文件里一些宏的引用出错。查了一下源代码,取了部分关键的几个宏定义:
1)va_list 宏定义
#ifndef _VA_LIST
#define _VA_LIST
typedef char* va_list;
#endif
2)va_argsiz(t)获取类型占用的空间长度,最小占用长度为int的整数倍
#define __va_argsiz(t)
(((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
3)va_start,使ap指向第一个无名参数
#ifdef __GNUC__
#define va_start(ap, pN)
((ap) = ((va_list) __builtin_next_arg(pN)))
#else
#define va_start(ap, pN) \
((ap) = ((va_list) (&pN) + __va_argsiz(pN)))
#endif
4)清空va_list列表
#define va_end(ap) ( ap = (va_list)0 )
5)返回与当前无名参数具有相同类型和数值的值,并使ap指向下一个无名参数
#define va_arg(ap, t)
(((ap) = (ap) + __va_argsiz(t)),
*((t*) (void*) ((ap) - __va_argsiz(t))))
——————————————————————————————————————–
我写的读取标准输入流中的字符程序,就需要修改参数变量的值。
那么问题来了,如果参数是一个地址,如何修改地址中的值呢?其实是一样的,比如传递一个(int*)的参数进去,*((int**)(void*) ((ap) - __va_argsiz(int*))))。
这里int**是将指向void类型的指针转为指向int*的指针,再解引用,就变成了(int*)的参数。
——————————————————————————————————————–
下面是简单版的scanf函数
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdarg.h>
#define MAXWORD 50
void minscanf(char *fmt,...)
{ va_list ap;
char *p, *sval;
int i, *ival;
double *fval;
va_start(ap, fmt);
for(p = fmt; *p; p++)
{
char c[MAXWORD];
while(*p==' ' || *p=='\t' || *p=='\n')
p++;
if(*p == '%'){
while( (*c = getchar() ) == ' ' )
;
i = 0;
while((c[++i] = getchar()) != ' ' && c[i]!='\n' )
;
c[i] = '\n';
switch(*++p)
{ case 'd':
ival = va_arg(ap,int *);
*ival = atoi(c);
\\ ap++;
break;
case 'f':
fval = va_arg(ap,double *) ;
*fval = atof(c);
\\ ap++;
break;
case 's':
sval = va_arg(ap,char *);
strcpy(sval,c);
\\ ap++;
break;
default:
continue;
}
}
printf("\n");
}
va_end(ap);
}
调试过程中,操作系统给出了“SIGSEGV”信号,查了一下资料。SIG是信号名的通用前缀。SEGV是segmentationviolation(段违例)的缩写。SIGSEGV是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。通常是访问了一个不可用的内存地址,或着往只读内存地址写数据。
检查了很久,才发现是ap++;这句话出错了,应该删去。
仔细想想,这种毛病在高中时代做题时就有了,惯性动作带来的后果。为使ap指向下一个位置,就直接ap++了。呵呵~
此外,写这个程序的过程中还犯了一个老毛病,变量的位置放置错误,导致不恰当的作用域,从而在循环的过程中上一次循环变量的值影响这一次。