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

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;
}

The statement
scanf("%s", &people[i].name);
is wrong for two reasons:
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.
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

Pointer to structure in C

How can I display the stored values via pointer, my code outputs to NULL. I've already tried assigning the struct emp to ptr before the 2nd loop but it won't run.
struct rec {
char *Name;
}emp[100];
int main() {
int x;
int i;
struct rec *ptr = NULL;
ptr = emp;
printf("Enter Number of Clients: ");
scanf("%d", &x);
getchar();
for(i=0; i!=x; i++){
printf("Enter Name: ");
scanf("%[^\n]", &ptr->Name);
getchar();
ptr++;
printf("\n");
}
i = 0;
while(i!=x){
printf("Name is: %s\n", *ptr->Name);
i++;
ptr++;
}
If the code iterates twice it prints
Name: (NULL)
Name: (NULL)
How can I display the stored values via pointer, my code outputs to NULL. I've already tried assigning the struct emp to ptr before the 2nd loop but it won't run.
There are several issues in your code which are listed below:
You need to re-initialize your ptr pointer to the beginning of the array emp after the first loop.
ptr = emp;
Second one being data member name of your struct emp. It is just a pointer and it doesn't point towards a valid memory address where you can save user input cstring. Either you need to allocate the memory dynamically using malloc or use statically allocated memory just like shown below. However, you can have buffer overflow if you input characters more than DEFAULT_NAME_SIZE (including /0 character).
%[^\n] matches a char *, not a char **. You need to use scanf("%[^\n]", ptr->Name); not scanf("%[^\n]", &ptr->Name);.
"%s" format specifier of printf requires an arugment of type char * but you have been passing char type to it. The correct statement is printf("Name is: %s\n", ptr->Name);.
-
#include <stdio.h>
#define DEFAULT_NAME_SIZE 200
struct rec {
char Name[DEFAULT_NAME_SIZE];
} emp[100];
int main()
{
int x;
int i;
struct rec *ptr = NULL;
ptr = emp;
printf("Enter Number of Clients: ");
scanf("%d", &x);
getchar();
for (i=0; i< x; i++){
printf("Enter Name: ");
scanf("%[^\n]", ptr->Name);
getchar();
ptr++;
printf("\n");
}
ptr = emp;
for (i = 0; i < x; i++){
printf("Name is: %s\n", ptr->Name);
ptr++;
}
return 0;
}
You can try to enable all warning while you compile your code. Most of the bugs can be pointed by the compiler's warning message.
char *Name;
You never allocated space for the name. Did you mean
char Name[100]
?
This should work, until somebody enters too long of a name. You will get to buffer overflow later and learn about dynamic memory allocation.
When scanning or printing strings, we don't use & and * for referencing and dereferencing pointers, but we must also alloc and free space for them. Fixed your code:
#include <stdio.h>
#include <stdlib.h>
struct rec {
char *Name;
}emp[100];
int main() {
int x;
int i;
struct rec *ptr = NULL;
ptr = emp;
printf("Enter Number of Clients: ");
scanf("%d", &x);
getchar();
for(i=0; i!=x; i++)
{
printf("Enter Name: ");
/* alloc space for string pointer */
ptr->Name = (char*) malloc(sizeof(char)*10);
scanf("%s[^\n]",ptr->Name);
getchar();
ptr++;
printf("\n");
}
i = 0;
/* reset array pointer position */
ptr=emp;
while(i!=x)
{
printf("Name is: %s\n", (ptr->Name));
/* free space for string pointer */
free(ptr->Name);
i++;
ptr++;
}
}
PS: Remember resetting a pointer to the initial position whenever using pointers to arrays.

C compiler error: undefined reference to function

After I execute the exe I get this error :
undefined reference to `StudentScan'
error: ld returned 1 exit status|
Note: I'm bad and new to coding so don't mind my bad coding please^^
Note2: I'm just messing with random functions.
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
char AverageNum[2];
};
void StudentScan(int, struct student[]);
void StudentPrint(int, struct student[]);
int main() {
int i;
int length;
struct student *studentp;
printf ("\nEnter the host of students: ");
scanf ("%d ", &length);
struct student list[length];
studentp=malloc(length*sizeof(struct student));
if (studentp==NULL)
{
printf("Out of memory!");
return 0;
}
for(i = 0; i < length; i++) {
StudentScan(i,studentp);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
free (studentp);
void StudentScan(int i, struct student list[])
{ printf("\nEnter first name : ");
scanf("%s", list[i].firstName);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
return 0;
}
The posted code has defined StudentScan() within main(). But nested function definitions are not allowed in C. This should generate a compiler warning, such as:
warning: ISO C forbids nested functions [-Wpedantic]
void StudentScan(int i, struct student list[])
Pay attention to all compiler warnings and fix them. If no warning is seen when compiling this code, turn up the level of compiler warnings. On gcc, I suggest to always use at least gcc -Wall -Wextra, and I always add -Wpedantic. The -Wpedantic is needed with gcc to see a warning for this. Some compilers, and gcc is one of these, do support nested function definitions as a compiler extension. Still, this feature is nonstandard, and it is best to not rely on it.
The fix is simple: move the definition of StudentScan() out of main():
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
char AverageNum[2];
};
void StudentScan(int, struct student[]);
void StudentPrint(int, struct student[]);
int main(void) {
int i;
int length;
struct student *studentp;
printf ("\nEnter the host of students: ");
scanf ("%d ", &length);
struct student list[length];
studentp=malloc(length*sizeof(struct student));
if (studentp==NULL)
{
printf("Out of memory!");
return 0;
}
for(i = 0; i < length; i++) {
StudentScan(i,studentp);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
free (studentp);
return 0;
}
void StudentScan(int i, struct student list[])
{ printf("\nEnter first name : ");
scanf("%s", list[i].firstName);
printf("\nEnter average number: ");
scanf("%s", list[i].AverageNum);
}
Also note that you should always specify maximum widths when reading strings using scanf() family functions with %s or %[] to avoid buffer overflow. For example:
scanf("%19s", list[i].firstName);
Note that 19 is used, even though the firstName field is an array of 20 char values. Remember that one space must be reserved for the \0 terminator. And since you are using %s to read a string into the AverageNum field, you should also have:
scanf("%1s", list[i].AverageNum);
That is, this field can only hold one digit. If the intention is to hold two digits, this field must be changed within the struct to: char AverageNum[3].
And while we are discussing scanf(), note that this function returns the number of successful assignments made during the function call. If no assignments are made, 0 is returned. This return value should always be checked. Consider: if the user mistakenly enters a letter when a digit is expected, nothing is stored in the intended variable. This may lead to undefined behavior. You may try something like this to validate numeric input:
printf ("\nEnter the host of students: ");
while (scanf ("%d ", &length) < 1) {
puts("Please enter a number");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
This code asks the user to enter input again if a number is not entered when expected. Note that if the user does enter a non-digit, this character remains in the input stream and must be cleared before attempting to process more user input. The while loop is a typical construction which accomplishes this task.
Edit
Based on comments made by the OP, here is a modified version of the posted code. This version uses a float value instead of a character array for the AverageNum field of the struct. A floating-point type may be more useful than an integer type for storing averages. It is usually best to use double for floating-point values, but in this case it looks like AverageNum has little need for precision (the char array was intended to hold only two digits); float is probably sufficient for this use. If a different type is desired, it is simple enough to modify the code below.
Some input validation is implemented, but note that more could be done. The user is prompted to enter a number when non-numeric input is found where numeric input is expected. The input stream is cleaned with the while loop construction after such an input mistake; it would be good to remove this code to a separate function called clear_input(), for example.
If the user signals end-of-file from the keyboard, scanf() will return EOF; the code below chooses to exit with an error message rather than continue with malformed input in this case. This could also occur with input redirected from a file, and this condition may need to be handled differently if such input is expected.
The loop that populated the list[] array seemed to be operating inefficiently, asking for AverageNum twice in each pass. This has been streamlined.
Note that the call to malloc() can be rewritten as:
studentp = malloc(length * sizeof *studentp);
This is a very idiomatic way of writing such an allocation. Here, instead of using an explicit type as the operand of sizeof, that is, instead of sizeof (struct student), the variable which holds the address of the allocation is used. sizeof only uses the type of the expression *studentp, so this variable is not dereferenced here. Coding this way is less error-prone and easier to maintain when types change during the maintenance life of the code.
Yet, it is unclear why memory is allocated for studentp in the first place. In the posted code, both the firstName and AverageNum fields are filled for members of the dynamically allocated studentp in calls to StudentScan() in a loop; the same loop fills the AverageNum field of the members of list[] (a different array of structs) with different input. There seems to be no need for one of these arrays of student structs; I have commented-out the dynamically allocated array in favor of the statically allocated version.
Here is the modified code:
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
float AverageNum;
};
void StudentScan(int, struct student[]);
void StudentPrint(int, struct student[]);
int main(void) {
int i;
int length;
// struct student *studentp;
printf ("\nEnter the host of students: ");
while (scanf ("%d", &length) < 1) {
puts("Please enter a number");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
struct student list[length];
/* This is fine */
// studentp = malloc(length * sizeof (struct student));
/* But this is better */
// studentp = malloc(length * sizeof *studentp);
// if (studentp == NULL)
// {
/* Not wrong, but... */
// printf("Out of memory!");
// return 0;
// fprintf(stderr, "Allocation failure\n");
// exit(EXIT_FAILURE);
// }
for(i = 0; i < length; i++) {
StudentScan(i, list);
}
/* Code to display results here */
// free (studentp);
return 0;
}
void StudentScan(int i, struct student list[])
{
putchar('\n');
printf("Enter first name: ");
if (scanf("%19s", list[i].firstName) != 1) {
puts("Input error");
exit(EXIT_FAILURE);
}
printf("Enter average number: ");
while (scanf("%f", &list[i].AverageNum) < 1) {
puts("Please enter a number");
int c;
while ((c = getchar()) != '\n' && c != EOF) {
continue;
}
}
}
You have to remove the scan function from the main. Also there is not a printstudent function you are declaring. You must remove /n from the printf and the scanf functions and place them accordingly. You can then test if your data are being added correctly in your struct with a simple loop.
#include <stdio.h>
#include <stdlib.h>
struct student {
char firstName[20];
char AverageNum[2];
};
int main() {
int i=0;
int length;
struct student *studentp;
printf ("Enter the host of students:");
scanf ("%d", &length);
struct student list[length];
studentp=malloc(length*sizeof(struct student));
if (studentp==NULL)
{
printf("Out of memory!");
return 0;
}
for(i = 0; i < length; i++) {
printf("Enter first name :");
scanf("%s", list[i].firstName);
printf("Enter average number: ");
scanf("%1s", list[i].AverageNum);
}
for(i = 0; i< length; i++){
printf("number of host is: %d , his/her first name: %s , his/her avg number: %s \n", i, list[i].firstName, list[i].AverageNum);
}
free (studentp);
return 0;
}

Passing String to Main from a function

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void get_name();
void display_name(char *fullname);
int main(void)
{
char first[80];
char second[80];
char *fullname[80];
get_name();
display_name(*fullname);
system("pause");
return 0;
}
void get_name()
{
char first[80];
char second[80];
char *fullname[80];
printf("Please enter first name: ");
scanf("%s", &first);
printf("\nPlease enter last name: ");
scanf("%s", &second);
strcpy(*fullname, first);
strcat(*fullname, " ");
strcat(*fullname, second);
printf("\n\nFull name is : %s ", *fullname);
}
void display_name(char *fullname)
{
int index;
char check;
int count=0;
printf("\n\nFull name is : %s ", fullname); //check to see if string is passes correctly
for(index=0; fullname[index] != '\0'; index++)
{
check=fullname[index];
if(check != ' ')
{
count++;
}
}
printf("\n\nNumber of characters in string is: %i\n", count);
}
im trying to send the string from get_name() to display name to count the number of characters. Everytime i pass the string, its comes out as gibberish. Am i passing wrong? I need to use one function to get the first and last name and concatenate the full name, then use another function to count the number of characters.
You're using pointers and scanf quite wrongly.
First of all scanf argument to for %s is supposed to be an array of characters. Remember that the array is in fact the pointer to the array.
Second you declare fullname to be an array of 80 pointers which is probably not what you want to do. Especially when you don't allocate the space for the string.
Instead it should be something like:
void get_name()
{
char first[80];
char second[80];
char fullname[80]; // an array of chars instead of pointers
printf("Please enter first name: ");
scanf("%s", first); // not taking the address of first - is already an address
printf("\nPlease enter last name: ");
scanf("%s", second); // not taking the address of second - is already an address
strcpy(fullname, first); // don't dereference fullname
strcat(fullname, " "); // don't dereference fullname
strcat(fullname, second); // don't dereference fullname
printf("\n\nFull name is : %s ", fullname); // don't dereference fullname
}
The declarations of variables are local to the scope where they are declared.
IOW when you declare first, second and fullname in your function get_name, they are local to that function. In order to pass the value outside of the function you have two, no three ways to do this starting with the worst way:
(1) declare the variable global, i.e. outside of main then share that variable in your function(s).
(2) declare the variable in main but pass it to the function who then fills in the string
int main()
{
char fullname[80];
get_name(fullname,sizeof(fullname)); // good to tell function avail size
...
void get_name(char* fullname, size_t length)
{
...
(3) Allocate memory on the heap, heap memory can be passed around between functions via a pointer
int main()
{
char* fullname = NULL;
get_name(&fullname);
...
void get_name(char** fullname)
{
*fullname = malloc(80);
...
EDIT
In order to read strings from the keyboard it is better to use fgets()
char buffer[128];
if (fgets(buffer,sizeof(buffer),stdin) != NULL) {
// remove the \n
char* p = strchr(buffer,'\n');
if ( p != NULL ) {
*p = '\0';
}
}
Using scanf reading from the keyboard is to be avoided, if you need to extract information use instead sscanf on the string read with fgets

Code for extracting string crashes

I wrote this code to accept a string and until where it should extract a string and print it.
Given below is the code:
#include <stdio.h>
#include <stdlib.h>
int strlen(char s[]){
int i = 0;
while(s[i]!='\0')
i++;
return i;
}
char *extract(char s[], int n){
char *result = (char *)malloc(sizeof(char)*3);
for(int j=0;j<n;j++){
result[j]=s[j];
}
return result;
}
int main(){
char str[100];
int till;
printf("Enter a string: ");
scanf("%s", str);
printf("Until where to extract: ");
scanf("%d", till);
char *ret = extract(str, till);
printf("%s is extracted", ret);
return 0;
}
This is what happens:
Enter a string: hello
Enter from where to extract: 2
And then it crashes.
I don't see what the problem is.
At very first, you need to change
scanf("%d", till);
to
scanf("%d", &till);
as scanf() needs a pointer to the data type argument for the supplied format specifier. Using a wrong type of argument causes undefined behavior.
Then, there are many issues, like
You're allocating only 3 chars, where you're looping based on the incoming value of n.
You never checked for the success of malloc().
You're not null-terminating your result which you intend to use as a string later.
That said,
You should always limit the input for your strings to avoid the possibility of overrun, like
scanf("%99s", str); //considering the length of the array is 100.
There is a library function strlen() available with string.h. Even if you want to roll out your own functions, try to follow a different naming convention.
You did not free() the allocated memory.
I you add -Wall to command when you compile your code you'll see
test.c:40:2: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
scanf("%d", till);
^
Then change you must change it to scanf("%d", &till);
In addition to the other suggestions:
char *result = (char *)malloc(sizeof(char)*3);
Should be:
char *result = malloc(n + 1);

Array of pointers to string

When we are using a 2-D array of characters we are at liberty to either initialize the strings where we are declaring the array,or receive the string using scanf() fucntion.However when we are using an array of pointers we can not receive the strings from keyboard using scanf(). why?
#include<stdio.h>
int main()
{
char *names[6];
int i;
for(i=0;i<=5;i++)
{
printf("Enter name:");
scanf("%s",names[i]);
}
return 0;
Your code doesn't work because it invokes undefined behavior by storing strings into pointers which are not initialized to point to anything. You need to use malloc().
valgrind is a tool which would catch this sort of program for you, automatically.
char *names[6];
declares names as an array of pointers to char but does not allocate memory for elements of names. You need to allocate memory for each element before writing to it, otherwise it will invoke undefined behavior.
Try this:
for(i = 0; i <= 5; i++)
{
printf("Enter name:");
names[i] = malloc(SIZE); // SIZE is for length of string
if( names[i] != NULL)
scanf("%s", names[i]);
}
scanf does not allocate memory automatically, so you have to allocate a buffer for storing the input before calling scanf.
#include<stdio.h>
int main()
{
char *names[6];
int i;
for(i=0;i<=5;i++)
{
printf("Enter name:");
names[i]=(char *)malloc(256); // 256 is size of buffer
scanf("%s",names[i]);
}
return 0;

Resources