Reading through lines of a txt file containing words - c

Currently in school, I am working on a game, arcade-like, project. I have this idea to put a random name on the level each time we proceed to the next one instead of just "Level 1", "Level 2", etc...
So I was wondering how could I do that using fscanf or fgets or something like that? I got my text file containing 70 lines with different names.
Thanks already :D

If it's just one name per line, then use fgets().
/******************************************************************************************
* This function reads a file line by line putting each line as a C-sting in an array
******************************************************************************************/
void read_names(char **names_table, int *n_names)
{
FILE *fp = fopen(<filename>, "r");
int i = 0;
for(i = 0; i < MAX_N_NAMES; ++i){
char *name = malloc(MAX_NAME_LENGTH);
fgets(name, MAX_NAME_LENGTH, fp);
if(name == NULL)
break;
name_table[i] = name;
}
*n_names = i; // There's a gotcha here
}
This way, you would call your function once and I leave it to you to decide where your array should live (where in memory, who owns and manages it). Then I would, using rand() % n_names to select a random index of that array for the level.
Somewhere in your program, you should have
enum NameParams { MAX_N_NAMES = 80, MAX_NAME_LENGTH = 20 };
(I like this better than #defines because the debugger will show you the names of the constants.)
This should get you started, one thing you might want to do is mark the names that you have used so as to not use them for two levels. I would do this with another array of ints (C doesn't have bools) where you mark a level as used. This has the problem that you'll have to do rand() % n_names until you find an unused name.
Or you could shuffle your name_table once and use name_table[i] for level i.
void swap_indices(char **array, int i, int j)
{
char *tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
void shuffle(char **array, int length)
{
// Do a bunch of swaps, depending on how much you care about randomness,
// you might want to look up shuffling algorithms or see if some already
// exist that you could use, but if you don't care that much, just
// do a bunch of swaps.
}
Finally, I would suggest a standalone C-file (a module) that would manage a struct like this as a global variable. Your .h file could look like this (you include it in other files and it will give you access to the public interface of your module).
#ifndef _LEVEL_NAMES_H_
#define _LEVEL_NAMES_H_
enum NameErrorCodes { ERR_NONE = 0, ERR_FILE, ERR_OTHER};
int names_init(char *filename);
char *get_name();
#endif
Then your c-file could look like this:
#include "LevelNames.h"
struct LevelNames{
int n_names;
char * names[MAX_N_NAMES];
};
static struct LevelNames level_names; // static makes this global variable
// accessible only from this file
// It does the same thing for functions
static int read_names(char *filename);
static void swap_indices(char **array, int i, int j);
static void shuffle_names();
/*******************************************************************************
*
*******************************************************************************/
int names_init(char *filename)
{
int err = 0;
err = read_names(filename);
if( err != ERR_NONE )
return err;
shuffle_names();
}
/*******************************************************************************
*
*******************************************************************************/
char *get_name(int level)
{
if(level >= level_names.n_names || level < 0)
return NULL;
return level_names.names[i];
}
/*******************************************************************************
*
*******************************************************************************/
static int read_names(char *filename)
{
FILE *fp = fopen(<filename>, "r");
if( fp == NULL )
return ERR_FILE;
int i;
for(i = 0; i < MAX_N_NAMES; ++i){
char *name = malloc(MAX_NAME_LENGTH);
fgets(name, MAX_NAME_LENGTH, fp);
if(name == NULL)
break;
level_names.names[i] = name;
}
level_names. = i; // There's a gotcha here
return ERR_NONE;
}
/*******************************************************************************
*
*******************************************************************************/
static void swap_indices(char **array, int i, int j)
{
...
}
/*******************************************************************************
*
*******************************************************************************/
static void shuffle_names()
{
...
}
That's what I would do. And don't forget about that gotcha in read_names.
I want to end with a discussion of global variables. You may have been told in school, as I was, that you should never use global variables. There are many reasons for this. They are good reasons and you should listen to people who say this to you.
However, I have a module whose sole purpose is to manage an array of names. Therefore it makes sense that THIS MODULE has a global variable. Also, it is crucial that this variable be static. This way, clients of your module don't know about your global variable (which they shouldn't).
Many modules of C's standard library have a static global variable that they manage. If you want to use a global variable because you don't know how something should be passed from function to function, then figure it out and don't use a global variable. If you want to imitate the behavior of a static class in C++ or Java, then it's legit to do it this way.

Related

C Programming: Reading data from a file, dynamically allocating memory, placing contents in struct array

This is my very first post on stackoverflow. I am a CS student learning C, and I am having some issues with the problem I'm working on. Additionally, I should mention that I know very little, so if anything I put here comes off as foolish or ignorant, it is absolutely not my intention
I am aware that there are other posts similar to this one, however so far I feel that I have tried making a lot of amendments that all end with the same result.
I am given a text file in which each line contains studentName(tab)gpa. The total size of the file is unknown, this I must use dynamic memory allocation.
Example of text file format
Jordan 4.0
Bhupesh 2.51
General steps for program
Many details will be left out to save myself from embarrassment, however I will give a high-level overview of the process I am struggling with:
1.) Create dynamic memory array to hold struct for each line
2.) Start looping through file
3.) check the current size of the array to see if reallocation is necessary
4.) Create dynamic array to hold name
5.) Place name and gpa into struct
6.) rinse & repeat
Finally, one last thing. The error occurs when my initial allocated memory limit is reached and the program attempts to reallocate more memory from the heap.
Screenshot of error being thrown in clion debugger
My code is shown below:
#define EXIT_CODE_FAIL 1
#define ROW_COUNT 10
#define BUFFER_SIZE 255
#define VALID_ARG_COUNT 2
struct Student {
float gpa;
char * name;
};
// read the file, pack contents into struct array
struct Student * readFileContents(char *filename, int *rowCounter) {
// setup for loop
int maxDataSize = ROW_COUNT;
float currentStudentGpa = 0;
char studentNameBuffer[BUFFER_SIZE];
// initial structArray pre-loop
struct Student * structArray = calloc(maxDataSize, sizeof(*structArray));
FILE *pFile = fopen(filename, "r");
validateOpenFile(pFile);
// loop through, get contents, of eaach line, place them in struct
while (fscanf(pFile, "%s\t%f", studentNameBuffer, &currentStudentGpa) > 0) {
structArray = checkArraySizeIncrease(*rowCounter, &maxDataSize, &structArray);
structArray->name = trimStringFromBuffer(studentNameBuffer);
structArray->gpa = currentStudentGpa;
(*rowCounter)++, structArray++;
}
fclose(pFile);
return structArray;
}
// resize array if needed
struct Student * checkArraySizeIncrease(int rowCount, int * maxDataSize, struct Student ** structArray) {
if (rowCount == *maxDataSize) {
*maxDataSize += ROW_COUNT;
**// line below is where the error occurs**
struct Student * newStructArray = realloc(*structArray, *maxDataSize * sizeof(*newStructArray));
validateMalloc(newStructArray);
return newStructArray;
}
return *structArray;
}
// resize string from initial data buffer
char *trimStringFromBuffer(char *dataBuffer) {
char *string = (char *) calloc(strlen(dataBuffer), sizeof(char));
validateMalloc(string);
strcpy(string, dataBuffer);
return string;
}
Once again, I apologize if similar questions have been asked, but please know I have tried most of the recommendations that I have found on stack overflow with no success (of which I'm well aware is the result of my poor programming skill level in C).
I will now promptly prepare myself for my obligatory "first post on stackoverflow" roasting. Cheers!
You are reusing structArray as both the base of the array and a pointer to the current element. This won't work. We need two variables.
There are a number of "loose" variables related to the dynamic array. It's cleaner to define a struct (e.g. dynarr_t below) to contain them and pass just the struct pointer around.
When you're duplicating the string, you must allocate strlen + 1 [not just strlen]. But, the entire function does what strdup already does.
I tried to save as much as possible, but I've had to refactor the code a fair bit to incorporate all the necessary changes.
By passing sizeof(*structArray) to the arrnew function, this allows the struct to be used for arbitrary size array elements.
Anyway, here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
#define EXIT_CODE_FAIL 1
#define ROW_COUNT 10
#define BUFFER_SIZE 255
#define VALID_ARG_COUNT 2
struct Student {
float gpa;
char *name;
};
// general dynamic array control
typedef struct {
void *base; // base address
size_t size; // bytes in array element
size_t count; // current number of used entries
size_t max; // maximum number of entries
size_t grow; // number of entries to grow
} dynarr_t;
// arrfind -- return pointer to array element
void *
arrfind(dynarr_t *arr,size_t idx)
{
void *ptr;
ptr = arr->base;
idx *= arr->size;
ptr += idx;
return ptr;
}
// arrnew -- create new array control
dynarr_t *
arrnew(size_t siz,size_t grow)
// siz -- sizeof of array element
// grow -- number of elements to grow
{
dynarr_t *arr;
arr = calloc(1,sizeof(*arr));
if (arr == NULL)
sysfault("arrnew: calloc fail -- %s\n",strerror(errno));
arr->size = siz;
arr->grow = grow;
return arr;
}
// arrgrow -- grow array [if necessary]
// RETURNS: pointer to element to fill
void *
arrgrow(dynarr_t *arr)
{
void *ptr;
// grow array if necessary
// NOTE: use of a separate "max" from "count" reduces the number of realloc
// calls
if (arr->count >= arr->max) {
arr->max += arr->grow;
arr->base = realloc(arr->base,arr->size * arr->max);
if (arr->base == NULL)
sysfault("arrgrow: realloc failure -- %s\n",strerror(errno));
}
// point to current element
ptr = arrfind(arr,arr->count);
// advance count of elements
++arr->count;
return ptr;
}
// arrtrim -- trim array to actual number of elements used
void
arrtrim(dynarr_t *arr)
{
arr->base = realloc(arr->base,arr->size * arr->count);
if (arr->base == NULL)
sysfault("arrtrim: realloc failure -- %s\n",strerror(errno));
arr->max = arr->count;
}
void
validateMalloc(void *ptr)
{
if (ptr == NULL) {
perror("validateMalloc");
exit(1);
}
}
void
validateOpenFile(FILE *ptr)
{
if (ptr == NULL) {
perror("validateOpenFile");
exit(1);
}
}
// resize string from initial data buffer
char *
trimStringFromBuffer(char *dataBuffer)
{
#if 0
#if 0
char *string = calloc(1,strlen(dataBuffer));
#else
char *string = calloc(1,strlen(dataBuffer) + 1);
#endif
validateMalloc(string);
strcpy(string, dataBuffer);
#else
char *string = strdup(dataBuffer);
validateMalloc(string);
#endif
return string;
}
// read the file, pack contents into struct array
dynarr_t *
readFileContents(char *filename)
{
dynarr_t *arr;
// setup for loop
float currentStudentGpa = 0;
char studentNameBuffer[BUFFER_SIZE];
struct Student *structArray;
arr = arrnew(sizeof(*structArray),10);
FILE *pFile = fopen(filename, "r");
validateOpenFile(pFile);
// loop through, get contents, of eaach line, place them in struct
while (fscanf(pFile, "%s\t%f", studentNameBuffer, &currentStudentGpa) > 0) {
structArray = arrgrow(arr);
structArray->name = trimStringFromBuffer(studentNameBuffer);
structArray->gpa = currentStudentGpa;
}
fclose(pFile);
arrtrim(arr);
return arr;
}
I think your issue is with the calculation of the size of the realloc. Rather than using sizeof(*newStructArray), shouldn't you really be using the size of your pointer type? I would have written this as realloc(*structArray, *maxDataSize * sizeof(struct Student *))
There's a lot of other stuff in here I would never do - passing all those variables in to checkArraySizeIncrease as pointers is generally a bad idea because it can mask the fact that things are getting changed, for instance.
There is an issue in allocation of the buffer for string
char *string = (char *) calloc(strlen(dataBuffer), sizeof(char));
it should be:
char *string = (char *) calloc(1 + strlen(dataBuffer), sizeof(char));
as C-strings require extra 0-byte at the end.
Without it, the following operation:
strcpy(string, dataBuffer);
may damage data after the buffer, likely messing malloc() metadata.

Global Array of Structs, declare size in program and use globally without passing?

Just for context, I am making a cache simulator as a C project. I need to declare a global double array of a struct, set the size of this global struct array in the program, and then use that global array in the project later on.
Here is a shortened up version of what I have:
// Declare globals
int mapping = 0;
int offset = 0;
int index = 0;
char* allocation;
char* writePolicy;
struct cache {
int validBit;
int dirtyBit;
unsigned int tag;
unsigned int nextToReplace;
};
void analyzeParameters(char* line, int lineNum) {
switch(lineNum) {
case 1:
mapping = atoi(line);
case 2:
offset = atoi(line);
case 3:
index = atoi(line);
case 4:
allocation = malloc(4);
strcpy(allocation, line);
case 5:
writePolicy = malloc(4);
strcpy(writePolicy, line);
}
}
setupCache() {
int numSets = 1 << index;
struct cache cache[mapping][numSets];
printf("Declared cache struct with size %d ", numSets);
printf("num of ways %d\n", mapping);
// initialize bits in cache to 0
int j;
int i;
for (j = 0; j < mapping; j++) {
for (i = 0; i < numSets; i++) {
cache[j][i].validBit = 0;
cache[j][i].dirtyBit = 0;
}
}
}
void hitChecker() {
for (d = 0; d < mapping; d++) {
if (cache[d][index].validBit == 1) {
if (cache[d][index].tag == tag) {
//found a hit
if (type == "r") {
// hit with a read instruction.
rhits++;
}
else if (type == "w") {
// hit with a write instruction
whits++;
}
}
else {
// tag in cache index is not equal to tag being checked
if (type == "r") {
// missed with a read instruction.
rmisses++;
}
else if (type == "w") {
// missed with a write instruction
wmisses++;
}
}
}
else {
//cache is not valid
printf("Cache has not been validated");
}
}
void main(int argc, char**argv) {
analyzeParameters(passInEachLineOfFile, this works not important);
setupCache();
hitChecker();
}
This works up until I try to utilize the cache struct. I declare it globally, set the size in setUpCache, and then in another method I want to use that double array declared globally. Is there a way I can use it globally or do I have to pass the struct around via method parameters?
To have a global cache structure with run-time determined sizes, use:
int mapping, numsets;
struct cache **cache;
and:
void init(int nmaps, int nsets)
{
cache=malloc(nmaps*sizeof(struct cache *));
for (int i=0;i<nmaps;i++)
cache[i]=malloc(nsets*sizeof(struct cache));
mapping= nmaps;
numsets= nsets;
}
If you want to use cache as a global variable, declare it in the global scope, and before using it.
struct cache {
int validBit;
int dirtyBit;
unsigned int tag;
unsigned int nextToReplace;
};
struct cache cache[mapping][numSets];
It's not a good idea to put a variable in the global scope if you can avoid it.
Here, to "encapsulate" the caching feature, I suggest to use rather a static variable in a separate file with all cache-related functions, to let it within a "file" scope. Since mapping, offset and index are arguments from the outside of your caching functions, pass them as parameters.
// cache-lib.c
// Declare the struct in the c file, it's only needed here
struct cache {
int validBit;
int dirtyBit;
unsigned int tag;
unsigned int nextToReplace;
};
static struct cache cache[mapping][numSets];
// Stores below variable here, in the "file" scope.
static mapping, offset, index;
// Below cache functions. Declare them an includable header.
/// setupCache() definition. Since it needs mapping, offset and index which
/// are retrieved from the outside, you can pass them as parameter.
void setupCache(int mapping, int offset, int index) {
//// [...]
}
/// hitChecker. Maybe you need here to store extra variables
/// as "file-scope" ones like index and mapping
void hitChecker() {
//// [...]
}
Finally, the header you would use everywhere you need your cache lib:
// cache-lib.h
void setupCache(int mapping, int offset, int index);
void hitChecker();
You may also include it in your cache-lib.c to not bother with issues related to the order of functions declaration.

c Struct Array, Storing string and its occurrence and writing it to a file

so I'm having a little problem with my struct array not doing what its supposed to. I get no compiler warnings or errors when building the program.
int Array_Size=0;;
int Array_Index=0;
FILE *Writer;
struct WordElement
{
int Count;
char Word[50];
};
struct WordElement *StructPointer; //just a pointer to a structure
int Create_Array(int Size){
StructPointer = (struct WordElement *) malloc(Size * sizeof(StructPointer));
Array_Size = Size;
return 0;
}
int Add_To_Array(char Word[50]){
int Word_Found=0;
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
{
if(strcmp(StructPointer[i].Word, Word)) // This should only run if the word exists in struct array
{
StructPointer[i].Count++;
Word_Found=1;
}
}
if(Word_Found==0) // if the above if statement doesnt evualate, this should run
{
strcpy(StructPointer[Array_Index].Word, Word); //copying the word passed by the main function to the struct array at a specific index
printf("WORD: %s\n", StructPointer[Array_Index].Word); // printing it just to make sure it got copied correctly
Array_Index++;
}
return 0;
}
int Print_All(char File_Name[50])
{
Writer = fopen(File_Name, "w");
printf("Printing starts now: \n");
for(int i=0; i < Array_Size; i++)
{
fprintf(Writer, "%s\t\t%d\n",StructPointer[i].Word, StructPointer[i].Count);
}
free(StructPointer);
return 0;
}
These functions get called from a different file, The Add_To_Array is called when the program reads a new word form the text file. That function is supposed to check if the word already exists in the struct array and if it does, it should just increment the counter. If it doesn't, then it adds it.
The Print_All function is called after all the words have been stored in the struct array. Its supposed to loop through them and print each word and their occurrence. In the text file, there are 2 of every words but my program outputs:
this 13762753
document -1772785369
contains 1129268256
two 6619253
of 5701679
every 5570645
word 3342389
doccontains 5374021
I don't know what to make of this as im really new to C programming... It's probably worth mentioning the if(Word_Foun==0) doesn't execute
StructPointer = malloc(Size * sizeof(*StructPointer));
This will be the correct allocation. Otherwise you will have erroneous behavior in your code. Also check the return value of malloc.
StructPointer = malloc(Size * sizeof(*StructPointer));
if(NULL == StructPointer){
perror("malloc failure");
exit(EXIT_FAILURE);
}
You are allocating for struct WordElement not a for a pointer to it. You already have a pointer to struct WordElement all that you needed was memory for a struct WordElement.
Also in the loop you are accessing array index out of bound
for(int i=0; i <= Array_Size && Word_Found!=1; i++)
^^^
It will be i < Array_Size.
In case match occurs you want to set the variable Word_found to 1.
if(strcmp(StructPointer[i].Word, Word) == 0){
/* it macthed */
}
Also Writer = fopen(File_Name, "w"); you should check the return value of fopen.
if(Writer == NULL){
fprintf(stderr,"Error in file opening");
exit(EXIT_FAILURE);
}
Also when you are increasing the Array_index place a check whether it might access the array index out of bound.
The more global variable you use for achieving a small task would make it more difficult to track down a bug. It is always problematic because the places from which data might change is scattered - making it difficult to manage.

Can a char * or char ** masquerade as a FILE *?

In C, I often want to handle data read from a file and data read from an array of strings the same way. Usually reading from a file is for production and from strings is for testing. I wind up writing a lot of code like this:
void handle_line(char *line, Things *things) {
...
}
Things *read_from_chars(char *lines[]) {
Things *things = Things_new();
for (int i = 0; lines[i] != NULL; i++) {
handle_line(lines[i], things);
}
return things;
}
Things *read_from_input(FILE *input) {
char *line = NULL;
size_t linelen = 0;
Things *things = Things_new();
while (getline(&line, &linelen, input) > 0) {
handle_line(line, things);
}
return things;
}
This is a duplication of effort.
Is there a way I can make an array of strings masquerade as a FILE * pointer? Or vice-versa? Or is there a better pattern for dealing with this problem?
For bonus points: the solution should make char * or char ** usable with the standard file functions like fgets and getline.
You could use a discriminated union that contains a FILE* and a pointer to the array, then write a get_next function that does the right thing with it.
typedef struct {
enum { is_file, is_array } type;
union {
FILE *file;
struct {
int index;
int size;
char **lines;
} array;
} data;
} file_or_array;
char *get_next(file_or_array foa) {
if (foa.type == is_file) {
char *line = NULL;
size_t linelen = 0;
getline(&line, &linelen, foa.data.file);
return line;
} else {
if (foa.data.array.index < foa.data.array.size) {
return strdup(foa.data.array.lines[foa.data.array.index++]);
} else {
return NULL;
}
}
}
The call to strdup() is necessary to make this work consistently. Since getline() returns a newly-allocated string, which the caller needs to free, it also does the same thing when returning a string from the array. Then the caller can safely free it in both cases.
There's a nonstandard function fmemopen that lets you open a char[] for reading or writing. It's available in most versions of GNU libc, I think, and most versions of Linux.
(This lets you read from or write to a single string, not the array of strings you asked about.)
One of the most powerful ways to handle this is via streams. I use them to hide file/string/serial ports etc
I have rolled my own stream library which I mainly use on embedded systems
the general idea is :-
typedef struct stream_s stream_t;
struct stream_s
{
BOOL (*write_n)(stream_t* stream, char* s, WORD n);
BOOL (*write_byte)(stream_t* stream, BYTE b);
BOOL (*can_write)(stream_t* stream);
BOOL (*can_read)(stream_t* stream);
BYTE (*read_byte)(stream_t* stream);
void* context;
};
then you make a whole bunch of functions
BOOL stream_create(stream_t* stream);
BOOL stream_write_n(stream_t* stream, char* s, WORD n);
BOOL stream_can_read(stream_t* stream);
BYTE stream_read_byte(stream_t* stream);
etc
that use those base function call backs.
the context in the stream struct you use to point to a struct for serial, string, file, or whatever you want. Then you have things like file_create_stream(stream_t* stream, char* filename) which will populate the callbacks on stream with the file related functions. Then for strings you have something similar but handles strings
There's more than one way to skin this particular cat, but in general the solution to this is hiding the implementation of the public interface behind an indirection which allows you to inject separate 'implementations'.
(This incarnation of your problem is also closely related to somewhat different problem of ensuring ABI compatibility between versions of code.)
To solve this in C you can do it similar to the pimpl with-inheritance in C++ (protected instead of private d-pointer, with overridden protected constructors):
You create an opaque 'reader'/'stream' object (pointer to forward declared struct w/ typedef in C) and suitably named constructor functions to instantiate the opaque object which inject the desired implementation.
Let's sketch out example header files to give you an idea of how the functions fit together. Let's start with the guts, the definition of the d-pointer/p-impl objects (N.B.: I'm omitting some boilerplate like header guards):
reader-private.h:
/* probably should be in its proper C file, but here for clarification */
struct FileReaderPrivateData {
FILE * fp;
};
/* probably should be in its proper C file, but here for clarification */
struct StringReaderPrivateData {
size_t nlines;
size_t cursor;
char ** lines;
};
/* in C we don't have inheritance, but we can 'fix' it using callbacks */
struct ReaderPrivate {
int (* close)(void* pData); /* impl callback */
ssize_t (* readLine)(void* pData, char** into); /* impl callback */
/* impl-specific data object, callbacks can type cast safely */
void * data;
};
/* works like a plain p-impl/d-pointer, delegates to the callbacks */
struct Reader {
struct ReaderPrivate * dPtr;
}
reader.h:
typedef struct Reader* Reader;
/* N.B.: buf would be a pointer to set to a newly allocated line buffer. */
ssize_t readLine(Reader r, char ** buf);
int close(Reader r);
file-reader.h
#include "reader.h"
Reader createFileReader(FILE * fp);
Reader createFileReader(const char* path);
string-reader.h
#include "reader.h"
Reader createStringReader(const char**, size_t nlines);
That's a general pattern for doing pimpl/d-pointer with inheritance in C, so you can abstract the implementation guts behind a public interface which is accessed through opaque pointers. This mechanism is generally useful to guarantee API and ABI compatibility between various implementations of the public interface and to implement a simple inheritance pattern.
Here's an implementation using fcookieopen [IIRC, BSD has something similar]:
// control for string list
struct cookie {
char **cook_list; // list of strings
int cook_maxcount; // maximum number of strings
int cook_curidx; // current index into cook_list
int cook_curoff; // current offset within item
};
int cookie_close(void *vp);
ssize_t cookie_read(void *vp,char *buf,size_t size);
cookie_io_functions_t cook_funcs = {
.read = cookie_open;
.close = cookie_close;
};
// cookie_open -- open stream
FILE *
cookie_open(char **strlist,int count,const char *mode)
// strlist -- list of strings
// count -- number of elements in strlist
// mode -- file open mode
{
cookie *cook;
FILE *stream;
cook = calloc(1,sizeof(cookie));
cook->cook_list = strlist;
cook->cook_maxcount = count;
stream = fopencookie(cook,mode,&cook_funcs);
return stream;
}
// cookie_close -- close stream
int
cookie_close(void *vp)
{
free(vp);
return 0;
}
// cookie_read -- read stream
ssize_t
cookie_read(void *vp,char *buf,size_t size)
{
cookie *cook = vp;
char *base;
ssize_t totcnt;
totcnt = 0;
while (size > 0) {
// bug out if all strings exhausted
if (cook->cook_curidx >= cook->cook_maxcount)
break;
base = cook->cook_list[cook->cook_curidx];
base += cook->cook_curoff;
// if at end of current string, start on the next one
if (*base == 0) {
cook->cook_curidx += 1;
cook->cook_curoff = 0;
continue;
}
// store character and bump buffer and count
*buf++ = *base;
size -= 1;
totcnt += 1;
cook->cook_curoff += 1;
}
return totcnt;
}
If you need this functionality just for debugging, write a fopen_strings(char *list[]) function to:
create a temporary file
open that with fopen with mode "r+"
write all your strings into it
delete the file (the FILE* can still operate on it, until it is closed either explicitly or implicitly at program end. You might need to skip this step on some operating systems that prevent deletion of open files.
rewind the stream
return the stream and let your program use it as it would a regular file.
is there a better pattern for dealing with this problem?
My proposed solution is to do function overloading.
Provide all possible parameters:
Things* readThings(FILE *f, char *l[])
{
char *line = NULL;
size_t linelen = 0;
Things *things = Things_new();
if (f)
{
while(getline(&line, &linelen, input) > 0)
handle_line(line, things);
}
else
{
for(int i = 0; lines[i] != NULL; i++)
handle_line(lines[i], things);
}
return things;
}
Things* readThingsChar(char *l[]){ return readThings(0, l); }
Things* readThingsFile(FILE *f){ return readThings(f, 0); }
How to use
FILE *f;
char *l[100];
..
Things *a = readThings(f,0); // or readThingsFile(f)
Things *b = readThings(0,l); // or readThingsChar(l)
You could embed it in the data:
Things* readThings(char *l[])
{
char *line = NULL;
size_t linelen = 0;
Things *things = Things_new();
FILE *f = NULL;
if (l[0][0]==UNIQUE_IDENTIFIER)
{
f = fopen(l[0]+1);
while(getline(&line, &linelen, input) > 0)
handle_line(line, things);
fclose(f);
}
else
{
for(int i = 0; lines[i] != NULL; i++)
handle_line(lines[i], things);
}
return things;
}
How to use
char *f[1] = { "_file.txt" };
char *l[100] = { "first line", .. "last line" };
f[0][0] = UNIQUE_IDENTIFIER;
Things *a = readThings(f);
Things *b = readThings(l);

Struct member corrupted after passed but not after passed again

I'm having some very strange bug in my ANSI C program.
I'm using debugger and I've observed that 'size' variable is corrupted in function 'doSthing.' Outside of 'doSthing' 'size' got a proper value, but inside 'doSthing' I've got a value nothing similar to what it should be, possibly some random data. This would be not be such a mystery but...
In 'doAnotherThing' which is called from 'doSthing' I get the proper value again. I suppose if it passes the correct value, it is not corrupted anyway, am I wrong? But then why does it have a different value?
The pointer in struct does not change inside the functions.
Memory is allocated for both oTV and oTV->oT.
I really don't see what's happening here...
typedef struct{
ownType *oT[] /* array of pointers */
int size;
} ownTypeVector;
void doSthing(ownTypeVector* oTV);
void doAnotherThing(ownTypeVector* oTV);
void doSthing(ownTypeVector* oTV)
{
...
doAnotherThing(oTV);
...
}
Thanks for your comments, I collected all the code that contains control logic and data structures so that it compiles. It runs on in an embedded systems, that can receive characters from multiple sources, builds strings from it by given rules and after the strings are ready, calls a function that needs that string. This can also be a list of functions. This is why I have function pointers - I can use the same logic for a bunch of things simply by choosing functions outside the 'activityFromCharacters' function.
Here I build a data structre with them by adding A-s, B-s and C-s to the AVector.
Of course every one of these separate sources has their own static strings so that they do not bother each other.
The problem again in the more detailed version of the code:
'aV->size' has got a proper value everywhere, except 'handleCaGivenWay.' Before it gets calles, 'aV->size' is ok, in 'addA' 'aV->size' is ok, too. After leaving 'handleCaGivenWay' it is ok again.
#define NUMBER_OF_AS 1
#define NUMBER_OF_BS 5
#define NUMBER_OF_CS 10
typedef struct{
char name[81];
} C;
typedef struct{
C *c[NUMBER_OF_CS]; /* array of pointers */
int size;
int index;
} B;
typedef struct{
B *b[NUMBER_OF_BS]; /* array of pointers */
char name[81];
int size;
} A;
typedef struct{
A *a[NUMBER_OF_AS]; /* array of pointers */
int size;
} AVector;
typedef struct {
char *string1;
char *string2;
} stringBundle;
typedef struct{
void (*getCharacter)(char *buffer);
void (*doSthingwithC)(stringBundle* strings,AVector* aV);
AVector* aV;
} functionBundle;
void getCharFromaGivenPort(char *buffer)
{
//...
}
void addA(AVector * aV, stringBundle* strings)
{
aV->a[aV->size]->size = 0;
++aV->size;
int i = 0;
if(strlen(strings->string2) < 81)
{
for(i;i<81;++i)
{
aV->a[aV->size-1]->name[i] = strings->string2[i];
}
}
else {report("Too long name for A:");
report(strings->string2);}
}
void handleCaGivenWay(stringBundle* strings,AVector* aV)
{
A* a;
a = NULL;
if(aV->size) { a = aV->a[aV->size-1]; }
switch(1)
{
case 1: addA(aV,strings); break;
case 2: //addB()...
default: if (a && aV->size)
{ //addC(a->thr[a->size-1],c);
}
else report("A or B or C invalid");
break;
}
//handleCaGivenWay
}
void activityFromCharacters(stringBundle* strings,functionBundle* funcbundle)
{
/* some logic making strings from characters by */
/* looking at certain tokens */
(* funcbundle->doSthingwithC)(strings,funcbundle->aV);
}
//activityFromCharacters
AVector* initializeAVector(void)
{
AVector* aV;
if (NULL == (aV = calloc(1,sizeof(AVector))))
{ report("Cannot allocate memory for aVector."); }
int i = 0;
int j = 0;
int k = 0;
for(i; i < NUMBER_OF_AS; ++i)
{
if (NULL == (aV->a[i] = calloc(1,sizeof(A))))
{ report("Cannot allocate memory for As."); }
aV->a[i]->size = 0;
aV->a[i]->name[0] = 0;
for(j; j < NUMBER_OF_BS; ++j)
{
if (NULL == (aV->a[i]->b[j] = calloc(1,sizeof(B))))
{ report("Cannot allocate memory for Bs."); }
aV->a[i]->b[j]->size = 0;
for(k; k < NUMBER_OF_CS; ++k)
{
if (NULL == (aV->a[i]->b[j]->c[k] = calloc(1,sizeof(C))))
{ report("Cannot allocate memory for Cs."); }
}
}
}
aV->size = 0;
return aV;
//initializeProgramVector
}
int main (void)
{
AVector* aV;
aV = initializeAVector();
while(1)
{
static stringBundle string;
static char str1[81];
static char str2[81];
string.string1 = str1;
string.string2 = str2;
functionBundle funcbundle;
funcbundle.getCharacter = &getCharFromaGivenPort;
funcbundle.doSthingwithC = &handleCaGivenWay;
funcbundle.aV = aV;
activityFromCharacters(&string,&funcbundle);
}
//main
}
your code shows that it hasn't any error...
But i think you are doing mistake in getting the value of size in doSthing function.
you are printing there its address. so concentrate on some pointer stuff..
Try printing the oTV->size just before the call and as the first statement in doSthing function. If you get the correct value in both print, then the problem is with the function doSthing. Problem could be better understood if you've shown the code that calls doSthing.
Searched a long time to find this. I found 2 problems, but dont know what exactly you are trying to accomplish so i cannot tell for certain that the fix'es i propose are what you intend.
typedef struct{
A *a[NUMBER_OF_AS]; /* array of pointers */
int size;
} AVector;
// and in addA():
aV->a[aV->size]->size = 0;
First: You are inlining the array of pointers in the struct. What i think what you want and need is a pointer to a pointer array so that it can grow which is what you want in addA() i think. The line from addA() aV->a[aV->size]->size = 0; does not communicate your intention very well but it looks like you are trying to change the value beyond the last entry in the array and since it is inlined in the struct it would result to the separate field size by pure coincidence on some alignments; this is a very fragile way of programming. So what i propose is this. Change the struct to contain A** a; // pointer to pointer-array, malloc it initially and re-malloc (and copy) it whenever you need it to grow (in addA()).

Resources