【问题标题】:How did printf handle this char** parameter?printf 是如何处理这个 char** 参数的?
【发布时间】:2015-02-07 10:57:43
【问题描述】:

我正在用 C 编写一个汇编程序并修复了我的解析器中的一个错误(请参阅下面的解析器函数定义和输入文件)。我的程序的输出是...

-bash-3.2$ ./a.out branch.asm out.obj
input file name = 'branch.asm'
output file name = 'out.obj'


Error, couldn't find an opcode in token sequence: '▒▒▒▒mul' 'r0' ...
-bash-3.2$

我意识到当我将 char** pLabel 传递给 printf 时,我需要取消引用它。我不明白的是:即使我给它一个错误的参数,printf 如何仍然能够打印字符串?如果它仍然能够找到字符串,为什么要在它前面加上垃圾(我假设垃圾来自字符串地址值)?

如果您希望我提供更多信息,请告诉我。这只是一个大程序,我觉得其余的源代码对于理解我的问题没有必要(这实际上只是围绕 printf 如何处理字符串引用)。

输入文件跟随...

        .ORIG x3000
LABEL1  AND R0,R0,#0
        ADD R0,R1,R2            ;
LABEL2  ADD R0,R0,#-1           ;
        BRP LABEL2
        BRNZ    label
        BRNZ    LABEL1
        BRNZP   LABEL1
        BR      LABEL1
LABEL   BRP     LABEL1
MUL     R0,R1,R2                ;
        .END

输入文件结束 解析器定义的开始...

/* Input: Assembly Program Input File
          Pointer to current assembly line
   Work:  attaches pointer vars to strings holding the
          LABEL, Opcode, Arg1, Arg2, Arg3, and Arg4
   Ret:   EMPTY_LINE,   (Line was empty)
          OK,           (Line is now parsed)
          DONE          (No more lines in File)
   Note:  Pseudo-ops are returned as Opcodes.
*/
int readAndParse( FILE * pInfile, char * pLine, char ** pLabel, char ** pOpcode,
                  char ** pArg1, char ** pArg2, char ** pArg3, char ** pArg4)
{
  char * lRet, * lPtr;
  int i;
  /* Pull line (INCLUDING \n) and return in pLine string */
  if( !fgets( pLine, MAX_LINE_LENGTH, pInfile ) )  return( DONE );

  for( i = 0; i < strlen( pLine ); i++ ) /* Convert entire line to lowercase  */
    pLine[i] = tolower( pLine[i] );

  *pLabel = *pOpcode = *pArg1 = *pArg2 = *pArg3 = *pArg4 = pLine + strlen(pLine); /* Point to '\0'! */

  /* ignore the assembly comments ... */
  lPtr = pLine;
  while( *lPtr != ';' && *lPtr != '\0' && *lPtr != '\n' )
    lPtr++; /* Find the 1st occurence of comment or a newline */

  *lPtr = '\0'; /* Chunck any comments or newlines. We only want relevant tokens. */

  /* Fetch first token in pLine delimted by \t \n (SPACE) or ,. strtok returns NULL when \0.
     Subsequent calls expect the a parameter of NULL & continue where it left off.
  */
  if( !(lPtr = strtok( pLine, "\t\n ," ) ) )  return( EMPTY_LINE );

  /* Logic to handle option of LABEL vs Opcode as starting token for line */
  if( isOpcode( lPtr ) == -1 && lPtr[0] != '.' ) /* found a label */
  {
    *pLabel = lPtr;
    if( !( lPtr = strtok( NULL, "\t\n ," ) ) ) return( OK );
  }
  *pOpcode = lPtr;
  if( isOpcode( *pOpcode ) == -1 && lPtr[0] != '.' )
    {
      printf("\nError, couldn't find an opcode in token sequence: '%s' '%s' ...\n", pLabel, *pOpcode);
      exit(2);
    }

  if( !( lPtr = strtok( NULL, "\t\n ," ) ) ) return( OK );
  *pArg1 = lPtr;

  if( !( lPtr = strtok( NULL, "\t\n ," ) ) ) return( OK );
  *pArg2 = lPtr;

  if( !( lPtr = strtok( NULL, "\t\n ," ) ) ) return( OK );
  *pArg3 = lPtr;

  if( !( lPtr = strtok( NULL, "\t\n ," ) ) ) return( OK );
  *pArg4 = lPtr;

  return( OK );
}

【问题讨论】:

  • 哦,顺便说一句,如果您想知道,MUL 不是这个(16 位)ISA 中的有效操作码......
  • 问题和显示的代码并不能很好地相互配合...无论如何printf()%s格式说明符查找作为参数传递的内存位置并打印出来字符 util 它找到一个空终止符..
  • 我从发布的代码中得到的行为是我提出问题的原因;也许你的意思是我给了你太多不必要的信息?另外,我理解 printf 打印直到它到达一个空终止符。我想知道的是垃圾是如何在我打算打印的字符串之前进入那里的。
  • 是的,请准确说明您的问题是什么?信息量太大了..!!
  • 详细介绍 printf 如何处理 char** 会满足我的好奇心 ...

标签: c string parsing reference printf


【解决方案1】:

发生的情况如下:在内存布局中,*pLabel 后面跟着 pLine 指向的字符。 在对 printf 的调用中,其中 pLabel 未被取消引用,printf 在 pLabel 处找到四个非零字节并打印它们(这实际上是存储在 pLabel 中的地址)。因为 printf 没有看到终止的空字符,所以它继续打印在 pLabel 之后找到的字节,即 pLine。 pLine 以 'mul' 开头,后跟 strtok 放置的 \0。

【讨论】:

  • 谢谢保罗!这就是我一直在寻找的。现在唯一还有我的是我想要的是字符串 *pLabel = mul\0 打印,所以当我错误地打印它的地址 pLabel 时,为什么该地址的内容(即 mul\0 )立即跟随作为打印的地址正在递增并搜索空终止?这是否与调用 printf 时堆栈的组织方式有关?再次感谢。
  • @Michael,是的,这可能与堆栈或全局内存有关。在您的代码中,我看不到函数的调用方式或参数的存储位置。如果我能看到这一点,我可以更准确地解释。现在我只能得出结论,在内存中 pLine 遵循 pLabel。
猜你喜欢
  • 2021-01-02
  • 1970-01-01
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
  • 1970-01-01
  • 2011-01-26
相关资源
最近更新 更多