0

I have an array of strings, and I would like to extand it when it no longer has NULL pointers (meaning the array is full). I have tried realloc with no success, I think i'm not thinking right pointer-wise.

Here is my code:

int storage; //global, outside of main
int i, key;
char **people;
char **phones;

printf("Please enter a storage cacpity:\n");
scanf("%d",&storage);
printf("\n");

people=malloc(storage*sizeof(char *));
phones=malloc(storage*sizeof(char *));

for (i=0; i<storage; i++) {
    people[i] = NULL;
    phones[i] = NULL;
}

void AddNewContact(char * people[], char * phones[]) {
    char name[100];
    char phone[12];
    int i, listfull = 0;

    printf("Enter a contact name:\n");
    scanf("%s",&name);
    printf("Enter a phone number:\n");
    scanf("%s",&phone);

    for (i=0; i<storage; i++) {
        if (people[i]==NULL) {
            people[i] = (char *)malloc(strlen(name));
            phones[i] = (char *)malloc(strlen(phone));
            strcpy(people[i],name);
            strcpy(phones[i],phone);
            break;
        }
        listfull = 1;
    }

    if (listfull == 1) {
        storage++;
        people = realloc(&people,(storage)*sizeof(char *));
        phones = realloc(&phones,(storage)*sizeof(char *));
        people[storage-1] = NULL;
        phones[storage-1] = NULL;
        strcpy(people[storage-1],name);
        printf("\nData Base extanded to %d",storage);
    }
    printf("\n");
    return;
}

void PrintAll(char * people[], char * phones[]) {
    int i;
    for (i=0; i<storage; i++) {
        if (NULL != people[i]) {
            printf("Name: %s, ",people[i]);
            printf("Number: %s\n",phones[i]);
        }
    }
    printf("\n");
    return;
}

Any help would be highly appreciated, I'm stuck on it for a few hours and having no luck sovling this.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
Nir Tzezana
  • 2,275
  • 3
  • 33
  • 56
  • What exactly do you mean by "no success"? What are you doing to see whether it succeeded or not? – jwodder Jan 20 '15 at 20:21
  • Sorry if it wasn't clear, I'm adding users, when the capcity is maxed out (no NULLs) I want to extand it by one. – Nir Tzezana Jan 20 '15 at 20:23
  • 1
    @NirTzezana: And what exactly is the problem that you're having? – jwodder Jan 20 '15 at 20:23
  • 2
    `people` and `&people` are not the same. – Sourav Ghosh Jan 20 '15 at 20:25
  • @jwodder the realloc isn't working, the array isn't increasing by one. – Nir Tzezana Jan 20 '15 at 20:25
  • 2
    Just as a suggestion, you should probably change your `strcpy(people[i], name);` and `strcpy(phones[i], phone);` to `strncpy(people[i], name);` and `strncpy(phones[i], phone);`. This won't solve your problem, but it will NULL terminate the string. – Spencer D Jan 20 '15 at 20:25
  • 1
    @NirTzezana: How can you tell? – jwodder Jan 20 '15 at 20:26
  • @SouravGhosh I know they're not the same, &people is the memory address of the main pointer. – Nir Tzezana Jan 20 '15 at 20:27
  • @jwodder Sorry, I updated the code, I'm using PrintAll after realloc but the program terminates. – Nir Tzezana Jan 20 '15 at 20:28
  • If the string you input into phone is `"123-456-7890"` then you will overflow `char phone[12]`. – Bill Lynch Jan 20 '15 at 20:29
  • Do you have statements outside of any function? Like the for loop or the malloc calls at the beginning of your code. – Philipp Murry Jan 20 '15 at 20:30
  • Thumb rule: You cannot realloc a pointer which is not returned by mallloc and family. Q: is `&people` in that category? – Sourav Ghosh Jan 20 '15 at 20:30
  • 1
    Oh and as a second suggestion, please **NEVER** do this: `people = realloc(&people,(storage)*sizeof(char *));`. If the realloc fails, NULL is returned which means `people` becomes a NULL pointer. Thus you lose the pointer to that block of memory. You should assign the return value of realloc to a separate variable and `if ( NULL != returnedReallocAddress ) people = returnedReallocAddress;` – Spencer D Jan 20 '15 at 20:30
  • @PhilippMurry Everything needed for those function is in the code up there, you can view the full code on pastebin: http://pastebin.com/pCjUwsdA – Nir Tzezana Jan 20 '15 at 20:30
  • 1
    @Elvisjames, he could also just use `peoples = calloc(storage, sizeof(char *));`. Calloc returns a pointer to a block of memory initialized/filled with 0's. Thus you only call calloc instead of calling both malloc and memset. – Spencer D Jan 20 '15 at 20:37
  • @SpencerDoak Thanks for the useful tips, i'll be sure to use them from now on... sadly I still can't understand why this doesn't work. – Nir Tzezana Jan 20 '15 at 20:40
  • @iharob yes, if the loop doesn't break that means the array has no NULLs, thus needs another cell. – Nir Tzezana Jan 20 '15 at 20:54
  • `scanf("%s",&name);` will be a problem if the name has a space in it like "Nir Tzezana". Suggest posting sample input. – chux - Reinstate Monica Jan 20 '15 at 21:34

2 Answers2

8

You have 4 important mistakes, first you are passing the address of the array to scanf() that's wrong, you should change

scanf("%s", &name);

to

scanf("%s", name);

as well as scanf("%s",&phone);, I also should recommend to use length specifiers for scanf to prvent buffer overflow, for example

scanf("%99s", name);

i.e. the length of the name array -1, for the '\0' terminator.

Second, your realloc call is also wrong, you should pass the pointer instead of it's address, instead of this

people = realloc(&people,(storage)*sizeof(char *));

you should do this

people = realloc(people, storage * sizeof(char *));

but even this is not 100% right, because in case realloc fails, you will overwrite the pointer, and then you wont have a chance to clean up memory, so you should actually do something like

void *pointer;
pointer = realloc(people, storage * sizeof(char *));
if (pointer == NULL)
    free_people_andCleanUpOtherResourcesAndExitFromThisFunction();
people = pointer;

the same goes for phones.

Third you should always allocate space for one extra character, the terminating '\0', this

people[i] = (char *)malloc(strlen(name));

should read

people[i] = malloc(1 + strlen(name));

notice that I removed the cast which is not necessary.

Fourth you break out from the loop in the first iteration leaving listfull == 1, even when there is list is not full yet.

for (i=0; i<storage; i++) {
    if (people[i]==NULL) {
        people[i] = malloc(1 + strlen(name));
        phones[i] = malloc(1 + strlen(phone));
        strcpy(people[i],name);
        strcpy(phones[i],phone);
        break;
    }
    listfull = 1;
}

I'd recommend this outside of the loop

listfull = (i == storage);

Note: it doesn't matter how unlikely a function will fail, if it theoretically would fail, you should always check for it's failure, that will save you hours of debugging to find a very stupid mistake, you didn't check for the possible failure.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
4

You have

listfull = 1;

inside your (for i ...) loop, it should be outside the loop, like this

if (i == storage)           // if loop completed
    listfull = 1;

Next, your partial program comments that some variables are declared globally, yet they are followed by executable code statements which must be within a function, so are the "global" variables actually, local?

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • Weather, I believe the OP has pasted pieces of the actual code rather than including all of the actual code. I would guess the variables which are commented as "global" are likely defined globally and the code following them is likely within some function which he did not include the declaration for. That said, the code is a tad confusing because of that. – Spencer D Jan 20 '15 at 20:54
  • Are you talking about storage, it needs to be global beacuse its being used in many functions. You can see the full code here: http://pastebin.com/45grLUrs – Nir Tzezana Jan 20 '15 at 20:54
  • @SpencerDoak I thought it would be better to paste parts of the code instead the whole code, beacuse its shorter and more focused on the part that isn't working. – Nir Tzezana Jan 20 '15 at 20:56
  • @NirTzezana if `people` and `phones` are global why did you need to pass them to `AddNewContact()`? – Weather Vane Jan 20 '15 at 20:58
  • @WeatherVane they are not global, only storage is global. – Nir Tzezana Jan 20 '15 at 21:02
  • 2
    @NirTzezana, yes only showing the relevant pieces of code is usually preferred on SO, but might I recommend for future questions that you include the function declarations (e.g. `returnType functName(params) { ... }`). This allows readers to notice separations between functions and global variables. That said, thank you for being conservative and not throwing all of your code into the question. It can be quite overwhelming to help someone who posts every line of their project. – Spencer D Jan 20 '15 at 21:10