【问题标题】:C Basic String Return FunctionC基本字符串返回函数
【发布时间】:2012-09-16 12:12:15
【问题描述】:

我对 C 语言还是很陌生。我仍然完全不了解指针的所有内容。 我正在尝试创建一个返回字符串的方法。 函数就到这里了,还不完整。

char getS(char *fileName){
    FILE *src;
    if((src = fopen(fileName, "r")) == NULL){
        printf("%s %s %s", "Cannot open file ", fileName, ". The program is now ending.");
        exit(-1);
    }
    char *get = " ";        
    //insert getting a random word here
    return(*get);
}

我正在尝试这样调用方法

char *article = getS("articles.txt");
char *noun = getS("nouns.txt");
char *verb = getS("verbs.txt");

编译器给了我这个:

error: invalid type argument of unary ‘*’ (have ‘int’)

我该怎么办?

【问题讨论】:

  • 这完全取决于您的字符串是否具有恒定大小...无论如何,它是 char * 您期望函数的返回类型。
  • 它给你的是哪个声明?请始终包含此类详细信息,以获得更好的答案。

标签: c string function pointers methods


【解决方案1】:

以下信息可能比您要查找的信息多得多。现在不要太担心吸收它,但你以后可能会需要它。

首先,关于术语的重要说明。 C 没有“字符串”类型。引用 ISO C 标准:

string 是一个连续的字符序列,以第一个 null 结尾,包括第一个 null 特点。 [...] 指向字符串的指针 是指向其初始(最低地址)的指针 字符。

特别是,char* 值是一个指针,不是一个字符串(尽管我们通常使用char* 指针来访问和操作字符串)。即使一个数组本身也不是一个字符串,尽管它可以包含一个字符串。

对于您的(相对简单的)函数,您返回的 char* 值指向字符串文字的(第一个字符),因此内存管理不是问题。对于更复杂的情况,该语言坦率地说并不是特别有用,您必须自己做一些工作来管理内存。

一个函数可以很容易地返回一个指向字符串的char* 值,允许调用者对该字符串做它喜欢做的事情——但是构成该字符串的字符存储在哪里?

有(至少)三种常见的方法。

(1) 函数返回一个指向char静态数组开头的指针:

char *func(void) {
    static char result[100];
    // copy data into result
    return result;
}

这可行,但它有一些缺点。 result 数组只有一个副本,对func() 的连续调用将破坏该数组的内容。并且数组有固定的大小;它必须足够大以容纳它可以返回的最大字符串。标准 C asctime() 函数就是这样工作的。

(2)调用者可以传入一个指向字符串的指针,并让函数填充:

void func(char *buffer) {
    // code to copy data into the array pointed to by buffer
}

这给调用者带来了负担,它必须分配char 的数组,尤其是必须知道它需要多大。

(3) 函数可以使用malloc()为字符串分配内存:

char *func(void) {
    char *result = malloc(some_number);
    if (result == NULL) {
         // allocation failed, cope with the error
    }
    // copy data into the array pointed to by result
    return result;
}

这样做的好处是函数可以决定它需要分配多少内存。但是调用者必须知道字符串是在堆上分配的,所以它可以稍后通过调用free() 来释放它。 malloc()free() 函数也可能相对昂贵(但不要担心,除非您确定程序的性能不够好)。

其实还有第四种方法,但是错了

char *bad_func(void) {
    char result[100];
    // copy data into result
    return result; // equivalent to "return &result[0];"
}

这里的问题是result是函数本地的,并没有定义为static,所以一旦函数返回,数组对象就不再存在了。调用者将收到一个指向不再保留的内存的指针,并且可以在你背后重复使用。您可以返回指向本地 static 对象的指针(因为在程序的整个生命周期中都存在单个副本),并且您可以返回本地非 static 对象的 ,但是您不能安全地返回本地非static 对象的地址

comp.lang.c FAQ 是一个很好的资源。

【讨论】:

    【解决方案2】:

    这完全取决于您的字符串是否具有恒定大小。通常人们对这些函数使用缓冲区参数,因为如果你在函数内部分配字符串内存空间,并返回一个指向它的指针,那么你必须释放函数外部的内存,如果不这样做,你会丢失内存引用导致内存泄漏。

    所以返回字符串的函数的最佳方法是这样的:

    void getS(char *fileName, char *output, size_t len)
    {
        FILE *src;
        if((src = fopen(fileName, "r")) == NULL)
        {
            printf("Cannot open file '%s'. The program is now ending.", fileName);
            exit(-1);
        }
        fread(output, sizeof(char), len, src); // Just an example without error checking
        fclose(src);
    }
    

    然后你会这样使用它:

    char content[512];
    getS("example.txt", content, 512);
    // From now, you can use 'content' safely
    

    或者使用malloc.h进行动态分配:

    char *content = (char *)malloc(512 * sizeof(char));
    getS("example.txt", content, 512);
    // Use 'content', and after using it, free its memory:
    free(content);
    

    但是从那时起,您将学会正确使用它。但是,如果您想以任何方式返回文字字符串(这不是您的情况,除了您不完整的示例代码),您必须使用 char * 作为函数的返回类型,并且使用 return get; 而不是 *get ,因为使用 *get 您返回的是一个字符(例如第一个字符),而不是指向字符串的指针。

    FrankieTheKneeMan 的回答对您了解返回的内容以及代码无法正常工作的原因非常有用...

    【讨论】:

      【解决方案3】:

      C 中的指针是一些其他信息的地址。 C 自带两个一元运算符来处理这个问题。

      *
      

      dereferences 指针 - 或获取指向的内存空间的信息。

      &
      

      获取你正在谈论的信息的地址,所以:

      int p;
      int* q = &p; //int* is a pointer to an it.
      
      q==p; //Error (or at least a warning)
      *q == p; //true
      q == &p; //true
      *q == &p; //ERROR
      &q == &p; //false, but also probably an error, depending on your compiler and settings
      

      所以,当你声明 char * get 时,你声明的是“一个指向字符的指针”——C 知道按照惯例它可以被视为一个字符数组。但是,当您尝试 return * get 时,C 认为您正在尝试返回 get 寻址的内存中的字符。

      所以改为return get,你会返回你正在寻找的指针。

      这是否为你揭开了万能指针的神秘面纱?

      (顺便说一句:你可能想malloc char * 以避免指针被堆栈内存清除,但这完全是一个不同的问题)。

      【讨论】:

      • '这是否为你揭开了万能指针的神秘面纱?' ——我没想到会这样。如果它能区分声明中使用的*,这个答案会好很多,这 not 是取消引用。它还可以谈论数组衰减为指针。
      • '避免指针被堆栈内存清除'——指针被返回,所以它的本地副本发生了什么并不重要。将缓冲区设为本地绝对是另一回事。
      • 指针被返回,但它指向堆栈内存中的一个位置。因此,如果您声明一个本地 char*(没有 malloc)并用信息填充它,然后将其传回,您将度过一段糟糕的时光。
      • '它指向堆栈内存中的一个位置' -- 不,它不...不在此页面上的任何代码中; OP 的代码是char *get = " ";,它是一个指向字符串文字的指针。但正如我所写的,'在本地制作缓冲区绝对是另一回事'。我这样写的原因是因为 that 在 C 中是 UB;语言定义中没有“堆栈”。
      【解决方案4】:

      您的函数应该返回一个 char *(一个字符串),而不是一个 char,并且它应该返回相同的值。于是函数变为:

      char * getS(char *fileName) {
          FILE *src;
          if((src = fopen(fileName, "r")) == NULL) {
              printf("%s %s %s", "Cannot open file ", fileName, ". The program is now ending.");
              exit(-1);
          }
      
          char *get = " ";        
          //insert getting a random word here
      
          return get;
      }
      

      【讨论】:

      • @OstapHnatyuk:是的,但是当事情变得更复杂时,你需要小心。在此函数中,您返回的值是字符串文字,因此内存管理不是问题。 comp.lang.c FAQ 是一个很好的资源。
      【解决方案5】:

      getS 的类型是char,但您将其分配给char* 类型的变量。大概你希望getS返回一个字符串,所以它的类型应该是char*,你应该返回get而不是*get(这只是get的第一个字符)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-11-06
        • 1970-01-01
        • 2013-10-30
        • 1970-01-01
        相关资源
        最近更新 更多