Changing the array's value through a function - c

The function saves the new string and sets it at position 2. But when I run the last "for" there are just some strange symbols.
#include <stdio.h>
#include <stdlib.h>
void edit(int target, char *listf[]){
char *name[1];
printf("\nInsert the new name: \n");
scanf("%s", &name[0]);
listf[target] = &name[0];
printf("\nThe information has change!\n");
printf("Name: %s\n", listf[target]);
system("pause"); }
int main() {
char *listf[3] = {"Alpha", "Beta", "Omega"};
int i;
int target = 2;
for(i = 0; i < 3; i++){
printf("%s\n", listf[i]);
}
edit(target,listf);
printf(" \n\n");
for(i = 0; i < 3; i++){
printf("%s\n", listf[i]);
}
return 0; }

There are two problems with your code. First, the char name is an array of pointers, essentially. And those pointers.......they are not pointing to allocated memory, just garbage data. If you want to make good use of it, you have to assign a pointer to an allocated memory. Now, normally, when you use a function like scanf, they assume that the pointer you given to it(in this case &name[0]) have allocated memory to store the input string. But if you dont do this, your pointer would be pointing to a random address in the memory, possibly editing some other memory cells and causing your program to behave very strangely. So how do you solve this? I'm not claiming my solution to be perfect, but just use a plain char array, preferably a large one, to store the input string. Like this-
char name[1000];
The second problem is this line-
listf[target] = &name[0];
The thing here is that the data the name array is pointing to, is temporary. It will only last as long as the function edit() runs. Why? A simple way to say this is that, name belongs to the edit() function. So when edit() terminates, so does everything along with it. Meaning char name wont store whatever you put into it. This is the reason why you were getting strange symbols. So you have to put the contents of the input in another place in the memory. So you'll have to use malloc() here. Here's my fixed version of the edit function-
void edit(int target, char *listf[]){
char name[1000];
printf("\nInsert the new name: \n");
scanf("%s", name);
//figure out the length of the string and allocate memory accordingly
int len = strlen(name);
char* new_name = malloc(len * sizeof(char));
printf("Test %d\n", len * sizeof(char));
//copy the contents of name to new_name
strcpy(new_name,name);
listf[target] = new_name;
printf("\nThe information has change!\n");
printf("Name: %s\n", listf[target]);
system("pause");
}

Related

Setting return value of char pointer to a char array

Trying to learn C. To that end, I'm coding a program that creates a TroubleTicket report. The user is prompted to enter, priority, name and problem. I'm using a struct to store the data and create a struck array. Using the array approach for now as I'm developing the code. Eventually, I'd to use a linked list.
Anyhow, the get_input() function reads in the user input and returns a char pointer.
Included the get_input() and create_ticket() functions.
#define NUM_TICKETS 10
char* get_input(int maxlen)
{
static char s[110];
char ch;
int i = 0;
int chars_remain = 1;
while (chars_remain)
{
ch = getchar();
if ((ch == '\n') || (ch == EOF))
{
chars_remain = 0;
}
else if (i < maxlen - 1)
{
s[i] = ch;
i++;
}
}
s[i] = '\0';
return s;
void create_ticket()
{
struct ticket
{
int priority;
int number;
char * name;
char * problem ;
char * assigned_to;
char * status ;
};
struct ticket tkt_array[NUM_TICKETS];
struct ticket new_ticket;
printf("Enter your name: ");
new_ticket.name = get_input(20);
printf("new_ticket.name: %s \n", new_ticket.name);
printf("Enter problem description: ");
new_ticket.problem = get_input(100);
printf("new_ticket.problem: %s \n", new_ticket.problem);
printf("Assigned to: ");
new_ticket.assigned_to = get_input(20);
printf("new_ticket.assigned_to %s\n ", new_ticket.assigned_to);
printf("Enter ticket status: ");
new_ticket.status = get_input(10);
printf("new_ticket.status: %s \n", new_ticket.status);
}
I noticed that initial input was read and displayed correctly but subsequent inputs overwrote prior input.
For example, after name was entered, the entered value is displayed
printf("Enter your name: ");
new_ticket.name = get_input(20);
printf("new_ticket.name: %s \n", new_ticket.name);
But after problem description is entered, new_ticket.name was changed to display the problem description text. Took me a while to figure out that the problem is the return s in get_char(). It returns a pointer. The address is the same but the value changes and struct ticket pointers point to the same address of return s from get_char().
How can I save the return value in a variable and not get it reset on subsequent call to get_input?
s is a char *, how can I save the return value of a pointer in a variable?
printf("Enter problem description: ");
new_ticket.problem = get_input(100);
printf("new_ticket.problem: %s \n", new_ticket.problem);
printf("new_ticket.name: %s \n", new_ticket.name);
How can I save the return value in a variable and not get it reset on subsequent call to get_input?
s is a char *, how can I save the return value of a pointer in a variable?
I hope I clearly stated the issue.
This answer addresses only the problem stated in the question, not other errors or problems in your code.
Your function get_input uses a single array static char s[110]; to store the input and always returns the address of this array.
Assignments like new_ticket.name = get_input(20); will store the same address in all pointer variables, so all will actually point to the same variable s which will contain the last input.
One option to solve this would be to replace the pointers in your structure with arrays and either pass this to get_input or copy the value using strcpy.
Or you could use the (non-standard) function strdup to get a dynamically allocated duplicate of the input string. If this function is not available, you could implement it using malloc and strcpy.
Example:
new_ticket.name = strdup(get_input(20));
Note: When using strdup, you should free the memory when it's no longer needed.
or using a replacement for strdup:
const char *s1;
char *s2;
/* ... */
s1 = get_input(20);
s2 = malloc(strlen(s1) + 1);
if(s2 == NULL)
{
/* handle error */
}
else
{
strcpy(s2, s1);
}
new_ticket.name = s2;
(You could implement your own function that wraps malloc and strcpy.)
or with arrays instead of pointers as structure fields
struct ticket
{
int priority;
int number;
char name[20];
char problem[100];
char assigned_to[20];
char status[10];
};
/* ... */
strcpy(new_ticket.name, get_input(sizeof(new_ticket.name));
The static keyword creates a value that exists once, so multiple invocations of the function point to the same data.
If you want to dynamically create data with a lifetime that you control, you may allocate your own memory.
malloc(size) will allocate a block of memory with the size provided. This memory will exist and be valid until you call free on that memory.
You should be careful though, calling free twice, or forgetting to call it at all, are common sources of bugs within c code.
The other option in this is to have the caller of the function provide the memory. You could change your struct ticket to have char name[MAX_NAME_LEN], and update the function get input to get_input(int max_len, char * buffer). This also solves the problem, while avoiding potentially erroneous malloc and frees.

Problem with pointer in array of structs in C

A problem stops the program working correctly. I guess it is related with using pointer in array of structs. Despite trying many ways, nothing could find out:
#include <stdio.h>
typedef struct {
char* courseName;
char* courseDate;
double grade;
}EXAM;
int main() {
double averageGrade = 0;
EXAM *exams[5];
for(int i = 0; i < 5; i++){
printf("Please enter course name:\n");
scanf("%s", exams[i]->courseName);
printf("Please enter course date:\n");
scanf("%s", exams[i]->courseDate);
printf("Please enter course grade:\n");
scanf("%lf", &exams[i]->grade);
averageGrade += exams[i]->grade;
}
averageGrade /= 5;
printf("Your average grade is: %.1lf.", averageGrade);
return 0;
}
1:
typedef struct {
char* courseName;
char* courseDate;
double grade;
}EXAM;
Having defined courseName and courseDate as 'char *' means that they are only pointers. Pointers that without any allocation point to either NULL or more probably to garbage. How to fix it?
Either define your structure with statically allocated char arrays, like:
typedef struct {
char courseName[41];
char courseDate[41];
double grade;
}EXAM;
or allocate them dynamically before signing anything into them:
for (int i = 0; i < 5; i++) {
exams[i]->courseName = (char *) calloc(41, sizeof(char));
exams[i]->courseDate = (char *) calloc(41, sizeof(char));
}
In both cases you should make sure, that user doesn't overflow this fields so instead of "%s" in scanfs I would suggest using "%40[^\n]", which is simply Get up to 40 characters from user until you find a '\n' character (user hits enter).
In addition in first case, you should make sure that both strings are null-terminated strings, a simple way to do it is to set zeros in all tables.
memset(exams, 0, sizeof(EXAM)*5);
2:
EXAM *exams[5];
This are also only pointers. That unallocated point to NULL or garbage. Go with either:
EXAM exams[5];
and use '.' instead of '->'.
or allocate them:
for (int i = 0; i < 5; i++) {
exams[i]= (EXAM *) calloc(1, sizeof(EXAM));
}
EXAM[*] points to unallocated memory:
To create an array of 5 exam objects do:
EXAM exams[5]; // Note there is no * character.
If the pointer is necessary for some reason, then you will need to allocate space for these objects:
EXAM *exams[5];
for (int i=0; i<5; i++) {
exams[i] = calloc(1, sizeof(EXAM));
}
You also need to allocate space for the strings:
In C, when you use scanf on a %s, the space for that string must already be allocated.
So you should either use char courseName[256];, allocate sufficient space on the heap for the string, or use functions like getline() which will allocate the space for you.

How to set multiple strings according to user's request? And then sort them and put in an array of strings?

I received the following question:
Write a program that receives the user's number of friends from the user.
Once the number is received, the program will record the members' names.
Keep the names in the array (it will be a string array, i.e. a char ** array) so that each cell in the array points to the beginning of a string.
Then sort the array in alphabetical order (using the strcmp function and swap between pointers) and print the member names in order.
So far this is what I did and I don't understand why it doesn't work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int choice = 0;
int i = 0;
printf("Enter number of friends: ");
scanf_s("%d", &choice);
char* friends = (char*)malloc(choice * 50 * sizeof(char));
for (i = 0; i < choice; i++)
{
printf("Enter name of friend %d: ", (i + 1));
scanf_s("%s", &friends[i]);
}
getchar();
return 0;
}
Can someone please help me?
Never cast malloc as you did in your code.
char* friends = (char*)malloc(choice * 50 * sizeof(char));
If you declared friends as a pointer as above, friends[i] is only the i_th character in string friends (for example, with friends = "abcd", friends[0] = 'a', friends[1] = 'b'and so on).
If you want to store all names, you should use 2D array, double pointer, or an array of pointer:
char friends[choice][50]; // (1)
OR
char * friends[choice]; // (2)
OR
char ** friends; // (3)
For solution (2), you have to allocate for each pointer in array friends (do not forget to free the pointer at the end of program):
for(int i = 0; i < choice; i++) {
friends[i] = malloc(sizeof(char) * 50);
if (!friends[i])
// handle the error of malloc function
return -1;
}
Then for scanf function (you can use fgets instead of scanf):
scanf("%49s", friends[i]);
If you want to use double pointer (solution (3)), you have to allocate for pointer friends then for each pointer friend[i] (do not forget to free all pointers at the end of program):
friends = malloc(sizeof(char *)* choice);
if (!friends)
// handle the error of malloc function
return -1;
for(int i = 0; i < choice; i++) {
friends[i] = malloc(sizeof(char) * 50);
if (!friends[i])
// handle the error of malloc function
return -1;
}
One attention, solutions (1) and (2) require VLAs to be available. They are with C since C99 and became optional since C11. Thanks #alk

How to dynamically allocate a two dimensional array of pointers ? (C)

I have an assignment to make a dictionary.
It will contain an x amount of words and their definitions (input by user).
Instructions say that the dictionary should be of type char*** (2D array of pointers=arrays=strings), but I've got absolutely no idea of how to dynamically allocate the size of the array. it should have 2 lines, 1 for words and another 1 for their definitions, and the number of columns is decided by how many words are in the dictionary. While looking for help online i came up with this:
char** AllocateArray(int line, int column)
{
char** pArray=(char**)malloc(line*sizeof(char*));
int i;
for(i=0;i<2;i++)
pArray[i]=(char*)malloc(column*sizeof(char));
return pArray;
}
What changes should i make in the code for it to work with my char*** ?
Using Visual studio 2012
Edit:
I have a problem with this right now:
void inputString(char* p1)
{
char buffer[80];
printf("\nEnter a word:");
scanf("%s",buffer);
p1=(char*)malloc(strlen(buffer)+1);
if(p1!=NULL)
{
strcpy(p1,buffer);
free(buffer);
}
}
it crashes right after i input a word. the char* that the function receives is dictionary[i][j]. –
Don't free() anything allocated on the stack (i.e. buffer).
Also, your function inputString() will not tell its client what memory it had allocated, since p1 is local to it.
Here is an example.
char*** dictionary;
int i = 0;
int j = 0;
int lines = 10;
dictionary = (char***)malloc(sizeof(char**)*lines);
for(i=0;i<lines;i++)
{
dictionary[i] = (char**)malloc(sizeof(char*)*4);
for(j=0;j<4;j++)
dictionary[i][j] = (char*)malloc(sizeof(char)*25);
}
You have to modify the malloc's parameters in order to adapt to your problem/ or modify them when you need more memory for your strings.
Also it might be a good idea to try and free memory when you do not need it
Don't forget to malloc like this...
dictionary[i][j] = (char*)malloc(sizeof(char)*strlen(word_to_insert)+1);
...because each word end with a supplementary byte filled with 0 "null terminate string".
a sample
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char ***dictionary;
const char *words[] = { "ASEAN", "United Nations", "OPEC" };
size_t howManyWords = sizeof(words)/sizeof(*words);
int i;
dictionary = malloc(howManyWords*sizeof(char**));
printf("Please enter the definition of this word\n");
for(i = 0; i < howManyWords; ++i){
char buff[80];
char **keyValue;
printf("%s : ", words[i]);
fgets(buff, sizeof(buff), stdin);
keyValue = malloc(2*sizeof(char*));
keyValue[0] = (char*)words[i];
keyValue[1] = malloc(strlen(buff)+1);
strcpy(keyValue[1], buff);
dictionary[i] = keyValue;
}
//print
for(i=0;i<howManyWords;++i){
printf("%s : %s", dictionary[i][0], dictionary[i][1]);
}
//release
for(i=0;i<howManyWords;++i){
free(dictionary[i][1]);
free(dictionary[i]);
}
free(dictionary);
return 0;
}

How do I create (and use) an array of pointers to an array of strings in C?

I need to create an array of pointers that will each point to an array of strings.
The base, is a size 2 array of strings (the length of the strings is unknown at start). For example an array of 2 strings (first-name and last-name):
char *name[2];
Now I need to create an array of an unknown size (entered by the user) that will point to the type that I just created.
My idea was to create it this way:
char **people=name;
And then ask the user how many names he would like to enter and allocate enough space to hold all the names.
people=(char**)malloc(sizeof(char*)*num); //num is the number received by the user.
This is where things got too complicated to me and I can't figure out how to I call each individual name to put a string in it.
I built a loop that will receive all the names but I have no idea how to store them properly.
for(i=0;i<num;i++){
printf("Please enter the #%d first and last name:\n",i+1);
//Receives the first name.
scanf("%s",&bufferFirstName);
getchar();
//Receives the last name (can also include spaces).
gets(bufferLastName);
people[i][0]=(char*)malloc(strlen(bufferFirstName)+1);
people[i][1]=(char*)malloc(strlen(bufferLastName)+1);
//^^Needless to say that it won't even compile :(
}
Can anyone please tell me how to properly use this kind of an array of points?
Thanks.
from cdecl:
declare foo as array of pointer to array 2 of pointer to char
char *(*foo[])[2];
So, foo[0] is a pointer to array 2 of char *
That is the array, but for your use, you want:
declare foo as pointer to array 2 of pointer to char;
char *(*foo)[2];
Now you can do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *(*foo)[2];
printf("How many people?\n");
int n; scanf("%d", &n);
foo = malloc(sizeof *foo * n);
for (int i = 0; i < n; i++) {
char bufFirstName[1024];
char bufLastName[1024];
printf("Please insert the #%d first and last name:\n", i+1);
scanf("%s %s", bufFirstName, bufLastName);
char *firstName = malloc(strlen(bufFirstName) + 1);
char *lastName = malloc(strlen(bufLastName) + 1);
strcpy(firstName, bufFirstName);
strcpy(lastName, bufLastName);
foo[i][0] = firstName;
foo[i][1] = lastName;
}
for (int i = 0; i < n; i++) {
printf("Name: %s LastName: %s\n", foo[i][0], foo[i][1]);
}
return 0;
}
Compile with -std=c99
Note that using scanf, strcpy, strlen like that is unsafe because there can be a buffer overflow.
Also, remember to free your malloc's!
Not that your approach is wrong, but have you considered instead using a struct that includes first and last name, and then malloc'ing based on the number of names the user will enter:
typedef struct {
char* first;
char* last;
} person;
person* people = malloc(num * sizeof(*person));
This just simplifies pointer interaction. While the way you are doing it is a good exercise in understanding pointers better, it may not be the easiest way to understand.
If you are unable to use structs, you should instead be doing:
char** people;
people = malloc(2*num*sizeof(char*));
for (int i = 0; i < 2*num; i++)
people[i] = malloc(MAX_NAME_SIZE*sizeof(char));
Now you would need to reference the i th person via:
first name: people[i*2 + 0]
last name: people[i*2 + 1]

Resources