I am currently building a symbol table program using C. It needs to stay as simple as possible while having the required functionality as I am expected to produce a working compiler by the end of the semester. I currently have a working implementation that creates entries into the symbol table from user input but it is not 100% where it needs to be. I just need some guidance based on the feedback I was given from my professor. I understand that there are some things I need to change, I am new to coding in C and I am also trying to learn Python and R at the same time so im a little overwhelmed. I know I need a separate initialize and print function, That there should be no Input or Output in the create function, and that every entry has a scope of 0. where I'm stuck at, is creating the functions for initialize and print without losing the current functionality that I already have. Any help is appreciated. Here is my current implementation of the code:
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct ADT {
char name[18]; // lexeme name
char usage;
char type; // I is integer, S is type string, I for identifier
int scope; // scope of where it was declared, inserted for later use
int reference;
};
typedef struct ADT new_type;
new_type table[200];
int i = 0;
int read(char *name, char usage, char type, char scope) { //Read function to read input and check for duplicates
for (int j = sizeof(table) / sizeof(table[0]); j >= 0; --j) {
if (strcmp(table[j].name, name) == 0 &&
table[j].usage == usage &&
table[j].type == type &&
table[j].scope == scope)
return 1; // found
}
return -1; // not found! that's good
}
int create( char *name, char usage, char type, char scope) { //Create function to insert new input into symbol table
strcpy(table[i].name, name);
table[i].usage = usage;
table[i].type = type;
table[i].scope = scope;
if (table[i].usage == 'I' && table[i].type == 'L')
table[i].reference = atoi(name);
else
table[i].reference = -1;
return i++;
}
int initialize(char *name, char usage, char type, char scope) { // Function to initialize the symbol table and clear it. also creates the fred lexeme
create("Fred", 'I', 'I', '0');
}
int print(char *name, char usage, char type, char scope) { // Print function to print the symbol table
printf("Nate's Symbol Table\n");
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
}
int main() { // Main function to take input and produce the symbol table lexemes
printf("Course: CSCI 490 Name: Nathaniel Bennett NN: 02 Assignment: A03\n");
printf("\n");
create("Fred", 'I', 'I', 0);
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
// keep asking for a lexeme until we type STOP or stop
while (1) {
char lexeme[256];
char nUsage;
char nType;
char nScope;
printf("Enter a lexeme: \n"); //enter lexeme name
scanf("%s", lexeme);
if (strcmp(lexeme, "stop") == 0) break;
printf("Enter its usage: \n");
scanf(" %c", &nUsage);
printf("Enter its type: \n");
scanf(" %c", &nType);
printf("Enter its scope: \n");
scanf(" %c", &nScope);
printf("%s, %c, %c, %c\n", lexeme, nUsage, nType, nScope);
create(lexeme, nUsage, nType, nScope);
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
}
printf("Nate's Symbol Table\n");
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
for (int j = 0; j < i; j++) {
if (table[j].name == NULL)
break;
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n", j, table[j].name, table[j].scope, table[j].type, table[j].usage, table[j].reference);
}
return 0;
}```
...I think we're normally reluctant to get up in people's course assignments, but you seem like you have thought about this for a while Nate.
I can't quite make out what your instructor is suggesting. I do not see I/O in your code for the create() function. Unless the call to strcpy() is considered I/O in their view.
I do see some room for improvement in your print() function though. Your function relies upon a global entity (table) and then it ties your loop both to an imaginary value (what is "i" in your loop initialization?) AND to a condition where your logic asks effectively, "did I run out of table?"
Choose one condition or the other. There is a semantic elegance in simply printing everything you find in the table. You can make the function better if you pass a reference to the table rather than code to the existence of a static global value. So instead of passing all those values to your print() function, how about just one argument? Pass a reference to table, and your function could then be used for other similar dump operations. It becomes more generalized, and that's a good thing.
I would also say this. I prefer using sprintf() to stage my output in a string and then when everything is ready, I output it all at one time. This is easier to inspect and debug.
Also, not related to your assignment I imagine, but be extra-vigilant every time you use scanf() -- it was often my number one suspect whenever I had a bad pointer.
Definitely try to isolate or eliminate calls to chaotic functions like that one.
Keep thinking about how to make your function stronger, keep refactoring. You'll do great!
There are a number of issues. This won't even compile:
read conflicts with the syscall (i.e. rename it)
read has UB (undefined behavior) because it starts the for loop at one beyond the end of the table array
The symbol printing code is replicated everywhere. Better to define a table printing function (e.g. tblprint) and a symbol printing function (e.g. symprint).
The format used to print a symbol uses (incorrectly) variable precision format specifiers (e.g.) %*s expects two arguments: int len,char *str With -Wall as a compile option, these statements are flagged.
AFAICT, ordinary format specifiers work fine.
The if (sym->name == NULL) will never be valid because it is a fixed length array. We need to use a char *.
Using i as a global for the count of the array is misleading. Try something more descriptive (e.g.) tabcount
Using table[i].whatever everywhere is cumbersome. Try using a pointer (e.g. sym->whatever)
initialize [and some others] need a return with a value.
I've used cpp conditionals to denote old code vs new code:
#if 0
// old code
#else
// new code
#endif
Here is the refactored code. It is annotated. It compiles cleanly and passes a rudimentary test:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct ADT {
// NOTE/BUG: the if (sym->name == NULL) will fail
#if 0
char name[18]; // lexeme name
#else
const char *name; // lexeme name
#endif
char usage;
// I is integer, S is type string, I for identifier
char type;
// scope of where it was declared, inserted for later use
int scope;
int reference;
};
#if 0
typedef struct ADT new_type;
new_type table[200];
#else
typedef struct ADT ADT;
ADT table[200];
#endif
int tabcount = 0;
// NOTE/BUG: "read" conflicts with a syscall name
#if 0
//Read function to read input and check for duplicates
int
read(char *name, char usage, char type, char scope)
#else
// find_entry -- find a matching entry (if it exists)
int
find_entry(char *name, char usage, char type, char scope)
#endif
{
// NOTE/BUG: this is UB (undefined behavior) because you're starting at one
// past the end of the array
#if 0
for (int j = sizeof(table) / sizeof(table[0]); j >= 0; --j) {
#else
for (int j = tabcount - 1; j >= 0; --j) {
#endif
ADT *sym = &table[j];
if (strcmp(sym->name, name) == 0 &&
sym->usage == usage &&
sym->type == type &&
sym->scope == scope)
return 1;
}
// not found! that's good
return -1;
}
//Create function to insert new input into symbol table
int
create(char *name, char usage, char type, char scope)
{
ADT *sym = &table[tabcount];
// NOTE/BUG: this needs to be a pointer to a string to allow long strings and
// for "if (sym->name == NULL)" to be valid
#if 0
strcpy(sym->name, name);
#else
sym->name = strdup(name);
#endif
sym->usage = usage;
sym->type = type;
sym->scope = scope;
if (sym->usage == 'I' && sym->type == 'L')
sym->reference = atoi(name);
else
sym->reference = -1;
return tabcount++;
}
// Function to initialize the symbol table and clear it. also creates the fred
// lexeme
int
initialize(char *name, char usage, char type, char scope)
{
create("Fred", 'I', 'I', '0');
return 0;
}
void
symprint(ADT *sym)
{
int j = sym - table;
// NOTE/BUG: with (e.g) %*d this is variable precision field -- it requires
// _two_ arguments: <int wid>,<int val>
#if 0
printf("%*d\t\t%*s\t%*d\t%*c\t%*c\t%*d\n",
j, sym->name, sym->scope, sym->type,
sym->usage, sym->reference);
#else
printf("%d\t\t%s\t%d\t%c\t%c\t%d\n",
j, sym->name, sym->scope, sym->type,
sym->usage, sym->reference);
#endif
}
void
tblprint(int title)
{
if (title)
printf("#\t\tName\tScope\tType\tUsage\tReference\n");
for (int j = 0; j < tabcount; j++) {
ADT *sym = &table[j];
if (sym->name == NULL)
break;
symprint(sym);
}
}
// Print function to print the symbol table
int
print(char *name, char usage, char type, char scope)
{
printf("Nate's Symbol Table\n");
tblprint(1);
return 0;
}
// Main function to take input and produce the symbol table lexemes
int
main()
{
printf("Course: CSCI 490 Name: Nathaniel Bennett NN: 02 Assignment: A03\n");
printf("\n");
create("Fred", 'I', 'I', 0);
tblprint(1);
// keep asking for a lexeme until we type STOP or stop
while (1) {
char lexeme[256];
char nUsage;
char nType;
char nScope;
// enter lexeme name
printf("Enter a lexeme: \n");
scanf("%s", lexeme);
if (strcmp(lexeme, "stop") == 0)
break;
printf("Enter its usage: \n");
scanf(" %c", &nUsage);
printf("Enter its type: \n");
scanf(" %c", &nType);
printf("Enter its scope: \n");
scanf(" %c", &nScope);
printf("%s, %c, %c, %c\n", lexeme, nUsage, nType, nScope);
create(lexeme, nUsage, nType, nScope);
tblprint(0);
}
printf("Nate's Symbol Table\n");
tblprint(1);
return 0;
}
Related
It's my first time here on stackoverflow. I hope my question fits.
We started programming C at the university this semester. Unfortunately there are only a few online lectures. But we still have to solve the tasks.
We should program a kind of Hang-Man this time. In other words, guess a hidden word.
I have the following problem. I get a char, but after entering it, the contents of the riddle array change. If I leave out the input it works. I don't understand why this happens because scanf doesn't actually access riddle.
I myself don't know what to do here. I hope someone can tell me what's wrong with the code.
//sry some variables and texts are in german
char* createRiddle(char const* const str){
int laenge = strlen(str);
char temp[laenge+1];
char *t = temp;
strcpy(temp, str);
int te = strcmp(temp, str);
if (te != 0){
printf("ERROR: Bei der Speicherreservierung ist ein Fehler aufgetreten");
exit(0);
}
int i;
for (i=0; i < (int)strlen(temp);i++){
if (str[i] > 65 && str[i] < 90){ //ASCII Großbuchstaben-Bereich prüfen
char verdeckt = '*';
temp[i] = verdeckt;
} else {
temp[i] = str[i];
}
}
return t;
}
//----------------------------
int uncoverLetter(char *riddle, const char *solution, char letter){
printf("RD3: %s\n",riddle);
letter = toupper(letter);
int i;
int treffer = 0;
for (i=0; i < (int)strlen(solution); i++) {
if (letter == solution[i]) { // Buchstabe im Wort?
if (letter != riddle[i]) { //Buchstabe schon aufgedeckt?
riddle[i] = solution[i];
treffer = treffer + 1;
}
}
}
return treffer;
}
//----------
int gamingLoop(const char* solution){
int punkte; //points
printf("Lets GO!\n\n");
char *riddle = createRiddle(solution);
printf("Gesuchtes Wort: %s\n\n",riddle); //Word: *-******* ( = C-Compiler )
int highscore = 0;
while ((strcmp(riddle, solution)) != 0) {
printf("RD1: %s\n",riddle); //Test: What does Riddle look like?
printf("Bitte geben Sie einen Buchstaben ein: "); // pls enter letter
char eingabe;
scanf(" %c", &eingabe); //-----!!Here is the point where things go wrong!!------
printf("RD2: %s\n",riddle); //Test2
int treffer = uncoverLetter(riddle, solution, eingabe);
//----------- probably unimportant for the problem ----------------
//Zufallszahl
int zufz = (rand() % 11) + 1;
int ii = 1;
for (ii=1; ii < 11 ; ii++){
if ( zufz == ii) {
punkte = zufz*100;
}
}
//------------
if (treffer != 0) {
printf("Du hast %d richtige Treffer.\n", treffer);
highscore = highscore + (treffer*punkte);
printf("Punkte: %i\n\n", highscore);
} else {
printf("Du hast leider keinen Treffer.\n");
highscore = highscore - punkte;
printf("Punkte: %d\n\n", highscore);
}
printf("%s\n\n",riddle);
}
return highscore;
}
OUTPUT:
Sry no pic because i dont have 10 rep :(
Link: https://imgur.com/UIeltVR
// R3 in funktion uncoverLetter
I strongly suspect that I made a very stupid mistake, but unfortunately I can't see it myself / can't see it yet.
I look forward to advice and help.
Thank you.
Your problem is in createRiddle, where you create the *** pattern:
char* createRiddle(char const* const str){
int laenge = strlen(str);
char temp[laenge+1];
char *t = temp;
// ... create pattern ...
return t;
}
You return a local array. (t is just an alias to the array temp.) That array will be out of scope when the function exits and therefore invalid.
There are several possible solutions.
Make the caller provide space
Pass in an array that the caller can fill:
void createRiddle(char *temp, char const* const str)
{
// ... create pattern in temp ...
}
Then call it like this:
char riddle[MAX];
createPattern(riddle, solution);
You don't need to return the array here, because it is the same array you provided, only filled. (You could return it if it makes calling easier. You could also return an error code. Use your good judgement.)
Of course, the function and caller need to agree how much space must be provided. (That could be another function parameter or a global constant.)
Allocate memory dynamically
Dynamic memory is allocated on the heap and guaranteed not to be used by anyone else:
char *createRiddle(char const* const str)
{
int laenge = strlen(str);
char *temp = malloc(laenge + 1);
// ... create pattern in temp ...
return temp;
}
Then use it like this:
char *riddle = createRiddle(char const* const str);
// ... play the game ...
free(riddle); // be nice and clean up
Static arrays
Make the array static.
static char temp[laenge+1];
Here, the static keyword means that there is only one array that retains its value between calls. It is really as if you has declared the array as global outside the function, but with the addition that its name is only known to your function.
That's a quick and easy solution, but it fails when your function is recursive or when other parts of your code use the same function. (That's probably not the case in your game, though.)
I am trying to take number of inputs from a user and take the inputs then and store them under dynamically created variable names. can anyone help?
I want to take the number of array user want to input then create the exact number of variables which maintains a common pattern so I can know which array is under which variable and I can call them for further processing.
My current code is as follows
int input, eqn, m, i,n,x;
char inputarr[100], eqnarr[100];
printf("Enter number of variables: ");
scanf("%d",&n);
m=n;
printf("Enter your variables: \n");
while(n!=-1){
gets(inputarr[n]);
n--;
}
while(m!=0){
puts(inputarr[m]);
printf("\n");
m--;
}
my inputs are like
2 (here 2 is number of inputs user intended to give)
a = 3
b = 4
I need to save them in 2 variables say var1 and var2 as I need to work with them later.
C does not support dynamically created variables. You can instantiate dynamic objects through calls to malloc(), but these will not be named. In C, names are just labels used to associate names to memory locations at compile time, and resolved at link time. It is way too late at run time.
You can create a mapping from names to int values, but you cannot create new variables. A Mapping will work for you. You need to create a method to add a named value to your mapping, a method to retrieve the value, a method to update its value, and for completeness, you need a fourth method to delete an element when you no longer need it.
Here is a simple example of mapping variable names to int values using a dynamic lookup table. To be complete, you would need to add methods for updating values, and deleting them, etc.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define MAX_VARIABLES 100
typedef struct Lookup_Entry_Struct {
char* Name;
int Value;
} LookUp_Entry;
typedef struct Mapping_Struct {
int MaxEntries;
int NumEntries;
LookUp_Entry* mapping;
} Mapping;
void initMapping(Mapping* map, int MaxEntries)
{
map->NumEntries = 0;
map->MaxEntries = MaxEntries;
map->mapping = calloc(sizeof(LookUp_Entry), MaxEntries);
if (map->mapping == NULL) {
// Failed to allocate the Mapping table
fprintf(stderr, "Failed to alloc Mapping table of %d entries\n", MaxEntries);
map->MaxEntries = 0;
}
}
bool addMap(Mapping* map, char* Name, int Val)
{
bool Added = false;
if (map->NumEntries < map->MaxEntries) {
// There is still room in the table, add this new variable
LookUp_Entry* newEntry = &(map->mapping[map->NumEntries]);
newEntry->Value = Val;
newEntry->Name = malloc(strlen(Name)+1);
strcpy(newEntry->Name, Name);
map->NumEntries++;
Added = true;
}
return Added;
}
int lookup(Mapping* map, char* Name)
{
int val = -1;
int i = 0;
bool Found = false;
// Search the map to see if we can find Name
for(i=0; i < map->NumEntries && !Found; i++)
{
LookUp_Entry* entry = &(map->mapping[i]);
if (strcmp(entry->Name, Name) == 0) {
// Found a match, return the value in *Val
val = entry->Value;
Found = true;
}
}
if (!Found)
fprintf(stderr, "lookup of \"%s\" not found in map\n", Name);
// Found value, or -1 if not found
return val;
}
void getVariablesFromUser(Mapping* map)
{
#define MAXNAMELEN 100
// Code modified from Buno's sample
int NumVariables = 0;
int i;
char inputName[100];
int inputVal;
while ((NumVariables<1) || (NumVariables > MAX_VARIABLES)) {
printf("Enter number of variables: ");
scanf("%d", &NumVariables);
if (NumVariables<0 || NumVariables>MAX_VARIABLES)
fprintf(stderr, "Please enter no more than %d variables!\n", MAX_VARIABLES);
}
printf("Init mapping for %d variables\n", NumVariables);
initMapping(map, NumVariables);
for(i=0; i<NumVariables; i++) {
printf("Enter variable #%d name and initial value: ", i+1);
scanf("%s %d", &(inputName[0]), &inputVal);
printf("Adding variable %s with initial value %d\n", inputName, inputVal);
addMap(map, inputName, inputVal);
}
}
int main(int argc, char** argv)
{
Mapping myVarMap;
char* varName;
int i;
getVariablesFromUser(&myVarMap);
// Display all the variables to show how to retrieve values
printf("%d variables added by user\n", myVarMap.NumEntries);
for(i=0; i<myVarMap.NumEntries; i++) {
LookUp_Entry *entry = &(myVarMap.mapping[i]);
char* name = entry->Name;
printf("Entry #%d: %s = %d\n", i+1, name, lookup(&myVarMap,name));
}
}
Save this in file lookup.c, then to compile it:
gcc lookup.c -o lookup
Here is sample run:
scott> lookup
Enter number of variables: 3
Init mapping for 3 variables
Enter variable #1 name and initial value: Bob 123
Adding variable Bob with initial value 123
Enter variable #2 name and initial value: Ted 999
Adding variable Ted with initial value 999
Enter variable #3 name and initial value: Sally 0
Adding variable Sally with initial value 0
3 variables added by user
Entry #1: Bob = 123
Entry #2: Ted = 999
Entry #3: Sally = 0
scott>
You cannot create variable names dynamically, however you can dynamically allocate memory using a function called 'malloc()'. You need to first understand pointers and then learn how to create and access memory during run-time.
As mentioned in the comments and answers of others, you cannot dynamically create variables in C.
However, you can follow this approach:
Dynamically read the characters for each line of n expressions (When the
length of input string is not fixed) For ex, Consider n =1 and the
input line is "a=10" Read each character 'a', '=' , '1' , '0'
Parse the string "a=10" to obtain the value 10. Dynamically allocate
memory to store this value 10.
Do this for all n input lines.
Here is the code to achieve this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Dynamically read the input string character by character: Taken from a SO answer
char *inputString(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
char *str;
int ch;
size_t len = 0;
str = realloc(NULL, sizeof(char)*size);//size is start size
if(!str)return str;
while(EOF!=(ch=fgetc(fp)) && ch != '\n'){
str[len++]=ch;
if(len==size){
str = realloc(str, sizeof(char)*(size+=16));
if(!str)return str;
}
}
str[len++]='\0';
return realloc(str, sizeof(char)*len);
}
int main(void) {
int n,i,numindex;
char *variableline,*vartemp;
char keys[] = "1234567890";
//Read in the number of variables
printf("Enter n\n");
scanf("%d\n",&n);
printf("Enter %d lines\n",n);
//Allocate memory for these variables
int *variable_values = malloc(n*sizeof(int));
for(i=0;i<n;i++){
// Read the characters dynamically from the n input lines
variableline = inputString(stdin, 10);
//numindex - index where the number starts. For ex, in the string "a=12", numindex is the position of '1'
numindex = strcspn (variableline,keys);
//Read the number into vartemp
vartemp = malloc(strlen(variableline));
strncpy(vartemp, variableline+numindex, strlen(variableline) - numindex);
//Convert the string to number
*(variable_values+i) = atoi(vartemp);
}
printf("The variable values are:\n");
// All the variable values are stored in the dynamically created memory variable_values
for(i=0;i<n;i++)
printf("%d\n",variable_values[i]);
return 0;
}
I prefer to create a Dictionary object and add 3 words to it.
My program has no compilation error but gets a run time error in the second for loop, is the problem in addNewWord function? Do I need pass a pointer to the DictionaryWord object ?
Please help me.
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
typedef struct{
char* name;
char* mean;
} Words;
typedef struct{
Words* word;
int size;
} Dictionary;
Dictionary createNewDictionary();
Words createNewWord();
void addNewWord(Words newword, Dictionary dic);
Dictionary createNewDictionary(){
Dictionary dic;
dic.size = 0;
dic.word = (Words*)malloc(dic.size*sizeof(Words));
return dic;
}
Words createNewWord(){
Words newword;
newword.name = (char*)malloc(30*sizeof(char));
newword.mean = (char*)malloc(30*sizeof(char));
printf("============================\n");
printf("Enter word: ");
scanf("%[^\n]", newword.name);
fflush(stdin);
printf("\nEnter meaning: ");
scanf("%[^\n]", newword.mean);
return newword;
}
void addNewWord(Words newword, Dictionary dic){
dic.size++;
dic.word = (Words*)realloc(dic.word,dic.size*sizeof(Words));
strcpy(dic.word[dic.size-1].name, newword.name);
strcpy(dic.word[dic.size-1].mean, newword.mean);
}
int main(){
Dictionary d = createNewDictionary();
for (int i=0;i<3;i++){
addNewWord(createNewWord(), d);
}
return 0;
}
There are lots of problem with your code:
Given the longest word in English is around 30 characters, this size allocation is realistic for the word, but not for the defintion:
newword.name = (char*)malloc(30*sizeof(char));
newword.mean = (char*)malloc(30*sizeof(char));
This makes little obvious sense:
dic.size = 0;
dic.word = (Words*)malloc(dic.size*sizeof(Words));
you called malloc() on zero! You're only spared by your later realloc(). Even if intentional, it really deserves a comment.
This doesn't really work as fflush() is for output streams:
fflush(stdin);
see: How to clear input buffer in C? And whatever fix you use has to apply to both scanf() calls, not just one!
Per #Jarvis, this doesn't work:
dic.word = (Words*)realloc(dic.word,dic.size*sizeof(Words));
strcpy(dic.word[dic.size-1].name, newword.name);
strcpy(dic.word[dic.size-1].mean, newword.mean);
as you didn't allocate any space for name and mean in dic so you're copying into random memory.
Per #Jarvis, doesn't work:
void addNewWord(Words newword, Dictionary dic){
dic.size++;
dic.word = (Words*)realloc(dic.word,dic.size*sizeof(Words));
You're passing dic by value so inside addnewWord() you've a copy of dic so the original dic's size will be the same as it was before the call!
Memory leak:
addNewWord(createNewWord(), d);
you dropped your handle onto what createNewWord() returned so you can never free the memory it malloc()'d
You malloc() memory but provide no means to eventually free it.
Passing and returning structs by value is a disaster in a situation like this, as the data keeps getting copied. At the least it's inefficient, at worst its buggy like the size issue above. Rather than risk it, pretend they can only be passed and returned by pointer and you'll be playing it safe and get a better result.
Below is a rework of your code (in C) with fixes, style tweaks and an attempt at a consistent terminology. It also provides some minimal test code and the ability to free your data:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORD_LENGTH 30
#define MAX_DEFINITION_LENGTH 1024
typedef struct entry {
char *word;
char *definition;
} Entry;
typedef struct dictionary {
Entry *entries;
int num_entries, max_entries;
} Dictionary;
Dictionary *createNewDictionary() {
Dictionary *dictionary = malloc(sizeof(*dictionary));
dictionary->num_entries = 0;
dictionary->max_entries = 1;
dictionary->entries = calloc(dictionary->max_entries, sizeof(*dictionary->entries));
return dictionary;
}
void freeEntry(Entry *entry) {
free(entry->word);
free(entry->definition);
free(entry);
}
void freeDictionary(Dictionary *dictionary) {
for (--dictionary->num_entries; dictionary->num_entries >= 0; --dictionary->num_entries) {
// we can't call freeWord() here -- why.
free(dictionary->entries[dictionary->num_entries].word);
free(dictionary->entries[dictionary->num_entries].definition);
}
free(dictionary->entries);
free(dictionary);
}
void purgeInput() {
int c;
while ((c = getchar()) != '\n' && c != EOF) { }
}
Entry *requestNewEntry() {
Entry *entry = malloc(sizeof(*entry));
entry->word = malloc(MAX_WORD_LENGTH);
entry->definition = malloc(MAX_DEFINITION_LENGTH);
printf("============================\n");
printf("Enter word: ");
scanf("%[^\n]", entry->word);
purgeInput();
printf("\nEnter definition: ");
scanf("%[^\n]", entry->definition);
purgeInput();
return entry;
}
void addNewEntry(Entry *entry, Dictionary *dictionary) {
if (dictionary->num_entries == dictionary->max_entries) {
dictionary->max_entries *= 2;
dictionary->entries = realloc(dictionary->entries, dictionary->max_entries * sizeof(*dictionary->entries));
// check if realloc returns NULL and if so, handle the error.
}
dictionary->entries[dictionary->num_entries].word = strdup(entry->word);
dictionary->entries[dictionary->num_entries].definition = strdup(entry->definition);
dictionary->num_entries++;
}
int main() {
Dictionary *d = createNewDictionary();
for (int i = 0; i < 3; i++) {
Entry *e = requestNewEntry();
addNewEntry(e, d);
freeEntry(e);
}
printf("\nRead: ");
for (int i = 0; i < d->num_entries; i++) {
printf("%s (%lu chars) ", d->entries[i].word, strlen(d->entries[i].definition));
}
printf("\n");
freeDictionary(d);
return 0;
}
CREATING A PUN DICTIONARY
> ./a.out
============================
Enter word: silkworm
Enter definition: Two silkworms had a race but ended up in a tie.
============================
Enter word: horse
Enter definition: A horse is a stable animal.
============================
Enter word: termite
Enter definition: A termite walks into a pub and asks, "Is the bar tender here?"
Read: silkworm (47 chars) horse (27 chars) termite (62 chars)
>
I see what's wrong with your code. First of all, you need to pass your Dictionary object by pointer to the function, addNewWord, and in the function addNewWord, you again need to allocate memory to each of the char* fields, name and mean, of the dic object. Here is the corrected code :
void addNewWord(Words newword, Dictionary *dic){
dic->size++;
dic->word = (Words*)realloc(dic->word, dic->size*sizeof(Words));
dic->word[dic->size-1].name = (char*)malloc(30*sizeof(char)); //added
dic->word[dic->size-1].mean = (char*)malloc(30*sizeof(char)); //added
strcpy(dic->word[dic->size-1].name, newword.name);
strcpy(dic->word[dic->size-1].mean, newword.mean);
}
Pass the dictionary's address as :
addNewWord(createNewWord(), &d);
and change the definition as well as prototype of the function as well :
void addNewWord(Words newword, Dictionary *dic)
Find the complete code here : http://pastebin.com/ZN69hevj
User give us longer string s and a shorter one t.
If t is in s, where the symbol ? in string t can be used as any character, display a message.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int isit(char*,char*,int);
int main()
{
char s[20],t[20];
int k,i,fir;
puts("Enter first string");
gets(s);
puts("Enter second string");
gets(t);
k=strlen(s)-strlen(t);
for(i=0;i<=k;i++){
fir=i;
if(isit(s,t,fir)==1){
printf("It's in there");
return 1;
}
}
}
int isit(char*s,char*t,int fir){
int i;
for(i=fir;i<i+strlen(t)-1;i++)
if(t[i]!=s[i] && t[i]!='?')
return -1;
return 1;
}
Among other problems, function isit does not use the correct index for t. It would be much simpler written with pointers and should return boolean values 0 or 1:
int isit(const char *s, const char *t, int first) {
for (s += first; *t; s++, t++) {
if (!*s || (*s != *t && *t != '?'))
return 0;
}
return 1;
}
Also use more consistent spacing and more explicit variable names: 1 letter local variable names are OK if the function is short and the name is idiomatic (i for an index, s for a char *...), but silly abbreviations are not (fir for first). Function name isit bears no information as to what it does! As a matter of fact, you do not even need a function, you could use a standard function for this:
strncmp(s + first, t, strlen(t)) == 0
EDIT: but as M.Oehm noted, this would not handle the ? wildcards.
The problem is in the function isit()
1) When you are checking for the string, in isit(). The index for t and s has to be different. For s it has to start from fir but for t it has to start from 0.
2) you dont need to do -1 in the termination condition check, because you are already using <, and not <=
3) you need to change the termination condition to i<fir+strlen(t), because i is changing at every iteration, and that invalidates the termination condition
Change the code to
int isit(char*s,char*t,int fir){
int i, j;
for(i=fir, j=0; i<fir+strlen(t); i++, j++)
if(t[j]!=s[i] && t[j]!='?')
return -1;
return 1;
}
I am learning to work with structures and this doubt come to me when doing one exercise with C.
I have this code:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#define MAX_STRING 256
#define MAX_CHILD 2000
#define MAX_GIFTS 20
#define MAX_LINE 1024
typedef char String[MAX_STRING];
typedef char Line[MAX_LINE];
typedef struct {
String child_name;
int grade; //integer between 0 and 5
String gift_name;
int price; //price of the gift
} Data;
typedef struct {
String name;
int price;
bool received; //true if the child will get this gift
} Gift;
typedef Gift Gifts[MAX_CHILD];
typedef struct{
String name;
int grade;
Gifts asked; //gifts the child asked for
int n_asked;
} Child;
typedef Child Children[MAX_CHILD];
Data make_data (String line){
Data d;
sscanf(line,"%s %d %s %d", d.child_name, &d.grade, d.gift_name, &d.price);
return d;
}
Child make_child(Data d) {
Child c;
strcpy(c.name, d.child_name);
c.grade = d.grade;
c.n_asked = 0;
return c;
}
Gift make_gift(Data d){
Gift g;
strcpy(g.name, d.gift_name);
g.price = d.price;
g.received = false;
return g;
}
int process(char file_name[]){
Line line;
FILE *f = fopen(file_name, "r");
while(fgets(line, MAX_LINE, f) != NULL){
make_data(line);
}
int fclose (FILE *f);
}
int main(){
process("data.txt");
return 0;
}
So this program receives a file text of this format:
John 4 Bike 200
Alice 3 Computer 800
Alice 3 Candy 10
Mike 5 Skate 100
and constructs the data in the function process.
The problem is, I want to store all the children in the array Children[ ] and to print it( print all the array or just something similar to Children[0], Children[1],etc). I have tried some ways but no success...as the array is of type Children and not char*. Even when I just do Children cs; I get segmentation fault. Is there a way I can accomplish this?
And my second question is, initially I had #define MAX_CHILD 20000 and when I tried to compile I got an error saying "size of array ‘Children’ is too large". Why does this happen? I see it doesn't happen to Gifts, but happens to Children because the struct Child has a Gifts type as on the members, which means it requires more space.
Any help appreciated.
the use of the typedef's (and so on)
instead of just writing the code out where it is needed
is unneeded (and distracting) and mis-leading and
makes the code much more difficult to follow.
This function:
Data make_data (String line)
{
Data d;
sscanf(line,"%s %d %s %d", d.child_name, &d.grade, d.gift_name, &d.price);
return d;
}
has several problems:
1) the parameter list will cause the compiler to 'set aside' enough room
for the String struct,
invoke a memcpy() to copy the String struct
to that 'set aside' memory from the callers' memory
Then copy the 'set aside' memory to the called functions' stack
That 'set aside' memory will never be used for anything else
The stack will be cluttered with the contents of the String struct
until the function returns
such activity is a real 'bear' to debug
2) the returned value from sscanf() needs to be checked
to assure that all 4 conversion operations were successful
3) the function return is a instance of the Data struct.
This will cause the compiler to 'set aside' enough room
for the Data struct.
that 'set aside' memory will never be used for anything else
invoke a memcpy() to copy the Data struct from the stack
to the 'set aside' memory
then perform the return from the function
then the compiler will cause the caller to
invoke memcpy() to copy the Data struct
from the 'set aside' memory to the caller's Data struct area.
such activity is a real 'bear' to debug.
The function should be written more like this:
int make_data (String *pLine, Data* pData)
{
int returnValue = 1; // initialize to indicate function successful
if( 4 != sscanf(pLine," %s %d %s %d",
pData->child_name,
&pData->grade,
pData->gift_name,
&pData->price) )
{ // then sscanf failed
perror( "sscanf failed for Line" );
returnValue = 0; // indicate to caller that function failed
} // end if
return( returnValue );
} // end function: make_data
and the caller(s) of this function should be adjusted accordingly
the make_gift() function has the same passed parameter
and returned parameter problems.
The problem is, I want to store all the children in the array
Children[ ] and to print it( print all the array or just something
similar to Children[0], Children[1],etc).
to store:
static Children cs;
size_t nc = 0; // number of children
while (fgets(line, MAX_LINE, f))
{
#include <search.h>
Data d = make_data(line);
Child c = make_child(d);
Child *cp = lsearch(&c, cs, &nc, sizeof c, (int (*)())strcmp);
cp->asked[cp->n_asked++] = make_gift(d);
}
to print:
int i, j;
for (i = 0; i < nc; ++i)
{
printf("%s (grade %d) asked for %d:\n",
cs[i].name, cs[i].grade, cs[i].n_asked);
for (j = 0; j < cs[i].n_asked; ++j)
printf("\t%s\t%d\n", cs[i].asked[j].name, cs[i].asked[j].price);
}
(We cannot simply print an aggregate type object - we have to print the individual elements.)