精华内容
下载资源
问答
  • <p style="text-align:center"><img alt="" src="https://img-ask.csdnimg.cn/upload/1623158045891.jpg" /></p>  </p>
  • <p style="text-align:center"><img alt="" height="292" src="https://img-ask.csdnimg.cn/upload/1611987004202.png" width="429" /></p> ... 无奈呀,找百度找了半天也没有解决的办法=...
  • <p style="text-align:center"><img alt="" src="https://img-ask.csdnimg.cn/upload/1623156135926.jpg" /></p>  </p>
  • 在cmd窗口编译运行C语言程序总共分两步:第一步,在编译器目录下执行编译命令;第二步,在编译器目录下运行可执行程序。需要插件?完全没这回事儿!下面用一个例子来说明。一、经典的C程序代码最经典的C程序代码就是...

    在cmd窗口编译运行C语言程序总共分两步:第一步,在编译器目录下执行编译命令;第二步,在编译器目录下运行可执行程序。需要插件?完全没这回事儿!下面用一个例子来说明。

    一、经典的C程序代码

    156475779_1_201903140903326

    最经典的C程序代码就是这个“Hello, World!”

    不过我可没有使用任何的IDE(集成开发环境)来写这段C程序代码,相反我就用win自带的文本文件来写的,别忘了把文件后缀名改为c啊,不是txt!我的这段C程序代码文件名叫做“1.c”。

    二、CMD窗口下编译

    我使用的编译器是流行的GCC编译器。我把它安装在C盘的一个目录下。

    现在打开CMD窗口,进入GCC编译器所在的目录,同时把我刚才写的那段C程序代码文件“1.c”拷贝到GCC编译器所在的目录下。

    OK,现在我们执行最简单的编译命令,如下图所示:

    156475779_2_20190314090332131

    如果没有任何提示的话,就表明编译通过了,在GCC编译目录下会生成一个可执行文件:a.exe

    三、运行C程序代码

    经过上面一步我们生成了一个可执行文件:a.exe,下面来运行它。

    由于这个可执行文件的后缀名是exe,那么我们只需要直接运行它即可,当然了,也是在CMD窗口运行,方法如下:

    156475779_3_20190314090332209

    你看,就是这么简单!

    不过此处我要留一个疑问。如果我双击这个可执行文件的话,也是可以正常运行的,只是这个运行窗口会一闪而过。那么我如何让这个运行窗口等待我的指令再去消失呢?可以在下面一起讨论。

    在CMD窗口编译、运行程序是古老的方法了,是过去程序员的编程方法。现如今有很多的IDE可以供选择。不过不管咋样,我们程序员也不能忘记曾经这种流行的编程方法!

    展开全文
  • C语言获取程序运行时间

    万次阅读 多人点赞 2018-05-29 18:28:51
    用于获取程序启动时间到这次调用clock函数的CPU占用时间其在time文件里还有一个宏叫#define CLOCKS_PER_SEC ((clock_t)1000)用于衡量秒的单位,1000也就是千分之一秒,也就是当过了一毫秒clock会自动增加1在其他操作...

    所需头文件:"time.h"

    所需函数:clock

    函数原型:

    clock_t __cdecl clock(void);

    函数介绍:

    用于获取程序启动时间到这次调用clock函数的CPU占用时间

    其在time文件里还有一个宏叫#define CLOCKS_PER_SEC ((clock_t)1000)

    用于衡量秒的单位,1000也就是千分之一秒,也就是当过了一毫秒clock会自动增加1

    在其他操作系统下可能各有不同,但是可以经过时间汇率换算出相应的秒差,在计算之前可以用printf打印看一下

    返回值:

    clock_t //long 长整型

    用法:

    	clock_t start, finish;     //定义第一次调用CPU时钟单位的实际,可以理解为定义一个计数器
    	double Total_time;        //定义一个double类型的变量,用于存储时间单位
    	start = clock();        //获取进入要测试执行时间代码段之前的CPU时间占用值
         int i = 100000L;while (i--);    //循环 0假

    finish = clock();    //获取执行完后的CPU时间占用值Total_time = (double)(finish - start) / CLOCKS_PER_SEC;    //单位换算,换算成毫秒printf("\n函数运行时间:%0.3f毫秒 \n", Total_time); //打印小数点的后三位,毫秒为单位,计算机最低以皮秒-纳秒等为最低单位getchar();return 0;

    运行结果:

    这里要解释一下这段代码

    Total_time = (double)(finish - start) / CLOCKS_PER_SEC;    //单位换算,换算成毫秒

    上面说过clock是获取程序启动到调用clock这个函数时所用的CPU占用时间,怎么获取?

    答:PCB进程控制块里有一个clock_t 的成员变量,当你程序获取到CPU控制权时该值会不断递增,不以秒为单位以CPU赫兹执行你代码频率为单位的递增!

    clock会把这个值并根据宏定义的CLOCKS_PER_SEC来进行毫秒单位转换,假如clock_t是1000,那么CLOCKS_PER_SEC也是1000,clock就会返回给你1毫秒!

    clock以毫秒为单位

    这里我们要计算的是要测试的代码执行时间,而非程序运行时间!

    所以我们要把程序执行要测试的代码之前的时间段保存下来:

    start = clock();        //获取进入要测试执行时间代码段之前的CPU时间占用值

    然后在执行,执行完以后在程序总执行时间

     int i = 100000L;while (i--);    //循环 0假	finish = clock();    //获取执行完后的CPU时间占用值

    最后在用程序总执行时间减去程序执行要测试代码段之前的执行时间的时间得到程序执行要测试代码的时间!

    Total_time = (double)(finish - start) / CLOCKS_PER_SEC;    //单位换算,换算成毫秒

    后面的除去CLOCKS_PER_SEC,因为clock_t是长整型 long,返回的是整形,所以这里我们要以(double)类型转换以浮点数类型返回,然后除去1000,得到毫秒单位!

    假如finish是135毫秒(上面说clock函数会根据PCB进程控制块里的clock_t成员变量值以1000为换算单位,换算成毫秒),start是75毫秒,代码段执行时间是60毫秒(135-75),如果直接以double类型返回就是60.00000000,除去换算单位:1000,也就得到:0.06000000,也就是60毫秒,打印时0.3f,用于控制打印输出格式,因为我们只要小数点后三位,1秒=1000毫秒,即可得出换算单位!

     

    展开全文
  • 原标题:敲代码4年多了,发现C语言里几个有意思的问题虽然自己敲了4年多代码了,但是还敢说自己精通了C语言,今天研究了一番,介绍几个有意思的问题吧。前言自己虽然一直交叉的敲着 C 和 c plus plus 两种语言,...

    原标题:敲代码4年多了,发现C语言里几个有意思的问题

    虽然自己敲了4年多代码了,但是还不敢说自己精通了C语言,今天研究了一番,介绍几个有意思的问题吧。

    前言

    自己虽然一直交叉的敲着 C 和 c plus plus 两种语言,但是其实自己就是使用一下常用的语法。

    工作后又没有那么时间来看书,于是研究了一些C语言的细节来学习学习。

    建议看的时候先不要看问题分析,这样才能考察自己到底会不会的。

    遍历数组

    问题

    有时候我们要遍历一个不知道大小的数组,但是我们有数组的名字,于是我们可以通过 sizeof 获得数组的大小了。

    有了大小我们就可以遍历这个数组了。

    一般情况下大家都是从下标 0 开始计数,于是从来不会遇到下面的问题。

    如果你遇到下面的问题你能想出是什么原因吗?

    代码

    #include

    #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))

    int array[] = {23,34,12,17,204,99,16};

    int main {

    for(int d=-1; d <= (TOTAL_ELEMENTS-2); d++) {

    printf("%dn",array[d+1]);

    }

    return 0;

    }

    输出

    Process returned 0 (0x0) execution time : 0.050 s

    Press any key to continue.

    end

    分析

    这个原因我一提,大家也都可以想到。

    sizeof 返回的类型是 unsigned int .

    unsigned int 与 int 进行运算还是 unsigned int。

    然后 -1 和 unsigned int 比较,会先把 -1 转化为 unsigned int。

    这样 -1 的 unsigned int 就很大了,所以没有输出了。

    do while

    问题

    大家在 do while 中使用过 continue 吗?

    没有的话来看看这个问题吧。

    代码

    #include

    int main {

    int i=1;

    do {

    printf("%dn",i);

    i++;

    if(i < 15) {

    continue;

    }

    } while(false);

    return 0;

    }

    输出

    1

    Process returned 0 (0x0) execution time : 0.041 s

    Press any key to continue.

    end

    分析

    这个需要查看文档了:continue 会到循环的哪个地方继续运行。

    于是我查看了官方文档

    For the for loop, continue causes the conditional test and increment portions of the loop to execute.

    For the while and do...while loops, program control passes to the conditional tests.

    什么意思呢?

    for 循环遇到 continue 会执行for 小括号内的第三个语句。

    while 和 do...while 则会跳到循环判断的地方。

    宏的展开

    问题

    大多数情况下,我们的宏定义常常是嵌套的。

    这就涉及到展开宏定义的顺序了。

    下面来看看其中一个问题。

    代码

    #include

    #define f(a,b) a##b

    #define g(a) #a

    #define h(a) g(a)

    int main {

    printf("%sn",h(f(1,2)));

    printf("%sn",g(f(1,2)));

    return 0;

    }

    输出

    12

    f(1,2)

    Process returned 0 (0x0) execution time : 0.041 s

    Press any key to continue.

    end

    分析

    这个问题又需要查看文档了:Macro 是怎么展开的。

    于是我查看了官方文档

    Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens.

    After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded.

    The result is that the arguments are scanned twice to expand macro calls in them.

    简单的说就是 宏会扫描一遍,把可以展开的展开,展开一次后会再扫描一次看又没有可以展开的宏。

    下面我们模拟一下这个过程就可以明白了。

    对于第一个,是下面的过程。

    > h(f(1,2))

    > g(f(1,2))

    > g(12)

    > g(12)

    > "12"

    对于第二个,是这个过程。

    > g(f(1,2))

    > "f(1,2)"

    print 返回值

    问题

    你知道 printf 的返回值是什么吗?

    猜猜下面的代码输出是什么吧。

    代码

    #include

    int main {

    int i=43;

    printf("%dn",printf("%d",printf("%d",i)));

    return 0;

    }

    输出

    4321

    Process returned 0 (0x0) execution time : 0.035 s

    Press any key to continue.

    end

    分析

    printf 的返回值是输出的字符的长度。

    所以第一个输出 43 返回2.

    第二个输出 2 返回 1. 第三个输出1. 于是输出的就是 4321 了。

    数组参数

    问题

    对于函数传参为数组时,你知道到底传的是什么吗?

    代码

    #include

    #define SIZE 10

    void size(int arr[SIZE][SIZE]) {

    printf("%d %dn",sizeof(arr),sizeof(arr[0]));

    }

    int main {

    int arr[SIZE][SIZE];

    size(arr);

    return 0;

    }

    输出

    4 40

    Process returned 0 (0x0) execution time : 0.039 s

    Press any key to continue.

    end

    分析

    对于第二个输出,应该是 40 这个大家都没有什么疑问的。

    但是第一个是几呢?

    你是不是想着是 400 呢?

    答案是 4.

    这是因为对于数组参数。第一级永远是指针形式。

    也就是说数组参数永远是指针数组。

    所以第一级永远是指针,而剩下的级数由于需要使用 [] 运算符, 所以不能是指针。

    sizeof 的参数

    问题

    当我们有时候想让代码简洁点的时候,会把运算压缩到一起。

    但是在 sizeof 中就要小心了。

    代码

    #include

    int main {

    int i;

    i = 10;

    printf("i : %dn",i);

    printf("sizeof(i++) is: %dn",sizeof(i++));

    printf("i : %dn",i);

    return 0;

    }

    输出

    i : 10

    sizeof(i++) is: 4

    i : 10

    Process returned 0 (0x0) execution time : 0.039 s

    Press any key to continue.

    end

    分析

    你猜第二个i的输出时什么呢?

    11 吗?

    恭喜你,猜错了。

    这个还需要查看文档了。

    首先我的印象中 sizeof 是个宏,在编译器运算的。

    The sizeof is a keyword, but it is a compile-time operator that determines the size, in bytes, of a variable or data type.

    文档上说 sizeof 是一个关键字,但是在编译器运算。

    所以编译器是不会进行我们的那些算术等运算的。

    而是直接根据返回值推导类型,然后根据类型推导出大小的。

    位运算左移

    问题

    这个问题没什么说的,你运行一下就会先感到诧异,然后会感觉确实应该是这个样字,甚至会骂这代码写的太不规范了。

    代码

    #include

    #define PrintInt(expr) printf("%s : %dn",#expr,(expr))

    int FiveTimes(int a) {

    return a<<2 + a;

    }

    int main {

    PrintInt(FiveTimes(1));

    return 0;

    }

    输出

    FiveTimes(1) : 8

    Process returned 0 (0x0) execution time : 0.624 s

    Press any key to continue.

    end

    分析

    需要我提示吗?

    三个字:优先级

    浮点数

    问题

    大家经常使用 浮点数,知道背后的原理吗?

    代码

    #include

    int main {

    float a = 12.5;

    printf("%dn", a);

    printf("%dn", *(int *)&a);

    return 0;

    }

    输出

    12.500000

    1095237632

    Process returned 0 (0x0) execution time : 0.651 s

    Press any key to continue.

    end

    分析

    首先 int 和 float 在 32位机器上都是 四字节的。

    对于整数储存,大家都没什么疑问。

    比如 10 的二进制,十六进制如下

    00000000 00000000 00000000 00001010

    0X0000000A

    由于最高位代表符号,所以整数可以表示的范围就是

    0X80000000 -2^32

    0XFFFFFFFF -1 负整数最小值

    0X00000000 0

    0X00000001 1 正整数最小值

    0X7FFFFFFF 2^32-1 正整数最大值

    上面的二进制也就决定了 4字节的整数范围是 -2 ^ 32 到 2 ^ 32 - 1 .

    对于一个浮点数,可以表示为 (-1)^S * X * 2^Y .

    其中 S 是符号,使用一位表示。

    X 是一个 二进制在 [1, 2) 内的小数,一般称为尾数,用23位表示。

    Y 是一个整数,代表幂,一般称为阶码,用8位表示。

    其中 Y 又涉及符号问题了。

    8位的Y可以表示0到255,取中间数127作为分界线,小于127的数是负数,大于的是正数。

    这里我不明白为什么不使用以前数字的表示方法。

    比如 12.5 的二进制是 1100.1 。

    转化为上面的公式就是 (-1)^0 * 1.1001 * 2^3

    下面我们来推导一下这个数字的二进制是什么吧。

    符号为正,所以第一位就是0了

    3 + 127 就是 130 了。于是使用 10000010 可以表示。

    1.1001 一般不表示小数前的1,于是只需要表示 1001 即可,于是使用 10010000000000000000000 就可以表示了。

    于是 12.5 的 float 的二进制表示就推算出来了

    0 10000010 10010000000000000000000

    01000001 01001000 00000000 00000000

    然后这个二进制对应着整数 1095237632 。

    这样一切都解释清楚了。

    当然还要注意一个问题,这里有这么一个特殊规定:阶码Y如果是0, 尾数X就不再加1了。

    来自:tiankonguse

    链接:http://github.tiankonguse.com/blog/2014/12/05/c-base/

    你和大牛工程师之间到底差了啥?

    加入技术交流群,与高手面对面

    加入“中国电子网微信群”交流

    具体加群详情请戳

    →“中国电子网技术交流群”←返回搜狐,查看更多

    责任编辑:

    展开全文
  • 程序运行不了的原因 #include #include int main(void) { char str[1000];//定义一个数组 gets(str); int len=strlen(str); for (int i=len;i>0;i--) { if(str[i]!=' '||str[i]!='\n') { printf("%c",str[i]); } ...
  • 嵌入式C语言程序运行

    千次阅读 2012-09-05 21:02:52
    嵌入式C语言程序运行 2011-08-16 15:05  我们做C语言中这么多年,都知道这样一句话,C语言代码形成可执行程序,需要经过编译->汇编->链接三个阶段。背都背熟了,但是到底啥意思,每一步都会产生一些...

     

    嵌入式C语言程序的运行

    2011-08-16 15:05

       我们做C语言中这么多年,都知道这样一句话,C语言代码形成可执行程序,需要经过编译->汇编->链接三个阶段。背都背熟了,但是到底啥意思,每一步都会产生一些什么东西,很多人都不是太了解。今天就详细的来说说这个问题:

      先看下图,在这个图中,我详细的描述了,整个过程及中间的一些步骤:

        代码段,只读数据段,读写数据段,未初始化数据段属于静态区域。栈和堆属于动态区域。代码段,只读数据段和读写数据段将在连接之后产生,未初始化数据段将在程序初始化的时候开辟,而堆和栈将在程序的运行中分配和释放。

       C语言程序分为映像和运行两种状态。在编译连接后形成的映像中,将只包含代码段,只读数据段和读写数据段。在程序运行之前,将动态生成未初始化数据段,在程序的运行时还将动态形成堆和栈区域。

    在嵌入式系统中,程序最终是要放置在内存中运行的,程序的几个段,最终会转化为内存中的几个区域。C语言可执行程序的内存布局如图13-5所示。

    13-5 C语言可执行程序的内存布局

    在内存中,从低地址到高地址,依次是只读段、读写段、未初始化数据段、堆段、栈段。

    映像文件中将包含代码段(Code)、只读数据段(RO Data)以及读写数据段(RW Data),未初始化数据段(BSS)将在程序的初始化阶段中开辟,堆栈在程序运行时动态开辟。

    只读区(RO)包括了代码和只读数据,在内存区域中,代码段(Code)和只读数据段(Ro Data)的存放形式上基本没有区别。

    对于程序运行时的内存使用,堆和栈一般是相向扩展的。堆的分配由程序决定,栈由编译器管理。

    在以上概念中,只是一种内存分布,并没有考虑实际系统的情况。在实际的系统中,程序有载入和运行两个概念。嵌入式系统由两种内存,一种是可以固化只读的内存(如:ROMNor Flash),另一种是易失的可读写的内存(如:SRAMSDRAM)。程序中的各个段也有需要固化和需要读写的。程序中的各段必须载入到内存的恰当位置,程序才可以运行。C语言各部分的需要固化和可写的情况如表13-2所示。

    13-2 C语言各部分的需要支持固化和可写的情况

    需要固化

    需要可写

    代码(Code

    只读数据(RO data

    读写数据(RW data

    未初始化数据(BSS

    堆(heap

    栈(stack

     

     

     

     

    在嵌入式系统中,经过编译的C语言程序可以通过操作系统运行,也可以在没有操作系统的情况下运行。程序存放的位置和运行的位置通常是不一样的。

    一般情况下,经过编译后的程序存储在Flash或者硬盘中,在运行时需要将程序加载到RAM中。嵌入式系统的 Nor Flash和硬盘还有一定的差别,在硬盘的程序必须加载到RAM中才可以运行,但是在Nor Flash中的程序可以通过XIPeXcutive In Place)的方式运行。

    在嵌入式系统中,C语言程序的运行包括3种类型:第一种是调试阶段的程序运行,这个阶段程序存放的位置和运行的位置是相同的;第二种是程序直接在Flash中运行(XIP);第三种是将Flash或者硬盘中的程序完全加载到RAM中运行。

    C语言程序的运行中,存在着两个基本的内存空间,一个是程序的存储空间,另一个是程序的运行空间。程序的存储空间必须包括代码段、只读数据段和读写数据段,程序的加载区域必须包括读写数据段和未初始化数据段如表13-3所示。

    13-3 C语言各部分使用的存储空间

    代码

    只读数据

    读写数据

    未初始化数据

    程序的存储空间(ROM

    需要

    不需要

    程序的加载空间(RAM

    不需要

    需要

    由于程序放入系统后,必须包括所有需要的信息,代码表示要运行的机器代码,只读数据和读写数据包含程序中预先设置好的数据值,这些都是需要固化存储的,但是未初始化数据没有初值,因此只需要标示它的大小,而不需要存储区域。

    在程序运行的初始化阶段,将进行加载动作,其中读写数据和未初始化数据都是要在程序中进行操作,因此不可能放在只读的区域内,必须放入RAM中。当然,程序也可以将代码和只读数据放入RAM

    在程序运行后,堆和栈将在程序运行过程中动态地分配和释放。

    13.4.1 RAM调试运行

    本节介绍程序的一种特殊的运行方式,即在程序的调试阶段将主机的映像文件直接放置到目标系统的RAM中。在这种应用中,RAM既是程序的存储空间,也是程序的运行空间。

    在嵌入式系统中,这是一种常用的调试方式,而不是通常的运行方式。在通常的运行方式下,程序运行的起始地址一般不可能是RAMRAM在掉电之后内容会丢失,因此系统上电的时候,RAM中一般不会有有效的程序。但是在程序的调试阶段,可以将程序直接载入RAM然后在RAM的程序载入地址处运行程序。

    嵌入式系统RAM中的调试程序的内存布局如图13-6所示。

    13-6 RAM中的调试程序的内存布局

    这是一种相对简单的形式,因为代码段的存储地址和运行地址是相同的,都是RAMSDRAM或者SRAM)中的地址。在这种情况下,程序没有运行初始化阶段加载的问题。

    从主机向目标机载入程序的时候,程序映像文件中代码段(codetext)、只读数据段、读写数据段依次载入目标系统RAMSDRAM或者SRAM)的空间中。

    程序载入到目标机之后,将从代码区的地址开始运行,在运行的初始化阶段,将开辟未初始化数据区,并将其初始化0,在运行时将动态开辟堆区和栈区。

    在没有操作系统的情况下,开辟内存的工作都是由编译器生成的代码完成的,实现的原理是在映像文件中加入这些代码。主要工作包括:在程序运行时根据实际大小开辟未初始化的数据段;初始化栈区的指针,这个指针和物理内存的实际大小有关;在调用相关函数mallocfree)时使用堆区,这些函数一般由调用库函数实现。表13-4列出了C语言程序在RAM中的调试过程。

    13-4 C语言程序在RAM中的调试过程

    阶段

    涉及的部分

    主要工作

    程序的映像

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    将程序放置在RAM

    初始化阶段

    未初始化数据段(BSS

    开辟BSS并且清零

    运行阶段

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    未初始化数据段(BSS

    堆(heap

    栈(stack

    运行RAM码段中的程序,动态地在RAM中开辟堆和栈

    知识点:程序直接载入RAM运行时,程序的加载位置和运行位置是一致的,因此不存在段复制的问题,需要在初始化阶段开辟未初始化区域,在运行时使用堆栈。

    13.4.2 固化程序的XIP运行

    固化应用是一种嵌入式系统常用的运行方式,其前提是目标代码位于目标系统ROMFlash)中。ROM中的区域包括映像文件的代码段(codetext)、只读数据段(RO Data)、读写数据段(RW Data)。

    XIP(在位置执行)方式运行程序时内存布局如图13-7所示。

    代码的运行也是在ROMFlash)中,因此,在编译过程中代码的存储地址和运行地址是相同的,由于上电时需要启动,因此该代码的位置一般是(0x0)。

    在这种应用中,一件重要的事情就是将已初始化读写段的数据从Flash中复制到SDRAM中,由于已初始化读写段既需要固化,也需要在运行时修改,因此这一步是必须有的,在程序的初始化阶段需要完成这一步。

    13-7 XIP运行程序时的内存布局

    一般来说,在编译过程中需要定义读写段和未初始化段的地址。在程序中可获取这些地址,然后就可以在程序的中加入复制的代码,实现读写段的转移。表13-5列出了C语言程序的XIP运行过程。

    13-5 C语言程序的XIP运行过程

       

    涉及的部分

    主要工作

    程序的映像

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    程序放置在Flash

    初始化阶段

    读写数据段(RW Data

    未初始化数据段(BSS

    复制读写数据段到RAM

    开辟未初始化段并且清零

    运行阶段

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    未初始化数据段(BSS

    堆(heap

    栈(stack

    运行Flash码段中的程序,动态地在RAM中开辟堆和栈

    知识点:程序在ROM或者Flash中以XIP形式运行的时候,不需要复制代码段和只读数据段,但是需要在RAM中复制读写数据段,并另辟未初始化数据段。

    13.4.3 固化程序的加载运行

    在某些时候,在存放程序的位置是不能运行程序的,例如程序存储在不能以XIP方式运行的Nand-Flash或者硬盘中,在这种情况下,必须将程序完全加载到RAM中才可以运行。固化程序加载运行的内存布局如图13-8所示:

     



    13-8 固化程序加载运行的内存布局

    依照这种方式运行程序,需要将Flash中所有的内容全部复制到SDRAM或者SRAM中。在一般情况下,SDRAM或者SRAM的速度要快于Flash。这样做的另外一个好处是可以加快程序的运行速度。也就是说,即使Flash可以运行程序,将程序加载RAM中运行也还有一定的优势。

    这样做也产生了另外一个问题:代码段的载入地址和运行地址是不相同的,载入地址是在ROMFlash)中,但是运行的地址是在RAMSDRAM或者SRAM)中。对于这个问题,不同的系统在加载程序的时候有不同的解决方式。

    C语言固化程序的加载运行过程如表13-6所示。

    13-6 C语言固化程序的加载运行时各段的情况

       

    涉及的部分

    主要工作

    代码的映像

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    将程序放置在Flash

    初始化阶段

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    未初始化数据段(BSS

    加载代码段和只读数据段到RAM

    复制读写数据段到RAM

    开辟未初始化段并且清零

    运行阶段

    代码段(Code

    只读数据段(RO Data

    读写数据段(RW Data

    未初始化数据段(BSS

    堆(heap

    栈(stack

    运行RAM码段中的程序,动态地在RAM中开辟堆和栈

    知识点:固化程序在加载运行时,需要复制代码段、只读数据段和读写数据段到RAM中,并另辟未初始化数据段,然后在RAM中运行程序(执行代码段)。

    以这种加载方式的运行程序,另外一个重要的问题是:如何把代码移到RAM中。在有操作系统的情况下,代码的复制工作是由操作系统完成的,在没有操作系统的情况下,处理方式相对复杂,程序需要自我复制。显然,这种方式实现的前提是代码最初放置在可以以XIP方式执行的内存中。

    程序本身复制的过程也是需要通过程序代码完成的,这时需要程序中的代码根据将包含自己的程序从ROM或者Flash中复制到RAM中。这是一个比较复杂的过程,程序的最前面部分是具有复制功能的代码。系统上电后,从ROM或者Flash起始地址运行,具有复制功能的代码将全部代码段和其他需要复制的部分复制到RAM中,然后跳转到RAM中重新运行程序。

    固化程序加载复制和跳转过程如图13-9所示。



    13-9 固化程序加载复制和跳转过程

    在代码的前面一小部分是初始化的内容,这部分内容中有一部分是复制程序,这段复制程序将代码段复制至RAM中,当这段初始化程序运行完成后,将跳转到RAM中的某个地址运行。

    13.4.4 C语言程序的运行总结

    在上面几节,主要介绍了C语言运行时内存的使用情况。其关注点是程序中主要的段,事实上,程序可能不仅包括了上述主要段,还可能包括一些头信息。程序实际的运行也分为在操作系统下运行和直接运行等情况。在具有操作系统的情况下,程序由操作系统加载运行,加载的时候可执行程序可以是一个文件,这个文件将包含程序的主要段以及头信息。

    对于Linux操作系统,目标程序是可执行的 ELFExecutable and linking Format)格式;对于uCLinux操作系统,目标程序是Flat格式;对于需要在系统直接运行的程序,目标程序应该是纯粹的二进制代码,载入系统后,直接转到代码区地址执行。

    事实上,无论运行环境如何,C语言程序在运行时所进行的动作都是类似的。程序在准备开始运行的时候,以下几个条件都是必不可少的:

    1.代码段必须位于可运行的存储区。

    2.读写数据段必须在可以读写的内存中,而且必须经过初始化。

    3.未初始化数据段必须在可以读写的内存中开辟,并被清空。

    对于第1点,代码段如果位于可以运行的存储区域中(例如Nor Flash或者RAM),它就不需要加载,可以直接运行;如果代码段位于不能运行的存储区域中(例如:Nand Flash或者硬盘)中,它就必须被加载到RAM运行。

     

     

    展开全文
  • #include<stdio.h> #include<stdlib.h>... printf("3能走的区域 空格表示未到的区域\n"); Random(); InitStack(S); MazePath(mg[20][20],start,end,S); PrintMaze(); }
  • 上午按CTRL+F5还可以运行来着,下午就可以了,咋回事啊</p>
  • 各位大佬能能帮看看这是什么错误啊 error C2061: syntax error : identifier 'n'
  • C/C++ code#include void output(struct student stu);struct student{long number;char name[20];char sex;float score[3];};struct student stu[2];void main(){int i,j;for(i=0;i<...i++){printf("请输入第%d个...
  • 一级标题VS 2019如何运行c语言程序 ****## 标题 注:VS中是没办法写纯c的,VS只支持CPP,但毕竟C是CPP的真子集,所以写时要创建CPP的Project. 1.创建新项目 2.选择空项目,单机下一步 3.填写项目名称(生成的文件...
  • 关于c语言问题改错 程序出了一个错运行不了 请各位帮忙我程序输完了有一个错误怎么都找不出来,请大神们帮忙找一下并改正,谢了!!是关于学生籍贯信息的每个学生信息包括:学号、姓名、性别、籍贯、年龄。主要功能...
  • 如果你熟悉Functors,你可以阅读它们here.或者,由于问题没有提供代码示例,我们假设您已经编写了启动和停止电机的功能.对于更直接的方法,with C++11,你可以这样做:StartMotor(1);// Stop in 2...
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼想做一个把科学计数法转换成实数的程序,为什么输入1 e 1,运行结果出现乱码,一点都幸福#include#include#includemain(){char a[50],b[50],c;int i,k,d,m,n=0,p,q,e=0;...
  • 实用标准文档 VC++6.0中如何编译运行 C语言程序 VC++6.0是 Microsoft 公司推出的一个基于 Windows系统平台 可视化的集成开发环境 它的 源程序按 C++语言的要求编写并加入了微软提供的功能强大的 MFC(Microsoft ...
  • 1可执行文件的格式在UNIX传统的操作系统中,所有编译生成的输出文件都缺省地使用同一个名字a.out,在现代操作系统中,a.out格式的可执行文件是链接器的输出,而不是汇编程序的输出(在计算机的远古时代,a.out是汇编...
  • C语言 第一个C语言程序

    千次阅读 2016-01-10 21:10:55
    前言 前面已经唠叨了这么多理论知识,从这讲开始,就要通过...由于我们的最终目的是学习iOS开发,学习iOS开发的话必须在Mac系统下,因此我就在Mac系统环境下开发C语言程序,而不是在Windows环境下。   接下来,就在
  • 不管你是否决定使用C-Free,都建议阅读《在C-Free下运行C语言程序》,文中讲到了几个重要概念,对初学者大有裨益。Visual C++ 6.0简称VC或者VC6.0 确实有点老了,在XP、Win7下都可能会存在兼容性问题,在Win8下根本...
  • 因为Android使用的也是linux内核,所以我想当然的认为按照同样的方法将程序移植到mini6410的android系统中也可以成功运行,但是当我运行程序的时候却提示我能找到可执行文件(xlisten-arm是交叉编译出来的可执行...
  • 可是写完之后编译运行还是要放到一些IDE中去运行,电脑配置高,打开个环境还要个几分钟,于是就想着能能在windows上调用GCC,搜了下,还真有,GNU的大神们已经将GCC编译器和GNU Binutils(一整套的编程语言工具...
  • 开发环境选择了Visual C++2010,但是与VC2006还是有很大区别的,即使要开个C程序也不得找度娘。在这里记录一下VC++2010创建并运行C程序的过程,一个是加深印象,一个也是想对有同样问题的同学有个参考作用。1、...
  • =0) //系数和为零,改变qa的系数,Pa中的改变结束,ha更新(后移一位) { qa->coef=sum; ha=qa; } else //系数和为零 { Delfirst(ha,qa); //删除Pa中ha指向的结点后的第一个结点,即qa指向的...
  • #include#include#define SIZE 200typedef struct node{int len,num[SIZE];}hugeint;hugeint times(hugeint#include#include#define SIZE 200typedef struct node{int len,num[SIZE];}hugeint;hugeint times(hugeint...
  • 本文从一个最简单C程序(hello)是如何被编译和链接讲起,描述了一个程序运行的原理。 一、程序运行之前 使用IDE(集成开发环境)的朋友们经常会有这样的疑问:代码是怎么从一个文本文件变成可执行程序的呢?代码...
  • 手把手教你用vim编译器运行一个C语言程序

    千次阅读 多人点赞 2019-12-12 17:58:59
    用vim编译器运行一个C语言程序菜鸟实例 菜鸟实例 1、首先你需要在Linux操作系统环境下(可以是虚拟机),这是大前提 2、ctrl+alt+t 三个键一起按,能让你进入vim编译器,大致长这个样子 3、在光标下输入 vi ...
  • 正常情况下,int main() 函数返回值应该是0,但是有些情况下我们的程序结束返回结果并不是正常的值 如下: 异常结束示例: 返回值: 3221225477 (0xC0000005): 访问越界,一般是读或写了野指针指向的内存 ...
  • #include<stdio.h> double main(){ double a,b,c,d; printf("请输入方程的未知数系数和常数项,并按Enter键求解:"); scanf("%%%%",&a,&b,&c,&d); double x; for(x=0.00;...}

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,645
精华内容 1,458
关键字:

c语言程序运行不了

c语言 订阅