I am having problems trying to copy the contents of a char array into an array of char pointers in C. My code is listed below:
# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# define RECORD_SIZE 300
# define BUFFER_SIZE 3
/* Structure for representing a single
* password entry */
typedef struct Record {
char * sitename;
char * username;
char * password;
struct Record * next;
} Record;
/* Declare function prototypes */
int isCorrectKey(char *,char *);
int isValidOperation(char);
int isValidSyntax(char *);
void getFields(char *,char * []);
void listEntries();
int updateEntries(char *);
int deleteEntries(char *);
int entryExists(char *);
void init(char *,char *,char *);
void readDB();
void writeToDB(char *);
void encrypt(char *,char * [],char *);
void decrypt(char *,char * [],char *);
/* Declare global variables */
char * database;
Record * records;
// Pre-condition: A character pointer to plaintext
// A character pointer to an empty buffer,
// A character pointer to the user input key
// Post-condition: The plaintext is encrypted and
// inserted into the buffer
void encrypt(char * text,char * buffer[BUFFER_SIZE],char * key) {
int i = 0;
for(;i < strlen(text);i++) {
buffer[i] = ((char *) (text[i]+3));
}
buffer[i] = 0;
}
// Pre-condition: A character pointer to ciphertext
// A character pointer to an empty buffer
// A character pointer tothe user input key
// Post-condition: The ciphertext is decrypted and
// inserted into the buffer
void decrypt(char * text,char * buffer[BUFFER_SIZE],char * key) {
int i = 0;
for(;i < strlen(text);i++) {
buffer[i] = ((char *) (text[i]-3));
}
buffer[i] = 0;
}
// Pre-condition: The database variable must be set
// The records variable must exist
// Post-condition: The value in database variable is
// used as filename to read the data. The data that
// is read and used to initialize the records variable
void readDB() {
// Open the file in read-only mode
FILE * f = fopen(database,"r");
char buffer[BUFFER_SIZE];
int index = 0;
if(f == NULL) {
// Print error if file is invalid
printf("Sorry. Unable to find password database.");
} else {
char c = 0;
// Read the file
while((c = fgetc(f)) != EOF) {
if(c != '\n') {
buffer[index++] = c;
} else {
buffer[index] = 0; // terminate each entry with null
index = 0;
}
}
fclose(f); // Close the file handle
}
}
// Pre-condition: The database variable must be set
// A character pointer to some text must be provided
// as input
// Post-condition: The value in database variable is
// used as filename to write the data.
void writeToDB(char * text) {
// Open the file in append mode
FILE * f = fopen(database,"a");
int index = 0;
if(f == NULL) {
// Print error if file is invalid
printf("Sorry. Unable to find password database.");
} else {
fputs(text,f); // Write to the file
fclose(f); // Close the file handle
}
}
// Pre-condition: A character pointer to the user input key
// A character pointer to the actual key
// The records variable must be set
// Post-condition: Returns 1 if the key value in the records variable
// has been properly decrypted.
// Returns 0 otherwise.
int isCorrectKey(char * uKey,char * mkey) {
return strcmp("ThisIsTheSecretKey",records->password);
}
// Pre-condition: A character indicating an operation value
// Post-condition: Returns 1 if the operation value is supported
// Returns 0 otherwise.
int isValidOperation(char operation) {
return operation == 'L' || operation == 'U' || operation == 'D';
}
// Pre-condition: A character pointer to a user input command string
// Post-condition: Returns 1 if the syntax of the command is correct
int isValidSyntax(char * command) {
return 0;
}
// Pre-condition: A character pointer to a comma delimited string
// Post-condition: The string is split into segments and stored into the buffer
void getFields(char * record,char * buffer[BUFFER_SIZE]) {
int i = 0;
int buffer_i = 0;
int record_len = strlen(record);
char tmp_buffer[RECORD_SIZE+1];
int tmp_i = 0;
for(;i < record_len;i++) {
if(record[i] != ',') {
tmp_buffer[tmp_i++] = record[i];
} else {
tmp_buffer[tmp_i] = 0;
strcpy(buffer[buffer_i++],tmp_buffer);
tmp_i = 0;
}
}
}
int main(int argc,char * argv[]) {
//database = "test.txt";
//readDB();
char * buffer[BUFFER_SIZE];
getFields("google,geek,pass123",buffer);
int i = 0;
for(;i<BUFFER_SIZE;i++) {
printf(buffer[i]);
printf("\n");
}
return 0;
}
From what I see, the offending line is in the getFields() function:
strcpy(buffer[buffer_i++],tmp_buffer);
I am trying to copy the contents of tmp_buffer into the indexes of buffer. My program just keeps crashing. I don't know why. Could someone please help me? Thanks.
You're not initializing the char *buffer to point to anything, e.g. buffer[0] = malloc(SIZE).
You need to allocate memory for each of the three entries into buffer. As it stands now, you are copying strings into the void.
You need something like this:
for(i=0;i<BUFFER_SIZE;i++) {
buffer[i] = malloc(MAX_STR_SIZE);
}
And then free them when you are done at the end:
for(i=0;i<BUFFER_SIZE;i++) {
free(buffer[i]);
}
Related
I've been struggling with C pointers for hours now. I'm trying to create a C program which manages flights. A flight contains the following:
flight-number, from, to, date, price
OS772,Vienna,New York,15.12.2018,638.00
Therefore, I'm reading a textfile of this given structure. On every line read, I need to create another struct and add it to my array or "list" of structs.
The struct looks like:
typedef struct flights {
char *flnum;
char *from;
char *to;
char *date;
float price;
struct person *fPerson;
}flights;
My problem: Inside the function, the array of structs is created properly. But back in the main-function, the pointer to the array called 'flights **flight_list' is still NULL.
Here is the code (only the necessary parts):
int main(void) {
flights **flight_list = NULL;
int numFlights = 0;
if (!(numFlights = load_flights(flight_list)))
return EXIT_FAILURE;
/* value of flight_list = 0x0000 -> unchanged! */
/* ... */
Function short load_flights(flights **flight_list):
short load_flights(flights **flight_list) {
FILE *fp = NULL;
char file_buffer[256] = {};
int i = 0;
if (fp = fopen("flights.txt", "r")) {
/* create array of structs */
flight_list = (flights **)calloc(1, sizeof(int));
while (!feof(fp)) {
/* read current line of flight from textfile */
fgets(file_buffer, sizeof(file_buffer), fp);
/* create a new struct and add it to the array */
if ((flight_list[i] = (flights *)calloc(1, sizeof(flights))) != NULL) {
/* create every variable of the struct */
flight_list[i]->flnum = (char *)calloc(1, strlen(ptr)+1);
/* ... */
}
i++;
}
}
else return 0;
/* values of the struct-array are properly set; look in attached picture */
return i;
}
This image was taken while debugging the array-creation process before return i;:
And here outside the function; inside main:
So, why is my array of structs gone in the main-function?
You need to pass the address of a pointer variable to load_flights. Then load_flights needs to indirect through the variable to modify the caller's variable.
To handle the dynamic size of the input, you need to use realloc() each time through the loop to grow the array.
int main(void) {
flights **flight_list = NULL;
int numFlights = 0;
if (!(numFlights = load_flights(&flight_list)))
return EXIT_FAILURE;
/* ... */
}
short load_flights(flights ***flight_list) {
FILE *fp = NULL;
char file_buffer[256] = {};
int i = 0;
if (fp = fopen("flights.txt", "r")) {
/* create array of structs */
flight_list **temp_flight_list = NULL;
/* read current line of flight from textfile */
while (fgets(file_buffer, sizeof(file_buffer), fp)) {
// Grow the flight list array
flights **new_flight_list = realloc(*flight_list, (i+1) * sizeof(flight_list *));
if (new_flight_list == NULL) { // allocation failed, throw everything away
for (int j = 0; j < i-1; j++) {
free(temp_flight_list[i]->flnum);
free(temp_flight_list[i]->from);
/* ... */
free(temp_flight_list[i]);
}
free(temp_flight_list);
return 0;
}
temp_flight_list = new_flight_list;
/* create a new struct and add it to the array */
if ((temp_flight_list[i] = calloc(1, sizeof(flights))) != NULL) {
// Parse the buffer ...
/* create every variable of the struct */
temp_flight_list[i]->flnum = calloc(1, strlen(ptr)+1);
/* ... */
} else { // allocation failed, throw everything away
for (int j = 0; j < i-1; j++) {
free(temp_flight_list[i]->flnum);
free(temp_flight_list[i]->from);
/* ... */
free(temp_flight_list[i]);
}
free(temp_flight_list);
return 0;
}
i++;
}
// Store new flight list in caller's variable
*flight_list = temp_flight_list;
return i;
}
else return 0;
}
See also
Do I cast the result of malloc?
and
Why is “while (!feof(file))” always wrong?
i have two structs
typedef enum { False = 0, True = 1 } bool;
//defenition of candy structure
typedef struct _Candy {
char candy_name[16];
bool vegan;
}Candy;
typedef struct _Child {
char child_name[16];
Candy *candy_of_child;
}Child;
now im trying to create an array of pointers that each one is Child type
[*Child,*Child...] etc
now i can initialize it i need to do it dynamically
the function that does in is:
int AllocateKidsArray(int NumOfKids, Child** ptr_to_child_arr) {
//=================================================
//"AllocateKidsArray" intializing an array of childrens
//Input: 1. int indicating the number of kids
// 2. pointer to an array of children
//Output: 1. return an int value {0}->Success {-1}->Failure
// 2. pointer to an empty initialized array of childerns
//=================================================
// array of length NumOfKids, consisting of Child pointers
Child **ptr_to_childs = malloc(NumOfKids * sizeof(Child*));
int i;
for (i = 0; i < NumOfKids; i++) {
ptr_to_childs[i] = malloc(sizeof(Child));
strncpy((*ptr_to_childs[i]).child_name, "", 16);
(*ptr_to_childs)[i].candy_of_child = NULL;
}
*ptr_to_child_arr = *ptr_to_childs;
//for (i = 0; i < NumOfKids; i++) {
// free(ptr_to_childs[i]);
//}
//free(ptr_to_childs);
return 0;
}
im calling it from the main in the following manner:
int main(int argc, char** argv) {
//=================================================
if (argc < 3) {
printf("Incorrect number of arguments. Please invoke the program \n\t./program.exe < input.txt> <output.txt> \n");
exit(1);
}
int i, lines, checker = 0;
Candy *test = NULL;
char* name_test = NULL;
char* candy_test = NULL;
char* line = "Tamar,Apple\n";
int* NumLinesFile = NULL;
Child *ArrayOfChild = NULL;
.
.
.
//GetNumLines check
printf("%s\n", argv[0]);
printf("%s\n", argv[1]);
printf("%s\n", argv[2]);
GetNumLines(argv[1], &NumLinesFile);
lines = *NumLinesFile;
*NumLinesFile = NULL;
printf("%d\n", lines);
//=================================================
//AllocateKidsArray check
//AllocateKidsArray(lines, &ArrayOfChild);
AllocateKidsArray(lines, &ArrayOfChild);
//ImportKidsArray check
ImportKidsArray(argv[1], lines, &ArrayOfChild);
for (i = 0; i < lines; i++) {
//ArrayOfChild[i].candy_of_child = (Candy*) malloc(sizeof(Candy*));
printf("%s,%s\n", ArrayOfChild[i].child_name, ArrayOfChild[i].candy_of_child->candy_name);
}
//=================================================
and im statistically get heap/buffer violation
i suspect this function but i dont know what is wrong with it.
after the init of the array i pass it to another function that fills it in:
int ImportKidsArray(char* file_addr, int num_kids, Child** array_of_kids_to_fill) {
//=================================================
//"ImportKidsArray" reads the file and assign each valid line to cell in the array
//Input: 1. string to a location of a file
// 2. int indicating the number of kids
// 3. pointer to an array of children
//Output: 1. return an int value {0}->Success {-1}->Failure
// 2. pointer to an initialized array of childerns
//=================================================
FILE *fp;
char character;
char line[32];
int i = 0, j = 1, checker = 0, arr_count = 0;
char* TempChild = NULL;
char* TempCandy = NULL;
Child *arr = *array_of_kids_to_fill;
fp = fopen(file_addr, "r");
// Check if file exists
if (fp == NULL) {
printf("Could not open file %s", file_addr);
return -1;
}
while (!feof(fp)) {
fgets(line, 32, fp);
checker = ParseLine(line, &TempChild, &TempCandy);
GetCandy(TempCandy, &(arr[arr_count].candy_of_child));
strncpy((arr[arr_count]).child_name, TempChild, 16);
arr_count++;
}
return 0;
}
please if anyone can help, it will save my life :)
You want to change to ArrayOfChild. Passing it's address from main().
Change it by appropriately de-referencing it.
*ptr_to_childs = malloc(NumOfKids * sizeof(Child));
Then do rest of the operation on *ptr_to_childs. That will retain the change that you made in the called function.
Also check the return value of malloc. And free(using free()) the memory dynamically allocated.
If you notice carefully you will see in the ArrayOfChild() function you are working with a local variable Child **ptr_to_childs. You certainly don't want that as that variable will not be alive when the function ends.
Also while (!feof(fp)) is not appropriate to use. Check this link for that.
Another thing is check the return value of char *fgets(char *str, int n, FILE *stream).
On success, the function returns the same str parameter. If the
End-of-File is encountered and no characters have been read, the
contents of str remain unchanged and a NULL is returned.
// Struct for Country Data
typedef struct
{
char name[50]; // Country name
char code[3]; // Country code
int population; // Country Population
double lifeExp; // Country Life expectancy
} CountryData;
// Struct for Dir File
typedef struct
{
char code[3];
int offSet;
} DirData;
// Function Declarations
void fillCountryStructs(CountryData ** dataPtr, int nLines, int fd);
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines,int fd2);
void sortStructs(DirData **director, int nLines);
int verifyString(char *s1, char *s2);
// Main Function
// - This function starts the program, get the number of lines as a
// parameter, fills the structs and writes the data to the Country
// File and the Directory file.
int main(int argc, char *argv[]) // Always remember to pass an argument while executing
{
// Some variables
int nLines; // The number of lines
char *pEnd; // For String functions
FILE *Fin,*Fout; // File pointers
int fd;
int fd2;
nLines = strtod(argv[1], &pEnd);
CountryData **countryDataPtr; // Array of structs
CountryData **tempStruct;
DirData **director;
// Allocate memory for the struct pointers
countryDataPtr = calloc(nLines, sizeof(CountryData*));
director = calloc(nLines, sizeof(DirData*));
// File Stream for "AllCountries.dat"
if((fd = open("AllCountries.dat", O_RDWR)) ==-1)
err_sys("File not found...\n");
// File Stream for "RandomStruct.bin"
if ((fd2 = open("RandomStruct.bin", O_RDWR)) == -1)
err_sys("Failed to open binary\n");
// Filling the Country stucts
fillCountryStructs(countryDataPtr, nLines, fd);
close (fd);
//fclose(Fin); // Closing the file "AllCountries.dat"
// Writing Binary File
write(fd2, (countryDataPtr[0]->name[0]), sizeof(CountryData));
close (fd2);
//fclose(Fout);
printf("RandomStruct.bin written Sucessfully\n");
// Filling the Directory File
// File Stream for "RandomStructDir.dir"
if ((fd2 = open("RandomStructDir.dir",O_RDWR|O_TRUNC)) != -1)
err_sys("Failed to open binary\n");
fillDirectoryStructs(countryDataPtr, director, nLines, fd2);
sortStructs(director, nLines); // Sorting the structs
// Write the number of lines in the FIRST LINE
// of the Directory File
write(fd2, nLines, sizeof(nLines));
// Writing Directory File after the number of lines was written
write(fd2,(director[0]->code[0]), sizeof(DirData));
close (fd2);
//fclose(Fout);
printf("RandomStructDir.dir written Sucessfully\n\n");
exit(0);
}
// Filling the Country structs
// - This function extracts the data from the file using strtok
// and fills all the structs with their corresponding values.
void fillCountryStructs(CountryData **dataPtr, int nLines, int fd)
{
int curLine = 0; // Current line
int index = 0; // The index
char buf[BUFSIZE]; // The Buffer with the size of BUFSIZE
char *tok; // Token
char *pEnd; // For the String functions
char ch = 'a'; // The temp character
int temPop;
double temLifeExp;
int num=0;
for(curLine = 0; curLine < nLines; curLine++)
{
// Reading each line
dataPtr[curLine] = (CountryData *)calloc(1, sizeof(CountryData));
index = 0;
do
{
read(fd, &ch, 1);
buf[index++] = ch;
}
while(ch != '\n');
// Strtoking...
tok = strtok(buf, ",\n");
index = 1;
while(tok != NULL)
{
tok = strtok(NULL, ",\n");
// Get the Country Code
if(index == 1)
{
strcpy(dataPtr[curLine]->code, tok); // Copying code to the struct
}
// Get the Country Name
if(index == 2)
{
strcpy(dataPtr[curLine]->name, tok); // Copying name to the struct
}
// Get the Country Population
if(index == 7)
{
temPop = (int)strtol(tok, &pEnd, 10);
dataPtr[curLine]->population = temPop; // Copying population to the struct
}
// Get the Country Life expectancy
if(index == 8)
{
num=countchar(tok);
printf ("The number of characters entered is %d\n", num);
printf ("The character entered is %s\n",tok);
temLifeExp = strtod(tok, &pEnd);
dataPtr[curLine]->lifeExp = temLifeExp; // Copying life expectancy to the struct
}
index++;
}
}
}
int countchar (char list[])
{
int i, count = 0;
for (i = 0; list[i] != '\0'; i++)
count++;
return (count);
}
// Filling the Directory Structs
// - This function fills the directory with the offset
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines, int fd2)
{
int i = 0;
for(i = 0; i < nLines; i++)
{
strcpy(director[i]->code, dataPtr[i]->code); //It crashes in this Line
director[i]->offSet = sizeof(CountryData) * (i);
}
}
// Sorting the Dir Structs
// - This function sorts the Directory Structs.
void sortStructs(DirData **director, int nLines)
{
int maxNumber;
int i;
DirData **temp;
temp = calloc(1, sizeof(DirData));
// Sorting the array of pointers!
for(maxNumber = nLines - 1; maxNumber > 0; maxNumber--)
{
for(i = 0; i < maxNumber; i++)
{
if((verifyString(director[i]->code, director[i+1]->code)) == 1)
{
temp[0] = director[i];
director[i] = director[i+1];
director[i+1] = temp[0];
}
}
}
}
// Veryfying the strings
// - This function compares two strings and return a specific value
// accordingly.
int verifyString(char *s1, char *s2)
{
int i;
if(strcmp(s1,s2) == 0)
return(0); // They are equal
for(i = 0; s1[i] != 0; i++)
{
if(s1[i] > s2[i])
return(1); // s1 is greater
else if(s1[i] < s2[i])
return(2); // s2 is greater
}
return (2); // s2 is greater
}
So I get segmentation fault and I have no Idea why? maybe is something about the pointers. I specified where it crashes (void fillDirectoryStructs) that method the first line.
When I compile I get :
Countries.c: In function 'main':
Countries.c:68: warning: passing argument 2 of 'write' makes pointer from integer without a cast
Countries.c:84: warning: passing argument 2 of 'write' makes pointer from integer without a cast
Countries.c:86: warning: passing argument 2 of 'write' makes pointer from integer without a cast
Countries.c:232:2: warning: no newline at end of file
I don't know a lot about pointers but I have to use system calls, so I can't use any of the FILE * functions (fwrite(), etc) that is why I'm using plain write() and read().
When I run it I get segmentation fault when It gets to that point I just specified.
for test purposes I'm trying to print this
printf("test: %s\n", countryDataPtr[0]->code[0]);
instead of writing and it crashes there, why? what am I doing wrong? shouldn't that get the code of that first country in my struct? thanks
Well, you need to listen to your compiler and take its warnings seriously.
This:
write(fd2, nLines, sizeof(nLines));
is wrong, and would explain the warning. The variable nLines has type int, but if you look at the [documentation for write()] you can see that the 2nd argument has type void *.
So it will interpret your integer value as a pointer, and start reading memory which you have no right to be reading.
You need:
write(fd2, &nLines, sizeof nLines);
Note that sizeof is not a function, it only needs parenthesis when the argument is a type name (since it then needs a cast expression to the type in question, and casts are writen as a type name enclosed in parenthesis).
Also, you need to be prepared for the reality that I/O can fail. The write() function has a return value which you should be checking.
There are a number of other problems with your code, in addition to the serious one unwind pointed out.
This:
CountryData **countryDataPtr; // Array of structs
is not an Array of structs. Once allocated, it could be an array of pointers to structs.
This:
write(fd2, (countryDataPtr[0]->name[0]), sizeof(CountryData));
does not write one CountryData instance (much less a whole array of them). It takes the integer value of the first character of the first element's name, and treats it as a pointer just like you do with nLines.
If you want to write the first element, it would look like this:
write(fd2, countryDataPtr[0], sizeof(CountryData));
and if you wanted to write all the elements, you'd either need a loop, or a contiguous array of structs you can write in one go.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I seem to be going in circles with this project! I get so many errors of 'dereferencing pointer to incomplete type', and have quite a few others. it seems like when I fix one another one will pop up to take its place!
It's my first time using hash tables, and I admit that I am rather lost but I think I made a very good start at least. Any input as to how to solve my 'dereferencing pointer to incomplete type' problems would be amazing!
htable.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "htable.h"
struct htablerec {
int size;
int num_entries;
hashing_t method;
char **keys;
int *freqs;
int *stats;
};
void *emalloc(size_t s) {
void *result = malloc(s);
if (NULL == result) {
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/*moves pointer to point to something appropriate*/
htable htable_new(int capacity) {
int i;
htable ht = emalloc(sizeof *ht);
ht->size = size;
ht->method = method;
ht->num_keys = 0;
ht->keys = emalloc(size * sizeof ht->keys[0]);
ht->freqs = emalloc(size * sizeof ht->freqs[0]);
ht->stats = emalloc(size * sizeof ht->stats[0]);
for(i = 0; i<size; i++){
ht->keys[i] = NULL;
ht->freqs[i] = 0;
ht->stats[i] = 0;
}
return ht;
}
static unsigned int htable_step(htable h, unsigned int i_key){
return 1 + (i_key % (h->size - 1));
}
static unsigned int htable_wtoi(char *word){
unsigned int result = 0;
while(*word != '\0') result = (*word++ +31 * result);
return result;
}
static unsigned int htable_hash(htable h, unsigned int i_key){
return i_key % h->size;
}
void htable_free(htable h) {
int i;
for(i = 0; i<h->size; i++){
if(h->keys[i] != NULL){
free(h->keys[i]);
}
}
if(h->keys != NULL){
free(h->keys);
}
if(h->freqs != NULL){
free(h->freqs);
}
free(h);
}
static unsigned int htable_word_to_int(char *word) {
unsigned int result = 0;
while (*word != '\0') {
result = (*word++ + 31 * result);
}
return result;
}
int htable_insert(htable h, char *str) {
int num_collisions = 0;
int i_key = htable_wtoi(key);
int pos = htable_hash(h, i_key);
int step = 1;
if(h->method == DOUBLE)
step = htable_step(h, i_key);
while(h->keys[pos]!=NULL &&
strcmp(h->keys[pos],key)!=0 &&
num_collisions < h->size ){
pos = htable_hash(h, pos + step);
num_collisions++;
}
if(h->keys[pos] == NULL){
h->keys[pos] = emalloc((strlen(key)+1) * sizeof h->keys[0][0]);
strcpy(h->keys[pos],key);
h->stats[h->num_keys] = num_collisions;
h->num_keys++;
}
if(num_collisions >= h->size) /* We must be full, so return zero.*/
return 0;
return ++(h->freqs[pos]);
}
static int htable_search(htable h, char *key){
int num_collisions = 0;
int i_key = htable_wtoi(key);
int pos = htable_hash(h, i_key);
int step = 1;
if(h->method == DOUBLE)
step = htable_step(h, i_key);
while(h->keys[pos]!=NULL &&
strcmp(h->keys[pos],key)!=0 &&
num_collisions < h->size ){
pos = htable_hash(h, pos + step);
num_keys++;
}
if(num_keys >= h->size)
return 0;
else
return h->freqs[pos];
}
void htable_print(htable h, FILE *stream){
int i;
for(i = 0; i<h->size; i++){
if(h->keys[i] != NULL)
fprintf(stream, "%d\t%s\n",i, h->freqs[i], h->keys[i]);
}
}
void htable_print_entire_table(htable h, FILE *stream) {
int i;
for (i=0; loop < h->capacity; i++) {
if (h->key[i] != NULL) {
fprintf("%d\t%s\n", h->freqs[i], h->key[i]);
}
}
}
/**
* Prints a line of data indicating the state of the hash table when
* it is a given percentage full.
*
* The data is printed out right justified (with the given field widths,
* and decimal places) in this order:
*
* - How full the hash-table is as a percentage (4)
* - How many keys are in the hash-table at that point (11)
* - What percentage of those keys were placed 'at home' (12, 1 dp)
* - The average number of collisions per key placed (12, 2 dp)
* - The maximum number of collisions while placing a key (12)
*
* #param h the hash-table to get data from.
* #param stream the place to send output to.
* #param percent_full the point at which to print the statistics.
* If the hashtable is less full than that, then
* nothing will be printed.
*/
static void print_stats_line(htable h, FILE *stream, int percent_full) {
int current_entries = h->capacity * percent_full / 100;
double average_collisions = 0.0;
int at_home = 0;
int max_collisions = 0;
int i = 0;
if (current_entries > 0 && current_entries <= h->num_keys) {
for (i = 0; i < current_entries; i++) {
if (h->stats[i] == 0) {
at_home++;
}
if (h->stats[i] > max_collisions) {
max_collisions = h->stats[i];
}
average_collisions += h->stats[i];
}
fprintf(stream, "%4d%11d%12.1f%12.2f%12d\n", percent_full,
current_entries, at_home * 100.0 / current_entries,
average_collisions / current_entries, max_collisions);
}
}
void htable_print_stats(htable ht, FILE *stream, int num_stats) {
int i;
fprintf(stream, "Percent Current Percent Average Maximum\n");
fprintf(stream, " Full Entries At Home Collisions Collisions\n");
fprintf(stream, "-----------------------------------------------------\n");
for (i = 1; i <= num_stats; i++) {
print_stats_line(ht, stream, 100 * i / num_stats);
}
fprintf(stream, "-----------------------------------------------------\n\n");
}
htable.h
#ifndef HTABLE_H
#define HTABLE_H
#include <stdio.h>
typedef struct hashtable *htable;
typedef enum hashing_e { LINEAR, DOUBLE } hashing_t;
extern htable htable_new(int size);
extern void htable_destroy(htable ht);
extern int htable_insert(htable h, char *key);
extern int htable_search(htable h, char *key);
extern void htable_print(htable h, FILE *stream);
extern void htable_print_stats(htable ht, FILE *stream, int num_stats);
#endif
main.c (given by tutor for project to fit)
/**
* #file main.c
* #author Iain Hewson
* #date August 2012
*
* This program is written to test the hash table ADT specified in
* Cosc242 assignment two. It creates a hash table which can use
* linear-probing or double-hashing as a collision resolution
* strategy. Various options are provided which make it possible to
* examine a hash table as well as see how it performs while being
* filled.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "mylib.h"
#include "htable.h"
/* A boolean type which can be TRUE or FALSE */
typedef enum bool_e {FALSE, TRUE} bool_t;
/* function declarations */
static void usage(char *progname);
static void setup(int argc, char **argv, bool_t *double_hashing,
bool_t *entire_table, bool_t *print_stats,
int *snapshots, int *tablesize);
/**
*
* Creates a hash-table and inserts words into it read from stdin.
* Arguments on the command line alter the behaviour of the program
* as follows:
* - -d Use double hashing (linear probing is the default)
* - -e Display entire contents of hash-table on stderr
* - -n NUM Show NUM statistics snapshots (if -p is used)
* - -p Print stats info instead of frequencies & words
* - -s SIZE Use the first prime >= SIZE as htable size
* - -h Display this message
*
* By default each word and it's frequency are printed to stdout.
*
* #param argc the number of command-line arguments.
* #param argv an array of strings containing the command-line arguments.
*
* #return EXIT_SUCCESS if the program is successful.
*/
int main(int argc,char **argv) {
bool_t entire_table = FALSE, double_hashing = FALSE, print_stats = FALSE;
int tablesize = 0, snapshots = 0;
char word[256];
htable ht;
setup(argc, argv, &double_hashing, &entire_table, &print_stats,
&snapshots, &tablesize);
ht = htable_new(tablesize, (double_hashing) ? DOUBLE_H : LINEAR_P);
while (getword(word, sizeof word, stdin) != EOF) {
htable_insert(ht, word);
}
if (entire_table) {
htable_print_entire_table(ht, stderr);
}
if (print_stats) {
htable_print_stats(ht, stdout, snapshots);
} else {
htable_print(ht, stdout); /* print words and frequencies */
}
htable_free(ht);
return EXIT_SUCCESS;
}
/**
* Prints out a usage message to stderr outlining all of the options.
* #param prog_name the name of the program to include in usage message.
*/
static void usage(char *prog_name) {
fprintf(stderr, "Usage: %s [OPTION]... <STDIN>\n\n%s%s", prog_name,
"Perform various operations using a hash-table. By default read\n"
"words from stdin and print them with their frequencies to stdout.\n\n"
" -d Use double hashing (linear probing is the default)\n"
" -e Display entire contents of hash-table on stderr\n",
" -n NUM Show NUM stats snapshots (if -p is used)\n"
" -p Print stats info instead of frequencies & words\n"
" -s SIZE Use the first prime >= SIZE as htable size\n\n"
" -h Display this message\n\n");
}
/**
* Handle options given on the command-line by setting a number of
* variables appropriately. May call usage() if incorrect arguments
* or -h given.
*
* #param argc the number of command-line arguments.
* #param argv an array of strings contain the command-line arguments.
* #param double_hashing set to TRUE if -d given
* #param entire_table set to TRUE if -e given
* #param snapshots set to NUM if -n NUM given and NUM > 0 else set to 10
* #param print_stats set to TRUE if -p given
* #param tablesize set to SIZE if -t SIZE given and SIZE > 0 else set to 113
*/
static void setup(int argc, char **argv, bool_t *double_hashing,
bool_t *entire_table, bool_t *print_stats,
int *snapshots, int *tablesize) {
const char *optstring = "dehpn:s:";
char option;
while ((option = getopt(argc, argv, optstring)) != EOF) {
switch (option) {
case 'd':
*double_hashing = TRUE;
break;
case 'e':
*entire_table = TRUE;
break;
case 'p':
*print_stats = TRUE;
break;
case 'n':
*snapshots = atoi(optarg);
break;
case 's':
*tablesize = atoi(optarg);
break;
case 'h':
default:
usage(argv[0]);
exit(EXIT_SUCCESS);
}
}
/* set default values if nothing sensible entered */
if (*tablesize < 1) *tablesize = 113;
if (*snapshots < 1) *snapshots = 10;
}
mylib.c
#include <stdlib.h>
#include "mylib.h"
/**************************
* *
* emalloc *
* *
**************************
Used to handle new memory allocation to a pointer and handle exceptions
which may arrise if memory allocation fails, which happens all the time
when you try and enter negative values.
PARAMETERS: s = calculated size of required memory.
RETURN VALUE: a pointer of any type.
*/
void *emalloc(size_t s){
void *result = malloc(s);
if(NULL == result){
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/**************************
* *
* erealloc *
* *
**************************
Handles the reallocation of an updated amount of memory to an existing
with existing data attached.
PARAMETERS: p = the existing pointer we would like additional memory
allocated to.
s = calculated size of required memory.
RETURN VALUE: a pointer of any type.
*/
void *erealloc(void *p, size_t s){
void *result = realloc(p,s);
if(NULL == result){
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/**************************
* *
* getword *
* *
**************************
Is used to read input from the designated file stream (standard in for the
assignment). Getword removes white space with the first while loop. And only
returns one word. Maintaining continous input is therefore the responsibility
of the calling function.
PARAMETERS: s = the pointer to the character array.
limit = the maximum size a word can be.
stream = where to read from.
RETURN VALUE: the integer value of the character at s[0]. The string s does
not need to be returned, since it is an array and is passed as
a memory address. If no chars were read into the string s, then
s[0] would have the '\0' [NULL] value which equates to 0 if used
in a boolean equation.
*/
int getword(char *s, int limit, FILE *stream){
int c;
while(!isalnum( c = getc(stream)) && c != EOF);
if(c == EOF)
return EOF;
else
*s++ = tolower(c);
while(--limit > 0){
if(isalnum(c = getc(stream)))
*s++ = tolower(c);
else if('\'' == c) continue;
else break;
}
*s = '\0';
return s[0];
}
/**************************
* *
* stoi *
* *
**************************
Not using this function now.
PARAMETERS: s = string representation of a number.
RETURN VALUE: the integer value.
*/
int stoi(char *s){
int i,j,r;
int sign = 1;
i=1;
r=0;
for(j=my_strlen(s)-1; j>=0; j--){
if(j == 0 && s[j] == '-')
sign = -1;
else {
if(s[j]>='0' && s[j]<='9'){
r+=((s[j]-'0')*i);
i*=10;
} else {
fprintf(stderr, "Invalid input for String to int\n");
exit(EXIT_FAILURE);
}
}
}
return r * sign;
}
/**************************
* *
* my_strlen *
* *
**************************
I am using my own string length function, but I wrote it with the stoi
function, so am using it here. Perhaps not as safe as the library
functions?
PARAMETERS: s = a string delimited by the NULL character.
RETURN VALUE: the number of characters in the string.
*/
int my_strlen(char *s){
int i=0;
while(s[i]!='\0')
i++;
return i;
}
/**************************
* *
* factors *
* *
**************************
Another unrequired function used to calculate the possiblity of factorisation.
PARAMETERS: x = An integer to be factored towards.
RETURN VALUE: 0 if x has factors, 1 if x is a prime number.
*/
static int factors(int x){
int f = 2;
while(f*f < x){
if(x % f == 0){
return 0;
} else {
f++;
}
}
return 1;
}
/**************************
* *
* prime_gt *
* *
**************************
Used in conjunction with factors to find factorless integers. We increment
bound until it is truely prime.
Bound - We start with bound, sending it to the factors function. If it is
a prime number, then stop searching. Otherwise loop until we find
an prime integer larger than the input integer.
PARAMETERS: s = the input integer.
RETURN VALUE: Bound, for it is now a prime number.
*/
static int prime_gt(int s){
int bound = s;
while(bound > 0){
if(factors(bound))
break;
else
bound++;
}
return bound;
}
/**************************
* *
* relative_prime *
* *
**************************
Decides on a prime number to use to set the table size to.
PARAMETERS: s = the required size of the table.
RETURN VALUE: the newer beter prime number size for the table.
*/
unsigned int relative_prime(int s){
return prime_gt(s);
}
Sorry for it being so big, it's ok if it's just a complete unfixable jumble.
You don't seem to have defined struct hashtable anywhere. You need to say somewhere what fields that struct should actually contain, at the moment there is only a forward declaration in htable.h.
Such a forward declaration just says that the type exists, but not how it exactly looks like. Therefore it is considered an incomplete type, until the compiler sees a full definition of it.
Cause the definition of struct hashtable does not exist, the following line creates an alias of an incomplete pointer type (htable).
typedef struct hashtable *htable;
It is impossible to deference such pointer (the compiler doesn't know the different fields of your structure).
In your file htable.c, maybe you meant :
/* instead of struct htablered */
struct htable {
int size;
int num_entries;
hashing_t method;
char **keys;
int *freqs;
int *stats;
};
My c program, written in the visual-studio 2010, is throwing an unhandled win32 exception.
I think it's in a strlen function, based on the debugger output, but I'm not sure.
The file I'm reading in is multiple lines with ; used as a delimiter, and the error seems to happen as I reach the end of the first linked list so presumably in readFile or insertNode.
The first line of the file is something like:
blah division;first department;second department
Any help would be appreciated. I searched through the first few pages of a StackOverflow search on unhandled win32 exceptions, and they seem to relate to access violations or memory overflow problems
#define _CRT_SECURE_NO_WARNINGS 1
#define FLUSH while (getchar () != '\n')
#define DEFAULT "dept.txt"
#define LENGTH 50
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
//Structures
typedef struct DEPT {
char * DeptName;
struct DEPT * link;
} DEPT;
typedef struct {
char divisionName[LENGTH];
DEPT * first;
} DIVISION;
//Function Declarations
int ReadFile (DIVISION DivArr [], int * lastDiv);
FILE * getFileName (void);
DEPT * insertNODE (DEPT * pList, char * string);
int main (void) {
//Local Declarations
//Create the array of the DIVISION Structure
DIVISION DivArr[20];
int i;
int lastDiv;
//Statements
//Read in File
if (ReadFile (DivArr, &lastDiv)) {
return 1;
}
for (i = 0; i < lastDiv; i++) {
printf ("%s\n",DivArr[i].divisionName);
}
return 0;
}
/*==================================ReadFile==================================
Calls getFileName to get the file name to open, then reads the file's data into
DivArr, parsing them appropriately, returning 1 if the file can't be opened */
int ReadFile (DIVISION DivArr [], int * lastDiv){
//Local Declarations
FILE * datafile;
char tempstring[300], *Ptoken;
int linenum = 0;
//Statements
datafile = getFileName();
//return from function with 1 if file can't be opened
//go through file line by line
while (fgets(tempstring, sizeof(tempstring), datafile)) {
//tokenize string
Ptoken = strtok (tempstring , ";");
//first part of string is assigned to divisionName
strncpy(DivArr[linenum].divisionName, Ptoken, LENGTH - 1);
DivArr[linenum].first = NULL;
//subsequent parts are assigned to linked list
while(Ptoken) {
Ptoken = strtok (NULL, ";\n");
DivArr[linenum].first = insertNODE (DivArr[linenum].first, Ptoken);
}
linenum++;
}
*lastDiv = linenum;
fclose(datafile);
return 0;
} //ReadFile
/* =================================getFileName===============================
Gets input from the keyboard and if enter is pressed, returns default, otherwise returns specified filename */
FILE * getFileName (void){
//local declarations
int open = 1;
char read[LENGTH];
FILE * datafile = NULL;
//Statements
//open file
do{
printf ("Enter a filename to open, or press enter for default:");
fgets (read, LENGTH - 1, stdin);
if ('\n' == read[0]) {
strncpy (read , DEFAULT, LENGTH - 1);
}
else
read[strlen(read) - 1] = '\0';
if((datafile = fopen(read, "r")) == NULL)
printf ("Error opening %s\n", read);
else
open = 0;
} while (open == 1);
return datafile;
} //getFileName
/* =================================insertNODE================================
Gets the address of the beginning of the list for the structure, then
allocates memory for nodes, then allocates memory for string, then passes
string to allocated memory, then links node
*/
DEPT * insertNODE (DEPT * pList, char * string)
{
//Local Declarations
DEPT * pNew;
DEPT * pWalker = pList;
DEPT * pPre;
//Statements
if ( !(pNew = (DEPT*)malloc(sizeof(DEPT))))
printf ("\nMemory overflow in insert\n"),
exit (100);
printf ("size of string + null = %d\n",strlen(string) + 1);
if(!(pNew->DeptName =(char*)calloc(strlen(string) + 1, sizeof(char))))
{
printf ("\nMemory overflow in string creation\n");
exit (100);
}
strncpy(pNew->DeptName, string, strlen(string));
printf("%s is %d long", pNew->DeptName, strlen(pNew->DeptName));
if (pWalker == NULL) //first node in list
{
pNew->link = pList;
pList = pNew;
}
else {
while (pWalker){
pPre = pWalker;
pWalker = pWalker->link;
}
pPre->link = pNew;
pNew->link = NULL;
}
return pList;
}
This loop might be the reason of your error:
//subsequent parts are assigned to linked list
while(Ptoken) {
Ptoken = strtok (NULL, ";\n");
DivArr[linenum].first = insertNODE (DivArr[linenum].first, Ptoken);
}
What happens when strtok returns NULL? Add a check for that between strtok and insertNODE.