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.
Related
I am trying to reverse the order of letters given by the user, but I keep getting [segmentation fault [core dumped]] on some results and just the last few letters on others, the only word size that seems to work is 9 letters. For this I needed to use a while loop and ask the user how long the word is, so I can't use a more condensed function.
#include<stdio.h>
#include<stdlib.h>
int main() {
//intitializes variables for loops
int a, b, d;
int c = 0;
int x = 0;
//initializes other variables and the arrays
int length = 0;
int wordSize[length];
int wordReverse[length];
//asks user how long the word they want to use is
printf("How long is the word you would like to print backwards?:");
scanf("%d", &x);
length = x;
//Takes the letters from the user
for(a = 0; a < length; a = a + 1){
printf("Please enter letter %d: ", a + 1);
scanf(" %c", &wordSize[a]);
}
//returns the word that was given by user
printf("Your word is ");
for(b = 0; b < length; b++){
printf(" %c", wordSize[b]);
}
printf("\nYour word reversed is:");
//inserts letters into the second array array backwards
while(c < length) {
wordReverse[c] = wordSize[length - c];
printf(" %c", wordReverse[c]);
c++;
}
return 0;
}
Your problem is this part of the code:
//initializes other variables and the arrays
int length = 0;
int wordSize[length];
int wordReverse[length];
As you can see, length is 0, so wordSize and wordReverse are gonna be empty arrays. You'd need to create a dynamic array or use a hardcoded size to store your input.
You are accessing some random addresses at the moment which don't belong to your program.
So in your case, I'd change it to the following code:
int length = 0;
char * wordSize;
char * wordReverse;
//asks user how long the word they want to use is
printf("How long is the word you would like to print backwards?:");
scanf("%d", &x);
// don't forget the null-terminating string!
length = x;
wordSize = malloc(length + 1);
wordReverse = malloc(length + 1);
if (wordSize == NULL || wordReverse == NULL)
return 1;
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'm trying to write a small menu based program to maintain records.
The user inputs a number to be used for the total amount of people to be stored (first name, last name, score). User enters info all on one line, separated by spaces, and I split them up into 3 columns (first name, last name, score), then presses enter and continues to enter more info until the max people is hit.
My problem being, when I run it, it doesn't work properly; it runs and accepts user input, but only for two students (even though I've been using numbers greater than 5 for test cases), then the program immediately ends (no error codes, just ends...) and it doesn't even make it to the menu. Can someone tell me what is wrong with my code?
int i, j, count, numberPeople, temp, choice;
char people[15][3], tempArr[20];
char *token;
printf("Please indicate number of records you want to enter (min 5, max 15): ");
scanf("%d", &temp);
while ((temp > 15) || (temp < 5)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min 5, max 15): ");
scanf("%d", &temp);
}
numberPeople = temp;
printf("\nEnter the first name, last name, and grade (put a space in between each): ");
for (i = 0; i < numberPeople; i++) {
fgets(tempArr, 20, stdin);
token = strtok(tempArr, " ");
for (j = 0; j < 3; j++) {
while (token != NULL) {
people[i][j] = *token;
printf("%s\n", token); // this is here to as a test case to see if my data was being stored.
token = strtok(NULL, " ");
}
}
}
EDITED: changed scanf to fgets
OUTPUT
Please indicate number of records you want to enter (min 5, max 15): 5
Enter the first name, last name, and grade (put a space in between each): firstname1 lastname1 85
firstname1
lastname1
85
firstname2 lastname2 84
firstname2
lastname2
Program ended with exit code: 0
One problem is you're using scanf() to read in the entire input line which has spaces between the first name, last name and grade:
for (i = 0; i < numberPeople; i++) {
scanf("%s", tempArr);
}
scanf("%s", tempArr) quits reading as soon as it hits the first space. For this loop you want to use fgets():
for (i = 0; i < numberPeople; i++) {
fgets(tempArr, 20, stdin);
}
But as #Pooya notes, this string size is too small for what you're doing. Although you allocate the two dimensional array of students and information fields, you never allocate the string space to hold their names and grades:
char people[15][3]
If you're doing this on the stack, it becomes conceptually a third dimension:
char people[15][3][24]
After this scanf(), there's still a return character left in the buffer:
scanf("%d", &temp);
it probably should be cleared out. #KevinDTimm and #bruceg hint at a problem here:
for (j = 0; j < 3; j++) {
while (token != NULL) {
people[i][j] = *token;
But I don't think Kevin's suggestion accounts for the index j. #Weather_Vane suggests adding \r\n to the strtok() separator string:
token = strtok(NULL, " ")
Otherwise your grade string (last field) will have a dangling newline. Also, you need to make a copy of the token returned by strtok(), you shouldn't store it directly.
Putting all these suggestions together and cleaning/fixing anything else I ran into, I offer the following rework:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MINIMUM_STUDENTS 5
#define MAXIMUM_STUDENTS 15
#define DATA_FIELDS 3
#define MAXIMUM_LINE_LENGTH 100
#define MAXIMUM_DATA_LENGTH 50
int main(int argc, char **argv) {
int numberPeople;
char people[MAXIMUM_STUDENTS][DATA_FIELDS][MAXIMUM_DATA_LENGTH];
printf("Please indicate number of records you want to enter (min %d, max %d): ", MINIMUM_STUDENTS, MAXIMUM_STUDENTS);
scanf("%d", &numberPeople);
while ((numberPeople < MINIMUM_STUDENTS) || (numberPeople > MAXIMUM_STUDENTS)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min %d, max %d): ", MINIMUM_STUDENTS, MAXIMUM_STUDENTS);
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 (int i = 0; i < numberPeople; i++) {
char tempArr[MAXIMUM_LINE_LENGTH];
fgets(tempArr, MAXIMUM_LINE_LENGTH, stdin);
char *token = strtok(tempArr, " ");
for (int j = 0; j < DATA_FIELDS && token != NULL; j++) {
strncpy(people[i][j], token, MAXIMUM_DATA_LENGTH);
// this is here to as a test case to see if my data was being stored.
printf("%s\n", people[i][j]);
token = strtok(NULL, " \r\n");
}
}
// do what you need to do with the data here!
return 0;
}
First off, use fgets() not scanf()
Next, you don't need the for (j=0; j<3; j++) part, that's handled by strtok. Instead use the following:
token = strtok(tempArr, " ");
while (token != NULL) {
people[i][j] = *token;
printf("%s\n", token); // this is here to as a test case to see if my data was being stored.
token = strtok(NULL, " ");
}
According to your sample, your input takes about 24 character but you defined tempArr[20]. because you overwrite something else in memory if you scan something more than 20 characters, it is better to have tempArr[100] or any number that makes sense for your tests.
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];
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.