Interactive, randomized program in C - c

My goal with this program is to incorporate the users inputs into a sort of interactive/randomized story but I'm not sure how I'm supposed to get the inputs from the users to fit between *ptrDescription, *ptrBeginning, *ptrMiddle, and *ptrEnd. Any help would be much, much appreciated!
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#include <string.h>
#include <ctype.h>
int main(void){
int i;
char name[20];
char color[20];
int age;
char sentence[1];
//array of pointers to char arrays
char *ptrDescription[]={"the painfully handsome","the one and only","who seemed much older than"};
char *ptrBeginning[]={"was blissfully ignoring","could clearly see","had no idea"};
char *ptrMiddle[]={"the huge truck","the falling meteor","the bucket of milk","the mailman","the most powerful wizard"};
char *ptrEnd[]={"that was barreling toward them.","on the horizon."};
srand(time(NULL));
printf("Enter your first name: ");
scanf("%s", &name);
printf("\nEnter your age: ");
scanf("%d", &age);
printf("\nEnter your favorite color: ");
scanf("%s", &color);
for (i = 0; i < 1; i++)
{
//strcpy(sentence,ptrDescription[rand()%3]);
//strcat(sentence," ");
//strcat(sentence,ptrBeginning[rand()%3]);
//strcat(sentence," ");
//strcat(sentence,ptrMiddle[rand()%5]);
//strcat(sentence," ");
//strcat(sentence,ptrEnd[rand()%2]);
//strcat(sentence,".");
//sentence[0]=toupper(sentence[0]);
puts(sentence);
}
getch();
return 0;
}
EDIT:
I've edited a section of my code so that directly following for (i = 0; i < 1; i++) it now looks like this:
snprintf(sentence, sizeof sentence,"%s, %s %d year old, %s %s %s %s", name, ptrDescription[rand()%3], age,ptrBeginning[rand()%3], ptrMiddle[rand()%5], ptrEnd[rand()%2]);
There are tons of strange characters after the sentence in the output, like Japanese characters and stuff. I'm not sure why they're there, though. This is what it looks like exactly:
"Enter your first name: Justin
Enter your age: 20
Justin, the arrogant 20 year old, was purposefully ignoring the most powerful wizard that was barreling toward them. 汽$0HβHζ(テフフフフフフフフフフフフフH・(DキHH広$0陏&・汽$0タHζ(テフフフフフフフフフフフフフフフH WH・ H櫛H・t9HνHテ<"
Anyone know how I can get rid of them?

If you already have a name and an age, it's just a matter of inserting them into the correct place in sentence, right? So strcat(sentence, name) would work for name. age is a little trickier since you have to format the number first, and strcat won't do it for you. One solution would be to use sprintf(buf, "%d", age), and then concatenate buf (which is a scratch char array you would have to declare).
Any time you work with strings in C, you have to be concerned about having enough space in the target buffer. Your program can run out of space during both input and output. For the output, I would get rid of sentence altogether; since you just end up writing to stdout I would printf("%s", [part]) each part as you go along. For reading, scanf supports adding a length argument to the format string.
If you use one of the *printf functions, there are 2 things you must be careful about:
The arguments you pass are correct for the format string you use
Your buffer ends up null-terminated
Your current problem is with #1 - your format string promises 7 arguments to follow, but you only supply 6. snprintf grabs a "random" 7th value from the stack, interprets it as a char pointer, and copies whatever it finds there to sentence. You could see similar problems if your format string promised a char pointer but you placed an int in a given position. In this case the format string is a constant, so a smart compiler can validate that your format string matches the subsequent parameters. You'll want to get into the habit of taking compiler warnings seriously and not ignoring them.
The second point could be an issue if your sentence ended up bigger than your sentence buffer. If there is no room for a null-terminator, one won't be applied. You can check the return value of snprintf, or you can defensively always write a 0 to the last array position.

Related

C printf function data formats

Very simple C printing question!
#include <stdio.h>
#include <conio.h>
int main() {
int Age = 0;
printf("Enter your Age\n");
scanf("%d",&Age);
char Name;
printf("Enter your Full name\n");
scanf("%s",&Name);
printf("My name is %s and I am aged %d" ,&Name,Age);
return 0;
}
When I input "blah" and 1, for some reason this returns:
"My name is Blah and I am aged 1929323232"
I presume I am misunderstanding a data format in either the scanf or the printf functions but can't work it out.
The problem is because of line
char Name;
Name is of type char. That means that it is supposed to store only one character. As a result
1. The scanf() is not able to store the input text properly (this will result in a crash in most cases or other undefined behaviour depending on the system - which judging by the output you provided is what you got)
2. (if the code didn't crash) Treating Name as a string with the %s argument in printf() essentially outputs garbage.
The type that corresponds to strings in C is char * (or char[]). Essentially, changing Name to some statically allocated char-array while performing the necessary changes in the next lines should fix your error:
char Name[256]; //allocated 256 bytes in Name array
printf("Enter your Full name\n");
scanf("%s",Name); // removed & before Name
printf("My name is %s and I am aged %d" ,Name,Age); // same here
You could also opt to go with a dynamically allocated string of type char * but I guess that's a different topic altogether.
As a general suggestion, I think you should look at pointers more closely. Especially in C, almost all string operations involve being aware of pointer mechanisms.

problems with scanf and conversion specifiers

Here you can see my source code:
#include <stdio.h>
int main()
{
char yourname;
char yoursex;
int yourage = 0;
printf("Hey, what's your name?\n");
printf("My name is: ");
scanf("%s", &yourname);
printf("Oh, hello %s! \n\n", &yourname);
printf("Are you a boy or a girl?: ");
scanf("%s", &yoursex);
printf("Nice to know you are a %s! \n\n", &yoursex);
printf("How old are you %s? I am ", &yourname);
scanf("%d", &yourage);
printf("I see you are %d, you have many years then!", &yourage);
return 0;
}
I was trying things that I didn't knew, and strangely it is not working for me. What's the problem? Also, why it needs to be %s and not %c? If I use %c instead it does not work!
Where it says:
How old are you %s? instead of putting my name, it says ''oy''
and instead of showing my age in the last line, it shows a big number.
These are the very basics of C Programming, and I strongly advise you to get a decent book - The C Programming Language by Dennis Ritchie would be a good start.
There are numerous errors in your code.
A char can contain only one character, like 'A', or 'a' or something like that. When you're scanning a name, it is going to be a group of characters, like 'E', 'd', 'd', 'y'. To store multiple characters, you need to use a character array. Also, the format specifier used to scan/print characters is %c, %s is for when you need to scan a group of characters, also called a string into an array.
When you use printf, you do not supply a pointer to the variable you are trying to print (&x is a pointer to variable x). The pointer is a 32/64-bit integer, which is likely why you see a random integer when trying to print. printf("%c\n", charVar) is sufficient.
scanf does not need an & while using %s as the format specifier, assuming you have passed a character array as the argument. The reason is, scanf needs to know where to store the data you are reading from the input - and that is given by a pointer to the memory location. When you need to scan an integer, you need to pass an &x - which means, pointer to memory location of x. But when you pass a character array, it is already in the form of a memory address, and doesn't need to be preceded by an ampersand.
I once again recommend you look up some decent tutorials online, or get a book (the one I mentioned above is a classic). Type the examples as given in the material. Experiment. Have fun. :)
%s is for reading a string -- multiple characters delimited by whitespace. %c is for reading a single char.
You declare your yourname and yoursex vars as characters, and then try to read strings into them. The string read will overwrite random other things in the stack frame and misbehave or crash.
You want to declare yourname and yoursex as character arrays, so they can hold strings:
char yourname[32];
char yoursex[32];
then, when reading into them, you want to include a length limit so they don't overflow:
scanf("%31s", yourname);
This is a single character:
char yourname;
But %s indicates that the variable is a string (i.e., an array of characters terminated by a NUL). That's why you need %c. If you really did mean to use a string, then define the variable like
char yourname[32]; /* just pick a big enough size */
Also, you are correct to use the address of the variable with scanf(), but printf() needs the value. So instead of
printf("I see you are %d, you have many years then!", &yourage);
use
printf("I see you are %d, you have many years then!", yourage);
The "big number" is the memory address.
Make sure you read the comments in code!
#include <stdio.h>
int main()
{
char yourname[10];
char yoursex[5]; // boy or girl + null terminator
int yourage = 0;
printf("Hey, what's your name?\n");
printf("My name is: ");
scanf("%s", &(*yourname)); // & and * cancel each other out,
// thus take a look at the next scanf()
printf("Oh, hello %s! \n\n", yourname); // yourname is now an array
printf("Are you a boy or a girl?: ");
scanf("%s", yoursex);
printf("Nice to know you are a %s! \n\n", yoursex);
printf("How old are you %s? I am ", yourname);
scanf("%d", &yourage); // ok
printf("I see you are %d, you have many years then!", yourage); // here you don't
// need the address of the variable!
return 0;
}
The expression char yourname; only holds space for a single character, so quite likely you end up corrupting the memory space when scanning for yourname. You should allocate a bigger buffer and make sure that you don't overrun its length by setting a maximum number of characters to be read with the scanf function; as described in some of the other answers.
The fact that the following printf print correctly the name doesn't mean that the memory doesn't get corrupted; as C/C++ don't really check the boundary of any strings or arrays used at runtime.
As suggested by others, starting by reading a good book about C and/or C++ wouldn't a bad idea.

1st string output not showing

This is part of an assignment that I'm supposed to submit, this program is supposed to take 2 inputs, a name containing 4 letters and a city containing 5 letters then sub them in that sentence. The problem is that the first word is not being output properly, like if I type john all I get is 'r' instead of the word. I'm new to this and there might be other mistakes, but I wanna fix this one. Help greatly appreciated. :l
#include <stdio.h>
int main(int argc, char **argv)
{
char name1[4];
char city1[5];
printf("\nEnter four letter name:\n");
scanf("%s", name1);
printf("\nEnter five letter city:\n");
scanf("%s", city1);
printf("%s was afraid of the airplane, he walked from %s to Romaine.", name1, city1);
return 0;
}
When I put John and Denver it output "r was afraid of the airplane he walked from Denver to Romaine"
Aham, so this is a buffer overflow. What you should do is:
I. Allocate two reasonably long buffers. 4 and 5 just make me cry. There's a LINE_MAX macro in <limits.h> which may be useful.
II. Use a safe function that lets you specify how big your buffer is. For example, fgets() is an excellent function for inputting a line of text. Using it also has the benefit that it really always reads an entire line (if the buffer passed to it is large enough). scanf() requires some messing with the %s conversion specifier before you can make it accept whitespace and stuff.
char name[LINE_MAX];
char city[LINE_MAX];
fgets(name, sizeof name, stdin);
fgets(city, sizeof city, stdin);
#include <stdio.h>
int main(int argc, char **argv)
{
char name1[8];
char city1[8];
printf("\nEnter four letter name:\n");
scanf("%s", name1);
printf("\nEnter five letter city:\n");
scanf("%s", city1);
printf("%s was afraid of the airplane, he walked from %s to Romaine.", name1, city1);
return 0;
}
The solution: Make sure the character arrays are large enough for the input.
scanf is an unsafe function as it will fetch data from its input stream and copy it past the end of the buffer you provide if it's not large enough. In addition it will place a nul terminating character at the end of the copied string.
In your example John requires at least 5 characters while Denver at least 7 because you must account for the terminating character.
You should consider using a larger buffer for both strings, or as a better alternative, write your own function which uses a buffer to store the string and then copies it to the final destination buffer (assuming length is enough, truncating otherwise).
Take a look at this answer: How to prevent scanf causing a buffer overflow in C?, there are some common work arounds for your problem.

An assignment requires that I gather a name with 2 scanf's

So here is my first ultra beginner computer programming question in C.
I need to set it up so that someone can type in their full name on the input. Here is part of the specs -
"You'll have to do a little thinking to figure out how to get the printed names lined up with all the other columns. The first hint is that it involves joining strings together, something called concatenation. Give it a go, and if you can't figure it out, look at the next document in this folder; it contains additional hints. Part of the purpose of this assignment is to implicitly teach you concatenation. Do NOT use tabs (\t) and make sure your C/C++ editor does not produce tab characters.
Do NOT use gets() in this program. Use scanf() for inputting the interactive information. If you try to use gets(), you may get VERY frustrated.
In essence, all numbers appearing in the report should be right-justified and decimal-aligned. All numbers appearing in the summary should appear without leading spaces (other than the one which normally separates the number from the previous word). Hourly wage amounts CAN be less than 10.00, so be very careful with your formatting. The sample output can appear correct, but you can still be docked a half-point if things don't align properly with hourly wages under $10.00."
Additional hints:
You may assume that the employee name is always two names, a first name and a last name separated by a space. Also assume that there are never any spaces within a first name or within a last name. This allows you to use two scanf() calls instead of one gets() call. gets() would introduce some oddities that make things not work correctly later down the line.
You may also assume that neither name exceeds 10 characters in length.
The input from the Process another employee? question should be a single character. Assume that N or n will stop the loop, but that any other character will continue the loop.
Anyone know how to do this? When I use gets(which he says not to do), the loop screws up on the 2nd time around and it asks for the name and salary all in one line. And if I try to use 2 scanf statements, I get a crash or only 1 of the names input.
I was thinking the only way to do it is by outputting the names to a text file, then reading them in again. But is there some other way? I'm not allowed to ask for the names individually. A user might type a full name with one space, as it says in the specs.
Here is the code I wrote so far. I also need totals for all the gross', overtime hours and regular hours.
//stupid program
#include <stdio.h>
#include <strings.h>
#include <math.h>
//global variables
FILE *reportfile; //output file
char department[21];
int count;
char name[21];
float hoursworked;
float hourlywage;
float overtimehoursworked;
float overtimehourlywage;
float gross;
char again;
char firstname;
char lastname;
float tothoursworked;
float totovertimehoursworked;
float totgross;
const float overtimerate = 1.5;
const float overtimethreshold = 40; //hours needed to get overtime
//function prototypes
void GetInfo(void);
void Finalreport(void);
//main
int main(void)
{
reportfile = fopen("c:\\class\\kpaul-pay.txt","w"); //open output file
//////////////////////////////////////////////////
// initialize accumulating variables
/////////////////////////////////////////////////
count = 0;
tothoursworked = 0;
totovertimehoursworked = 0;
totgross = 0;
GetInfo();
fclose(reportfile); //close output file
return 0;
}
void GetInfo (void)
{
printf("Mountain Pacific Corporation\n");
printf("Department Salary Program\n\n");
printf("Please enter the name of the department: ");
gets(department);
fprintf(reportfile, "Mountain Pacific Corporation\n");
fprintf(reportfile, "Department Salary Program\n\n");
fprintf(reportfile, "%s\n\n", department);
fprintf(reportfile, "Employee Reg Hrs Overtime Hrs Gross\n");
fprintf(reportfile, "-----------------------------------------------------------------\n");
do {
printf("\nEnter employee #1: ");
gets(name);
printf("Enter the hourly wage of %s", name);
scanf("%f", &hourlywage);
printf("\nEnter total number of hours: ");
scanf("%f", &hoursworked);
if (hoursworked<=overtimethreshold)
overtimehoursworked = 0;
else if (hoursworked > overtimethreshold)
overtimehoursworked = hoursworked - overtimethreshold;
gross = (hoursworked*hourlywage) + (overtimehoursworked*overtimehourlywage);
fprintf(reportfile, "%s%16.2f(%4.2f)%12.2f(%4.2f) $%7.2f", name, hoursworked, hourlywage, overtimehoursworked, hourlywage * overtimerate, gross);
tothoursworked = tothoursworked + hoursworked;
totovertimehoursworked = totovertimehoursworked +overtimehoursworked;
totgross = totgross + gross;
printf("\n");
printf("Would you like another conversion? ");
scanf ("%s", &again);
printf("\n\n");
} while (again!='N' && again!='n');
}
Careful use of the scanf format string is needed. %s indicates a string that is delimited by a white space, in non technical terms, scanf will read characters until it finds a whitespace character such as a tab or space.
This poses a problem for reading multiple words, this can be solved by using multiple %s in the format string, you must provide a seperate array for each word as an argument after the format string e.g.
scanf("%s%s%s", string1, string2, string3)
This scanf call expects three words from the user, where each word is delimited by a white space. Make sure there is allocated memory for each of arrays though otherwise you will write into memory the program does not own normally causing the program to crash. The exact same effect to the code above can be achieved by using scanf multiple times with a single %s in the format string.
If you need everything as one string the same array you can use strcat which will concatenate two strings together. The function is simple to use, but once again make sure your arguments are allocated arrays before calling strcat otherwise it will likely cause a crash or unexpected behaviour.
Here are references to both functions:
scanf - http://www.cplusplus.com/reference/clibrary/cstdio/scanf/
strcat - http://www.cplusplus.com/reference/clibrary/cstring/strcat/
Hope this helps =]
scanf with the format %s will accept a whitespace-delimited string.
The crash is most likely caused by overflowing (or failing to reserve space for) the input string.

Is there a way to read a c-string and then an int with a single scanf in C?

Hey,
I'm trying to get this function to get the following output with the listed input, the "..." is where I'm not sure what to write:
void Question8(void)
{
char sentence[100];
int grade;
scanf(….);
printf("%s %d", sentence, grade);
}
Input:
My CS Grade is 1000
Output:
My CS Grade is 100
However, the kicker is that I need the scanf to read a c-string and then an int with a single scanf command, is this even possible?
Edit:
I can only edit the code in the location with the three periods ( "..." ), I cannot use anything more. I can assume that the input listed is expected but I cannot change anything outside of the three periods.
The output does not contain typos, the purpose of this assignment is to use flags and escape sequences.
It is possible to read pre-formatted string using scanf, however the format must be strict.
This version will continue to read the input until a digit is encountered and then read an integer.
Here is your code again:
char sentence[100];
int grade;
scanf("%[^0-9] %d",sentence,&grade);
printf("%s %d\n", sentence, grade);
I'll get this over with quick:
<obligatory_rant>
stupid question, but I guess it's homework and you're
stuck with these absurd limitations
</obligatory_rant>
Then, if you need to read everything up to but excluding the first digit, then the number:
if (scanf("%100[^0-9] %3d", text, &number) == 2)
...
Notes:
100 in "%100[... should be whatever your actual buffer size is to protect against buffer overrun.
The %3d documents that at most 3 digits should partake the the numeric value, so 1000 is correctly read as 100.
[^...] means the string made up of characters not ("^") in the following set, which is then specified as 0-9 - the digits.
if (... == 2) tests whether both positional parameters were scanned / converted successfully.
If you can't add an if and error message, then simply:
scanf("%100[^0-9] %3d", text, &number)
Tested in Visual Studio 2008
#include <stdio.h>
int main()
{
char sentence[100];
int grade = 0;
scanf("%[^0-9] %d",sentence,&grade);
printf("%s %d", sentence, grade);
return 1;
}
Input :
My CS Grade is 100
Output :
My CS Grade is 100
This is a really horrible question. A correct set of scanf parameters would be "%14c%3d", sentence, &grade
Because a space is included in the printf statement the trailing space needs to not be stored in sentence. Because the input contains other spaces there is no other solution (that I can thing of) than a fixed length. The integer parsing also requires a fixed length to truncate 1000 to 100.
I can think of no reason to ever write code anything like this. The code fits the requirements but wouldn't be useful in any other circumstances. I think that this is a very poor training exercise.

Resources