1

我正在为一个假设的机器(SMAC-0 机器)开发一个汇编程序,需要一些内存分配方面的帮助。

我将从给定文件中获取和标记字符串,并将这些标记保存在指针中。

这是一个代码片段:

tokenCount = sscanf(buffer,"%s %s %s %s", tokenOne, tokenTwo, tokenThree, tokenFour);

其中tokenCount是整数,buffer是存储从输入文件中取出的行的临时缓冲区,tokenOnetokenTwotokenThreetokenFour是字符指针。

从文件中接受的字符串可以包含一到四个单词:

例子:


            READ    N
    N:      DS      1
    SUM:    DS      1
    LOOP:   MOVER   AREG    N
            ADD     AREG    N
            COMP    AREG    ='5'
            BC      LE      LOOP
            MOVEM   AREG    SUM
            PRINT   SUM
            STOP

我的查询是:

  • 我怎样才能知道令牌有多大,从而知道如何为相应的令牌指针分配内存?
  • (这个问题也适用于buffer指针,因为标签(例如LOOP, N, SUM)可以是可变大小的。)

  • 我怎样才能,使用scanf()或其他输入功能,如gets(),做同样的事情?
  • 4

    1 回答 1

    1

    您应该声明您的令牌缓冲区足够大。为了安全起见,最好使它们都与输入缓冲区本身一样大。看到这个这个线程如何防止scanf在C中导致缓冲区溢出?了解更多信息。

    如果您使用的是 GNU 编译器,则可以使用可以代表您动态分配缓冲区的扩展。使用 scanf()查看动态分配

    例子:

    为扫描的令牌使用预定义的缓冲区:

    请注意,所有标记的大小都与输入缓冲区相同:

    /* sscanf-test.c */
    #include <stdio.h>
    
    int main(int argc, char** argv)
    {
      FILE *file = fopen("sample.txt", "r");
      const int BufferSize=256;
      char buffer[BufferSize];
      char tokenOne[BufferSize];
      char tokenTwo[BufferSize];
      char tokenThree[BufferSize];
      char tokenFour[BufferSize];
    
      while (fgets(buffer, sizeof(buffer), file) != NULL)
      {
        tokenOne[0]='\0';
        tokenTwo[0]='\0';
        tokenThree[0]='\0';
        tokenFour[0]='\0';
        int tokenCount = sscanf(buffer, "%s %s %s %s", tokenOne, tokenTwo, tokenThree, tokenFour);
        printf("scanned %d tokens   1:%s 2:%s 3:%s 4:%s\n", tokenCount, tokenOne, tokenTwo, tokenThree, tokenFour);
      }
    
      fclose(file);
      return 0;
    }
    

    该程序产生以下输出(我稍微清理了格式以提高可读性):

    gcc sscanf-test.c -o sscanf-test
    ./sscanf-测试
    扫描 2 个令牌 1:READ 2:N 3:4:
    扫描 3 个令牌 1:N: 2:DS 3:1 4:
    扫描 3 个令牌 1:SUM: 2:DS 3:1 4:
    扫描 4 个令牌 1:LOOP:2:MOVER 3:AREG 4:N
    扫描 3 个令牌 1:ADD 2:AREG 3:N 4:
    扫描 3 个令牌 1:COMP 2:AREG 3:='5' 4:
    扫描 3 个令牌 1:BC 2:LE 3:LOOP 4:
    扫描 3 个令牌 1:MOVEM 2:AREG 3:SUM 4:
    扫描 2 个令牌 1:PRINT 2:SUM 3:4:
    扫描 1 个令牌 1:STOP 2:3:4:
    

    如果要存储扫描的令牌以供以后处理,则必须将它们复制到 while 循环中的其他位置。您可以使用该函数strlen来获取令牌的大小(不包括尾随字符串终止符 '\0')。

    为令牌使用动态内存分配:

    就像我说的,你也可以让 scanf 为你动态分配缓冲区。scanf(3)手册页声明您可以使用 GNU 扩展 'a' 或 'm' 来做到这一点。具体来说,它说:

    可选的“a”字符。这与字符串转换一起使用,并且使调用者无需分配相应的缓冲区来保存输入:相反,scanf() 分配了一个足够大小的缓冲区,并将该缓冲区的地址分配给相应的指针参数,这应该是一个指向 char * 变量的指针(这个变量在调用之前不需要初始化)。当不再需要此缓冲区时,调用者应随后释放(3)此缓冲区。这是一个 GNU 扩展;C99 使用 'a' 字符作为转换说明符(它也可以在 GNU 实现中使用)

    我无法使用 'a' 修饰符让 scanf 工作。但是,还有 'm' 修饰符可以做同样的事情(甚至更多):

    从 2.7 版开始,glibc 还提供了 m 修饰符,其目的与 a 修饰符相同。m 修饰符具有以下优点:

    • 它也可以应用于 %c 转换说明符(例如 %3mc)。

    • 它避免了关于 %a 浮点转换说明符的歧义(并且不受 gcc -std=c99 等的影响)

    • 它在即将到来的 POSIX.1 标准修订版中指定。

    /* sscanf-alloc.c */
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv)
    {
      FILE *file = fopen("sample.txt", "r");
      const int BufferSize=64;
      char buffer[BufferSize];
      char *tokenOne   = NULL;
      char *tokenTwo   = NULL;
      char *tokenThree = NULL;
      char *tokenFour  = NULL;
    
      while (fgets(buffer, sizeof(buffer), file) != NULL)
      {
        // note: the '&', scanf requires pointers to pointer to allocate the buffers.
        int tokenCount = sscanf(buffer, "%ms %ms %ms %ms", &tokenOne, &tokenTwo, &tokenThree, &tokenFour);
        printf("scanned %d tokens   1:%s 2:%s 3:%s 4:%s\n", tokenCount, tokenOne, tokenTwo, tokenThree, tokenFour);
    
        // note: the memory has to be free'd to avoid leaks
        free(tokenOne);
        free(tokenTwo);
        free(tokenThree);
        free(tokenFour);
        tokenOne   = NULL;
        tokenTwo   = NULL;
        tokenThree = NULL;
        tokenFour  = NULL;
      }
    
      fclose(file);
      return 0;
    }
    
    gcc sscanf-alloc.c -o sscanf-alloc
    ./sscanf-分配
    扫描 2 个令牌 1:READ 2:N 3:(null) 4:(null)
    扫描 3 个令牌 1:N: 2:DS 3:1 4:(null)
    扫描 3 个令牌 1:SUM: 2:DS 3:1 4:(null)
    扫描 4 个令牌 1:LOOP:2:MOVER 3:AREG 4:N
    扫描 3 个令牌 1:ADD 2:AREG 3:N 4:(null)
    扫描了 3 个令牌 1:COMP 2:AREG 3:='5' 4:(null)
    扫描 3 个令牌 1:BC 2:LE 3:LOOP 4:(null)
    扫描 3 个令牌 1:MOVEM 2:AREG 3:SUM 4:(null)
    扫描 2 个令牌 1:PRINT 2:SUM 3:(null) 4:(null)
    扫描 1 个令牌 1:STOP 2:(null) 3:(null) 4:(null)
    
    于 2012-08-10T19:18:44.777 回答