【问题标题】:Is it possible to allocate the correct amount of space to strings in C during run time?是否可以在运行时为 C 中的字符串分配正确的空间量?
【发布时间】:2015-01-31 19:08:26
【问题描述】:

有什么方法可以在运行时分配准确足够的空间而不询问字符串的长度?

int main() 
{
    char **tests;
    int counter;
    int i;
    int j;
    int testCases;

    scanf(" %d", &testCases);

    tests = malloc(sizeof(char *) * testCases);

    for(i = 0; i < testCases; i++)
    //stuck here, normally I would put tests[i] = malloc(length_of_string)
    //but I don't know the length of string until runtime
}

【问题讨论】:

  • 除非文件输入是一个选项,我不知道
  • @juice 有什么问题?如果您不知道长度,请使用 strlen 函数找出它,这对我来说似乎很容易,所以我怀疑是否正确解释了您的问题。您能否添加信息如何获取字符串?
  • @juice 字符串不是以空结尾还是什么?
  • 使用int main (int arc, char **argv )将#testcases作为输入参数并使用strnlen()获取输入的长度。

标签: c char runtime c-strings dynamic-allocation


【解决方案1】:

由于您使用scanf 来读取计数,我想您也将使用它来读取字符串。如果您的 C 库与 Posix 2008 兼容 [注 1],那么您可以将 m 长度修饰符用于 scanf %s %c%[ 格式,这将导致 scanf 自动为你。 (您需要提供字符串指针的地址——即char**——而不仅仅是一个字符串指针。)

注意在scanf格式" %d"中,空格字符是多余的。 %d 格式说明符,如 %s,会自动跳过前导空格。

如果您正在阅读空格分隔的单词,以下是一个示例:

int n_strings;
if (scanf("%d", &n_strings) != 1) {
  /* Handle input error; do not continue */
}
/* Should check to make sure n_strings is > 0 */
char** strings = malloc(n_strings * sizeof *strings);
if (!strings) {
  /* Handle alloc error; do not continue */
}
for (int i = 0; i < n_strings; ++i) {
  if (scanf("%ms", &strings[i]) != 1) {
    /* Handle input error, do not continue */
  }
}

您更有可能希望阅读完整的行文。在这种情况下,再次使用 Posix 2008 兼容库,您可以使用 getline 函数,该函数读取整行(包括换行符)并将其存储到 malloc 存储中。与 scanf m 修饰符不同,getline 要求您为其提供地址的缓冲区指针为 NULL 或先前调用 malloc 的结果。另外,调用成功会返回实际存储的字符数,可以很方便。

这是上面使用getline的例子:

int n_strings;
if (scanf("%d", &n_strings) != 1) {
  /* Handle input error; do not continue */
}
/* Skip the rest of the first line */
while (getchar() != '\n') {}
char** strings = malloc(n_strings * sizeof *strings);
if (!strings) {
  /* Handle alloc error; do not continue */
}

for (char **strp = strings, **limit = strings + n_strings;
     strp < limit;
     ++strp) {
  size_t n = 0;
  *strp = NULL;
  ssize_t len = getline(strp, &n, stdin);
  if (len <= 0) {
    /* Handle input error, do not continue */
  }
  /* You probably don't want the trailing newline. But remember
   * that is is possible that it doesn't exist if the last character
   * in the file is not a newline.
   */
  if (len && (*strp)[len - 1] == '\n')
    (*strp)[len - 1] = 0;
}

  1. 据我所知,如果您使用的是相当现代的 Linux 或 Mac OS X,标准 C 库将符合 Posix 2008。这里推荐的两个功能在被合并到标准之前都在 Gnu 标准 C 库中实现。

    要启用 Posix 2008 功能,您需要在源代码中任何系统包括:

    #define _POSIX_C_SOURCE 200809L
    #define _XOPEN_SOURCE 700
    

    如果您使用的是较旧的 glibc,其中 getlinem 标志仍被视为 Gnu 扩展,请使用以下定义:

    #define _GNU_SOURCE
    

【讨论】:

    【解决方案2】:

    使用固定缓冲区一次读取一点,然后重新分配内存。你不能使用scanf。它将忽略所有空格。

    #define BUFSIZE 100
    #define INITIALSIZE 20
    
    int main(int argc, char* argv[])
    {
    
    
        char buf[BUFSIZE];
        char **tests;
        int counter;
        int i;
        int j;
        int testCases;
    
        scanf(" %d", &testCases);
        // get rid of the CR/LF
        fgets( buf, sizeof(buf), stdin );
    
        tests = (char **)malloc(sizeof(char *) * testCases);
    
        for(i = 0; i < testCases; i++) {
            int availableSpace, newSize;
            availableSpace = newSize = INITIALSIZE;
            tests[i] = (char *)malloc(availableSpace * sizeof(char));   
            tests[i][0] = '\0';
            while ( fgets( buf, sizeof(buf), stdin ) != NULL )
            {
    
                if ( availableSpace <= (int) strlen(buf) ) {
                    newSize += (int) strlen(buf) - availableSpace + 1;
                    tests[i] =  (char *)realloc(tests[i], newSize * sizeof(char));
                    availableSpace = 0;
                }
                else {
                    availableSpace -= strlen(buf);
                }
                strcat(tests[i], buf);
                if (strlen(buf) < BUFSIZE-1) {
                    break;
                }
            }
        }
    }
    

    【讨论】:

    • 你知道strlen(s) 需要O(length of s)strcat(s, t) 需要O(length of s + length of t),不是吗?但更严重的问题是您的完成测试;它无法区分续行段和换行符之前正好有 BUFSIZ-2 字符的最终换行符终止行;在后一种情况下,它将错误地附加下一行,从而导致一个非常烦人的错误。 (烦人,因为它很少发生,使诊断变得困难。)
    猜你喜欢
    • 2010-12-07
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 2022-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多