今天本来想写段代码练练手,想法挺好结果,栽了个大跟头,在这个错误上徘徊了4个小时才解决,现在分享出来,给大家提个醒,先贴上代码:
/******************************************** * 文件名称:sqlist.h * 文件描述:线性表顺序存储演示 * 文件作者:by Wang.J,in 2013.11.16 * 文件版本:1.0 * 修改记录: *********************************************/ #ifndef __SQLIST_H__ #define __DWLIST_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXSIZE 50 #define OK 0 #define ERR -1 typedef int elemtype; typedef struct { elemtype data[MAXSIZE]; int len; }sqlist; int init_list(sqlist *L); int destroy_list(sqlist *L); int list_empty(sqlist L); int list_length(sqlist L); int disp_list(sqlist L); int get_elem(sqlist L, int i, elemtype *e); int local_elem(sqlist L, elemtype e); int list_insert(sqlist *L, int i, elemtype e); int list_delete(sqlist *L, int i, elemtype *e); #endif
/************************************************** * 文件名称:sqlist.c * 文件描述:线性表顺序存储的实现 * 文件作者:by Wang.J,in 2013.11.16 * 文件版本:1.0 * 修改记录: ***************************************************/ #include "sqlist.h" #if 0 #define ERR_NONE_ERROR 0 #define ERR_FUNC_EXEC 1 #define ERR_FILE_OPEN 2 char *error_msg[] = { /* 0 */ "成功执行,无错误", /* 1 */ "函数执行错误", /* 2 */ "文件打开错误", }; int my_errno = 0; #endif int main(void) { int ret = 0; int i = 0; sqlist slist; elemtype e; memset(&slist, 0, sizeof(slist)); printf("length:%d\n", slist.len); ret = init_list(&slist); if (OK != ret) return -1; ret = list_empty(slist); printf("长度:%d\n", slist.len); if (OK == ret) printf("顺序表为空\n"); if (ERR == ret) printf("顺序表不为空\n"); for (i = 0; i < 10; i++) { e = (elemtype)i; list_insert(&slist, i, e); } printf("插入数据\n"); ret = list_empty(slist); if (OK == ret) printf("顺序表为空\n"); if (ERR == ret) printf("顺序表不为空\n"); printf("after length%d\n", list_length(slist)); disp_list(slist); destroy_list(&slist); return 0; } /*===================================================== * 函数名称:init_list * 函数功能:初始化一个顺序表,创建一个空的顺序表 * 函数参数:sqlist *L 负责返回一个创建好的顺序表,如果创建 失败则返回NULL * 返 回 值:成功返回0并通过指针返回一个创建好的空表 失败返回-1指针返回NULL * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int init_list(sqlist *L) { L = (sqlist *)malloc(sizeof(sqlist)); if (NULL == L) { L = NULL; return -1; } L->len = 0; return 0; } /*===================================================== * 函数名称:destroy_list * 函数功能:销毁创建好的顺序表,释放顺序表的空间 * 函数参数:sqlist *L,已经存在的线性表 * 返 回 值:成功 0 失败 -1 通常free不会失败,其实这个函数可以直接使用void 的,这里只是自己顺手写的,看到代码就知道不会返回0 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int destroy_list(sqlist *L) { free(L); return 0; } /*===================================================== * 函数名称:list_empty * 函数功能:判断sqlist顺序表是否为空 * 函数参数:sqlist L,已存在的线性表 * 返 回 值:空 0 不空 -1 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int list_empty(sqlist L) { if (0 == L.len) return 0; return -1; } /*===================================================== * 函数名称:list_length * 函数功能:取得线性表的长度,返回顺序表中元素个数 * 函数参数:sqlist L,已经存在的线性表 * 返 回 值:L的长度 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int list_length(sqlist L) { return L.len; } /*===================================================== * 函数名称:disp_list * 函数功能:显示顺序表中所有的元素 * 函数参数:sqlist L,已经存在的线性表 * 返 回 值:成功 0 失败 -1 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int disp_list(sqlist L) { int i = 0; if (0 >= L.len) return -1; for (i = 0; i < L.len; i++) printf("%d\t", L.data[i]); /* * 这个地方我自己是有异议的,首先你可能不知道输出的类型为 * %d,再就是求长度是使用list_length函数还是使用L.len方式, * list_length是函数调用有着函数调用的额外开销,在PC上这点 * 开销不算什么,但是在嵌入式系统就不得不考虑这种开销了, * 这基本上算是良好的移植性和代码效率之间的问题,为了提高 * 移植性可以多添加几层抽象层,实现各种判断.除非是极其庞大 * 的项目或是为了匹配各种这样的设备,我认为像代码定义类型这 * 种小事,团队沟通就能解决.工作是避免问题,学习是自找问题. * 所以怎么取舍只能看个人了. */ printf("\n"); return 0; } /*===================================================== * 函数名称:get_elem * 函数功能:获取i位置元素的值域,为了方便对应i从0开始与 数组下标一致,用e返回获取的值 * 函数参数:sqlite L 存在的顺序表 int i 位置 elemtype *e 返回值域 * 返 回 值:成功 0 失败 -1 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int get_elem(sqlist L, int i, elemtype *e) { if (i < 0 || i >= L.len) { e = NULL; return -1; } *e = L.data[i]; /* * 这个地方要注意 * 看看与e = &(L.data[i])区别 */ return 0; } /*===================================================== * 函数名称:local_elem * 函数功能:按元素值查找,返回第一个与e相匹配的元素位置 * 函数参数:sqlist L,已经存在的顺序表 * 返 回 值:存在返回位置 失败返回-1 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int local_elem(sqlist L, elemtype e) { int i = 0; for (i = 0; i < L.len; i++) { if (e == L.data[i]) return i; } return -1; } /*===================================================== * 函数名称:list_insert * 函数功能:在sqlite的i位置插入元素 * 函数参数:sqlist *L 已存在的顺序表 int i 位置 elemtype e 元素 * 返 回 值:成功 0 失败 -1 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int list_insert(sqlist *L, int i, elemtype e) { int j = 0; if (i < 0 || i > MAXSIZE-1) return -1; for (j = L->len; j > i; j--) L->data[j] = L->data[j-1]; L->data[i] = e; L->len++; return 0; } /*===================================================== * 函数名称:list_delete * 函数功能:删除i位置的元素,元素通过e返回 * 函数参数:sqlite *L 已存在的顺序表 int i 位置 elemtype *e 删除位置的元素 * 返 回 值:成功 0 失败 -1 * 创 建 人:by Wang.J,in 2013.11.16 * 修改记录: ======================================================*/ int list_delete(sqlist *L, int i, elemtype *e) { int j = 0; if (i < 0 || i >=L->len) return -1; *e = L->data[i]; for (j = i; j < (L->len-1); j++) L->data[j] = L->data[j+1]; L->len--; return 0; }
很自得,自认为写的很好,运行一下看看,
结果完全出乎意料.
好吧!现在分析错误!
看看main中的定义
int ret = 0;
int i = 0;
sqlist slist;
elemtype e;
看看初始化函数init_list
int init_list(sqlist *L)
{
L = (sqlist *)malloc(sizeof(sqlist));
if (NULL == L) {
L = NULL;
return -1;
}
L->len = 0;
return 0;
}
相信聪明的你已经看出来了,我在main中定义的slist空间在栈上,而我在init_list中一下子将这个东东分配到了堆空间,并且slist并不是指针,根本无法进行指向,所以结果当然就非常的错误了.
打个比方,栈和堆是两个平行的世界,只有指针是穿梭于两个世界的虫洞,除此以为其他东西无法进行跨越.
知道了原因自然很容易解决了.
由于栈上会自动分配空间所以就无需再次申请空间.所以init_list改为:
int init_list(sqlist *L) { /* L = (sqlist *)malloc(sizeof(sqlist)); if (NULL == L) { L = NULL; return -1; } */ L->len = 0; return 0; }
就可以了
大家引以为戒.