简体   繁体   中英

Is there a maximum return length for a character array in C

I am trying to create a dynamic character "string pointer"/array and my code will not print values is the characters typed in exceed 249 characters. I am just wondering if there is a maximum return length for a character array/"string pointer".

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

char *input() {
    char *inpt; 
    char check;
    int i;
    int count;

    i = 0;
    count = 1;
    check = '\0';

    inpt = (char*)malloc(1);

    while (check != '\n') {
        check = getchar(); 
        if (check == '\n') {
            break;
        } else {
            inpt[i] = check;
            i++;
            count++;
            inpt = realloc(inpt, count);
        }
    }
    inpt[i] = '\0';

    char *retrn;
    retrn = inpt;

    free(inpt);

    printf("%d \n", i);

    return retrn;
}

int main(int argc, char **argv) {
    char *name;
    printf("Please print name: \n");
    name = input();
    printf("%s is the name \n", name);
    return 0;
}

The problem is not with the length of the string you attempt to return, but that you return a pointer to memory that no longer is allocated to you:

char *retrn;
retrn = inpt;

free(inpt);

return retrn;

When you do retrn = inpt you don't copy the memory, instead you have two pointers pointing to the same memory. Then you free that memory and return a pointer to the newly free'd memory. That pointer can't of course not be dereferenced and any attempt of doing that will lead to undefined behavior .

The solution is not any temporary variable like retrn , but to simply not free the memory in the input function. Instead return inpt and in the calling function ( main in your case) you free the memory.

It's most likely due to using free memory. Your assignment input to retrn is not creating another copy. You'll get undefined behaviour, perhaps including what you are experiencing.

Continuing from my comment, there are a number of schemes to allocate memory dynamically. One thing you want to avoid from an efficiency standpoint is needlessly reallocating for every character. Rather than call realloc for every character added to name, allocate a reasonable number of characters to hold name, and if you reach that amount, then reallocate, doubling the current allocation size, update your variable holding the current size and keep going.

You already have an array index, so there is no need to keep a separate count . Just use your array index as the counter, insuring your have at least index + 1 characters available to provide space to nul-terminate inpt .

There is no need to keep separate pointers in input() . Just allocate for inpt and return inpt as the pointer to your block of memory when done. (don't forget to free (name); in main() which will free the memory you allocated in input .

Never realloc the pointer directly. (eg DON'T inpt = realloc (inpt, size); ) If realloc fails it returns NULL causing the loss of a pointer to to the allocated block inpt referenced prior to the realloc call. Instead use a temporary pointer, validate that realloc succeeded, and then assign the new block to inpt (example below)

Putting it altogether, you could do something similar to:

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

#define MEMSZ 32  /* initial allocation size (must be at least 1) */

char *input (void)
{
    char *inpt = NULL, check;
    size_t mem = MEMSZ, ndx = 0;

    if (!(inpt = malloc (mem))) {   /* allocate/validate  mem chars */
        fprintf (stderr, "input() error: virtual memory exhausted.\n");
        return NULL;
    }

    /* you must check for EOF in addition to '\n' */
    while ((check = getchar()) && check != '\n' && check != EOF) 
    {   /* check index + 1 to insure space to nul-terminate */
        if (ndx + 1 == mem) {       /* if mem limit reached realloc */
            void *tmp = realloc (inpt, mem * 2);    /* use tmp ptr */
            if (!tmp) {             /* validate reallocation */
                fprintf (stderr, "realloc(): memory exhausted.\n");
                break;      /* on failure, preserve existing chars */
            }
            inpt = tmp;     /* assign new block of memory to inpt */
            mem *= 2;       /* set mem to new allocaiton size */
        }
        inpt[ndx++] = check;    /* assign, increment index */
    }
    inpt[ndx] = 0;          /* nul-terminate */

    return inpt;            /* return pointer to allocated block */
}

int main (void)
{
    char *name = NULL;

    printf ("Please enter name: ");
    if (!(name = input()))  /* validate input() succeeded */
        return 1;

    printf ("You entered      : %s\n", name);
    free (name);    /* don't forget to free name */

    return 0;
}

Example Use/Output

$ ./bin/entername
Please enter name: George Charles Butte
You entered      : George Charles Butte

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/entername
==2566== Memcheck, a memory error detector
==2566== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==2566== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==2566== Command: ./bin/entername
==2566==
Please enter name: George Charles Butte
You entered      : George Charles Butte
==2566==
==2566== HEAP SUMMARY:
==2566==     in use at exit: 0 bytes in 0 blocks
==2566==   total heap usage: 1 allocs, 1 frees, 32 bytes allocated
==2566==
==2566== All heap blocks were freed -- no leaks are possible
==2566==
==2566== For counts of detected and suppressed errors, rerun with: -v
==2566== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Let me know if you have any additional questions.

There are intrinsic limits for the size of an array:

  • available memory is limited: malloc() and realloc() may return NULL if the request cannot be honored due to lack of core memory. You should definitely check for malloc() and realloc() success.
  • system quotas may limit the amount of memory available to your process to a lower number than actual physical memory installed or virtual memory accessible in the system.
  • the maximum size for an array is the maximum value for the type size_t : SIZE_MAX which has a minimum value of 65535 , but you use type int for your requests to malloc() or realloc() , that may have a smaller range than size_t . Type int is 32 bits on most current desktop systems where size_t may be 64 bits and available memory may be much more than 2GB. Use size_t instead of int .

Note however that your problem comes from a much simpler bug: you free the memory block you allocated for the string and return a copy of the pointer, which now points to freed memory. Accessing this memory has undefined behavior, which can be anything, including apparent correct behavior upto 249 bytes and failure beyond.

Note also that you should use type int for check and compare the return value of getchar() to EOF to avoid an endless loop if the input does not contain a newline (such as en empty file).

Here is a corrected version:

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

char *input(void) {
    char *p = malloc(1);  /* simplistic reallocation, 1 byte at a time */
    size_t i = 0;         /* use size_t for very large input */
    int c;                /* use int to detect EOF reliably */

    if (p == NULL) {
        return NULL;  /* allocation error */
    }
    while ((c = getchar()) != EOF && c != '\n') {
        char *newp = realloc(p, i + 2);
        if (newp == NULL) {
            free(p);   /* avoid a memory leak */
            return NULL;  /* reallocation error */
        }
        p = newp;
        p[i++] = c;
    }
    if (i == 0 && c == EOF) {
        free(p);
        return NULL;  /* end of file */
    }
    p[i] = '\0';
    return p;
}

int main(int argc, char **argv) {
    char *name;
    printf("Please print name: ");
    name = input();
    if (name == NULL) {
        printf("input() returned NULL\n");
    } else {
        printf("%s is the name\n", name);
        free(name);
    }
    return 0;
}

Is there a maximum return length for a character array in C

The maximum size of a character array is SIZE_MAX . SIZE_MAX is at least 65535.

I am just wondering if there is a maximum return length for a character array/"string pointer".

For a string , its maximum size is SIZE_MAX and the maximum length is SIZE_MAX - 1 .

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