[南大ICS-PA2] 字符串处理函数和printf实现

文章详细介绍了AM抽象机器的裸机运行时环境,包括TRM、IOE、CTE、VME和MPE模块。重点讨论了如何在NEMU上实现批处理模式,并提供了字符串处理函数如strlen、strcpy、strncpy等的C语言实现。此外,还阐述了sprintf函数的实现原理,以及如何处理函数调用的参数列表。

AM-裸机(bare-metal)运行时环境

AM(Abstract Machine),作为一个向程序提供运行时环境的库,AM根据程序的需求把库划分成以下模块:
A M = T R M + I O E + C T E + V M E + M P E \rm AM = TRM + IOE + CTE + VME + MPE AM=TRM+IOE+CTE+VME+MPE

  • TRM(Turing Machine) - 图灵机,最简单得运行时环境,为程序提供基本得计算能力
  • IOE(I/O Extension) - 输入输出扩展, 为程序提供输入输出的能力
  • CTE(Context Extension) - 上下文扩展,为程序提供上下文管理能力
  • VME(Virtual Memory Extension) - 虚存扩展,为程序提供虚拟内存管理能力
  • MPE(Multi-Processor Extension) - 多处理器扩展,为程序提供多处理器通信的能力

RTFSC(3)

  • am:不同架构得AM API实现,目前我们只需要关注NEMU相关得内容即可。am.h中列出了AM中的所有API。
  • klib:一些架构无关的库函数,方便应用程序开发

通过批处理模式运行NEMU

我们知道, 大部分同学很可能会这么想: 反正我不阅读Makefile, 老师助教也不知道, 总觉得不看也无所谓.

所以在这里我们加一道必做题: 我们之前启动NEMU的时候, 每次都需要手动键入c才能运行客户程序. 但如果不是为了使用NEMU中的sdb, 我们其实可以节省c的键入. NEMU中实现了一个批处理模式, 可以在启动NEMU之后直接运行客户程序. 请你阅读NEMU的代码并合适地修改Makefile, 使得通过AM的Makefile可以默认启动批处理模式的NEMU.

你现在仍然可以跳过这道必做题, 但很快你就会感到不那么方便了.

答:在nemu的nemu-main.c中,main函数如下:

int main(int argc, char *argv[]) {
   
   
  /* Initialize the monitor. */
#ifdef CONFIG_TARGET_AM
  am_init_monitor();
#else
  init_monitor(argc, argv);
#endif
  /* Start engine. */
  engine_start();
  return is_exit_status_bad();
}

可以看出,当定义了CONFIG_TARGET_AM后,不会出现sdb的提示

不用修改makefile,初始化时使用批处理模式即可

void init_sdb() {
   
   
  /* Compile the regular expressions. */
  init_regex();

  /* Initialize the watchpoint pool. */
  init_wp_pool();

  sdb_set_batch_mode();
}

实现常用的函数

  • AM:架构相关的运行时环境
  • klib:架构无关的库函数,kernel library

实现字符串处理函数

根据需要实现abstract-machine/klib/src/string.c中列出的字符串处理函数, 让cpu-tests中的测试用例string可以成功运行. 关于这些库函数的具体行为, 请务必RTFM.

size_t strlen(const char *s);
char *strcpy(char *dst, const char *src);
char *strncpy(char *dst, const char *src, size_t n);
char *strcat(char *dst, const char *src)int strcmp(const char *s1, const char *s2)int strncmp(const char *s1, const char *s2, size_t n) ;
void *memset(void *s, int c, size_t n);
void *memmove(void *dst, const void *src, size_t n);
void *memcpy(void *out, const void *in, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);

size_t strlen(const char *s);

  • 返回以NULL终止得字节字符串得长度,即字符数组中的字符数,其第一个元素用str指向,直到但不包括第一个空字符
  • 参数s是空指针,函数返回0.
size_t strlen(const char *s) {
   
   
  if (s == NULL) {
   
   
    return 0;
  }
  size_t n = 0;
  while(s[n] != '\0') {
   
   
    ++n;
  }
  return n;
}

char *strcpy(char *dst, const char *src);

  • 将 src 指向的空终止字节字符串(包括空终止符)复制到其第一个元素由 dest 指向的字符数组。

  • 当dest数组长度不够时,行为未定义。两个string重叠的行为也未定义。如果 dest 不是指向字符数组的指针或 src 不是指向空终止字节字符串的指针,则行为未定义。

    注:实现时直接复制,未定义行为有操作系统处理

char *strcpy(char *dst, const char *src) {
   
   
  if (src == NULL || dst == NULL) {
   
      // 没有所指,直接返回dst
    return dst;
  }
  // 当成指向字符数组处理,所以即使没有空字符,导致内存访问越界,或修改了其他有用的数据也不管,因为这是函数调用者所需要保证的,下面一些string函数都是这样对带非字符串数组
  char *res = dst;
  do {
   
   
    *dst = *src;
    dst++;
    src++;
  } while(*src != '\0');  
  return res;
}

char *strncpy(char *dst, const char *src, size_t n);

  • 将 src 指向的字符数组中至多 n 个字符(包括终止空字符,但不包括空字符之后的任何字符)复制到 dest 指向的字符数组。
  • 如果在复制整个数组 src 之前达到 n,则生成的字符数组不是以 null 结尾的。
  • 如果从 src 复制终止空字符后,未达到 n,则将额外的空字符写入 dest,直到写入了 n 个字符的总数。
  • 如果字符数组重叠,如果 dest 或 src 不是指向字符数组的指针(包括如果 dest 或 src 是空指针),如果 dest 指向的数组的大小小于 n,或者如果 src 指向的数组的大小小于 n 并且它不包含空字符,则行为未定义。
char *strncpy(char *dst, const char *src, size_t n) {
   
   
  if (src == NULL || dst == NULL) {
   
   
    return dst;
  }
  char *ans = dst;
  while (*src != '\0' && n != 0) {
   
   
    *dst = *src;
    ++dst;
    ++src;
    --n;
  }
  // 将额外的空字符写入dest,直到写入了n个字符的总数。
  while (n != 0) {
   
   
    *dst = '\0';
    ++dst;
    --n;
  }
  return ans;
}

char *strcat(char *dst, const char *src);

  • 将 src 指向的空终止字节字符串的副本附加到 dest 指向的空终止字节字符串的末尾。 字符 src[0] 替换 dest 末尾的空终止符。 生成的字节字符串以 null 结尾。
  • 如果目标数组不足以容纳 src 和 dest 的内容以及终止空字符,则行为未定义。 如果字符串重叠,则行为未定义。 如果 dest 或 src 不是指向空终止字节字符串的指针,则行为未定义。
char *strcat(char *dst, const char *src) {
   
   
  if (src == NULL || dst == NULL) {
   
   
    return dst;
  }
  char *ans = dst;
  while (*dst != '\0') {
   
   
    ++dst;
  }
  do {
   
   
    *dst = *src;
    dst++;
    src++;
  } while(*src != '\0');  // 先做一次,可以保证将结束字符也复制进去
  return ans;
}

int strcmp(const char *s1, const char *s2) ;

  • 按字典顺序比较两个以 null 结尾的字节字符串。
  • 结果的符号是被比较字符串中不同的第一对字符(均解释为 unsigned char)的值之间的差异符号。
  • 如果 lhs 或 rhs 不是指向空终止字节字符串的指针,则行为未定义。
int strcmp(const char *s1, const char *s2) {
   
   
  if (s1 == NULL || s2 == NULL) {
   
   
    return 0;
  }
  while (*s1 != 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值