is there a way to avoid using this function twice? - c

void inputData(){
printf("Enter contact name : "); gets(temp.name);
fflush(stdin);
printf("Enter contact email : "); gets(temp.email);
fflush(stdin);
printf("Enter contact phone number : "); gets(temp.phoneNum);
fflush(stdin);
int index = hash(temp.phoneNum, sizeof(table1.listContact)/sizeof(table1.listContact[0]));
if (checkDuplicate(index)){
puts("Number is used");
return;
}
if(strcmp(table1.listContact[index].phoneNum, "foo")){
index = linearRehash(index, sizeof(table1.listContact)/sizeof(table1.listContact[0]));
if (index == -1){
puts("Memory Full);
return;
}
if (checkDuplicate(index)){
puts("Number is used");
return;
}
}
strcpy(table1.listContact[index].name, temp.name);
strcpy(table1.listContact[index].email, temp.email);
strcpy(table1.listContact[index].phoneNum, temp.phoneNum);
}
I'm using hash tables to create a contact list, and to prevent entering the same phone number i think i need to check the data (using checkDuplicate()) in the index twice (once after the first hash, second after the rehashing), is there a way so i only need to check it once?
int checkDuplicate(int index){
if (!strcmp(table1.listContact[index].phoneNum, temp.phoneNum)){
return 1;
}
return 0;
}
int linearRehash(int hash, int m){
int i = 0;
while (strcmp(table1.listContact[hash].phoneNum, "foo")){
hash = (hash+1)%m;
if (checkDuplicate(hash)){
return hash;
}//If duplicate found, return index with the same data
if (i == m){
return -1;
}//If no space in hashtable return -1
i++;
}
return hash;
}

I think it's the checkDuplicate inside the linearRehash function that you want to avoid doubling up on, right?
First of all, I think linearRehash could be modified into something like linearHash, which would do the initial hashing and the rehashing. Then all the duplicate checking could be done inside linearHash.
You'd still need a way for linearHash to return three different types of values: "memory full" (currently -1), "duplicate found" (currently just returns the index) and "available space found" (also currently just returns the index). Of course there are ways that you can do this (e.g. passing data through pointer parameters), but that seems unnecessarily complicated.
Instead I would suggest changing your "empty space" representation from "foo" to either a NULL pointer (if phoneNum is a pointer) or an empty string (if phoneNum is a fixed length char array). Then you can check if you're looking at an empty slot without doing a strcmp (either phoneNum == NULL or phoneNum[0] == '\0'). Then just have linearHash return the index of the slot found (either a duplicate or an empty slot, or -1 if memory full), and then have inputData check the returned index to see if it points to an empty slot (in which case insert the details into the slot) or a non-empty slot (in which case print "Number is used").
Of course, you're still doing that extra "empty slot" check on the returned value, but it doesn't matter so much.

Related

Implementing Quadratic Probing & Chaining - Search Dictionary

I have a few questions about an assignment that i need to do. It might seem that what im looking for is to get the code, however, what im trying to do is to learn because after weeks of searching for information im lost. Im really new atC`.
Here is the assignment :
Given 3 files (foo.txt , bar.txt , foo2.txt) they all have a different amount of words (I need to use dynamic memory).
Create a program that ask for a word and tells you if that word is in any of the documents (the result is the name of the document where it appears).
Example :
Please enter a word: dog
"dog" is in foo.txt and bar.txt
(I guess i need to load the 3 files, create a hash table that has the keyvalues for every word in the documents but also has something that tells you which one is the document where the word is at).
I guess i need to implement:
A Hash Function that converts a word into a HashValue
A Hash Table that stores the HashValue of every word (But i think i should also store the document index?).
Use of dynamic allocation.
Check for collisions while im inserting values into the hash table (Using Quadratic Probing and also Chaining).
Also i need to know how many times the word im looking for appears in the text.
I've been searching about hashmaps implementations, hash tables , quadratic probing, hash function for strings...but my head is a mess right now and i dont really now from where i should start.
so far i`ve read :
Algorithm to get a list of all words that are anagrams of all substrings (scrabble)?
Implementing with quadratic probing
Does C have hash/dictionary data structure?
https://gist.github.com/tonious/1377667
hash function for string
http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)HashTables.html?highlight=(CategoryAlgorithmNotes)
https://codereview.stackexchange.com/questions/115843/dictionary-implementation-using-hash-table-in-c
Sorry for my english in advance.
Hope you can help me.
Thanks.
FIRST EDIT
Thanks for the quick responses.
I'm trying to put all together and try something, however #Shasha99 I cannot use the TRIE data structure, i'm checking the links you gave me.
#MichaelDorgan Thanks for posting a solution for beginners however i must use Hashing (It's for Algorithms and Structures Class) and the teacher told us we MUST implement a Hash Function , Hash Table and probably another structure that stores important information.
After thinking for an hour I tried the following :
A Structure that stores the word, the number of documents where it appears and the index of those documents.
typedef struct WordMetadata {
char* Word;
int Documents[5];
int DocumentsCount;
} WordMetadata;
A function that Initializes that structure
void InitTable (WordMetadata **Table) {
Table = (WordMetadata**) malloc (sizeof(WordMetadata) * TABLESIZE);
for (int i = 0; i < TABLESIZE; i++) {
Table[i] = (WordMetadata*) NULL;
}
}
A function that Loads to memory the 3 documents and index every word inside the hash table.
A function that index a word in the mentioned structure
A function that search for the specific word using Quadratic Probing (If i solve this i will try with the chaining one...).
A function that calculates the hash value of a word (I think i will use djb2 or any of the ones i found here http://www.cse.yorku.ca/~oz/hash.html) but for now :
int Hash (char *WordParam) {
for (int i = 0; *WordParam != '\0';) {
i += *WordParam++;
}
return (i % TABLESIZE);}
EDIT 2
I tried to implement something, its not working but would take a look and tell me what is wrong (i know the code is a mess)
EDIT 3
This code is properly compiling and running, however , some words are not finded (maybe not indexed i' dont know), i'm thinking about moving to another hashfunction as i mentioned in my first message.
Approximately 85% of the words from every textfile (~ 200 words each) are correctly finded by the program.
The other ones are ramdom words that i think are not indexed correctly or maybe i have an error in my search function...
Here is the current (Fully functional) code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLESIZE 4001
#define LINESIZE 2048
#define DELIMITER " \t"
typedef struct TTable {
char* Word; /* The actual word */
int Documents[5]; /* Documents Index */
int DocumentsCount; /* Number of documents where the word exist */
} TTable;
int Hash (char *Word);
void Index (TTable **HashTable, char* Word, int DocumentIndex);
int Search (TTable **HashTable, char* Word);
int mystrcmp(char *s1, char *s2);
char* Documents[] = {"foo.txt","bar.txt","foo2.txt",NULL};
int main() {
FILE* file;
TTable **HashTable
int DocumentIndex;
char Line[LINESIZE];
char* Word;
char* Tmp;
HashTable = (TTable**) malloc (sizeof(TTable)*TABLESIZE);
for (int i = 0; i < TABLESIZE; i++) {
HashTable[i] = (TTable*) NULL;
}
for (DocumentIndex = 0; Documents[DocumentIndex] != NULL; DocumentIndex++) {
file = fopen(Documents[DocumentIndex],"r");
if (file == NULL) {
fprintf(stderr, "Error%s\n", Documents[DocumentIndex]);
continue;
}
while (fgets (Line,LINESIZE,file) != NULL) {
Line[LINESIZE-1] = '\0';
Tmp = strtok (Line,DELIMITER);
do {
Word = (char*) malloc (strlen(Tmp)+1);
strcpy(Word,Tmp);
Index(HashTable,Word,DocumentIndex);
Tmp = strtok(NULL,DELIMITER);
} while (Tmp != NULL);
}
fclose(file);
}
printf("Enter the word:");
fgets(Line,100,stdin);
Line[strlen(Line)-1]='\0'; //fgets stores newline as well. so removing newline.
int i = Search(HashTable,Line);
if (i != -1) {
for (int j = 0; j < HashTable[i]->DocumentsCount; j++) {
printf("%s\n", Documents[HashTable[i]->Documents[j]]);
if ( j < HashTable[i]->DocumentsCount-1) {
printf(",");
}
}
}
else {
printf("Cant find word\n");
}
for (i = 0; i < TABLESIZE; i++) {
if (HashTable[i] != NULL) {
free(HashTable[i]->Word);
free(HashTable[i]);
}
}
return 0;
}
/* Theorem: If TableSize is prime and ? < 0.5, quadratic
probing will always find an empty slot
*/
int Search (TTable **HashTable, char* Word) {
int Aux = Hash(Word);
int OldPosition,ActualPosition;
ActualPosition = -1;
for (int i = 0; i < TABLESIZE; i++) {
OldPosition = ActualPosition;
ActualPosition = (Aux + i*i) % TABLESIZE;
if (HashTable[ActualPosition] == NULL) {
return -1;
}
if (strcmp(Word,HashTable[ActualPosition]->Word) == 0) {
return ActualPosition;
}
}
return -1; // Word not found
}
void Index (TTable **HashTable, char* Word, int DocumentIndex) {
int Aux; //Hash value
int OldPosition, ActualPosition;
if ((ActualPosition = Search(HashTable,Word)) != -1) {
for (int j = 0; j < HashTable[ActualPosition]->DocumentsCount;j++) {
if(HashTable[ActualPosition]->Documents[j] == DocumentIndex) {
return;
}
}
HashTable[ActualPosition]->Documents[HashTable[ActualPosition]->DocumentsCount] = DocumentIndex; HashTable[ActualPosition]->DocumentsCount++;
return;
}
ActualPosition = -1;
Aux = Hash(Word);
for (int i = 0; i < TABLESIZE; i++) {
OldPosition = ActualPosition;
ActualPosition = (Aux + i*i) % TABLESIZE;
if (OldPosition == ActualPosition) {
break;
}
if (HashTable[ActualPosition] == NULL) {
HashTable[ActualPosition] = (TTable*)malloc (sizeof(TTable));
HashTable[ActualPosition]->Word = Word;
HashTable[ActualPosition]->Documents[0] = DocumentIndex;
HashTable[ActualPosition]->DocumentsCount = 1;
return;
}
}
printf("No more free space\n");
}
int Hash (char *Word) {
int HashValue;
for (HashValue = 0; *Word != '\0';) {
HashValue += *Word++;
}
return (HashValue % TABLESIZE);
}
I would suggest you to use TRIE data structure for storing strings present in all three files in memory as Hash would be more space consuming.
As the first step you should read all three files one by one and for each word in file_i, you should do the following:
if the word is already present in TRIE, append the file index to that node or update the word count relative to that particular file. You may need 3 variables for file1, file and file3 at each node to store the values of word count.
if the word is not present, add the word and the file index in TRIE node.
Once you are done with building your TRIE, checking whether the word is present or not would be an O(1) operation.
If you are going with Hash Tables, then:
You should start with how to get hash values for strings.
Then read about open addressing, probing and chaining
Then understand the problems in open addressing and chaining approaches.
How will you delete and element in hash table with open addressing and probing ? here
How will the search be performed in case of chaining ? here
Making a dynamic hash table with open addressing ? Amortized analysis here and here.
Comparing between chaining and open addressing. here.
Think about how these problems can be resolved. May be TRIE ?
Problem in the code of your EDIT 2:
An outstanding progress from your side !!!
After a quick look, i found the following problems:
Don't use gets() method, use fgets() instead So replace:
gets(Line);
with the following:
fgets(Line,100,stdin);
Line[strlen(Line)-1]='\0'; //fgets stores newline as well. so removing newline.
The line:
if ( j < HashTable[j]->DocumentsCount-1){
is causing segmentation fault. I think you want to access HashTable[i]:
if ( j < HashTable[i]->DocumentsCount-1){
In the line:
HashTable[ActualPosition]->Documents[HashTable[ActualPosition]->DocumentsCount];
You were supposed to assign some value. May be this:
HashTable[ActualPosition]->Documents[HashTable[ActualPosition]->DocumentsCount] = DocumentIndex;
Malloc returns void pointer. You should cast it to the appropriate
one:
HashTable[ActualPosition] = (TTable*)malloc (sizeof(TTable));
You should also initialize the Documents array with default value while creating a new node in Hash:
for(j=0;j<5;j++)HashTable[ActualPosition]->Documents[j]=-1;
You are removing everything from your HashTable after finding the
first word given by user. May be you wanted to place that code outside
the while loop.
Your while loop while(1) does not have any terminating condition, You
should have one.
All the best !!!
For a school assignment, you probably don't need to worry about hashing. For a first pass, you can just get away with a straight linear search instead:
Create 3 pointers to char arrays (or a char ** if you prefer), one for each dictionary file.
Scan each text/dictionary file to see how many individual words reside within it. Depending on how the file is formatted, this may be spaces, strings, newlines, commas, etc. Basically, count the words in the file.
Allocate an array of char * times the word count in each file and store it in the char ** for that file. (if 100 words found in the file , num_words=100; fooPtr = malloc(sizeof(char *) * num_words);
Go back through the file a second time and allocate an array of chars to the size of each word in the file and store it in the previously created array. You now have a "jagged 2D array" for every word in each dictionary file.
Now, you have 3 arrays for your dictionaries and can use them to scan for words directly.
When given a word, setup a for loop to look through each file's char array. if the entered word matches with the currently scanned dictionary, you have found a match and should print the result. Once you have scanned all dictionaries, you are done.
Things to make it faster:
Sort each dictionary, then you can binary search them for matches (O(log n)).
Create a hash table and add each string to it for O(1) lookup time (This is what most professional solutions would do, which is why you found so many links on this.)
I've offered almost no code here, just a method. Give it a shot.
One final note - even if you decide to use a the hash method or a list or whatever, the code you write with arrays will still be useful.

Printing all duplicate words from a file in C

What am I supposed to do in order to print only the duplicated words in a file. I'm a beginner in C.
Here's my code:
#include <stdio.h>
#define max 100
main(){
FILE *fpointer;
char ch[max],str[max][max];
int i = 0,j = 0,count = 0;
int a,b;
fpointer = fopen("1.txt","r");
if(fpointer == NULL){
printf("Error!");
return 0;
}
while(fscanf(fpointer,"%s",ch)!=EOF){
strcat(str[i++],ch);
strcat(str[i++]," ");
count++;
}
for(a=0;a<count*2;a=a+2){
for(b=0;b<count*2;b=b+2){
if(strcmp(str[a],str[b])==0){
printf("%s\n",str[b]);
}
}
}
fclose(fpointer);
return 0;
}
An example of input is "shine little star shine bright in the dark star"
and the output of this program is "shine shine little star star shine shine bright in the dark star star". Please help me to print
only duplicate words. Tnx
In your inner loop, instead of starting at the beginning of the list, start one element after the current one. That way you're not comparing an element against itself or comparing any two elements twice.
for(a=0;a<count*2;a=a+2){
for(b=a+2;b<count*2;b=b+2){ // b=a+2 instead of b=0
if(strcmp(str[a],str[b])==0){
printf("%s\n",str[b]);
}
}
}
Output:
shine
star
Since you wrote duplicate words, i am guessing the words in the file will be repeated twice only.
So you second nested for loop is starting from the beginning every time, and thats where the problem is. You have to make sure that the word already checked is not iterated over again. For that you can change your second for loop a little
for(a=0;a<count*2;a=a+2){
for(b=a+2;b<count*2;b=b+2){ //notice b=a+2
if(strcmp(str[a],str[b])==0){
printf("%s\n",str[b]);
}
}
}
Note
For cases when the words are repeating more then twice, my suggestion would be to use some hashing technique to store if a word has already occurred or not. And then compare and print accordingly.
You have a couple of problems here.
Go through and write some comments. For example, put these where they belong:
// Open the file
// Read in the strings
// Compare the strings
If you do this you will notice you have some problems. For instance, what is this line for?
strcat(str[i++]," ");
It's not reading in a string. So what is it doing?
In your final loop, you want to compare each string with all the others. So obviously, you start with the first string, and you compare it to all the others:
for (a=1; a<count; a++)
if(strcmp(str[0],str[a])==0)
{
printf("%s\n",str[0]);
break;
}
Now we can compare the first string with each following string and print it out if it is duplicated. The "break" is there so we only print it once.
But this only compares the first string with the others. How do we compare all of them? Another loop, of course.
// Compare each string against all the ones after it
for (target=0; target<count; target++)
for (a=target+1; a<count; a++)
// If the strings match AND they are not both null
// (why print out copies of null strings? - nobody wants that)
if(strcmp(str[target],str[a])==0) && strlen(str[a])
{
// Print it
printf("%s\n",str[target]);
// Why is this here?
strcpy(str[a],"");
for (copies=a+1; copies<count; copies++)
if(strcmp(str[copies],str[target])==0)
strcpy(str[copies],"");
// We got a match, stop checking for this string
break;
}
See how we've got one big loop that steps through each string, and a smaller loop that steps through all the remaining strings (no point in stepping through the ones we already looked at).
Now a question for you: why is "strcpy(str[a],"") and the next for loop in there? What purpose do they serve?
This is one way to do it. Please enable warnings - you left out a library header. I've also checked array limits. You need to find a better way to limit the string input too, than just guess a large array size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // included header
#define MAX 100
int main(void){ // correct function
FILE *fpointer;
int reported [MAX];
char ch[MAX], str[MAX][MAX];
int index = 0, a;
fpointer = fopen("1.txt","r");
if(fpointer == NULL){
printf("Error!");
return 0;
}
while(fscanf(fpointer,"%s",ch) == 1){ // testing for 1 item read
for(a=0; a<index; a++){
if(strcmp(ch, str[a])==0){
if (reported[a] == 0) // don't report twice
printf("%s ",ch);
reported[a]++;
break;
}
}
if(a >= index) { // check if loop completed
if (index >= MAX) { // check if array is full
printf("\nToo many words read\n");
exit(1);
}
strcpy(str[index], ch); // word was not found
reported[index] = 0;
index++; // keep tabs on array length
}
}
printf("\n",ch);
fclose(fpointer);
return 0;
}
Program output:
shine star
One issue is that you are storing every word, regardless of whether it has already been seen:
while(fscanf(fpointer,"%s",ch)!=EOF){
strcat(str[i++],ch);
strcat(str[i++]," "); // ??? what is this for
count++;
}
Ideally, you'd only like to store each word once, and maintain a count of how often that word appears in the file:
while ( fscanf( fpointer, "%s", ch ) != EOF ) // whitespace is your friend, use it
{
if ( ch_is_not_already_in_str )
{
strcpy( str[count++], ch );
}
else
{
update_count_for_that_string;
}
}
Then at the end, you just loop through ch and print each string that has a count greater than 1.
for ( i = 0; i < count; i++ )
{
if ( str[i]_occurs_more_than_once )
printf( "%s ", str[i] );
}
The bits you need to figure out are ch_is_not_already_in_str, update_count_for_that_string, and str[i]_occurs_more_than_once. How would you go about searching your str array for an occurence of the string in ch? How would you maintain a counter for each string?

How to properly pass array pointers to function in C

A different question may have been asked about this program, however in this C code I have three functions: one to print records, one to add a record and one to delete a record.
What I don't understand is why the (add) and (delete) do not make changes in the main function, so when I use the print all records function it prints changes, but it doesn't show changes, what is wrong?
Details are in the comments, please feel free to run the code to visualise the problem.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*The program is to store student record (firstname,lastname and score), it should not be more
than 15 records and the names should not be more than 20 chars, array pointersnare being used
instead of arrays, the (add_record) and (delete_record) functions must
do thsi change in the main function, so when you print all records, the changes are shown*/
void print_records(char **firstname,char **lastname,float *score,int *the_size)
{
int i;
printf("Printing All record(s)...\n");
for (i=0;i<*the_size;i++) /*loop to print records of all arrays in correct format*/
{
printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[i],lastname[i],score[i]);
}
}
void add_new_record (char **firstname,char **lastname,float *score,int the_size)
{
printf("Add new record in the format :\nFirstname Lastname Score\n");
/*the strategy here is to check if all 15 elemts are used, if they are, use realloc
to add one more, if not add the record after the last record*/
if (the_size == 15)
{
firstname=realloc(firstname,16*sizeof(char*));
firstname[15]=malloc((20+1)*sizeof(char));
lastname=realloc(lastname,16*sizeof(char*));
lastname[15]=malloc((20+1)*sizeof(char));
score=realloc(score,16*sizeof(float));
scanf("%s %s %f",firstname[15],lastname[15],&score[15]);
printf("New Record Added Successfully !\n");
printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[15],lastname[15],score[15]);
}
else if (the_size<15)
{
scanf("%s %s %f",firstname[the_size],lastname[the_size],&score[the_size]);
printf("New Record Added Successfully !\n");
printf("Firstname : %s, Lastname : %s, Score : %f\n",firstname[the_size],lastname[the_size],score[the_size]);
}
}
void delete_record (char **firstname,char **lastname,float *score,int the_size)
{
char *str=malloc(20*sizeof(char)); /*String entered by user must be 20 or less chars*/
int i,ctr=0;
char *temp_first=malloc(20*sizeof(char));/*temp array to swap firstname must be 20 or less chars*/
char *temp_last=malloc(20*sizeof(char)); /*temp array to swap lastname must be 20 or less chars*/
float temp_score;/*ctr is the counter used to check if there are no matchs in the end*/
printf("Enter the lastname of record(s) to delete : ");
scanf("%s",str);
/* the strategy here is to move the element to be deleted to the last index and use
relloc to shrink the size by 1 (-1) */
for (i=0;i< the_size;i++)
{
if (strcmp(str,lastname[i])==0)
{
printf("Deleting Record for %s %s...\n",firstname[i],lastname[i]);
temp_score=score[i];
score[i]=score[the_size-1];
score[the_size-1]=temp_score;
strcpy(temp_first, firstname[i]); /*using strcpy function to swap strings*/
strcpy(firstname[i], firstname[the_size-1]);
strcpy(firstname[the_size-1], temp_first);
strcpy(temp_last, lastname[i]);
strcpy(lastname[i], lastname[the_size-1]);
strcpy(lastname[the_size-1], temp_last);
score=realloc(score,(the_size-1)*sizeof(float));
firstname=realloc(firstname,(the_size-1)*sizeof(char*));
lastname=realloc(lastname,(the_size-1)*sizeof(char*));
ctr++;
the_size--;
}
}
if (!ctr) /*if ctr=0 (no increment), then print,there is no match*/
{
printf ("Sorry, no available record for %s",str);
}
free(temp_first);
free(temp_last);
free(str);
}
void main()
{
char **firstname;
char **lastname;
float *score;
int number_of_records,i,j=-1,ctr=1,row=15,col=20;
/*ctr is to keep track of the student's number (makes it easier to
the user), it starts with (1)*/
firstname=malloc(row*sizeof(char*));
for(i=0;i<row;i++)
{
firstname[i]=malloc((col+1)*sizeof(char));
}
lastname=malloc(row*sizeof(char*));
for(i=0;i<row;i++)
{
lastname[i]=malloc((col+1)*sizeof(char));
}
printf("\nPlease indicate number of records you want to enter (min 2, max 15): ");
scanf("%d",&number_of_records);
score=malloc(row*sizeof(float));
printf("\nPlease input records of students\n(enter a new line after"
"each record), with following format:\nfirst name last name score ");
for (i=0;i<number_of_records;i++)
{
printf("\nEnter record for student %d : ",ctr);
scanf("%s %s %f",firstname[i],lastname[i],&score[i]);
ctr++; /*ctr is to keep track of student number
(makes it easy to the user) */
}
while (j!=0) /*Main menu will keep looping after using a function as long as j is not 0
When the user enters 0 (zero) the loop will stop and therefore the program will terminate*/
{
printf("\nSelect desired function by pressing the corresponding key number\n");
printf("\n********** Main Menu **********\n");
printf("\n>>> Print records (press 1)\n");
printf("\n>>> Add a new Record (press 2 )\n");
printf("\n>>> delete record (press 3)\n");
printf("\n>>> Exit the program (press 0)\n");
scanf("%d",&j); /*getting j from the user (j is used for selection and for the while loop)*/
if (j==1)
{
print_records(firstname,lastname,score,&number_of_records);
}
else if (j==2)
{
add_new_record(firstname,lastname,score,number_of_records);
}
else if (j==3)
{
delete_record(firstname,lastname,score,number_of_records);
}
else if (j==0)
{
printf("Exitting program ...\n");
}
}
}
As other answers have observed, in C, all arguments are passed by value. That means the function gets a copy of the caller's value, therefore changes to that value are not visible to the caller. In other words, given
void f(any_type arg) {
arg = any_value;
}
The caller will never detect any change, regardless of what type any_type is or what value any_value is. Note carefully, however, the difference between that, and this:
void f(any_type *arg) {
*arg = any_value;
}
In that case, it is not the function argument (a pointer) that is being modified, it is the thing pointed to. The argument is a copy of the caller's value, so the two point to the same thing. The caller cannot detect changes to the argument, but after the call, it can detect changes to the thing it points to.
Your code exhibits some issues of this type, some of them responsible for your main problems. Most importantly, these have to do with your record keeping on the number of elements in your list (variable number_of_records in main()). Your addition and deletion functions work more or less ok, except that they cannot communicate the revised list size back to main.
There are additional issues in add_new_record() when there are already 15 records; if you can, I would just disallow that case. If you must support it, then you have multiple things to clean up. Some of them to do with pass-by-value issues, others to do with what your code should do when the list initially contains 16 or more records.
Update:
Since you're having so much trouble working this out, here's a revised version of delete_record(). It implements a lot more than the minimal changes required to get the desired output upon record deletion, as there was in fact a goodly number of other issues that I might as well call out as long as I'm going to the trouble. See new and modified comments.
/*
* Can realloc() *firstname, *lastname, and *score and return the updated
* values to the caller. Most importantly, can update *the_size and have
* the caller see the result.
*/
void delete_record (char ***firstname, char ***lastname, float **score, int *the_size)
{
int i;
int initial_size = *the_size;
char str[21]; /* no need to malloc a fixed-length local array */
printf("Enter the lastname of record(s) to delete : ");
fflush(stdout); /* The prompt might not appear if you don't flush */
/*
* Note the field width in the format below. Without it, a user can
* easily cause a buffer overflow.
*/
scanf("%20s", str);
/*
* The strategy for each element to delete (there may be more than one)
* is to free the element's name components (else their allocated memory
* leaks), copy the last (at that time) element's components into
* place (for the name components, just the pointers), and later
* realloc to shrink the overall size to exactly fit the remaining
* elements (once we know how many that is).
*/
for (i = 0; i < *the_size; )
{
if (strcmp(str, (*lastname)[i]) == 0)
{
printf("Deleting Record for %s %s...\n", (*firstname)[i], (*lastname)[i]);
free((*firstname)[i]);
free((*lastname)[i]);
(*firstname)[i] = (*firstname)[*the_size - 1];
(*lastname)[i] = (*lastname)[*the_size - 1];
(*score)[i] = (*score)[*the_size - 1];
*the_size -= 1; /* not the same as *the_size-- */
/* don't increment i, else we miss testing the new i'th element */
} else {
i += 1;
}
}
if (*the_size != initial_size)
{
void *temp;
/*
* Always check the return value of realloc(), even when you're
* shrinking the allocation. Usually, though, you'd want at least
* some kind of diagnostic in the event of failure.
*/
temp = realloc(*firstname, sizeof(char *) * (*the_size));
if (temp)
{
*firstname = temp;
}
temp = realloc(*lastname, sizeof(char *) * (*the_size));
if (temp)
{
*lastname = temp;
}
temp = realloc(*score, sizeof(float) * (*the_size));
if (temp)
{
*score = temp;
}
}
else /* there is no match */
{
printf ("Sorry, no available record for %s",str);
}
}
Your main() would call that like so:
delete_record(&firstname, &lastname, &score, &number_of_records);
Similar changes are needed to add_record(), though you do have the separate issue I already called out there with increasing the number of entries past 16.
Additionally, you're making extra work for yourself by using separate arrays of first name, last name, and score. It would be much easier to define a struct encompassing all three, and use just one dynamic array whose elements are instances of that struct.
First, you're declaring the arguments of add_ as pointers (presumably because you want the function to change the value of the variables whose addresses you pass):
add_new_record (char **firstname, char **lastname, float *score, int the_size) {
But then you simply assign the locals, not the things they point to:
firstname = realloc(firstname,16*sizeof(char*));
score = realloc(score,16*sizeof(float));
If you want to change the variables to which these point, you have to
dereference them for assignment:
*firstname = malloc(16);
*score = 1.0;
Now the original firstname pointer points to a valid chunk of memory (presumably you'll want to strcpy() some actual name there), and the original float variable to which score points is now 1.0.
There are other problems, but that's the primary reason this function doesn't change what you think it should, because you didn't tell it to.

In C check to see if an int exists?

I have a 13x13 array with elements but not every element is number. If its not a number, then its filled with blank space so I am trying to figure out how to check to see if a number exists at a given index. If a number exists, it does a job if not, it goes back to the loop. This is what I have:
int *spaceCheck;
int i, Value;
for(i=0;i<169;i++){
spaceCheck = &Array[i];
if(!spaceCheck){
continue;
else
//do this job
}
Can I do this? Is there any other better way to check? Thank you for you help.
Its an array of integers of 169 elements. So when the data(numbers) is initially loaded, there are only 80 elements loaded from the file into the array. The rest of the space in the array is filled with blank space (printf(" ")). Sorry for the confusion.
To do what you are asking, you need to use a sentinel value that indicates "no value", eg:
const int novalue = -1; // or whatever you want, as long as it is unique
int Array[169];
for (i=0;i<169;i++)
Array[i] = novalue;
...
for(i=0;i<169;i++){
Value = Array[i];
if(Value == novalue)
continue;
//do this job
}
Otherwise, create an array on structs instead of ints:
struct sValue
{
char valid;
int value;
};
sValue Array[169];
for (i=0;i<169;i++)
Array[i].valid = 0;
...
for(i=0;i<169;i++){
if(Array[i].valid == 0)
continue;
Value = Array[i].value;
//do this job
}
The array elements may not be initialized with zero always. The first thing you need to do is initialize it with a default value. say 0 or -1 or whatever. Then you can modify your code as follows:
int *spaceCheck;
int i, Value;
for(i=0;i<169;i++){
spaceCheck = &Array[i];
if(*spaceCheck!=default_value){
continue;
else
//do this job
}
Note that you were also storing the reference of the array element. If you want to check its data you need to use the dereferencing operator *. Hope that this is what you want. Simpler way to write the above code is to not use pointers at all. It is as follows:
int i, Value;
for(i=0;i<169;i++){
if(Array[i]!=default_value){
continue;
else
//do this job
}
I think here in this statement :if(!spaceCheck) you are checking the address of the variable to be true and you are not comparing it with any value.As for example ,if((*spacecheck)!=Array[i][j])//considering you have a 2-D array)will be logically correct as per your question.Well the following code may clear your concept,it is based on conditions similar to what you have provided here,
int *spaceCheck;
int i,j, Value;
for(i=0;i<13;i++){
for(j=0;j<13;j++){
spaceCheck=&array[i][j];//array is 2-D
if(*spaceCheck!=array[i][j])
continue;
else
//do this job
}}

input number in structs - check it already exists in file

I'm trying to write a data validation function, where the ID number has to be a unique number in an array of structs. Every time I try to save a valid ID number, the program is crashing - why is this happening?
this is the struct, and the declaration of an array of structs:
struct customer { // set up customer template //
char name [MAXNAM];
char surname [MAXNAM];
int idnum [MAX_ID];
};
struct customer data_cus[MAXCUS];
this is within the main program:
printf ("Please Enter ID Card, [NOTE! Only numbers are allowed!]\n");
int checkID;
while ((scanf ("%d",&checkID) == 0 || customerID(checkID, count)==1))
{
printf ("This ID is already taken! Please enter unique ID!\n");
while (getchar()!='\n')
{
continue;
}
*data_cus[count].idnum = checkID;
}
Here is the function used to check id the ID already exists in file:
int customerID (int cCheck, int count)
{
FILE * pcustomer;
int size = sizeof (struct customer);
struct customer temp;
rewind (pcustomer);
while (fread (&temp,size,count,pcustomer)==1)
{
if (*temp.idnum == cCheck)
{
return 1;
}
}
return 0;
}
Thank you for your help!
idnum is an array of integers. Is that really what you want, not just a simple integer?
I'm pretty sure that if you remove [MAX_ID] from your struct, things will work much better.
However, I reserve the right to alter the motivation as to why it's crashing once we see the code that is after the ID check code.
scanf returns the number of successfully matched items, I think you mean:
while (scanf ("%d",&checkID) == 1 && customerID(checkID, count)==1).
You should make sure count doesn't exceed MAXCUS only increment when you successfully read a unique ID, and you don't need an array of integer for idnum since each record has only one.
Also the function customerID is obviously wrong, here you read more than one customer structure and write to temp which is a single customer structure, this is most likely why it crashes:
while (fread (&temp,size,count,pcustomer)==1)
It's much easier if you keep track of IDs in an array instead of reading from file every time you want to check if a new ID is unique.

Resources