main函数调用子函数堆栈解析

main函数调用子函数堆栈解析
main函数调用子函数堆栈解析

任何一个程序通常都包括代码段和数据段,这些代码和数据本身都是静态的。程序要想运行,首先要由操作系统负责为其创建进程,并在进程的虚拟地址空间中为其代码段和数据段建立映射。光有代码段和数据段是不够的,进程在运行过程中还要有其动态环境,其中最重要的就是堆栈。图3所示为Linux下进程的地址空间布局:

图3 Linux下进程地址空间的布局

首先,execve(2)会负责为进程代码段和数据段建立映射,真正将代码段和数据段的内容读入内存是由系统的缺页异常处理程序按需完成的。另外,execve(2)还会将bss段清零,这就是为什么未赋初值的全局变量以及static变量其初值为零的原因。进程用户空间的最高位置是用来存放程序运行时的命令行参数及环境变量的,在这段地址空间的下方和bss段的上方还留有一个很大的空洞,而作为进程动态运行环境的堆栈和堆就栖身其中,其中堆栈向下伸展,堆向上伸展。

知道了堆栈在进程地址空间中的位置,我们再来看一看堆栈中都存放了什么。相信读者对C语言中的函数这样的概念都已经很熟悉了,实际上堆栈中存放的就是与每个函数对应的堆栈帧。当函数调用发生时,新的堆栈帧被压入堆栈;当函数返回时,相应的堆栈帧从堆栈中弹出。典型的堆栈帧结构如图4所示。

堆栈帧的顶部为函数的实参,下面是函数的返回地址以及前一个堆栈帧的指针,最下面是分配给函数的局部变量使用的空间。一个堆栈帧通常都有两个指针,其中一个称为堆栈帧指针,另一个称为栈顶指针。前者所指向的位置是固定的,而后者所指向的位置在函数的运行过程中可变。因此,在函数中访问实参和局部变量时都是以堆栈帧指针为基址,再加上一个偏移。对照图4可知,实参的偏移为正,局部变量的偏移为负。

图4 典型的堆栈帧结构

介绍了堆栈帧的结构,我们再来看一下在Intel i386体系结构上堆栈帧是如何实现的。图5和图6分别是一个简单的C程序及其编译后生成的汇编程序。

图5 一个简单的C程序example1.c

int function(int a, int b, int c)

{

char buffer[14];

int sum;

sum = a + b + c;

return sum;

}

void main()

{

int i;

i = function(1,2,3);

}

图6 example1.c编译后生成的汇编程序example1.s

1 .file "example1.c"

2 .version "01.01"

3 gcc2_compiled.:

4 .text

5 .align 4

6 .globl function

7 .type function,@function

8 function:

9 pushl %ebp

10 movl %esp,%ebp

11 subl $20,%esp

12 movl 8(%ebp),%eax

13 addl 12(%ebp),%eax

14 movl 16(%ebp),%edx

15 addl %eax,%edx

16 movl %edx,-20(%ebp)

17 movl -20(%ebp),%eax

18 jmp .L1

19 .align 4

20 .L1:

21 leave

22 ret

23 .Lfe1:

24 .size function,.Lfe1-function

25 .align 4

26 .globl main

27 .type main,@function

28 main:

29 pushl %ebp

30 movl %esp,%ebp

31 subl $4,%esp

32 pushl $3

33 pushl $2

34 pushl $1

35 call function

36 addl $12,%esp

37 movl %eax,%eax

38 movl %eax,-4(%ebp)

39 .L2:

40 leave

41 ret

42 .Lfe2:

43 .size main,.Lfe2-main

44 .ident "GCC: (GNU) 2.7.2.3"

这里我们着重关心一下与函数function对应的堆栈帧形成和销毁的过程。从图5中可以看到,function是在main中被调用的,三个实参的值分别为1、2、3。由于C语言中函数传参遵循反向压栈顺序,所以在图6中32至34行三个实参从右向左依次被压入堆栈。接下来35行的call指令除了将控制转移到function 之外,还要将call的下一条指令addl的地址,也就是function函数的返回地址压入堆栈。下面就进入function 函数了,首先在第9行将main函数的堆栈帧指针ebp保存在堆栈中并在第10行将当前的栈顶指针esp

保存在堆栈帧指针ebp中,最后在第11行为function函数的局部变量buffer[14]和sum在堆栈中分配空间。至此,函数function的堆栈帧就构建完成了,其结构如图7所示。

图7 函数function的堆栈帧

读者不妨回过头去与图4对比一下。这里有几点需要说明。首先,在Intel i386体系结构下,堆栈帧指针的角色是由ebp扮演的,而栈顶指针的角色是由esp扮演的。另外,函数function的局部变量buffer[14]由14个字符组成,其大小按说应为14字节,但是在堆栈帧中却为其分配了16个字节。这是时间效率和空间效率之间的一种折衷,因为Intel i386是32位的处理器,其每次内存访问都必须是4字节对齐的,而高30位地址相同的4个字节就构成了一个机器字。因此,如果为了填补buffer[14]留下的两个字节而将sum分配在两个不同的机器字中,那么每次访问sum就需要两次内存操作,这显然是无法接受的。还有一点需要说明的是,正如我们在本文前言中所指出的,如果读者使用的是较高版本的gcc的话,您所看到的函数function对应的堆栈帧可能和图7所示有所不同。上面已经讲过,为函数function的局部变量buffer[14]

和sum在堆栈中分配空间是通过在图6中第11行对esp进行减法操作完成的,而sub指令中的20正是这里两个局部变量所需的存储空间大小。但是在较高版本的gcc中,sub指令中出现的数字可能不是20,而是一个更大的数字。应该说这与优化编译技术有关,在较高版本的gcc中为了有效运用目前流行的各种优化编译技术,通常需要在每个函数的堆栈帧中留出一定额外的空间。

下面我们再来看一下在函数function中是如何将a、b、c的和赋给sum的。前面已经提过,在函数中访问实参和局部变量时都是以堆栈帧指针为基址,再加上一个偏移,而Intel i386体系结构下的堆栈帧指针就是ebp,为了清楚起见,我们在图7中标出了堆栈帧中所有成分相对于堆栈帧指针ebp的偏移。这下图6中12至16的计算就一目了然了,8(%ebp)、12(%ebp)、16(%ebp)和-20(%ebp)分别是实参a、b、c和局部变量sum的地址,几个简单的add指令和mov指令执行后sum中便是a、b、c三者之和了。另外,在gcc编译生成的汇编程序中函数的返回结果是通过eax传递的,因此在图6中第17行将sum的值拷贝到eax中。

最后,我们再来看一下函数function执行完之后与其对应的堆栈帧是如何弹出堆栈的。图6中第21行的leave指令将堆栈帧指针ebp拷贝到esp中(作者的意思其实就是一个esp = ebp的赋值过程,这样直接导致的结果就是esp指向ebp的地址,那么所以局部变量sum buffer的空间就被释放了),于是在堆栈帧中为局部变量buffer[14]和sum分配的空间就被释放了;除此之外,leave指令还有一个功能,就是从堆栈

中弹出一个机器字并将其存放到ebp中(作者的意思是紧接着上面的操作,从堆栈中pop一个字其实就是把previous ebp的值取出来放到了ebp寄存器中),这样ebp就被恢复为main函数的堆栈帧指针了。第22行的ret指令再次从堆栈中弹出一个机器字并将其存放到指令指针eip中(作者的意思是把address of addl pop出来付给寄存器eip),这样控制就返回到了第36行main函数中的addl指令处。addl指令将栈顶指针esp加上12,于是当初调用函数function之前压入堆栈的三个实参所占用的堆栈空间也被释放掉了。至此,函数function的堆栈帧就被完全销毁了。前面刚刚提到过,在gcc编译生成的汇编程序中通过eax 传递函数的返回结果,因此图6中第38行将函数function的返回结果保存在了main函数的局部变量i中。

数据结构-堆栈和队列实验报告

实验二堆栈和队列 实验目的: 1.熟悉栈这种特殊线性结构的特性; 2.熟练并掌握栈在顺序存储结构和链表存储结构下的基本运算; 3.熟悉队列这种特殊线性结构的特性; 3.熟练掌握队列在链表存储结构下的基本运算。 实验原理: 堆栈顺序存储结构下的基本算法; 堆栈链式存储结构下的基本算法; 队列顺序存储结构下的基本算法;队列链式存储结构下的基本算法;实验内容: 3-18链式堆栈设计。要求 (1)用链式堆栈设计实现堆栈,堆栈的操作集合要求包括:初始化Stacklnitiate (S), 非空否StackNotEmpty(S),入栈StackiPush(S,x), 出栈StackPop (S,d),取栈顶数据元素StackTop(S,d); (2)设计一个主函数对链式堆栈进行测试。测试方法为:依次把数据元素1,2,3, 4,5 入栈,然后出栈并在屏幕上显示出栈的数据元素; (3)定义数据元素的数据类型为如下形式的结构体, Typedef struct { char taskName[10]; int taskNo; }DataType; 首先设计一个包含5个数据元素的测试数据,然后设计一个主函数对链式堆栈进行测试,测试方法为:依次吧5个数据元素入栈,然后出栈并在屏幕上显示出栈的数据元素。 3-19对顺序循环队列,常规的设计方法是使用対尾指针和对头指针,对尾指针用于指示当 前的対尾位置下标,对头指针用于指示当前的対头位置下标。现要求: (1)设计一个使用对头指针和计数器的顺序循环队列抽象数据类型,其中操作包括:初始化,入队列,出队列,取对头元素和判断队列是否为空; (2)编写一个主函数进行测试。 实验结果: 3-18 typedef struct snode { DataType data; struct snode *n ext; } LSNode; /* 初始化操作:*/

c语言中命令行参数argc,argv

main( int argc, char ** argv ) argv:指针的指针 argc:整数 char **argv or char *argv[] or char argv[][] 为了能形象的说明这两个参数的含义,我们先用一个实例来进行讲解: 假设程序的名称为test,当只输入test,则由操作系统传来的参数为: argc = 1,表示只有一程序名称; argc只有一个元素,argv[0]指向输入的程序路径及名称:./ test 当输入test para_1,有一个参数,则由操作系统传来的参数为: argc = 2,表示除了程序名外还有一个参数; argv[0]指向输入的程序路径及名称; argv[1]指向参数para_1字符串 当输入test para_1 para_2 有2个参数,则由操作系统传来的参数为: argc = 3,表示除了程序名外还有两个参数; argv[0]指向输入的程序路径及名称; argv[1]指向参数para_1字符串; argv[2]指向参数para_2字符串; 以此类推……………… void main( int argc, char *argv[] ) char *argv[]: argv是一个指针数组,元素个数是argc,存放的是指向每一个参数

的指针,第一个元素即argv[0]为程序运行的全路径名,从二个元素(argv[1])开始,是每一个参数的名称,最后一个元素为NULL。总的来说,即: * argv: 字符串数组 argv[0] 为程序运行的全路径名 argv[1] 为执行程序名后的第一个字符串; argv[2] 为执行程序名后的第二个字符串; ... argv[argc]为NULL。 int argc:表示argv的大小,是实际参数个数+1,其中+1是因为argv[0]是编译后的可执行文件名 main() 参数: Turbo C2.0启动时总是把argc、argv、env(存放环境变量)这三个参数传递给main()函数, 可以在用户程序中说明(或不说明)它们, 如果说明了部分(或全部)参数, 它们就成为main()子程序的局部变量。 请注意: 一旦想说明这些参数, 则必须按argc, argv, env 的顺序, 如以下的例子: main() main(int argc) main(int argc, char *argv[]) main(int argc, char *argv[], char *env[]) 其中第二种情况是合法的, 但不常见, 因为在程序中很少有只用argc, 而不 用argv[]的情况。 以下提供一样例程序EXAMPLE.EXE, 演示如何在main()函数中使用三个参数: /*program name EXAMPLE.EXE*/ #i nclude

main函数的命令行参数

main函数的命令行参数 前面所用到的main函数都是不带参数的,即main()。其实main函数也可以有参数,只是它的参数之一是指针数组。 1.main函数中可以写两个形参,一般形式如下:main(int argc,char *argv[ ]); 2.说明:第一个形参argc是一个整型变量,第二个形参argv是一个指针数组,其元素指向字符型数据。 1 若以下程序所生成的可执行文件名为filel.exe ; 当输入以下命令行执行该程序 时,FILE1 CHINA BEIJING SHANGHAI程序的输出结果是()。 main( int argc,char *argv[]) { while(argc-->0) { ++argv; printf("%s",*argv);} } A)CHINA BEIJING SHANGHAI B)FILE1 CHINA BEIJING C)C B S D)F C B 2 假定下列程序的可执行文件名为prg.exe,则在该程序的子目录下输入命令行: main(int argc,char *argv[ ]) { int i; if(argc<=0) return; for(i=1;i后程序的输出结果是()。 A)hello good B)hg C)hel D)hellogood 3 假定以下程序经编译和连接后生成可执行文件PROG.EXE,如果在此可执行文件所在录的DOS提示符下键入:PROG ABCDEFGH IJKL<回车>,则输出结果为()。 main( int argc, char *argv[])) { while(--argc>0) printf("%s",argv[argc]); printf("\n"); } A) ABCDEFG B) IJHL C) ABCDEFGHIJKL D) IJKLABCDEFGH 4 不合法的main函数命令行参数表示形式是()。 A) main(int a,char *c[]) B) main(int arc,char **arv) C) main(int argc,char *argv) D) main(int argv,char *argc[]) 5 有以下程序:程序编译连接后生成的可执行文件是exl.exe,若运行时输入带参数的命令行是exl abcd efg 10<回车>则运行的结果是()。 #include main(int argc,char *argv[]) { int i,len=0; for (i=1; i,则运行结果为()。 main(int argc, char *argv[]) { int n,i=0; while(argv[1][i]!= '\0') { n=fun(); i++;} printf("%d\n",n*argc); } int fun() { static int s=0; s+=1; return s; } A)6 B)8 C)3 D)4

栈和队列的基本操作

《数据结构与算法》实验报告 专业班级学号 实验项目 实验二栈和队列的基本操作。 实验目的 1、掌握栈的基本操作:初始化栈、判栈为空、出栈、入栈等运算。 2、掌握队列的基本操作:初始化队列、判队列为空、出队列、入队列等运算。 实验容 题目1: 进制转换。利用栈的基本操作实现将任意一个十进制整数转化为R进制整数 算法提示: 1、定义栈的顺序存取结构 2、分别定义栈的基本操作(初始化栈、判栈为空、出栈、入栈等) 3、定义一个函数用来实现上面问题: 十进制整数X和R作为形参 初始化栈 只要X不为0重复做下列动作 将X%R入栈 X=X/R 只要栈不为空重复做下列动作 栈顶出栈输出栈顶元素 题目2: 利用队列的方式实现辉三角的输出。 算法设计分析 (一)数据结构的定义 1、栈的应用 实现十进制到其他进制的转换,该计算过程是从低位到高位顺序产生R进制数的各个位数,而打印输出一般从高位到低位进行,恰好与计算过程相反。因此,运用栈先进后出的性质,即可完成进制转换。 栈抽象数据结构描述 typedef struct SqStack /*定义顺序栈*/ { int *base; /*栈底指针*/ int *top; /*栈顶指针*/ int stacksize; /*当前已分配存储空间*/ } SqStack;

2、队列的应用 由于是要打印一个数列,并且由于队列先进先出的性质,肯定要利用已经进队的元素在其出队之前完成辉三角的递归性。即,利用要出队的元素来不断地构造新的进队的元素,即在第N行出队的同时,来构造辉三角的第N+1行,从而实现打印辉三角的目的。 队列抽象数据结构描述 typedef struct SeqQueue { int data[MAXSIZE]; int front; /*队头指针*/ int rear; /*队尾指针*/ }SeqQueue; (二)总体设计 1、栈 (1)主函数:统筹调用各个函数以实现相应功能 int main() (2)空栈建立函数:对栈进行初始化。 int StackInit(SqStack *s) (3)判断栈空函数:对栈进行判断,若栈中有元素则返回1,若栈为空,则返回0。 int stackempty(SqStack *s) (4)入栈函数:将元素逐个输入栈中。 int Push(SqStack *s,int x) (5)出栈函数:若栈不空,则删除栈顶元素,并用x返回其值。 int Pop(SqStack *s,int x) (6)进制转换函数:将十进制数转换为R进制数 int conversion(SqStack *s) 2、队列 (1)主函数:统筹调用各个函数以实现相应功能 void main() (2)空队列建立函数:对队列进行初始化。 SeqQueue *InitQueue() (3)返回队头函数:判断队是否为空,若不为空则返回队头元素。 int QueueEmpty(SeqQueue *q) (4)入队函数:将元素逐个输入队列中。 void EnQueue(SeqQueue *q,int x) (5)出队函数:若队列不空,则删除队列元素,并用x返回其值。 int DeQueue(SeqQueue *q) (6)计算队长函数:计算队列的长度。 int QueueEmpty(SeqQueue *q) (7)输出辉三角函数:按一定格式输出辉三角。 void YangHui(int n)

C语言编程实战-4-命令行参数(main函数)处理

命令行(main函数)参数处理 我们在执行某某命令的时候,经常会传入一些参数,告诉程序做不同的处理。如使用gcc编译程序时,我们经常会在后边指定c源码文件,如gcc test.c。我们自己写的程序,如何能够算其它命令一样,能够接收和处理不同的参数呢?答案是肯定的。我们今天来介绍自写程序对命令行参数的处理。 一个标准的C程序,其主函数通常是这样子定义的: int main(int argc,char *argv[]) { //用户代码 return xxx; } main函数有两个参数:argc与agv,前者是一个整数,表示命令行给程序参数的个数,后者是一个字符串数组,用于顺次存放命令行传给程序的参数,通过argc与argv,我们就能实现对命令行参数的处理。 对每一个程序而言,程序的名称总会作为命令行的第一个参数传给它的主函数,那怕命令行中并没有传任何参数。因此,主函数的参数argc最小值为1,argv[0]总是存放命令程序名。因此,假使test是我们程序的名字,如果执行: test 则main函数得到的参数是:argc = 1, argv = {“test”}; 如果执行: test arg1 arg2 则main函数得到的参数是:argc = 3, argv = {“test”, “arg1”, “arg2”}; 如此类推。 通过上面的讲解,我们不难在main函数中实现对命令行参数的代码。我们接下来做一个编程任务: 写一个程序,该程序接收两个参数,这两个参数都是整数,程序中需将这两个整数的和与差计算并输出到屏幕上。假设程序的名称为cal,在命令行执行: cal 10 15 则程序输出: 10 + 15 = 25 10 – 15 = -5 如果输入的参数不合法,则提示参数错误。 代码实现: #include

主函数main

①主函数main() ②保存单链表函数save() ③重载操作菜单函数load() ④显示单链表内容函数display () ⑤插入元素函数insert () ⑥删除元素函数decelt () ⑦修改元素函数modify() 各函数间关系如下: #include "stdafx.h" #include #include typedef char ElemType; struct LNode { ElemType data; struct LNode *next; }; //***********************************************************置空表setnull() void setnull(struct LNode **p) { *p=NULL; } //************************************************************求长度length() int length(struct LNode **p) { int n=0; struct LNode *q=*p; while (q!=NULL) { n++; q=q->next; }

return(n); } //*************************************************************取结点get() ElemType get(struct LNode **p,int i) { int j=1; struct LNode *q=*p; while (jnext;j++; } if (q!=NULL) /**//*找到了第i个结点*/ return(q->data); else { printf("位置参数不正确!\n"); return NULL; } } //************************************************************按值查找locate() int locate(struct LNode **p,ElemType x) { int n=0; struct LNode *q=*p; while (q!=NULL && q->data!=x) /**//*查找data域为x的第一个结点*/ { q=q->next; n++; } if (q==NULL) /**//*未找到data域等于x的结点*/ return(-1); else /**//*找到data域等于x的结点*/ return(n+1); }

数据结构基础练习(栈和队列)

数据结构基础练习(栈和队列) 学号姓名蓝礼巍班级 . 一、选择题 1.有5个元素a,b,c,d,e依次进栈,允许任何时候出栈,则可能的出栈序列是 c 。 A.baecd B.dceab C.abedc D.aebcd 2.下列有关递归的叙述,不正确的是 b 。 A.在计算机系统内,执行递归函数是通过自动使用栈来实现的。 B.在时间和空间效率方面,递归算法比非递归算法好。 C.递归函数的求解过程分为递推(进栈)和回推(出栈)两个阶段。 D.在递归函数中必须有终止递归的条件。 3.栈和队列均属于哪一种逻辑结构 A 。 A.线性结构B.顺序结构C.非线性结构D.链表结构4.设输入元素为1、2、3、P和A,输入次序为123PA,元素经过栈后得到各种输出序列,则可以作为高级语言变量名的序列有 d 种。 A.4 B.5 C.6 D.7 5.一个队列的入队序列为a,b,c,d,则该队列的输出序列是 b 。 A.dcba B.abcd C.adcb D.cbda 6.在一个链式队列中,假设f和r分别为队头和队尾指针,则插入s所指结点的运算是b 。 A. f->next=s; f=s; B. r->next=s; r=s; C. s->next=s; r=s; D. s->next=f; f=s; 7.如果5个元素出栈的顺序是1、2、3、4、5,则进栈的顺序可能是 c 。 A.3、5、4、1、2 B.1、4、5、3、2 C.5、4、1、3、2 D.2、4、3、1、5 8.若已知一个栈的入栈序列是1,2,3,…,n,其输出序列为p1,p2,p3,…,pn,若p1=n,则pi为。 A.i B.n-i C.n-i+1 D.不确定 二、填空题 1.栈和队列是一种特殊的线性表,其特殊性体现在是运算受限线性表。设现有元素e1,e2,e3,e4,e5和e6依次进栈,若出栈的序列是e2,e4,e3,e6,e5,e1,则栈S的容量至少是 3 。 2.顺序循环队列中,设队头指针为front,队尾指针为rear,队中最多可有MAX个元素,采用少用一个存储单元的方法区分队满与队空问题,则元素入队列时队尾指针的变化为 Rear=(rear+1)%MAX ;元素出队列时队头指针的变化为fort=(fotr+1)%MAX ;队列中的元素个数为 (rear-fort+MAX)%MAX 。若则可用表示队满的判别条件,队空的判别条件仍然为 rear==fort 。 三、解答题

带参数main函数

main函数也可以带参数。带参数main函数的定义格式如下: void main(int argc, char *argv[]) { ... ... } argc和argv是main函数的形式参数。这两个形式参数的类型是系统规定的。如果main函数要带参数,就是这两个类型的参数;否则main函数就没有参数。变量名称argc和argv 是常规的名称,当然也可以换成其他名称。 那么,实际参数是如何传递给main函数的argc和argv的呢?我们知道,C程序在编译和链接后,都生成一个exe文件,执行该exe文件时,可以直接执行;也可以在命令行下带参数执行,命令行执行的形式为: 可执行文件名称参数1 参数2 ... ... 参数n 执行文件名称和参数、参数之间均使用空格隔开。例如,在linux下运行程序./a.out 1 2 ,可执行文件名称为./a.out,参数1为字符串1,参数2为2。 如果按照这种方法执行,命令行字符串将作为实际参数传递给main函数。具体为: (1) 可执行文件名称和所有参数的个数之和传递给argc;所以上面的argc=3 (2) 可执行文件名称(包括路径名称)作为一个字符串,首地址被赋给argv[0],参数1也作为一个字符串,首地址被赋给argv[1],... ...依次类推。 本回答专业性由科学教育分类达人陶文认证 满意回答 运行方法: (假设你的程序为Untitled1.exe) 打开命令提示符: 转到你的Untitled1.exe的目录, 输入下列命令: Untitled1.exe a basdfsa 1314324 -k -f 回车就可以运行了。 这时,在Main函数里定义的 int main(int argc, char *args[]) 里的argc = 6,就是表示有六个参数, 对应的 args[0] = "Untitled1.exe"; args[1] = "a"; args[2] = "basdfsa"; args[3] = "1314324"; args[4] = "-k"; args[5] = "-f"; 就是这样子了。

计算机专业基础综合数据结构(栈和队列)历年真题试卷汇编6

计算机专业基础综合数据结构(栈和队列)历年真题试卷汇编6 (总分:60.00,做题时间:90分钟) 一、单项选择题(总题数:14,分数:28.00) 1.为解决计算机主机与打印机之间速度不匹配问题,通常设置一个打印数据缓冲区,主机将要输出的数据依次写入该缓冲区,而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是( )。【2009年 全国试题1(2)分】 A.栈 B.队列√ C.树 D.图 2.设栈S和队列Q的初始状态均为空,元素a,b,c,d,e,j,g=g依次进入栈S。若每个元素出栈后立即进入队列Q,且7个元素出队的顺序是b,d,c,f,e,a,g,则栈S的容量至少是( )。【2009年全国试题2(2)分】 A.1 B.2 C.3 √ D.4 按元素出队顺序计算栈的容量。b进栈时栈中有a,b出栈,cd进栈,栈中有acd,dc出栈,ef进栈,栈 中有aef,fea出栈,栈空,g进栈后出栈。所以栈S的容量至少是3。 3.若元素a,b,c,d,e,f依次进栈,允许进栈、退栈操作交替进行,但不允许连续三次进行退栈操作,则不可能得到的出栈序列是( )。【2010年全国试题1(2)分】 A.d,c,e,b,f,a B.c,b,d,a,e,f C.b,c,a,e,f,d D.a,f,e,d,c,b √ 4.某队列允许在其两端进行入队操作,但仅允许在一端进行出队操作。若元素a,b,c,d,e依次入此队列后再进行出队操作,则不可能得到的出队序列是( )。【2010年全国试题2(2)分】 A.b,a,c,d, e B.d,b,a,c,e C.d,b,c,a,e √ D.e,c,b,a,d a先入队,b和c可在a的任一端入队,选项A、B、D都符合要求,只有选项C不可能出现。双端队列出队结果的分析可参见四、36。 5.元素a,b,c,d,e依次进入初始为空的栈中,若元素进栈后可停留、可出栈,直到所有元素都出栈,则在所有可能的出栈序列中,以元素d开头的序列个数是( )。【2011年全国试题2(2)分】 A.3 B.4 √ C.5 D.6 元素d进栈时,元素a,b,c已在栈中,d出栈后,P可以在a,b,c任一元素的前面进栈并出栈,也可以在元素a后出栈,c,b,a必须依次出栈,所以元素d开头的序列个数是4。 6.已知循环队列存储在一维数组A[0.n-1]中,且队列非空时front和rear分别指向队头元素和队尾元素。若初始时队列为空,且要求第1个进入队列的元素存储在A[0]处,则初始时front和rear的值分别是( )。[2011年全国试题3(2)分】 A.0,0 B.0,n—1 √ C.n一1,0

int main参数详解

int main(int argc,char* argv[])详解 argc是命令行总的参数个数 argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数 命令行后面跟的用户输入的参数,比如: int main(int argc, char* argv[]) { int i; for (i = 0; i>i; return 0; } 执行时敲入 F:\MYDOCU~1\TEMPCODE\D1\DEBUG\D1.EXE aaaa bbb ccc ddd 输出如下: F:\MYDOCU~1\TEMPCODE\D1\DEBUG\D1.EXE aaaa bbb ccc ddd -------------------------------------------------------------------- char *argv[]是一个字符数组,其大小是int argc,主要用于命令行参数argv[] 参数,数组里每个元素代表一个参数; 比如你输入 test a.c b.c t.c 则 argc = 4 argv[0] = "test" argv[1] = "a.c" argv[2] = "b.c" argv[3] = "t.c" -------------------------------------------------------------------------------------------- argc记录了用户在运行程序的命令行中输入的参数的个数。 arg[]指向的数组中至少有一个字符指针,即arg[0].他通常指向程序中的可执行文件的文件名。在有些版本的编译器中还包括程序 文件所在的路径。 ------------------------------------------------------------------------- 在调用一个可执行程序时,某些情况下需要向程序传递参数。如我们可以在控制台中键入notepad.exe, 回车后将执行记事本程序。如果我们希望在打开notepad时同时打开一个文本文件,可以在notepad.exe

实验二 堆栈和队列基本操作的编程实现

实验二堆栈和队列基本操作的编程实现 【实验目的】 堆栈和队列基本操作的编程实现 要求: 堆栈和队列基本操作的编程实现(2学时,验证型),掌握堆栈和队列的建立、进栈、出栈、进队、出队等基本操作的编程实现,存储结构可以在顺序结构或链接结构中任选,也可以全部实现。也鼓励学生利用基本操作进行一些应用的程序设计。 【实验性质】 验证性实验(学时数:2H) 【实验内容】 内容: 把堆栈和队列的顺序存储(环队)和链表存储的数据进队、出队等运算其中一部分进行程序实现。可以实验一的结果自己实现数据输入、数据显示的函数。 利用基本功能实现各类应用,如括号匹配、回文判断、事物排队模拟、数据逆序生成、多进制转换等。 【思考问题】 1.栈的顺序存储和链表存储的差异? 2.还会有数据移动吗?为什么? 3.栈的主要特点是什么?队列呢? 4.栈的主要功能是什么?队列呢? 5.为什么会有环状队列? 【参考代码】 (一)利用顺序栈实现十进制整数转换转换成r进制 1、算法思想 将十进制数N转换为r进制的数,其转换方法利用辗转相除法,以N=3456,r=8为例转换方法如下: N N / 8 (整除)N % 8(求余) 3456 432 0 低 432 54 0 54 6 6 6 0 6 高 所以:(3456)10 =(6600)8 我们看到所转换的8进制数按底位到高位的顺序产生的,而通常的输出是从高位到低位的,恰好与计算过程相反,因此转换过程中每得到一位8进制数则进栈保存,转换完毕后依次出栈则正好是转换结果。 算法思想如下:当N>0时重复1,2 ①若N≠0,则将N % r 压入栈s中,执行2;若N=0,将栈s的内容依次出栈,算法结束。 ②用N / r 代替N 2、转换子程序

关于main函数的(int argc,char argv[])

main(int argc,char *argv[ ]) argv为指针的指针 argc为整数 char **argv or: char *argv[] or: char argv[][] main()括号内是固定的写法。 下面给出一个例子来理解这两个参数的用法: 假设程序的名称为prog, 当只输入prog,则由操作系统传来的参数为: argc=1,表示只有一程序名称。 argc只有一个元素,argv[0]指向输入的程序路径及名称:./prog 当输入prog para_1,有一个参数,则由操作系统传来的参数为: argc=2,表示除了程序名外还有一个参数。 argv[0]指向输入的程序路径及名称。 argv[1]指向参数para_1字符串。 当输入prog para_1 para_2 有2个参数,则由操作系统传来的参数为: argc=3,表示除了程序名外还有2个参数。 argv[0]指向输入的程序路径及名称。 argv[1]指向参数para_1字符串。 argv[2]指向参数para_2字符串。 void main( int argc, char *argv[] ) char *argv[] : argv 是一个指针数组,他的元素个数是argc,存放的是指向每一个参数的指针, 他的第一个元素即argv[0]为编译生成的可执行文件名(包括路径eg:"F:\VC\Ex1\Debug\Ex1.exe"),从二个元素(argv[1])开始,是每一个参数int argc 表示argv的大小,是实际参数个数+1,其中+1是因为argv[0]是编译后的可执行文件名main()主函数 每一C 程序都必须有一main()函数, 可以根据自己的爱好把它放在程序的某个地方。有些程序员把它放在最前面, 而另一些程序员把它放在最后面, 无论放 在哪个地方, 以下几点说明都是适合的。 1. main() 参数 在Turbo C2.0启动过程中, 传递main()函数三个参数: argc, argv和env。 * argc: 整数, 为传给main()的命令行参数个数。 * argv: 字符串数组。 在DOS 3.X 版本中, argv[0] 为程序运行的全路径名; 对DOS 3.0 以下的版本, argv[0]为空串("") 。 argv[1] 为在DOS命令行中执行程序名后的第一个字符串; argv[2] 为执行程序名后的第二个字符串; ... argv[argc]为NULL。 *env: 安符串数组。env[] 的每一个元素都包含ENVV AR=value形式的字符串。其中ENVV AR为环境变量如PA TH或87。value 为ENVV AR的对应值如C:\DOS, C: \TURBOC(对于PA TH) 或YES(对于87)。 Turbo C2.0启动时总是把这三个参数传递给main()函数, 可以在用户程序中说明(或不说明)它们, 如果说明了部分(或全部)参数, 它们就成为main()子程序的局部变量。

栈和队列的基本操作实现及其应用

实验二栈和队列的基本操作实现及其应用 一_一、实验目的 1、熟练掌握栈和队列的基本操作在两种存储结构上的实现。 一_二、实验内容 题目一、试写一个算法,判断依次读入的一个以@为结束符的字符序列,是否为回文。所谓“回文“是指正向读和反向读都一样的一字符串,如“321123”或“ableelba”。 相关常量及结构定义: #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef int SElemType; typedef struct SqStack { SElemType *base; SElemType *top; int stacksize; }SqStack; 设计相关函数声明: 判断函数:int IsReverse() 栈:int InitStack(SqStack &S )

int Push(SqStack &S, SElemType e ) int Pop(SqStack &S,SElemType &e) int StackEmpty(s) 一_三、数据结构与核心算法的设计描述 1、初始化栈 /* 函数功能:对栈进行初始化。参数:栈(SqStack S)。 成功初始化返回0,否则返回-1 */ int InitStack(SqStack &S) { S.base=(SElemType *)malloc(10*sizeof(SElemType)); if(!S.base) //判断有无申请到空间 return -1; //没有申请到内存,参数失败返回-1 S.top=S.base; S.stacksize=STACK_INIT_SIZE; S.base=new SElemType; return 0; } 2、判断栈是否是空 /*函数功能:判断栈是否为空。参数; 栈(SqStack S)。栈为空时返回-1,不为空返回0*/ int StackEmpty(SqStack S) { if(S.top==S.base) return -1; else return 0; } 3、入栈 /*函数功能:向栈中插入元素。参数; 栈(SqStack S),元素(SElemtype e)。成功插入返回0,否则返回-1 */ int Push(SqStack &S,SElemType e) { if(S.top-S.base>=S.stacksize) { S.base=(SElemType *)realloc(S.base,(S.stacksize+1) * sizeof(SElemType));

c c++ main 函数命令行参数的使用 知识小结

c/c++ main 函数命令行参数的使用知识小结 2012-12-31 13:13:43 我来说两句作者:wu_lai_314 收藏我要投稿C程序最大的特点就是所有的程序都是用函数来装配的。main()称之为主函数,是所有程 序运行的入口。其余函数分为有参或无参两种,均由main()函数或其它一般函数调用,若调用 的是有参函数,则参数在调用时传递。 C/C++语言中的main函数,经常带有参数argc,argv,如下: int main(int argc, char** argv)int main(int argc, char* argv[])从函数参数的形式上看,包含一个整型和一个指针数组。当一个C/C++的源程序经过编译、链接后,会生成扩展名为.EXE的可执行文件,这是可以在操作系统下直接运行的文件,换句话说,就是由系统来启动运行的。对main()函数既然不能由其它函数调用和传递参数,就只能由 系统在启动运行时传递参数了。 在操作系统环境下,一条完整的运行命令应包括两部分:命令与相应的参数。其格式为: 命令参数1参数2....参数n? 此格式也称为命令行。命令行中的命令就是可执行文件的文件名,其后所跟参数需用空格分隔,并为对命令的进一步补充,也即是传递给main()函数的参数。 命令行与main()函数的参数存在如下的关系: 设命令行为:program str1 str2 str3 str4 str5 其中program为文件名,也就是一个由program.c经编译、链接后生成的可执行文件program.exe,其后各跟5个参数。对main()函数来说,它的参数argc记录了命令行中命令与参数的个数,共6个,指针数组的大小由参数argc的值决定,即为char*argv[6],指针数组的取值情况如图6-15所示。

Main函数参数argc,argv说明

Main函数参数argc,argv说明 C/C++语言中的main 函数,经常带有参数argc,argv,如下:int main(int argc, char** argv) int main(int argc, char* argv[]) 这两个参数的作用是什么呢?argc 是指命令行输入参数的个数,argv 存储了 所有的命令行参数。假如你的程序是hello.exe,如果在命令行运行该程序,(首 先应该在命令行下用cd 命令进入到hello.exe 文件所在目录)运行命令为:hello.exe Shiqi Yu 那么,argc 的值是3,argv[0]是”hello.exe”,argv[1]是”Shiqi”,argv[2]是”Yu”。 下面的程序演示argc 和argv 的使用: #include int main(int argc, char ** argv) { int i; for (i=0; i 假如上述代码编译 为hello.exe,那么运行 hello.exe a b c d e 将得到 Argument 0 is hello.exe. Argument 1 is a. Argument 2 is b. Argument 3 is c. Argument 4 is d. Argument 5 is e.运行 hello.exe lena.jpg 将得到 Argument 0 is hello.exe. Argument 1 is lena.jpg.================================#include #include #include int main(int argc ,char * argv[]){int i;char arg[6][15]={0};printf(“argc: %d /n”,argc);for(i=0;i{sprintf(arg[i],”%s” ,argv[i]);}//for(i=1;i//printf(“%s/n”,arg[i]);execlp(“/bin/mkdir”,”mkdir”,arg[1],NULL); return 1;}======================================清单 2. BusyBox 使 用argv[0] 来确定调用哪个应用程序// test.c #include int main( int argc, char

建立堆栈和队列的库函数

建立堆栈和队列的库函数 摘要 堆栈是一种只允许在表的一端进行插入和删除运算的特殊的线性表。链式存储结构:栈的链式存储结构称为链栈,通常用单链表示。链栈的插入和删除操作只需处理栈顶的情况。每次进栈的数据元素都放在原当前栈顶元素之前成为新的栈顶元素,每次退栈的数据元素都是原当前栈顶元素,最后进入堆栈的数据元素总是最先退出堆栈。 队列是允许在表的一端进行插入,而在表的另一端进行删除的特殊线性表。允许进行插入的一端称为队尾,允许进行删除的一端称为队头。用链式存储结构存储的队列称为链队列。链队列的基本操作的实现基本上也是单链表操作的简化。通常附设头结点,并设置队头指针指向头结点,队尾指针指向终端结点。插入数据时只考虑在链队列的尾部进行,删除数据时只考虑在链队列的头部进行。 关键词:堆栈;队列;线性表;存储结构

第1章前言 栈和队列是两种常用的数据结构,广泛应用在编译软件和程序设计,操作系统、事物管理等各类软件系统中。从数据结构角度看,栈和队列是受限制的线性表,栈和队列的数据元素具有单一的前驱和后继的线性关系;从抽象数据类型角度看,栈和队列又是两种重要的抽象数据类型。 第2章堆栈和队列定义 2.1 定义 栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)。栈也称为后进先出表。 队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之最后插入的元素将是最后被删除的元素,因此队列又称为“先进先出”的线性表。 2.2 队列基本操作 2.2.1栈的建立和初始化: voidInitStack(SqStack * &s)

C语言主函数main的注意

# include main() { int i=50; while(i<=80) { szd-h: i += 10; if(i==80) goto szd-h cout<<"i is"<就行 第二,main 函数应该带上一个返回值int,这是出于兼容性的考虑,在前面的帖子中,有位兄弟找到了这样的一段话: 在 C 和C++ 中,不接收任何参数也不返回任何信息的函数原型为“void foo(void);”。可能正是因为这个,所以很多人都误认为如果不需要程序返回任何信息时可以把main 函数定义成void main(void) 。然而这是错误的!main 函数的返回值应该定义为int 类型,C 和C++ 标准中都是这样规定的。虽然在一些编译器中,void main 可以通过编译(如vc6),但并非所有编译器都支持void main ,因为标准中从来没有定义过void main 。g++3.2 中如果main 函数的返回值不是int 类型,就根本通不过编译。而gcc3.2 则会发出警告。所以,如果你想你的程序拥有很好的可移植性,请一定要用int main 。 有int main()后,就要加上return 0;当然,你不显示的写int 也就是说默认为int型,也要加上return 0;否则会有warning出现! 第三,标示符写错了,可能是作者的粗心所至,sxd_h,最好还是不要用这样的标示符,感觉没有什么可读性```` 第四,goto szd_h;后面要分号! 我稍微修改了一下,但是程序结构一点没变 # include using namespace std; int main() { int i=50; while(i<=80) { szd_h: i += 10; if(i==80)

main函数的参数

一、main函数的参数 我们经常用的main函数都是不带参数的。因此main 后的括号都是空括号。实际上,main函数可以带参数,这个参数可以认为是main函数的形式参数。C语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv。因此,main函数的函数头可写为:main (argc,argv)C语言还规定argc(第一个形参)必须是整型变量,argv( 第二个形参)必须是指向字符串的指针数组。加上形参说明后,main函数的函数头应写为: main (argc,argv) int argv; char *argv[];或写成: main (int argc,char *argv[]) 由于main函数不能被其它函数调用,因此不可能在程序内部取得实际值。那么,在何处把实参值赋予main函数的形参呢? 实际上,main函数的参数值是从操作系统命令行上获得的。当我们要运行一个可执行文件时,在DOS提示符下键入文件名,再输入实际参数即可把这些实参传送到main的形参中去。 DOS提示符下命令行的一般形式为:C:\>可执行文件名参数参数……; 但是应该特别注意的是,main 的两个形参和命令行中的参数在 位置上不是一一对应的。因为,main的形参只有二个,而命令行中的参数个数原则上未加限制。argc参数表示了命令行中参数的个数(注意:文件名本身也算一个参数),argc的值是在输入命令行时由系统按实际参数的个数自动赋予的。例如有命令行为:C:\>E6 24 BASIC dbase FORTRAN由于文件名E6 24本身也算一个参数,所以共有4个参数,因此argc取得的值为4。argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。指针数组的长度即为参数个数。数组元素初值由系统自动赋予。其表示如图6.8所示:main(int argc,char *argv){ while(argc-->1) printf("%s\n",*++argv); } 本例是显示命令行中输入的参数如果上例的可执行文件名为e24.exe,存放在A 驱动器的盘内。 因此输入的命令行为:C:\>a:e24 BASIC dBASE FORTRAN 则运行结果为: BASIC dBASE FORTRAN 该行共有4个参数,执行main时,argc的初值即为4。argv的4个元素分为4个字符串的首地址。执行while语句,每循环一次argv值减1,当argv等于1时停止循环,共循环三次,因此共可输出三个参数。在printf函数中,由于打印项*++argv是先加1再打印,故第一次打印的是argv[1]所指的字符串BASIC。第二、三次循环分别打印后二个字符串。而参数e24是文件名,不必输出。