第二次作业(4.c语言)

1.printf和scanf

printf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。printf函数调用的一般形式为: printf(“格式控制字符串”,输出列表)其中格式控制字符串用于指定输出格式。 格式控制串可由格式字符串和非格式字符串两种组成。格式字符串是以%开头的字符串,在%后面跟有各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。

  在Turbo C中格式字符串的一般形式为: [标志][输出最小宽度][.精度][长度]类型 其中方括号[]中的项为可选项。

各项的意义介绍如下:
      1.类型类型字符用以表示输出数据的类型,其格式符和意义下表所示:
      表示输出类型的格式字符       格式字符意义
      d                 以十进制形式输出带符号整数(正数不输出符号)
      o                 以八进制形式输出无符号整数(不输出前缀O)
      x                 以十六进制形式输出无符号整数(不输出前缀OX)
      u                 以十进制形式输出无符号整数
      f                 以小数形式输出单、双精度实数
      e                 以指数形式输出单、双精度实数
      g                 以%f%e中较短的输出宽度输出单、双精度实数
      C                 输出单个字符
      s                 输出字符串


      2.标志
      标志字符为-、+、#、空格四种,其意义下表所示:  
      标志格式字符      标 志 意 义
      -           结果左对齐,右边填空格
      +          输出符号(正号或负号)空格输出值为正时冠以空格,为负时冠以负号
      #          对C,s,d,u类无影响;对o类, 在输出时加前
      缀o         对x类,在输出时加前缀0x;对e,g,f 类当结果有小数时才给出小数点


      3.输出最小宽度
      用十进制整数来表示输出的最少位数。 若实际位数多于定义的宽度,则按实际位数输出, 若实际位数少于定义的宽度则补以空格或0。


      4.精度
      精度格式符以“.”开头,后跟十进制整数。本项的意义是:如果输出数字,则表示小数的位数;如果输出的是字符, 则表示输出字符的个数;若实际位数大于所定义的精度数,则截去超过的部分。


      5.长度
      长度格式符为h,l两种,h表示按短整型量输出,l表示按长整型量输出。


scanf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。scanf函数的一般形式为: scanf(“格式控制字符串”,地址表列); 其中,格式控制字符串的作用与printf函数相同,但不能显示非格式字符串, 也就是不能显示提示字符串。地址表列中给出各变量的地址。 地址是由地址运算符“&”后跟变量名组成的。

格式字符串

      格式字符串的一般形式为: %[*][输入数据宽度][长度]类型 其中有方括号[]的项为任选项。各项的意义如下:
      1.类型

      表示输入数据的类型,其格式符和意义下表所示。
      格式    字符意义  
      d     输入十进制整数
      o     输入八进制整数
      x     输入十六进制整数
      u     输入无符号十进制整数
      f或e    输入实型数(用小数形式或指数形式)
      C     输入单个字符
      s     输入字符串


      2.“*”符

      用以表示该输入项读入后不赋予相应的变量,即跳过该输入值。 如 sCanf("%d %*d %d",&a,&b);当输入为:1 2 3 时,把1赋予a,2被跳过,3赋予b。



      3.宽度

      用十进制整数指定输入的宽度(即字符数)。例如: sCanf("%5d",&a);
      输入:
      12345678
      只把12345赋予变量a,其余部分被截去。又如: sCanf("%4d%4d",&a,&b);
      输入:
      12345678将把1234赋予a,而把5678赋予b。


      4.长度

       长度格式符为l和h,l表示输入长整型数据(如%ld) 和双精度浮点数(如%lf)。h表示输入短整型数据。


使用scanf函数还必须注意以下几点:
      a. scanf函数中没有精度控制,如: scanf("%5.2f",&a); 是非法的。不能企图用此语句输入小数为2位的实数。
      b. scanf中要求给出变量地址,如给出变量名则会出错。如 scanf("%d",a);是非法的,应改为scnaf("%d",&a);才是合法的。
      C. 在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔则可用空格,TAB或回车作间隔。C编译在碰到空格,TAB,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。
      d. 在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。


2.malloc、realloc和calloc


malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。

函数原型是  extern void * malloc(unsigned int num_bytes);

malloc的全称是memory allocation,中文叫动态内存分配,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。



realloc 原型是extern void * realloc(void * mem_address, unsigned int newsize);
功能:
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。



calloc的函数原型:void *calloc(size_t n, size_t size);

功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。

与malloc的区别是calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。


这三个函数在分配了内存不想再要时,都要显示的调用free()函数,将这块内存释放。


malloc与new的区别:

从本质上来说,malloc是libc里面实现的一个函数,如果在source code中没有直接或者间接include过stdlib.h,那么gcc就会报出error:‘malloc’ was not declared in this scope。如果生成了目标文件(假定动态链接malloc),如果运行平台上没有libc(Linux平台,手动指定LD_LIBRARY_PATH到一个空目录即可),或者libc中没有malloc函数,那么会在运行时(Run-time)出错。new则不然,是c++的关键字,它本身不是函数。new不依赖于头文件,c++编译器就可以把new编译成目标代码(g++4.6.3会向目标中插入_Znwm这个函数,另外,编译器还会根据参数的类型,插入相应的构造函数)。

在使用上,malloc 和 new 至少有两个不同:1. new 返回指定类型的指针,并且2.new可以自动计算所需要大小。



3.编译屏障和内存屏障

现代 CPU中指令的执行次序不一定按顺序执行,没有相关性的指令可以打乱次序执行,以充分利用 CPU的指令流水线,提高执行速度。同时,编译器也会对指令进行优化,例如,调整指令顺序来利用CPU的指令流水线。这些优化方式,大部分时候都工作良好,但是在一些比较复杂的情况可能会出现错误,例如,执行同步代码时就有可能因为优化导致同步原语之后的指令在同步原语前执行。

内存屏障和编译屏障就是用来告诉CPU和编译器停止优化的手段。编译屏障是指使用伪指令“memory”告诉编译器不能把“memory”执行前后的代码混淆在一起,这时“memory”起到了一种优化屏障的作用。内存屏障是在代码中使用一些特殊指令,如ARM中的dmb、dsb和isb指令,x86中的sfence、lfence和mfence指令。CPU遇到这些特殊指令后,要等待前面的指令执行完成才执行后面的指令。这些指令的作用就好像一道屏障把前后指令隔离开了,防止CPU把前后两段指令颠倒执行。

(1)ARM平台的内存屏障指令。

dsb:数据同步屏障指令。它的作用是等待所有前面的指令完成后再执行后面的指令。

dmb:数据内存屏障指令。它的作用是等待前面访问内存的指令完成后再执行后面访问内存的指令。

isb:指令同步屏障。它的作用是等待流水线中所有指令执行完成后再执行后面的指令。

(2)x86平台上的内存屏障指令。

sfence:存储屏障指令。它的作用是等待前面写内存的指令完成后再执行后面写内存的指令。

lfence:读取屏障指令。它的作用是等待前面读取内存的指令完成后再执行后面读取内存的指令。

mfence:混合屏障指令。它的作用是等待前面读写内存的指令完成后再执行后面读写内存的指令。









你可能感兴趣的:(学习笔记)