-2

I am trying to write a function that allows a user to input a name and then return the string back as a variable which I can use in my program. However, every time I've attempted this it seems I've tried to return a local variable or something.

I wrote the following function outside of main():

char* employee_Input1(void)  
{
    char employee_name[20];
    printf(“Enter an employee name: “);
    scanf(“%s”, employee_name);

    return employee_name;
}

However when I compile it I'm getting the warning:

  • Address of stack memory associated with local variable 'employee_name' returned

I know the issue is with my function, as the code inside my function works as intended in main. My understanding is that employee_name is a local variable that ceases to exist outside the function. Would I need to use dynamic memory allocation so that it's in the heap or something? I'm not sure where to begin with that. How would I go about getting it returned so I could use it in the rest of my program?

Apologies in advanced if this is a basic question... I've only been learning C for a couple of months and I'm really stuck! Any help would be appreciated.

iyen
  • 1
  • You could define an array in the caller and pass that into `employee_Input1`; then there is no need to return anything. – ad absurdum Jul 14 '23 at 17:01
  • @TomKarzes Not to nitpick, but using "out of scope" to refer to an object's lifetime is actually wrong (I did the same mistake as you). [See this comment from Eric Postpischil below](https://stackoverflow.com/questions/74883309/static-variable-concepts-c/74883537#comment132152348_74883537). A variable can go out of scope yet can still exist, so the correct way is to say "lifetime has ended" not "goes out of scope". – Marco Jul 14 '23 at 17:40

2 Answers2

0

Passing A Buffer

You could define an array in the caller and pass that into employee_Input1; then there is no need to return anything.

Note that you should use a maximum field width specifier with %s to avoid buffer overflow, and this specification should be one less than the buffer size to include space for the \0 character that terminates a string. It is also a good idea to use a generous buffer size instead of minimal sizes like 20.

scanf returns the number of assignments made, or EOF on error. You should get in the habit of checking this (and all return values) to be sure that input is as expected.

#include <stdio.h>
#include <stdlib.h>  // for `exit`

void employee_input_1(char *buffer) {
    printf("Enter an employee name: ");
    if ((scanf("%1023s", buffer) != 1)) {  // specify maximum field width
        printf("Input error\n");
        exit(EXIT_FAILURE);
    }
}
        
int main(void) {
    char buffer[1024];
    employee_input_1(buffer);

    // Now you can do stuff with `buffer`:
    printf("Employee: %s\n", buffer);
}

Returning A Struct

You can't return arrays in C, and you can't return pointers to local objects in C, but you can return structs. This gives rise to another solution: you can wrap an array in a struct and return the struct from a function:

#include <stdio.h>
#include <stdlib.h>  // for `exit`

struct employee_name {
    char first[1024];
    char last[1024];
};

struct employee_name employee_input_2(void) {
    struct employee_name name = { 0 };

    printf("Enter employee's first name: ");
    if ((scanf("%1023s", name.first)) != 1) {
        printf("Input error\n");
        exit(EXIT_FAILURE);
    }

    printf("Enter employee's last name: ");
    if ((scanf("%1023s", name.last)) != 1) {
        printf("Input error\n");
        exit(EXIT_FAILURE);
    }

    return name;
}

int main(void) {
    struct employee_name some_name = employee_input_2();

    // Now you can do stuff with `some_name`:
    printf("Employee: %s, %s\n", some_name.last, some_name.first);
}

Passing A Dynamically Allocated Buffer

You can use malloc to dynamically allocate memory for a buffer and then pass that buffer to employee_input_1 just as before:

#include <stdio.h>
#include <stdlib.h>

void employee_input_1(char *buffer) {
    printf("Enter an employee name: ");
    if ((scanf("%1023s", buffer) != 1)) {  // specify maximum field width to include `\0`
        printf("Input error\n");
        exit(EXIT_FAILURE);
    }
}
        
int main(void) {
    // Using `malloc`:
    char *buffer = malloc(1024);
    if (buffer == NULL) {
        printf("Allocation failure\n");
        exit(EXIT_FAILURE);
    }

    // Use `employee_input_1` as before:
    employee_input_1(buffer);
    printf("Employee: %s\n", buffer);

    // Free memory allocation when finished:
    free(buffer);
}

You should check the value returned from malloc: a pointer to the allocation is returned, or a null pointer on error. If there is an error you should handle that error; here the program just prints an error message and exits.

You need to free memory allocated with malloc when you are finished with it to avoid memory leaks.

Allocating Memory In The Called Function

You can also use malloc to allocate memory from within the called function and return a pointer to this dynamic allocation. You still need to free the allocation when you are finished with it, but this time the caller will be responsible for freeing the allocated memory. You also still need to check for allocation failures; this can be done within the called function as it is here, or the function can return the pointer returned by malloc and the caller can check for errors by checking for a null pointer.

#include <stdio.h>
#include <stdlib.h>

char * employee_input_3(void) {
    char *buffer = malloc(1024);
    if (buffer == NULL) {
        printf("Allocation failure\n");
        exit(EXIT_FAILURE);
    }

    printf("Enter an employee name: ");
    if ((scanf("%1023s", buffer) != 1)) {  // specify maximum field width to include `\0`
        printf("Input error\n");
        exit(EXIT_FAILURE);
    }
    return buffer;
 }

int main(void) {
    // Dynamic allocation within the called function:
    char *buffer = employee_input_3();
    printf("Employee: %s\n", buffer);

    // Free memory allocation when finished:
    free(buffer);
}
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
0

Indeed: You are returning a local variable:

char employee_name[20];
return employee_name;

If you want to avoid that you need to dynamically allocate memory, e.g.

char* employee_name = malloc(20);

This array you can safely return. Before you do so, you do a dangerous scan, though; to prevent the user from potentially writing beyond array bounds you need to limit the numbers of characters to read:

scanf("%19s", employee_name);
//      ^^
// (one less than array length to leave space for the terminating null character!)

Now you can legally:

return employee_name;

Note, too, that scanf will stop at any white space, so entering a secondary name (or fore- and surname) is not possible. You can get around by usage of getline function, if supported on your system, which allows for spaces and additionally arbitrary input length.

Both versions have the disadvantage that the buffer returned needs to be freed again, responsibility for lies at the function caller.

You can avoid by letting the caller provide the buffer to write to:

void employee_Input1(size_t maxLength, char* employee_name)
{
    // you could now read entire LINES (unless too long) by:
    fgets(employee_name, maxLength, stdin);

    // or again use scanf; unfortunately you need to create the
    // safe format string DYNAMICALLY as we cannot pass in
    // the maximum length as a parameter to scanf, so:
    char format[32];
    snprintf(format, sizeof(format), "%%s%zu", maxLength - 1);
    scanf(format, employee_name);
}

This way you can get around dynamic memory allocation entirely, which usually is preferable.

Side note: If you use fgets then the string read will include the newline character the user entered – if it fit into the string. So you can use it to determine if the entire user input has been read (then you likely will remove it) or not (then you might ignore the surplus characters from stdin until discovering the owing newline character).

Note, too, that you used the bad quotes in your code, any string literals need to be surrounded by exactly " (ASCII code 34).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59