【问题标题】:How to Allocate Memory to a Pointer for Entered Input (in 'C')如何为输入的输入分配内存(在“C”中)
【发布时间】:2023-03-17 23:00:01
【问题描述】:

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

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

这是一个代码sn-p:

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 指针,因为标签(例如LOOPNSUM)可以是可变大小的。)

  • 我怎样才能使用scanf()gets() 等其他输入功能来做同样的事情?
  • 【问题讨论】:

    • 使标记的缓冲区与输入缓冲区的大小相同。

    标签: c pointers memory-management input


    【解决方案1】:

    您应该声明您的令牌缓冲区足够大。为了安全起见,最好使它们都与输入缓冲区本身一样大。有关详细信息,请参阅此线程 How to prevent scanf causing a buffer overflow in C?

    如果您使用的是 GNU 编译器,您可以使用一个可以代表您动态分配缓冲区的扩展。查看Dynamic allocation with 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 修饰符 作为修饰语的目的。 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)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多