简体   繁体   中英

C Basic String Return Function

I'm still pretty new to C. I still don't understand everything with pointers at all. I'm trying to make a method that returns a String. Here's the function, it's still incomplete.

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);
}

and I'm trying to call the method like this

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

The compiler is giving me this:

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

What should I do?

The following is probably going to be a lot more information than you're looking for. Don't worry too much about absorbing it all now, but you're likely to need it later.

First, an important note about terminology. C has no "string" type. Quoting the ISO C standard:

A string is a contiguous sequence of characters terminated by and including the first null character. [...] A pointer to a string is a pointer to its initial (lowest addressed) character.

In particular, a char* value is a pointer, not a string (though we generally use char* pointers to access and manipulate strings). Even an array is not itself a string, though it can contain a string.

For your (relatively simple) function, the char* value that you're returning points to (the first character of) a string literal, so memory management is not an issue. For more complicated cases, the language is frankly not particularly helpful, and you'll have to do some work to manage memory yourself.

A function can easily return a char* value that points to a string, allowing the caller to do what it likes with that string -- but where are the characters that make up that string stored?

There are (at least) three common approaches.

(1) The function return a pointer to the beginning of a static array of char :

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

This works, but it has some drawbacks. There's only one copy of the result array, and successive calls to func() will clobber the contents of that array. And the array has a fixed size; it has to be big enough to hold the largest string it can ever return. The standard C asctime() function works this way.

(2) The caller can pass in a pointer to a string, and let the function fill it in:

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

This places a burden on the caller, which has to allocate an array of char , and in particular has to know how big it needs to be.

(3) The function can allocate memory for the string using 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;
}

This has the advantage that the function can decide how much memory it needs to allocate. But the caller has to be aware that the string was allocated on the heap, so it can later release it by calling free() . The malloc() and free() functions can also be relatively expensive (but don't worry about that unless you're sure that your program's performance isn't good enough).

Actually, there's a fourth method, but it's wrong :

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

The problem here is that result is local to the function, and is not defined as static , so the array object ceases to exist as soon as the function returns. The caller will receive a pointer to memory that is no longer reserved, and that can be re-used behind your back. You can return a pointer to a local static object (because the single copy exists for the lifetime of the program), and you can return the value of a local non- static object, but you can't safely return the address of a local non- static object.

The comp.lang.c FAQ is an excellent resource.

Your function should return a char * (a string), instead of a char, and it should return the same. So the function becomes:

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;
}

It all depends if your string has a constant size or not. Normally people use buffer parameters for such functions, because if you allocate the string memory space inside the function, and return a pointer to it, then you'd have to free the memory outside the function, and if not done so, you'd have a memory leak by losing the memory reference.

So the best approach for functions that return strings, is something like this:

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);
}

Then you'd use it like this:

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

Or using malloc.h for dynamic allocation:

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

But it's from time you'll learn to use this correctly. But if in any way you want to return a literal string (that's not your case, except for your incomplete example code), you have to use char * as the return type for the function, and return get; and not *get , because with *get you're returning a character (the first one for instance), and not a pointer to the string.

FrankieTheKneeMan's answer is very useful for you to understand what you were returning and why your code was not working...

The type of getS is char but you're assigning it to variables of type char* . Presumably you want getS to return a string, so its type should be char* , and you should return get rather than *get (which is just the first character at get ).

A pointer in C is the address of some other piece of information. C comes with two unary operators to deal with that.

*

dereferences pointers - or gets the information at the memory space that is being pointed at.

&

Gets the address of the information You're talking about, so:

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

So, when you declare your char * get , you're declaring "a pointer to a char" - and C knows that by convention it can be treated as an array of chars. However, when you attempt to return * get , C thinks you're trying to return the char at the memory addressed by get .

So instead, return get , and you'll return the pointer you're looking for.

Did that demystify the almighty pointer for you?

(Aside: You may want to malloc that char * to avoid the pointer being cleared by the stack memory, but that's a different problem altogether).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM