简体   繁体   中英

Pointer and malloc issue

I am fairly new to C and am getting stuck with arrays and pointers when they refer to strings. I can ask for input of 2 numbers (ints) and then return the one I want (first number or second number) without any issues. But when I request names and try to return them, the program crashes after I enter the first name and not sure why.

In theory I am looking to reserve memory for the first name, and then expand it to include a second name. Can anyone explain why this breaks?

Thanks!

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



void main ()
{
    int NumItems = 0;

    NumItems += 1;
    char* NameList = malloc(sizeof(char[10])*NumItems);
    printf("Please enter name #1: \n");
    scanf("%9s", NameList[0]);
    fpurge(stdin);

    NumItems += 1;
    NameList = realloc(NameList,sizeof(char[10])*NumItems);
    printf("Please enter name #2: \n");
    scanf("%9s", NameList[1]);
    fpurge(stdin);

    printf("The first name is: %s",NameList[0]);
    printf("The second name is: %s",NameList[1]);

    return 0;

}

I think that your problem is in this code:

scanf("%9s", NameList[0]);

The problem here is that scanf requires that the argument you provide as a location to store the result must be a pointer. If you provide something that isn't a pointer, scanf will treat it as though it is and essentially write memory to a random location, crashing the program.

Fixing this requires two steps. First, you'll want to change your declaration of NameList so that it's no longer a char * . The reason is that a char * is a single string, whereas you want an array of strings. This would be defined as a char ** , a pointer to an array of char * s. This might look like this:

char** NameList;

Next, you'll need to allocate storage space for the strings. This is tricky and a bit subtle because you have to do two allocations. First, you need to allocate space for the array itself, which you could do like this:

NameList = malloc (sizeof(char*) * NumItems);

This allocates an array of pointers to characters, but it doesn't actually set up the pointers in those arrays to point to valid memory locations. To fix this, you'll want to then iterate across this array and set all of its elements to be pointers to buffers large enough to hold your strings - in this case, buffers of length 10:

int i;
for (i = 0; i < NumItems; ++i)
    NameList[i] = malloc (10); // Space for 10 characters

Now, you can call

scanf("%9s", NameList[0]);

Because NameList[0] is a char * pointing to the buffer into which the characters should be written.

One more comment on your code - rather than allocating an array of one element and then reallocating it to an array of two elements later on, consider just allocating all the space up-front. It's a bit clearer. Also, since you're now dealing with a buffer of char * s, each of which needs to be initialized to point to its own buffer, if you do incremental allocations you'll need to be sure to initialize all of the new char * s you allocate to point to a buffer somewhere. If you do this one step at a time there's a good chance you'll forget to set up the pointers and cause a crash, whereas if you do it up front there's no such risk.

When it comes time to free the dynamically-allocated memory, you'll need to run the allocation process in reverse, first freeing the dynamically-allocated buffers for the strings, then freeing the top-level buffer. For example:

for (i = 0; i < NumItems; ++i)
    free (NameList[i]);
free (NameList);

This is necessary because free doesn't work recursively. You need to explicitly deallocate all the memory that you allocate.

Note that you do not write the code like this:

free (NameList);
for (i = 0; i < NumItems; ++i)
    free (NameList[i]);

This will cause all sorts of Bad Things at runtime because if you free the top-level array first, then as you try iterating over its contents freeing the pointers, you'll be reading memory that you no longer own.

Hope this helps!

Your NameList variable is a char *, which is a pointer to a single string. (char is a single character, char * is a single string, char ** is an array of strings.)

When you use NameList[ 1], you are actually indexing the second character of the string, not the second string itself.

You should instead allocate an array of strings, something like this:

  char (*NameList)[10];
  NameList = malloc(10*sizeof(char)*NumItems);

Edit: Fixed some compile errors. (Sample code.) Note that the sizeof(char) isn't really needed, as it's always 1. Nice to be explicit, though.

If you need a 2d array of chars (an array in which every element is an array of char s), you are allocating memory in the wrong way. Correct way would be:

int main(){

int i;
int NumItems = 2;

/* Alocate a variable which every position points to an array of character */
char ** NameList = (char **) malloc(sizeof(char *) * NumItems);

/* For each position, allocate an array of 10 characters */
for(i = 0; i < NumItems; i++){
    NameList[i] = (char *) malloc(sizeof(char) * 10);
}

printf("Please enter name #1: \n");
scanf("%s", NameList[0]);

printf("Please enter name #2: \n");
scanf("%s", NameList[1]);

printf("The first name is: %s",NameList[0]);
printf("The second name is: %s",NameList[1]);

/* Free allocated memory. Always a good practice and prevents memory leaks. */
for(i = 0; i < NumItems; i++){
    free(NameList[i]);
}
free(NameList);

return 0;

}

Also consider just using stack space instead of using malloc and dynamically allocating heap space:

#define NumItems 2
char NameList[NumItems][10];

printf("Please enter name #1: \n");
scanf("%9s", NameList[0]);

printf("Please enter name #2: \n");
scanf("%9s", NameList[1]);

printf("The first name is: %s",NameList[0]);
printf("The second name is: %s",NameList[0]);

Generally, unless you have a need for dynamically sized arrays, it's much much easier to use plain arrays.

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