How to fix format error for char* and char**

Viewed 248

i'm writing a kind of phonebook program and i'm using typedef struct to store the name and number of the people. The user will input name and number as a string format, but when i try to scan it, the compiler gives me an error saying "format specifies type char * but the argument has type char **. Any solution on this? i really don't understand what it means. Error is on first loop on both scans any help would be appreciated. Thanks!

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

typedef struct
{
    char *name;
    char *number;
}
person;

int main(void)
{
    int n;
    scanf("%i", &n);
    person people[n];

    for(int i = 0; i < n; i++)
    {
        scanf("%s", &people[i].name); // these 2 lines are buggy
        scanf("%s", &people[i].number);
    }

    char *findName;
    scanf("%s", &findName);

    for(int i = 0; i < n; i++)
    {
        if(strcmp(people[i].name, findName) == 0)
        {
            printf("%s\n", people[i].number);
        }
        else
        {
            return 1;
        }
    }

    return 0;
}
4 Answers

The statement

scanf("%s", &people[i].name);

is wrong for two reasons:

  1. First of all the %s format expects a char * argument. By using the address-of operator you get a value of type char **. Mismatching format specifier and argument type leads to undefined behavior.

  2. If you remove the address-of operator you will pass an uninitialized pointer, it's not pointing anywhere valid. This means scanf will write the string to some seemingly random location in memory. Again this leads to undefined behavior.

A simple way to solve the second issue, you could change the structure to contain arrays of characters instead, as in:

typedef struct
{
    char name[64];
    char number[64];
}
person;

Then use a limit in the format so the scanf function will not attempt to read a string longer than the arrays:

scanf("%63s", people[i].name);  // The array decays to a pointer to its first element

Note that the length in the format is 63, so the string null-terminator will fit in the array.

Never use the "%s" format with scanf(). You cannot use it safely.

The problem is, that the user controls how many characters will get written into the memory buffer, but the program needs to supply the memory buffer without knowing how many characters the user is actually going to input. The result is invariable buffer overrun vulnerabilities of your program. Don't do this.

Instead, use allocating input functions. For scanf(), you need to add the "m" modifier:

scanf("%ms", &people[i].name);

This changes the expected argument type from char* to char**, it will malloc() a sufficiently sized buffer for you, and store the address at the given location. I.e, this call does precisely what you want it to do. Just don't forget to free() the resulting string once you are done with it.

The "m" modifier works with the "%ms", "%m[" and "%mc" conversion specifiers, i.e. all the conversions that can output a string of unknown length.

(The other allocating input functions that you should keep in mind are getline() and getdelim().)

findname is already a char *. You ask for reference of this value, it represent char **

scanf("%s", &findName);

Try this :

scanf("%s", findName);

Here is an example with dynamically allocated memory and scanf with %ms:

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

typedef struct
{
    char *name;
    char *number;
}
person;

int main(void)
{
    int n = 0;
    char *findName ;
    scanf("%i", &n);
    if (n == 0) return 1;
    
    person* people = (person*)malloc(n * sizeof(person));
    
    if (people == NULL) return 1;
    
    for(int i = 0; i < n; i++)
    {
        person p = people[i];
        printf("Enter Name and number:\n");
        printf("Number: ");
        scanf("%ms", &(p.number));
        printf("Name: ");
        scanf("%ms", &(p.name));
        people[i] = p;
    }
    
    printf("Enter name to search: ");
    scanf("%ms", &findName);
    printf("Searching for: %s\n", findName);

    for(int i = 0; i < n; i++)
    {
        person p = people[i];
        
        if(strcmp(p.name, findName) == 0)
        {
            printf("Number for %s is %s\n", p.name, p.number);
        }
    }
    free(findName);

    for(int i = 0; i < n; i++)
    {
        //notice how we have to free the strings for each name and number, 
        // and not just the people array 
        free(people[i].name);
        free(people[i].number);
    }
    free(people);
    return 0;
}
Related