1

我对 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’)

我该怎么办?

4

5 回答 5

15

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

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

字符串是由第一个空字符终止并包括第一个空字符的连续字符序列。[...]指向字符串的指针是指向其初始(最低地址)字符的指针。

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

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

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

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

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

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

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

(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()andfree()函数也可能相对昂贵(但不要担心,除非您确定程序的性能不够好)。

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

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是一个很好的资源。

于 2012-09-25T02:11:31.267 回答
7

您的函数应该返回一个 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;
}
于 2012-09-25T01:37:52.003 回答
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 的回答对您了解您返回的内容以及您的代码为何不起作用非常有用...

于 2012-09-25T02:08:16.993 回答
1

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

于 2012-09-25T01:43:41.417 回答
1

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

*

取消引用指针 - 或获取指向的内存空间的信息。

&

获取您正在谈论的信息的地址,因此:

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 认为您正在尝试将 char 返回到get.

因此,相反,return get您将返回您正在寻找的指针。

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

malloc(旁白:您可能希望char *避免指针被堆栈内存清除,但这完全是一个不同的问题)。

于 2012-09-25T01:48:44.300 回答