关于可变长数组的思考

关于可变长数组的思考

  前述:

第一条记:

最近在海纳百川的地铁上,脑海中常想起魏源说过一句话"师夷长技以制夷".
联想最近中国和日本的态度,再联想起自己的人生经历,可以说我是看日本动漫,打日本游戏长大的,到现在看的工具书中绝大部分是老美和日本的.还有一个那就是看金庸和古龙所代表的武侠江湖梦.总结就是,在日本文化我学到了坚强,永不服输.老美的文化给了我探求科学的阶梯.中国的文化给我烙印上了骨气,我一直认为最后着是最重要的.再总结就是"用别人的长处征服别人,用自己的长处统治别人"

第二条记:

我的好朋友网视悠悠教给我与人处态度"宁可人负我,我绝不负人",我再联想我那‘老乡‘阿瞒说的一句话"宁教我负天下人,休教天下人负我".一合并,就是"宁教天下人负我,休教我负天下人".我觉得很霸气很对.

第三条记:

最后我想起了,雷峰同志笔记中记叙的一句话:"对待同志要像春天般的温暖,对待工作要像夏天一样火热,对待个人主义要像秋风扫落叶一样,对待敌人要像严冬一样残酷无情.".用他的思想扩展一下,我领悟的是"接受变化,敢于变化,必须变化".举个例子就是:对待日本,采用第一条记,别人=日本.对待中国,采用第二条记,天下人=中国.有点不冷静了,估计是我恨我自己,这么快就忘记了自己的锐气!

正文:

最经看到下面一段程序(出自<<征服C指针>>,[日]前桥和弥,吴亚明 译):

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

char    buf[256];

int     size;

int     *variable_array;

int     i;

printf("Input array size>");

fgets(buf,256,stdin);

sscanf(buf,"%d",&size);

variable_array=malloc(sizeof(int)*size);

for(i=0;i<size;i++)

{

variable_array[i]=i;

}

for(i=0;i<size;i++)

{

printf("variable_array[%d]..%d\n",i,variable_array[i]);

}

return 0;

}

发现些这个程序的作者绝对是个另类的程序员,它发现了C中scanf()函数不安全,存在输入缓存问题.

scanf()函数缓存问题描述如下:

while(scanf("%d",&hoge)!=1)

{

fprintf(stderr,"Input error, please enter
again!\n");

}

上面的代码只要输入出错一次,就进入了死循环.导致BUG就在于,输入的缓存问题.解决这个问题的办法就是,消除缓存.写上面的作者

显然也是考虑到了这个问题,采用了下面的方法

char buf[256];

int size;

fgets(buf,256,stdin);

sscanf(buf,"%d",&size);

读取一串数据到第一参数中,在数据少于256个ASII字符的情况下都没问题.再从buf字符串中格式化读取数据到size中.这种解决缓存的方法,很巧妙,
用缓存来读缓存,来消除缓存.但是对于严谨的程序而言这显然是不行的.当用户输入的数据超出了你所设置的缓存阀值,并注入了一段破坏的代码,怎么办.这个问题高桥和弥也说的很详细,先列举网络上的蠕虫病毒,最后又说256的缓存够了.他很严谨,但他仍然保留了这个错误,用小日本的话说,这是不可原谅的.

我的解决办法如下,设计原则是不相信用户(的输入).

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

int main(void)

{

int size;/*数组大小*/

int i;/*下标的指示*/

int *variable_array;/*储存数据的数组*/

int ch;/*清除缓存用的临时变量*/

int hoge=0;/*记录用户输入变量是否和法*/

do

{

printf("Input array
size>");

hoge=scanf("%d",&size);/*获取用户输入的变量*/

if(hoge<=0||size<=0)

{

printf("You
input is error!\n");

printf("You
need to enter again.\n");

hoge=0;/*清空接收的值*/

}

while((ch=getchar())!=10&&ch!=EOF);/*这只适用于控制台和用t打开的文件*/

}while(hoge<=0);

/*下面是合法情况,声明空间*/

variable_array=malloc(size*sizeof(int));

/*为数组赋值*/

for(i=0;i<size;i++)

{

*(variable_array+i)=i;

}

/*输出刚才赋的值*/

printf("The following array data:\n");

for(i=0;i<size;i++)

{

printf("variable_array[%d]...%d
\n",i,variable_array[i]);

}

getch();

return 0;

}

解决的方法就是利用

int ch;

while((ch=getchar())!=10&&ch!=EOF);

上面的代码消除输出缓存

再根据

if(hoge<=0||size<=0)

{

printf("You input is error!\n");

printf("You need to enter again.\n");

hoge=0;/*清空接收的值*/

}

判断用户输入是否合法,不断的继续循环,利用getchar()吃掉多余的缓存,直到用户输入正确为止.

封装:

上面方法虽然解决BUG,但是感觉代码耦合性太强,能不能分离封装一下呢.当然是可以的,下面先封装一个清空输入缓存的

方法

/*

*描述:清除控制台输入流

*函数声明:void clear_input_cache(void)

*函数参数:void

*返回值:void

*使用例子:

*        int hoge;

*        scanf("%d",&hoge);

*        clear_input_cache();

*/

void clear_input_cache(void)

{

int ch;

while((ch=getchar())!=10&&ch!=-1);

}

但是感觉scanf()函数能够获取数据,但是第一次输入错了,还有第二次输入的机会怎么办呢.继续封装scanf()

封装代码如下:

#include <stdarg.h>

/*

*描述:安全的scanf,它会在用户输出错误的情况下,给予提示,让用户重新输入

*      
但是,还是存在scanf同样的BUG比如想得到%d数据,你输入了123ads也是可以的.

*函数声明:void security_scanf(int variable_count,char *format,...)

*函数参数:

*        int
variable_count;表示需要要接收多少个参数,这个参数是安全输入的关键.

*        char *format;接收数据的格式

*        ...:表示接收数据的变量,可变参数

*返回值:void

*使用例子:

*         int hoge,piyo;

*         security_scanf(2,"%d
%d",&hoge,&piyo);

*/

void security_scanf(int variable_count,char *format,...)

{

int scn_count=0;/*记录用户输入的变量*/

va_list arg_list;

for(;;)

{

va_start(arg_list,format);

scn_count=scanf(format,arg_list);

va_end(arg_list);

/*如果接收成功就直接结束循环*/

if(variable_count==scn_count)

break;

/*如果输入出错,先清空输入流,再重新开始,并给出用户提示*/

clear_input_cache();

printf("You input is
error!\n");

printf("You need to enter
again.\n");

printf("You input
data>");

}

}

利用上面封装方法,重新写一个程序,来解决scanf()输入缓存的问题.代码如下,说一些题外话,我没有‘赶尽杀绝‘,总是消灭输入

缓存.将scanf和clear_input_cache捆绑在一起,因为编程有的时候也需要输入缓存,例如可以输出缓存来分析程序出错的地方在哪.

而且scanf函数也能通过设置特殊的读取的格式来清除输入缓存.所以上面的方法只是一种参考,还有更多的方法.

自由在读者自己.自由也是C的一大乐趣吧.

下面是利用上面封装的方法写的程序:

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

#include <stdarg.h>

int main(void)

{

int size;/*数组大小*/

int i;/*下标的指示*/

int *variable_array;/*储存数据的数组*/

for(;;)

{

printf("Input array
size>");

security_scanf(1,"%d",&size);

if(size>0)/*0这个值是否保留,值得讨论,在这里我就排除0了*/

break;

printf("Input array size is
error!");

printf("He can‘t less than
and equal to zero!");

}

/*下面是合法情况,声明空间*/

variable_array=malloc(size*sizeof(int));

/*为数组赋值*/

for(i=0;i<size;i++)

{

*(variable_array+i)=i;

}

/*输出刚才赋的值*/

printf("The following array data:\n");

for(i=0;i<size;i++)

{

printf("variable_array[%d]...%d
\n",i,variable_array[i]);

}

getch();

return 0;

}

其实观看上面封装好的代码,还是有点不爽的,代码还是比较多.这没真没办法了.其实关键是scanf()函数支持可变参数,

继续封装起来比较麻烦,否则如果参数固定,直接加一个void *指针接收函数指针来处里.那样封装起来就更爽了.当然我觉得

封装的越好,用起来就越爽,速度就慢下来了,效率就低了.

扩展:

下面展示一下scanf()函数源码,来自于Microsoft的C++,源自scanf.c文件中

/***

*scanf.c - read formatted data from stdin

*

*       Copyright (c) 1985-1997, Microsoft
Corporation. All rights reserved.

*

*Purpose:

*       defines scanf() - reads formatted data
from stdin

*

*******************************************************************************/

#include <cruntime.h>

#include <stdio.h>

#include <dbgint.h>

#include <stdarg.h>

#include <file2.h>

#include <internal.h>

#include <mtdll.h>

/***

*int scanf(format, ...) - read formatted data from stdin

*

*Purpose:

*       Reads formatted data from stdin into
arguments.  _input does the real

*       work here.

*

*Entry:

*       char *format - format string

*       followed by list of pointers to storage
for the data read.  The number

*       and type are controlled by the format
string.

*

*Exit:

*       returns number of fields read and
assigned

*

*Exceptions:

*

*******************************************************************************/

/*

* stdin ‘SCAN‘, ‘F‘ormatted

*/

int __cdecl scanf (const char *format,...)

{

int retval;

va_list arglist;

va_start(arglist,
format);

_ASSERTE(format != NULL);

_lock_str2(0, stdin);

retval =
(_input(stdin,format,arglist));

_unlock_str2(0, stdin);

return(retval);

}

关于可变长数组的思考,布布扣,bubuko.com

时间: 2024-12-14 18:06:50

关于可变长数组的思考的相关文章

Problem E: 可变长数组

Problem E: 可变长数组 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 472  Solved: 368[Submit][Status][Web Board] Description 定义一个类模板: template <typename T> class DataVector { private: vector<T> members;//表示该数组中的所有元素 public: void show();//用于显示所有元素.

第九章 C99可变长数组VLA详解

C90及C++的数组对象定义是静态联编的,在编译期就必须给定对象的完整信息.但在程序设计过程中,我们常常遇到需要根据上下文环境来定义数组的情况,在运行期才能确知数组的长度.对于这种情况,C90及C++没有什么很好的办法去解决(STL的方法除外),只能在堆中创建一个内存映像与需求数组一样的替代品,这种替代品不具有数组类型,这是一个遗憾.C99的可变长数组为这个问题提供了一个部分解决方案. 可变长数组(variable length array,简称VLA)中的可变长指的是编译期可变,数组定义时其长

链接脚本在编程中的高级运用之一:可变长数组

作为嵌入式软件工程师,应该要清楚程序的每一条指令在哪里,什么时候会被加载到内存,什么时候会被执行.链接脚本会明确告诉你程序的代码和数据在内存中的分布.精确控制代码和数据在内存中的分布是高效利用内存资源的前提.自定义链接脚本是资深嵌入式软件工程师的必备技能,更是嵌入式架构师的最基本要求.此外,灵活定制链接脚本在编程方面有更高级的应用. 一.编译链接原理 简单讲述编译链接的基本原理有助于后面内容的理解. a. 简单点说,一个可执行程序包括文件头.代码段(.text).数据段(.bss).符号段等信息

C语言可变长参数 思考

1.我们为什么需要可变长参数 各种编程语言,如C.C++.java等都实现了可变长参数的函数.普通函数的参数个数是确定的,如果同一个函数可以接受不同数目的参数就需要适用到可变长度的参数.可变长度参数一个最典型的例子就是printf和scanf.以printf为例子,如可以有printf("Hello Word"),printf("Hello word%s","!"),printf("Hello word%s%d","

Java中可变长参数的使用及注意事项

在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用,例如print("hello");print("hello","lisi");print("hello","张三", "alexia");下面介绍如何定义可变长参数 以及如何使用可变长参数. 1. 可变长参数的定义 使用...表示可变长参数,例如 prin

C之变长数组

变长数组是C99标准新加入的一个特性,它的加入大大方便了我们的编程,所谓变长数组,不是数组的长度可变,而是指允许使用变量来定义数组.这可以使我们写出更具通用性的函数.下面是一个例子,函数sum2d完成将一个二位数组中的所有数值相加并返回其和. #include<stdio.h> #define SIZE 10 #define LOC 2 #define ROW 4int sum2d(int loc, int row, int num[loc][row]); int main(void){ in

c/c++的0长数组(柔性数组)

在标准C和C++中0长数组如charArray[0]是不允许使用的,因为这从语义逻辑上看,是完全没有意义的. 但是,GUN中却允许使用,而且,很多时候,应用在了变长结构体中,如: StructPacket { Int state; Int len; Char cData[0]; //这里的0长结构体就为变长结构体提供了非常好的支持,可以指向独立的数据空间 }; 首先对0长数组做一个解释: 用途 :长度为0的数组的主要用途是为了满足需要变长度的结构体. 用法 :在一个结构体的最后 ,申明一个长度为

变长数组 - 转

http://ericwang.github.io/program/2010/02/10/c_Variable_length_arrays/ C中的Variable length arrays (变长数组) Variable length arrays 是C99的特性,而不是 C++98 的,关于c99标准的变长数组, 在标准的6.7.5.2 Array declarators里面有这样的说明: 2.Only ordinary identifiers (as defined in 6.2.3)

javascript arguments解释,实现可变长参数。

在C#中,有可变长参数params[],但是在js中,如何实现这种可变参数呢? 一.可变长参数 arguments是非常好的解决方法,一直不知道javascript有这个东西. 先来看看应用场景,使用arguments传入任意个数的参数到js函数里的写法. function Test() { console.log(arguments[0]); console.log(arguments[1]); console.log(arguments[2]); }; Test(1, 2, 3); 输出 1