I need to build an Coin Dispenser application. I've written the withdrawal and deposit parts but in item inventory creation I couldn't create a solution to that. Item names should be taken from keyboard as strings. Item prices corresponding to the items should be taken from keyboard as unsigned ints.
Arrays should be variable-sized, aka VLAs. I've googled it and found some resources about creating VLAs, I think I should allocate memory to them dynamically using malloc. I couldn't do that, so I basically created limited sized arrays with using BUFSIZ.
How can I use VLAs in this case, and how can I fill them with strings?
Also I should prevent buffer overrun. This will be the second case.
Macros and code blocks:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMBER_OF_COINS 5
#define ITEM_NAME_LEN 9
...
void editorMain(void)
{
printf("Please enter the number of items: ");
scanf("%u", &itemQuantity);
printf("\n\n");
char ** itemNames[BUFSIZ] = {0};
unsigned int itemPrices[BUFSIZ] = {0};
printf("Please enter the names of items: \n");
for (int i = 0; i < itemQuantity; ++i) {
printf("#%d: ", i + 1);
gets(** itemNames[i]);
puts("");
}
printf("Please enter the prices of items: \n");
for (int i = 0; i < itemQuantity; ++i) {
printf("#%d: ", i + 1);
scanf("%u", &itemPrices[i]);
puts("");
}
dispenserMain(*itemNames, itemPrices);
return;
}
Also program should output the item names and prices as a list. Function dispenserMain does this:
void dispenserMain(char * itemNames[], unsigned int itemPrices[]) {
...
for (counterItemNames = 1; counterItemNames <= itemQuantity; ++counterItemNames) { //the for loop will print out the item name and prices with their correspondence.
printf("%10u.%14s%12.2f TL\n", counterItemNames, itemNames[counterItemNames - 1], currencyConverter(itemPrices[counterItemNames - 1]));
}
...
}
Edit (new code):
void editorMain(void)
{
printf("Please enter the number of items: ");
scanf("%u", &itemQuantity);
printf("\n\n");
char itemNames[itemQuantity][ITEM_NAME_LEN+1];
memset(&itemNames, 0, sizeof(itemNames));
printf("Please enter the names of items: \n");
char line[ITEM_NAME_LEN+1];
for (int i = 0; i < itemQuantity; ++i) {
printf("#%d: ", i + 1);
if (! fgets(line, sizeof(line), stdin) || !line[0])
// abort...
// fgets leaves the trailing newline in the input
if (line[strlen(line)-1] == '\n') {
line[strlen(line)-1] = 0;
}
snprintf(itemNames[i], ITEM_NAME_LEN+1, "%s", line);
puts("");
}
unsigned int itemPrices[] = {0};
printf("Please enter the prices of items: \n");
for (int i = 0; i < itemQuantity; ++i) {
printf("#%d: ", i + 1);
scanf("%u", &itemPrices[i]);
puts("");
}
dispenserMain(itemNames, itemPrices);
return;
} //end globalMenuOut
It outputs:
Welcome to CoinedDispenser!
Author: Buğra Ekuklu
Contact: bugraekuklu [at] hotmail.de
1. Dispenser
2. Editing variables
3. Readme
Please make a choice: 2
Please enter the number of items: 3
Please enter the names of items:
#1:
#2: blabla
#3: glagla
Please enter the prices of items:
#1: 45
#2: 65
#3: 75
................... AVAILABLE ITEMS oo
# Item Name Price
= ========= ======
1.
0.45 TL
2. blabla
0.65 TL
3. glagla
0.75 TL
Enter your item selection:
Dispenser function:
void dispenserMain(char (*itemNames)[ITEM_NAME_LEN+1], unsigned int itemPrices[])
{
printf("%s\n\n", " ................... AVAILABLE ITEMS oo");
printf("%10s%15s %14s\n", "#", "Item Name", "Price");
printf("%10s%15s %14s\n", "=", "=========", "======");
puts("");
unsigned int counterItemNames = 0; //initializing counter
for (counterItemNames = 1; counterItemNames <= itemQuantity; ++counterItemNames) { //the for loop will be print out the item name and prices with their correspondence.
printf("%10u.%12s%12.2f TL\n", counterItemNames, itemNames[counterItemNames - 1], currencyConverter(itemPrices[counterItemNames - 1]));
}
puts("");
...
}
Where you write char ** itemNames[BUFSIZ] = {0}; should be:
char itemNames[itemQuantity][ITEM_NAME_LEN+1];
This is a VLA because it has an array that has a dimension known at
runtime. Space allocated via malloc is not a VLA.
Unfortunately VLAs cannot have initializers, so you would also have
to write:
memset(&itemNames, 0, sizeof itemNames);
If you want to dynamically allocate this array it would be:
char (*itemNames)[ITEM_NAME_LEN+1];
itemNames = calloc( itemQuantity, sizeof *itemNames );
and in both cases the usage will be identical. itemNames[i] designates an array which you have allocated.
Your code gets(** itemNames[i]); has two problems; it dereferences a null pointer, and it uses the gets function which allows the user to cause a buffer overflow. You should be reading into the array itemNames[i], which after using my suggestion above, designates storage you've allocated.
Unfortunately it's awkward in C to just input a string of fixed length. You have to worry about what happens if the user types more than your line length. If that doesn't concern you too much then:
char line[BUFSIZ];
if ( ! fgets(line, sizeof line, stdin) || !line[0] )
// abort...
// fgets leaves the trailing newline in the input
if ( line[strlen(line)-1] == '\n' )
line[strlen(line)-1] = 0;
snprintf(itemNames[i], ITEM_NAME_LEN+1, "%s", line);
To call your function do:
dispenserMain(itemNames, itemPrices);
and the function has to be
void dispenserMain(char (*itemNames)[ITEM_NAME_LEN+1], unsigned int itemPrices[]);
If you want to use your original the syntax for dispenserMain then you have to allocate an array of pointers, as opposed to using a 2-D array as I have described. In that case you would use char **itemNames = malloc(itemQuantity * sizeof *itemNames); and the loop through and malloc each pointer to point to some more storage of length ITEM_NAME_LEN+1.
Related
I am trying to allocate a dynamic array of Country objects for my school project. I have malloc'd the array in main() function and I am reallocating it in a add_country() function. but it seems to give me realloc invalid ponter error. Could someone help? This is the minimal reproducable code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int count = 0;
typedef struct test_Country
{
char name[20];
int gold;
int silver;
int bronze;
} test_Country;
test_Country *addtest_Country(test_Country test_Country_obj, test_Country*array)
{
int flag = 0;
printf("%s\n", "before realloc");
test_Country *new_array;
new_array = realloc(array, sizeof(test_Country *) * (count + 1));
printf("%s\n", "after realloc");
//array[count].name = (char *)malloc(strlen(test_Country_obj.name) + 1);
if (count == 0)
{
strcpy(new_array[0].name, test_Country_obj.name);
}
else
{
for (int i = 0; i < count; i++)
{
if (strcasecmp(new_array[i].name, test_Country_obj.name) == 0)
{
printf("%s", "test_Country already added\n");
flag = 1;
break;
}
}
}
if (flag == 0)
{
strcpy(new_array[count].name, test_Country_obj.name);
test_Country_obj.gold = 0;
test_Country_obj.silver = 0;
test_Country_obj.bronze = 0;
new_array[count] = test_Country_obj;
count = count + 1;
}
flag = 0;
return new_array;
}
int main()
{
char choice;
test_Country *array = malloc(sizeof(test_Country));
test_Country test_Country_obj;
printf("%s", "Enter your choice : ");
scanf("%s", &choice);
//fgets(ptr, 80, stdin);
//sscanf(ptr, "%c %s %d %d %d", &choice, test_Country_obj.name, &test_Country_obj.gold, &test_Country_obj.silver, &test_Country_obj.bronze);
//printf("%s", &choice);
while (choice != 'E')
{
printf("%s", "Enter test_Country name : ");
scanf("%s", test_Country_obj.name);
array = addtest_Country(test_Country_obj, array);
//printf("%d%s", count, "is count");
printf("%s", "Enter your choice : ");
scanf("%s", &choice);
}
}
I cant seem to understand what is wrong.
char choice;
scanf("%s", &choice);
is bad. choice has only room for one character, so it can hold only strings upto zero characters. (the one-character room is for terminating null-character). Trying to store strings longer than zero character leads to dangerous out-of-range write and it may destroy data around that.
To avoid out-of-range write, you should allocate enough elements and specify the maximum length to read. The maximum length should be the buffer size minus one for terminating null-character.
char choice[16]; /* allocate enough elements */
scanf("%15s", choice); /* specify the maximum length */
After that, choice in the while and switch should be replaced with choice[0] to judge by the first character. Another way is using strcmp() to check the whole string.
I am trying to do this programming project in the book i study:
Write a program that sorts a series of words entered by the user:
Enter word: foo
Enter word: bar
Enter word: baz
Enter word: quux
Enter word:
In sorted order: bar baz foo quux
Assume that each word is no more than 20 characters long. Stop reading when the user enters an empty word (i.e., presses Enter without entering a word). Store each word in a dynamically sllocated string, using an array of pointers to keep track of the strings.
After all words have been read, sort the array (using any sorting technique) and then use a loop to print the words in sorted order.
That is what i am trying:
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 20
int compare ( const void * p, const void * q);
int main (void){
int n;
printf("How many words do you want to write");
scanf("%d", &n);
fflush(stdin);
char * word[n];
for( int i = 0; i < n; i++){
fprintf(stdout , "Waiting for the word:");
if(fgets(word[i] , MAX_LENGTH +1 , stdin) == "\n")
break;
}
qsort((void *)word,n,sizeof(int),compare);
printf("Ordered list is:\n\n");
for( int i = 0; i < n; i++){
fprintf(stdout , "\t %s", word[i]);
}
}
int compare (const void * p, const void * q){
return strcmp( * (char**) p , * (char **) q);
}
Edit : question with italic is solved thanks to fellow coders here. My new issue is i can't read past the first element of array. And program closes itself.
C:\Users\Lenovo\Desktop\ogrenme\ch17>sorting
How many words do you want to write4
Waiting for the word:asd
C:\Users\Lenovo\Desktop\ogrenme\ch17>
And there is one single annoying error that keeps me prevented from completing the exercise and "chill" with rest of this challenging book:
sorting.c:20:5: warning: implicit declaration of function 'qsort' [-Wimplicit-function-declaration]
qsort((void *)word,n,sizeof(int),compare);
I did a small trial (definitely the code could be optimized)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (void) {
int n = 0;
char **string_list;
char *tmp;
//allocate memory for the first element
string_list = (char**)malloc(sizeof(char*)*1);
//infinite loop
while ( 1 ) {
//allocate size of each element to a max of 20 chars
string_list[n] = (char*)malloc(sizeof(char)*20);
printf("Enter word : ");
fgets (string_list[n], 20, stdin);
//remove trailing return carriage
string_list[n][strlen(string_list[n]) - 1] = '\0';
//break the loop here if user enters empty string
if (strlen(string_list[n]) < 1) {
break;
}
//add counter
n++;
//add memory to contain another element
string_list = realloc(string_list, sizeof(char*)*(n+1));
}
printf("\n\nInitial List is:\n");
for (int i=0 ; i<n-1 ; i++) {
printf("%s - ", string_list[i]);
}
printf("%s\n\n", string_list[n-1]);
//sorting the list
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) {
if (strcmp(string_list[i], string_list[j]) < 0) {
tmp = string_list[i];
string_list[i] = string_list[j];
string_list[j] = tmp;
}
}
}
printf("Sorted List is:\n");
for (int i=0 ; i<n-1 ; i++) {
printf("%s - ", string_list[i], strlen(string_list[i]));
}
printf("%s\n\n", string_list[n-1], strlen(string_list[n-1]));
}
output
$ ./sort
Enter word : foo
Enter word : bar
Enter word : baz
Enter word : quux
Enter word :
Initial List is:
foo - bar - baz - quux
Sorted List is:
bar - baz - foo - quux
There is an array of n students( stu[n]).If gender is boy then my code adds
for boy b, 2nd,4th,6th,........even position elements of array and
for girl g, 1st,3rd,5th....odd position elements of array.
1> Gender of boys denoted by b.
2> Gender of girls denoted by g.
Input info>>
The 1st line contains n, denoting the number of students in the class, hence the number of elements in the array.
Each of the subsequent lines contains the marks of n students .
The last line contains gender b/g;
Output info>>
The output should contain the sum of all the alternate elements of marks as explained above.
#include <stdio.h>
int main() {
int n,i;
scanf("%d",&n);//n denotes number of students.
int stu[n],sum=0;
for(i=1;i<=n;++i)
scanf("%d",&stu[i]);//for accepting input in array.
char gen;
scanf("%s",&gen);//for accepting input gender b/g.
for(i=1;i<=n;i++){
if(gen=='g' && i%2!=0){ //girl g adds alternate odd position elements.
sum=sum+stu[i];
printf("%d",sum);
}
else if(gen=='b' && i%2==0){ //boy b adds alternate even position elements.
sum=sum+stu[i];
printf("%d",sum);
}
}
//code
return 0;
}
Sample Input
3
3
2
5
b
Sample Output
8
explanation>>
marks=[3,2,5] and gender = b so it will add 3+5(even position 0,2 alternate elements). If gender in place of b is g then it will produce output = 2.
My code is shows output of 0 in all test cases.
You have the major problem in
int n,i;
int stu[n],sum=0;
here, n being a uninitialized local scoped variable with automatic storage, the initial value is indeterminate.
Now, since the address of the variable was never taken and it has a type that can have trap representation, attempt to use the value (int stu[n]) will invoke undefined behavior.
You need to scan in the value into n first, then use that to define the VLA stu. Something like
int n,i;
scanf("%d",&n);//n denotes number of students.
// **Note: please check for errors in input with scanf return value.**
int stu[n],sum=0; // here, n has the scanned value.
That said,
char gen;
scanf("%s",&gen);
is also horribly wrong, you want to scan in a char, not a string, and with the address of a plain char variable, %s conversion specification would be UB, again. You should use %c and discard any whitespaces which is present in buffer altogether.
You're making things more complicated than they need to be. Here is how you can possibly do:
#include <stdio.h>
int main(void)
{
int mark;
int b = 0;
int g = 0;
char students_marks[5];
for (int i=0; i<5; i++) {
scanf("%d", &mark);
students_marks[i] = mark;
}
for (int i=0; i<5; i++) {
if (i%2 == 0) b += students_marks[i];
if (i%2 == 1) g += students_marks[i];
}
printf("Boys: %d\nGirls: %d\n", b, g);
return 0;
}
You should probably not use an array, and just ignore the first data point. It is (probably) easier to use a linked list. Or maybe just use two lists, alternating the inputs between them. And I would definitely not use scanf. If you are new to C, do NOT waste your time learning the foibles of the scanf format string language. The only time scanf is ever useful is in introductory courses where instructors incorrectly believe that you will be able to get input more quickly than if you spend time learning other methods. But in fact you will end up burning more time learning when to use spaces in the format string that you saved by not learning fread. After your introduction to C, you will (almost) never use scanf. Also, it seems like a horrible design to put the discriminant at the end of the input. The values to be summed (the gender) should be given as a command line argument. That said, you could just do:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
FILE *
xfopen(const char *path, const char *mode)
{
FILE *rv;
if( (rv = fopen(path, mode)) == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return rv;
}
int
main(int argc, char **argv)
{
int i;
int size; /* Should probably be size_t. Skipping that for now. */
FILE *in = argc > 1 ? xfopen(argv[1], "r") : stdin;
int sum = 0;
if( fscanf(in, "%d", &size) != 1 || size <= 0 ) {
fprintf( stderr, "invalid input\n" );
exit(EXIT_FAILURE);
}
int grades[size];
for( i = 0; i < size; i++ ) {
if(fscanf(in, "%d", grades + i) != 1) {
fprintf( stderr, "invalid input on line %d\n", i );
exit(EXIT_FAILURE);
}
}
char gender;
if(fscanf(in, " %c", &gender) != 1) {
fprintf( stderr, "invalid input on line %d\n", i );
exit(EXIT_FAILURE);
}
if(strchr("bBgG", gender) == NULL) {
fprintf( stderr, "invalid gender: %c\n", gender);
exit(EXIT_FAILURE);
}
for( i = tolower(gender) == 'b'; i < size; i += 2 ) {
sum += grades[i];
}
printf("sum: %d\n", sum);
}
Hmm… i changed your code a Little bit and hope this runs as described.
int main() {
int n, index, sum=0;
int* stu;
scanf("%d", &n); // input number of studens
if(n>0)
stu = malloc(n*sizeof(int)); // allocate memory
else
return 0;
for(index=0;index<n;index++)
scanf("%d", &stu[index]);
char gen;
scanf("%c", &gen);
for(index=0; index<n; index++){
if(gen=='g' && index%2!=0) {
sum += stu[index];
printf("%d", sum);
} else if(gen=='b' && index%2==0) {
sum += stu[index];
printf("%d", sum);
}
}
return 0;
}
I'm trying to teach myself C, so I wrote a program to keep records for a gradebook! In my effort to learn pointers, I'm trying to convert one of my beginning projects of arrays to pointers. I want to convert my 2d array to pointers. Below is my original program to with the 2d array, and below that is my attempt to convert it to 2d pointer array.
ORIGINAL PROGRAM
int numberPeople, choice, i, j;
char people[15][3][100];
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
while ((numberPeople < 5) || (numberPeople > 15)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
}
printf("\n");
while ((getchar()) != '\n'); // flush the return (and anything else) after the number input above
printf("Enter the first name, last name, and grade (put a space in between each): \n");
for (i = 0; i < numberPeople; i++) {
char tempArr[MAXIMUM_LINE_LENGTH];
fgets(tempArr, MAXIMUM_LINE_LENGTH, stdin);
char *token = strtok(tempArr, " ");
for (j = 0; j < DATA_FIELDS && token != NULL; j++) {
strncpy(people[i][j], token, MAXIMUM_DATA_LENGTH);
token = strtok(NULL, " \r\n");
}
}
ATTEMPT AT 2D ARRAY -> POINTER
int numberPeople, choice, i, j;
char* people;
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
people = (char*)(malloc(numberPeople*DATA_FIELDS*sizeof(char)));
while ((numberPeople < 5) || (numberPeople > 15)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
}
printf("\n");
while ((getchar()) != '\n'); // flush the return (and anything else) after the number input above
printf("Enter the first name, last name, and grade (put a space in between each): \n");
for (i = 0; i < numberPeople; i++) {
char* tempArr;
fgets(tempArr, 100, stdin); // Thread 1: EXC_BAD_ACCESS code=1 address=0x0
char *token = strtok(tempArr, " ");
for (j = 0; j < 3 && token != NULL; j++) {
strncpy(people, token, 50);
token = strtok(NULL, " \r\n");
}
}
During the person input step is when it breaks. It works for the first entry, but then it hits a breakpoint (I'm using Xcode), and it reads "EXC_BAD_ACCESS", and I'm not too sure what this means, any tips would help, thanks!
Yes, you can convert to all pointers but I wouldn't recommend it. Here's an example:
char ***people = malloc(sizeof(*people) * 15);
for (int i = 0; i < 15; i++) {
people[i] = malloc(sizeof(*people[i]) * 3);
for (int j = 0; j < 3; j++) {
people[i][j] = malloc(sizeof(*people[i][j]) * 100);
}
}
This would re-create the array char people[15][3][100];. As you can see, it's messy. Plus, you have to free all that mem when you are done.
I cleaner way would be to use a struct:
struct Student {
char firstName[100];
char lastName[100];
char grade[100];
};
struct Student people[15];
This will set aside as much memory as the array. You could also dynamically allocate as many students as needed:
struct Student *people = malloc(sizeof(*people) * 20);
That will get 20 students. You can access them using array notation:
printf("%s %s %s", people[0].firstName, people[0].lastName, people[0].grade);
And you only need to call free once.
this pair of lines is not correct:
char* tempArr;
fgets(tempArr, 100, stdin);
the declaration of the pointer: tempArr is not allocating any actual memory to contain the line read by the the call to fgets() Suggest:
#define MAX_INPUT_LEN (100)
...
char tempArr[MAX_INPUT_LEN];
fgets( tempArr, MAX_INPUT_LEN, stdin );
Also, the returned value from the call to fgets() should be checked (!=NULL) to assure the operation was successful.
I need to write a simple program (no fancy pointer stuff, no library functions. It's for educational purposes) that reads the first and second names from a user and prints them out in a single line separated by a space. I am not getting the result and I am not sure why:
# include <stdio.h>
//the program loosely simulates the behaviour of strcpy
main(){
char fname[16], sname[16], cat[31];
int i, j;
printf("Please enter your first name: ");
scanf("%s", fname);
printf("Please enter your second name: ");
scanf("%s", sname);
for (i=0; fname[i] != '\0'; cat[i++] = fname[i++])
;
cat[i+1] = ' '; //adds a space between the tokens
for (j=i+1; sname[j] != '\0'; cat[j++] = sname[j++])
;
printf("The final result is:\n%s", cat);
return 0;
}
You have several problems. First, since cat has to be big enough to hold the first two strings and a space between them, it should be declared cat[32] -- 15 characters of first name, 15 characters of surname, 1 space, and 1 trailing null byte.
You're putting the space between the words in the wrong place. The first loop left i holding the next position in cat, so it should be:
cat[i] = ' ';
Next, your array indexes in the second loop are incorrect. The positions in cat are correct, because they start from where you left off the previous loop. But you need to start from 0 in sname. So this loop should be:
for (j = i+1, k = 0; sname[k] != 0; cat[j++] = sname[k++])
;
Finally, after concatenating the two strings, you need to append a null byte to the result, to indicate the end.
cat[j] = 0;
Another problem is that you're incrementing i twice each time through the first loop, since you use cat[i++] = fname[i++]. Each of those i++ will increment the variable. You need to separate the assignment from the increments:
for (i=0; fname[i] != '\0'; i++) {
cat[i] = fname[i];
}
Here's a final version of the script that works:
# include <stdio.h>
//the program loosely simulates the behaviour of strcpy
int main() {
char fname[16], sname[16], cat[32];
int i, j, k;
printf("Please enter your first name: ");
scanf("%s", fname);
printf("Please enter your second name: ");
scanf("%s", sname);
for (i=0; fname[i] != '\0'; i++) {
cat[i] = fname[i];
}
cat[i] = ' ';
for (j = i+1, k = 0; sname[k] != 0; cat[j++] = sname[k++]) {
}
cat[j] = 0;
printf("The final result is: %s\n", cat);
return 0;
}
I realize you set yourself a challenge to try to learn how to do something specific (and I see you are making progress towards your goal). But I always like to keep in mind there are lots of ways to get the job done -- especially in C. You know you could just do this with printf, right?
char fname[16], sname[16];
int i, j;
printf("Please enter your first name: ");
scanf("%s", fname);
printf("Please enter your second name: ");
scanf("%s", sname);
printf("%s %s\n", fname, sname);
Watch the index you use :
cat[i++] = fname[i++] and cat[j++] = sname[j++]
try to increment 'i' and 'j' at the end of your loop :
for (i=0; fname[i] != '\0'; ++i)
cat[i] = fname[i];
// ...
for (j=0; sname[j] != '\0';++j)
cat[j+i+1] = sname[j];