I'm a total noob in C. I can't make the connect between this function and main. I'm trying to print out a 2d array and I keep getting segmentation fault. Any help would be greatly appreciated.
EDIT: When I changed the last line 'printf("%d:[%s]\n",i,*(p+i))' from %s to %c, I get the first word in the file i'm reading from. So turns out that something is in fact being returned from my function. Now just need to figure out how to get it to return words from other lines in the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define num_strings 20
#define size_strings 20
int *read_file(){
int j = 0;
static char text[num_strings][size_strings];
FILE *fp;
int x;
fp = fopen("dictionary2.txt", "r");
char s[100];
while(!feof(fp)) {
x = fscanf(fp,"%[^\n]",s);
fgetc(fp);
if (x==1) {
strcpy(text[j],s);
j++;
}
}
return text;
}
int main() {
int *p;
p = read_file();
int i;
for(i = 0; i < 10; i++) {
printf("%d:[%s]\n",i,*(p+i));
}
return(0);
}
In general, you should be creating your array in main() and passing it in, this kind of behavior is very unorthodox. However, if you do insist on doing it this way, you have to return a pointer to your array, since you cannot return arrays in C.
This is the kind of thing you'll need:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define num_strings 20
#define size_strings 20
typedef char (*PARR)[num_strings][size_strings];
PARR read_file(int * wordsread)
{
static char text[num_strings][size_strings];
FILE *fp;
if ( (fp = fopen("dictionary2.txt", "r")) == NULL ) {
fprintf(stderr, "Couldn't open file for reading\n");
exit(EXIT_FAILURE);
}
char s[100];
int j = 0;
while ( j < num_strings && fgets(s, sizeof s, fp) ) {
const size_t sl = strlen(s);
if ( s[sl - 1] == '\n' ) {
s[sl - 1] = 0;
}
if ( (strlen(s) + 1) > size_strings ) {
fprintf(stderr, "String [%s] too long!\n", s);
exit(EXIT_FAILURE);
}
strcpy(text[j++], s);
}
fclose(fp);
*wordsread = j;
return &text;
}
int main(void)
{
int wordsread = 0;
PARR p = read_file(&wordsread);
for ( int i = 0; i < wordsread; ++i ) {
printf("%d:[%s]\n", i, (*p)[i]);
}
return 0;
}
which, with a suitable input file, outputs:
paul#horus:~/src/sandbox$ ./twoarr
0:[these]
1:[are]
2:[some]
3:[words]
4:[and]
5:[here]
6:[are]
7:[some]
8:[more]
9:[the]
10:[total]
11:[number]
12:[of]
13:[words]
14:[in]
15:[this]
16:[file]
17:[is]
18:[twenty]
19:[s'right]
paul#horus:~/src/sandbox$
Note this only works because you declared your array in read_file() as static - don't return pointers to local variables with automatic storage duration in this way.
Try moving your #defines back and changing your function header to return a pointer to arrays of size_strings characters, as follows:
#define num_strings 20
#define size_strings 20
char (*read_file())[size_strings] {
Or alternately, with a typedef:
#define num_strings 20
#define size_strings 20
typedef char (*PCharArr)[size_strings];
PCharArr read_file() {
...and change the type of p in main accordingly:
char (*p)[size_strings];
That will return (a pointer to the first element of) an array of character arrays, which is more or less equivalent to a 2D array of char.
Update, oh I see, you pasted the code from main to the function, I know what happened here, you assumed p[20][20] is the same as a p* or maybe a p**, that's not correct, since now if you do *(p+1), the compiler doesn't know each element in p is 20 wide instead of 1 wide. You approach here should be to declare a pointer to an array of strings in read_file and return that instead:
static char text[num_strings][size_strings];
static char *texts[num_strings]
...
while....
....
if (x==1)
{strcpy(text[j],s);texts[j]=text[j];j++;}
return texts;
your p should be char* not int*. You also need to terminate the loop if 20 items have been read in.
Related
I have currently made this much of the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#define STRSIZE 21
struct PInven{
int count;
struct PItem{
char name[STRSIZE];
int amount;
}Pitem;
}Pinven;//this needs to be an output file
int ReadInProduce (){
//read in file and check to see if the file exist or not.
FILE * PinFile = fopen("produce.txt","r");
if (PinFile == NULL){
printf("ERROR: WRONG FILE");
}
else{
printf("I did it!!\n");
}
//assigning the value gotten into the struct variable(but need to maybe change this since it needs to be an output)
fscanf(PinFile,"%d",&Pinven.count);
printf("%d\n", Pinven.count);
int i;
for(i =0; i <Pinven.count; i++){
fscanf(PinFile,"%20s %d",Pinven.Pitem.name, &Pinven.Pitem.amount);
printf("%s %d\n",Pinven.Pitem.name, Pinven.Pitem.amount);
}
//making an array to hold the variables
//FILE * PoutFile = fopen("produce_update.txt","w");
fclose(PinFile);
return 0;
}
From there I want to get the file that is read to the structs to be printed out into an array so that later on I can make a function that will be able to compare to the to it.
Basically a store management system. Where the file of the inventory is read in and compared to the file that is store and return a new value for the amount of produce now either left or gained.
10 //number of items that will be stored in the store
apple 19
banana 31
broccoli 9
...
In general, it's a really bad idea to include header information in the file about the number of entries in the file. You want to be able to do stream processing, and that will be more difficult if you need that meta-data. More importantly, it is important to understand how to write the code so that you don't need it. It's not really that difficult, but for some reason people avoid it. One simple approach is just to grow the array for each entry. This is horribly inefficient, but for the sake of simplicity, here's an example that expects the file not not include that first line:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <limits.h>
#define STRSIZE 128
struct PItem{
char name[STRSIZE];
int amount;
};
struct PInven{
int count;
struct PItem *PItem;
};
static void
grow(struct PInven *p)
{
p->PItem = realloc(p->PItem, ++p->count * sizeof *p->PItem);
if( p->PItem == NULL ){
perror("out of memory");
exit(1);
}
}
int
ReadInProduce(struct PInven *P, const char *path)
{
FILE * PinFile = fopen(path, "r");
if( PinFile == NULL ){
perror(path);
exit(1);
}
char fmt[64];
int max_len;
max_len = snprintf(fmt, 0, "%d", INT_MAX);
snprintf(fmt, sizeof fmt, "%%%ds %%%dd", STRSIZE - 1, max_len - 1);
grow(P);
struct PItem *i = P->PItem;
while( fscanf(PinFile, fmt, i->name, &i->amount) == 2 ){
i += 1;
grow(P);
}
P->count -= 1;
fclose(PinFile); /* Should check for error here! */
return P->count;
}
int
main(int argc, char **argv)
{
struct PInven P = {0};
char *input = argc > 1 ? argv[1] : "produce.txt";
ReadInProduce(&P, input);
struct PItem *t = P.PItem;
for( int i = 0; i < P.count; i++, t++ ){
printf("%10d: %s\n", t->amount, t->name);
}
}
As an exercise for the reader, you should add some error handling. At the moment, this code simply stops reading the input file if there is bad input. Also, it would be a useful exercise to do fewer reallocations.
you should change Structure of PInven to it can save a dynamic array of Pitem with a Pitem pointer.
tested :
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#define STRSIZE 21
typedef struct {
char name[STRSIZE];
int amount;
} Pitem;
struct PInven {
int count;
Pitem *pitem;
} Pinven; // this needs to be an output file
int main() {
// read in file and check to see if the file exist or not.
FILE *PinFile = fopen("produce.txt", "r");
if (PinFile == NULL) {
printf("ERROR: WRONG FILE");
} else {
printf("I did it!!\n");
}
// assigning the value gotten into the struct variable(but need to maybe
// change this since it needs to be an output)
fscanf(PinFile, "%d", &Pinven.count);
Pinven.pitem = (Pitem *)malloc(sizeof(Pitem) * Pinven.count);
printf("%d\n", Pinven.count);
int i;
for (i = 0; i < Pinven.count; i++) {
fscanf(PinFile, "%20s %d", Pinven.pitem[i].name,
&Pinven.pitem[i].amount);
// printf("%s %d\n",Pinven.pitem[i].name, Pinven.pitem[i].amount);
}
for (i = 0; i < Pinven.count; i++) {
printf("%s %d\n", Pinven.pitem[i].name, Pinven.pitem[i].amount);
}
// making an array to hold the variables
// FILE * PoutFile = fopen("produce_update.txt","w");
fclose(PinFile);
// remember free
free(Pinven.pitem);
return 0;
}
I'm passing a matrix to a text file
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int llenarMatriz() {
int matriz[3][3]={1,2,3,4,5,6,7,8,9};
return matriz[3][3];
}
void guardarMatriz(int matriz[3][3]) {
char direccion[]="C:\\Users\\Usuario\\Desktop\\DIBU.txt";
FILE *archivo = fopen(direccion, "w");
if (archivo == NULL) {
exit(EXIT_FAILURE);
}
char linea[20];
sprintf(linea, "%d %d\n", 3, 3);
fputs(linea, archivo);
for (int i = 0; i < 3; i++) {
linea[0] = '\0';
for (int j = 0; j < 3; j++){
char buffer[10];
sprintf(buffer, "%d ", matriz[3][3]);
strcat(linea, buffer);
}
int len = strlen(linea);
linea[len - 1] = '\n';
fputs(linea,archivo);
}
fclose(archivo);
}
int main() {
llenarMatriz();
guardarMatriz(int matriz[3][3]);
system("pause");
return 0;
}
Error message;
In function 'main':
error: expected expression before 'int'
guardarMatriz(int matriz[3][3]);
You have two problems in your code. First you don't initialize a 2-D array correctly. It should look like this:
int matriz[3][3]={{1,2,3},{4,5,6},{7,8,9}};
second, you don't pass a type name to the function call to declare your variable, so it should look like this:
int matriz[3][3];
llenarMatriz();
guardarMatriz(matriz);
guardarMatriz(int matriz[3][3]);
Don't include the type and the dimensions when passing parameters to a function.
Also, when you call a function returning something, you should use the correct type (an int is not able to return a 2D array of int), and you must store the return somewhere.
In this case you can return a compound literal:
void *llenarMatriz(void)
{
return (int [][3]){{1,2,3},{4,5,6},{7,8,9}};
}
and in main:
int main(void) /* void is the correct argument for `main` */
{
int (*matriz)[3] = llenarMatriz(); /* A pointer to an array of int 3 */
guardarMatriz(matriz);
system("pause");
return 0;
}
If you don't want to declare the array inside main you can use the result of the first function as the argument of the second one:
guardarMatriz(llenarMatriz());
or you can pass the compound literal directly:
guardarMatriz((int [][3]){{1,2,3},{4,5,6},{7,8,9}});
There is a function that asks the user which text file to open, opens it and then passes the array of structures that was passed into the function along with the file pointer to another function that reads in data from file into the structure. The array structure for testing purposes only has the value char name[25];. I can assign one line at a time from the file to the same structure index all I want but when I try an increment it I get a segmentation fault no matter what approach I've taken.
The structure has been type defined as well.
The code is:
void oSesame(char usrTxt[], int len, FILE * pFile, Country * p)
{
pFile = fopen(usrTxt, "rw");
if(pFile != NULL)
{
readIn(pFile, &p);
}
else
{
printf("Error opening %s , check your spelling and try again.\n", usrTxt);
}
}
void readIn(FILE * pfile, Country ** p)
{
int count = 0;
int i = 0;
for(i = 0; i<3; i++)
{
fgets((*p[i]).cntName, MAX_COUNTRY_LENGTH, pfile);
}
fclose(pfile);
}
The header file:
//Header.h
#define COUNTRY_MAX 10
#define MAX_COUNTRY_LENGTH 25
#define MAX_CAPITAL_LENGTH 25
typedef struct country
{
char cntName[MAX_COUNTRY_LENGTH];
char capName[MAX_CAPITAL_LENGTH];
double population;
}Country;
int ask(char * usrTxt);
void oSesame(char usrTxt[], int len, FILE * pFile, Country * p);
void readIn(FILE * pFile, Country ** p);
The main code:
#include <stdio.h> //for testing within main
#include <string.h> //for testing within main
#include "headers.h"
int main()
{
int len;
FILE * fileP;
char UI[25];
Country c[10];
Country * ptr;
ptr = c;
len = ask(UI);
oSesame(UI, len, fileP, ptr);
return 0;
}
You are passing Country** for some reason and then handling it as *p[index]. This is wrong. You could use (*p)[index] but the correct way is not to take a reference to the Country* in the first place.
The way you're doing it means you have a pointer to pointer to Country. When you index that you are moving to next pointer to pointer, which is not the same as moving to the next pointer. Undefined behaviour happens.
The string lengths are not getting the correct lengths, so the rest of the program doesn't work. I am trying to read 62 chars per line then print a new line with another 62 chars.
Can anyone help me correctly pass the char arrays to the output function?
#include <stdio.h>
#include <string.h>
#define _CRT_SECURE_NO_DEPRECATE
void output(char *wbuf, char *lbuf, int lineLength);
void readFile(FILE *getty, char *wbuf, char *lbuf);
FILE *getty;
int main(void) {
char wbuf[1000] = {0}, lbuf[1000] = {0};
if (fopen_s(&getty,"getty.txt", "r") != 0 )
{
printf("Failed to open getty.txt for reading.");
} else {
readFile(getty, wbuf, lbuf);
}
fclose(getty);
return 0;
}
void readFile(FILE *getty, char *wbuf, char *lbuf)
{
static int lineLength = 62;
while (!feof(getty))
{
fscanf(getty, "%s", wbuf);
output(wbuf, lbuf, lineLength);
}
}
void output(char *wbuf, char *lbuf, int lineLength)
{
int wbufLength, lbufLength, i = 0;
wbufLength = strlen(wbuf);
lbufLength = strlen(lbuf);
//prints incorrect
printf("wbuflength %d lbuflength %d\n", wbufLength, lbufLength);
// lengths
if ( (wbufLength + lbufLength) <= lineLength)
{
strcat(lbuf,wbuf); //lbuf should be 0 but it starts at
} //274, wbuf not correct either
else
{
strcat(lbuf,"\n");
lineLength += 62;
strcat(lbuf, wbuf);
}
}
The problem is your loop condition:
while (!feof(getty)) { ... }
The EOF flag is not set until after an input operation fails.
In your case, the loop loops, then the fscanf operation fails because it's at the end of the file but you don't check for that inside the loop, and you then call output even though nothing was read from the file. Then the loop continues and then it notices that the file have reached EOF.
I have been going crazy trying to figure out what is done wrong. I admit I am inexperienced when it comes to C, but I don't know what is wrong. Is the way that I am accessing/using the struct incorrect?
EDIT: I keep getting EXC_BAD_ACCESS in debugger.
#include <stdio.h>
#include <string.h>
#define MAX_STRING 20
#define MAX_PLYR 16
typedef struct {
char pname[MAX_STRING];
int runs;
char *s;
} Team_t;
int
main(void)
{
Team_t *team_data[MAX_PLYR];
int i;
char *p;
char name[MAX_STRING];
FILE *inp;
inp = fopen("teamnames.rtf", "r");
for (i = 0; i < MAX_PLYR;) {
while ((fgets(name, MAX_STRING, inp) != NULL));
printf("Name(i): %s\n", name);
strcpy(team_data[i]->pname, name);
i++;
}
fclose(inp);
return(0);
}
Edit: Here's what's changed, still getting Segmentation Error
#include <stdio.h>
#include <string.h>
#define MAX_STRING 20
#define MAX_PLYR 16
typedef struct {
char pname[MAX_STRING];
int runs;
char s;
} Team_t;
int
main(void)
{
Team_t team_data[MAX_PLYR];
char name[MAX_STRING];
int i;
FILE *inp;
inp = fopen("teamnames.rtf", "r");
for (i = 0; i < MAX_PLYR; i++) {
((fgets(name, MAX_STRING, inp)));
if (feof(inp)) {
printf("End of stream\n");
i = MAX_PLYR;
}
else {
if (ferror(inp)) {
printf("Error reading from file\n");
}
printf("Name(i): %s\n", name);
strcpy(team_data[i].pname, name);
}
}
fclose(inp);
return(0);
}
You declare team_data but you don't allocate it; therefore it's pointing off into random memory, as are the imaginary contents of the array. You need to actually create the array, something like
Team_t *team_data[MAX_PLYR] = (Team_t**) malloc(MAX_PLYR * sizeof(Team_t *));
Use structs, not pointers (or if you insist using pointers the allocate space for those structs)
Team_t team_data[MAX_PLYR];
fgets(team_data[i].pname, MAX_STRING, inp)
when you write
Team_t *team_data[MAX_PLYR];
you are not allocating any memory for the actual Team_t records, instead you are setting up an array of pointers to records.
If instead you would write
Team_t team_data[MAX_PLYR];
you would have allocated the records. When you then want to copy into the team_data array you write instead
strcpy( team_data[i].name, name );