Converting struct in main() into function version [duplicate] - c

This question already has answers here:
How to access a local variable from a different function using pointers?
(10 answers)
Why can a function return an array setup by malloc but not one setup by "int cat[3] = {0,0,0};"
(7 answers)
Dynamically allocate memory for Array of Structs
(5 answers)
Closed 5 years ago.
I'm new to C programming. I just figured out how to read csv files into C by struct, but struggling to convert the script into a function, then call this function in int main(). I'll need to read loads of csv files so a function will be very helpful.
Here is my code which runs successfully (non-function version):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXROWS 2
struct cudaScenarioStruct{
char *type;
int trial;
int yPC;
int yPE;
int n;
};
struct cudaScenarioStruct cudaScenario[MAXROWS];
int main(){
int rowIndex = 0;
char line[128];
char* token = NULL;
FILE* fp = fopen("C:/Users/ms710557/Desktop/cudaScenario1(2).csv","r");
if (fp != NULL){
while (fgets( line, sizeof(line), fp) != NULL && rowIndex < MAXROWS){
cudaScenario[rowIndex].type = malloc(2);
token = strtok(line, ",");
strcpy(cudaScenario[rowIndex].type, token);
token = strtok(NULL, ",");
cudaScenario[rowIndex].trial = atoi(token);
token = strtok(NULL, ",");
cudaScenario[rowIndex].yPC = atoi(token);
token = strtok(NULL, ",");
cudaScenario[rowIndex].yPE = atoi(token);
token = strtok(NULL, ",");
cudaScenario[rowIndex].n = atoi(token);
rowIndex++;
}
fclose(fp);
}
for (int i = 0; i < MAXROWS; ++i){
printf("%s, %i, %i, %i, %i \n", cudaScenario[i].type, cudaScenario[i].trial, cudaScenario[i].yPC, cudaScenario[i].yPE, cudaScenario[i].n);
}
return 0;
}
The results look like this:
NI, 1, 61, 55, 80
NI, 2, 54, 59, 80
However when I convert it into a function and include the function in int main(), it's just not working... I've been googling for hours and couldn't find out why. This is my attempt:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXROWS 2
struct cudaScenarioStruct{
char *type;
int trial;
int yPC;
int yPE;
int n;
};
struct cudaScenarioStruct *readCudaScenarioData(){
struct cudaScenarioStruct csvData[MAXROWS];
int rowIndex = 0;
char line[128];
char* token = NULL;
FILE* fp = fopen("C:/Users/ms710557/Desktop/cudaScenario1(2).csv","r");
if (fp != NULL){
while (fgets( line, sizeof(line), fp) != NULL && rowIndex < MAXROWS){
csvData[rowIndex].type = malloc(2);
token = strtok(line, ",");
strcpy(csvData[rowIndex].type, token);
token = strtok(NULL, ",");
csvData[rowIndex].trial = atoi(token);
token = strtok(NULL, ",");
csvData[rowIndex].yPC = atoi(token);
token = strtok(NULL, ",");
csvData[rowIndex].yPE = atoi(token);
token = strtok(NULL, ",");
csvData[rowIndex].n = atoi(token);
rowIndex++;
}
fclose(fp);
}
return csvData;
}
int main()
{
struct cudaScenarioStruct *cudaScenario;
cudaScenario = readCudaScenarioData();
for (int i = 0; i < MAXROWS; ++i){
printf("%s, %i, %i, %i, %i \n", cudaScenario[i].type,
cudaScenario[i].trial, cudaScenario[i].yPC, cudaScenario[i].yPE,
cudaScenario[i].n);
}
return 0;
}
Any help will be highly appreciated!

Related

How to read from the file and write it in the structure? I have a little trouble with my code

I have to write this code, I mean I should read from the file name of students and their mark, and then sort students by the grow of mark. Now I just want to output only mark. I want to display grades using structures. I don't know where the problem is.
text.file
Jon 3
Alina 5
Ron 1
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
int main()
{
const int N = 3;
int i = 0;
struct student {
char surname[50];
int mark;
};
struct student PI1[N];
char str[50];
const char s[1] = " ";
char* token;
FILE* ptr;
token = strtok(str, s);
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
printf("file can't be opened \n");
}
while (fgets(str, 50, ptr) != NULL){
token = strtok(str, s);
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
PI1[i].mark = atoi(token);
i++;
}
fclose(ptr);
printf("The marks is:\n");
printf("%d %d %d", PI1[0].mark, PI1[1].mark, PI1[2].mark);
return 0;
}
You need to prevent the program from reading from the file pointer if opening the file fails:
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
perror("test.txt");
return 1; // this could be one way
}
The second argument to strok should be a null terminated string. const char s[1] = " "; only has room for one character. No null terminator (\0). Make it:
const char s[] = " "; // or const char s[2] = " "; or const char *s = " ";
Don't iterate out of bounds. You need to check so that you don't try to put data in PI1[N] etc.
while (i < N && fgets(str, sizeof str, ptr) != NULL) {
// ^^^^^^^^
Check that strok actually returns a pointer to a new token. If it doesn't, the line you've read doesn't fulfill the requirements.
while (i < N && fgets(str, sizeof str, ptr) != NULL) {
token = strtok(str, s);
if(!token) break; // token check
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
if (token) // token check
PI1[i].mark = atoi(token);
else
break;
i++;
}
You could also skip the strcpy by reading directly into your struct student since char str[50]; has the same length as surname. str should probably be larger though, but for now:
while (i < N && fgets(PI1[i].surname, sizeof PI1[i].surname, ptr) != NULL) {
token = strtok(PI1[i].surname, s);
if(!token) break;
token = strtok(NULL, s);
if (token)
PI1[i].mark = atoi(token);
else
break;
i++;
}
Only print as many marks as you successfully read
printf("The marks are:\n");
for(int idx = 0; idx < i; ++idx) {
printf("%d ", PI1[idx].mark);
}
putchar('\n');

Segmentation Fault using Strtok() for 2d array in C

I keep getting Segmentation Fault while trying to split a string such as "1,2,3;4,5,6;7,8,9" into a 2d Array why does it keep happening with this code could someone please explain this for me?
int main(void){
char string[100];
char *token;
char *end;
int num;
int row_counter;
int column_counter;
int counter_position;
char *rows[100];
int square_ints[40][40];
row_counter = 0;
column_counter = 0;
string[] = "1,2,3;4,5,6;7,8,9";
token = strtok(string, ";");
rows[row_counter] = token;
row_counter++;
while (token != NULL){
token = strtok(NULL, ";");
rows[row_counter] = token;
row_counter++;
}
counter_position = row_counter;
for (row_counter = 0; row_counter < counter_position; row_counter++) {
token = strtok(rows[row_counter], ",");
num = strtol(token, &end, 10);
square_ints[row_counter][column_counter] = num;
printf("%d\n", square_ints[row_counter][column_counter]);
column_counter++;
while (token != NULL) {
token = strtok(NULL, ",");
num = strtol(token, &end, 10);
square_ints[row_counter][column_counter] = num;
printf("%d\n", square_ints[row_counter][column_counter]);
column_counter++;
}
}
}
It should be printing: 1 2 3 4 5 6 7 8 9 instead I get 1 2 3 Segmentation Fault
To expand on what MikeCAT mentioned:
As the start of your for loop, you do:
token = strtok(rows[row_counter], ",");
num = strtol(token, &end, 10);
Later you do:
token = strtok(NULL, ",");
num = strtol(token, &end, 10);
The problem is that in both cases, strtok will return NULL, so the value of token is NULL. When you pass token to strtol, it will try to use/dereference this and it will produce a segfault.
So, to fix, after each strtok call, you need to add:
if (token == NULL)
break;
Here's the corrected code. I've added some debug printf, so you can see the issue.
I've used cpp conditionals to demarcate old vs new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Anyway, here it is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef DEBUG
#define dbgprt(_fmt...) \
do { \
printf(_fmt); \
} while (0)
#else
#define dbgprt(_fmt...) \
do { \
} while (0)
#endif
int
main(void)
{
#if 0
char string[100];
#else
char string[] = "1,2,3;4,5,6;7,8,9";
#endif
char *token;
char *end;
int num;
int row_counter;
int column_counter;
int counter_position;
char *rows[100];
int square_ints[40][40];
row_counter = 0;
column_counter = 0;
#if 0
string[] = "1,2,3;4,5,6;7,8,9";
#endif
token = strtok(string, ";");
rows[row_counter] = token;
row_counter++;
while (token != NULL) {
token = strtok(NULL, ";");
rows[row_counter] = token;
row_counter++;
}
counter_position = row_counter;
for (row_counter = 0; row_counter < counter_position; row_counter++) {
dbgprt("DEBUG: rows[%d]='%s'\n",row_counter,rows[row_counter]);
token = strtok(rows[row_counter], ",");
dbgprt("DEBUG: token='%s'\n",token);
#if 1
if (token == NULL)
break;
#endif
num = strtol(token, &end, 10);
square_ints[row_counter][column_counter] = num;
printf("%d\n", square_ints[row_counter][column_counter]);
column_counter++;
while (token != NULL) {
token = strtok(NULL, ",");
dbgprt("DEBUG2: token='%s'\n",token);
#if 1
if (token == NULL)
break;
#endif
num = strtol(token, &end, 10);
square_ints[row_counter][column_counter] = num;
printf("%d\n", square_ints[row_counter][column_counter]);
column_counter++;
}
}
return 0;
}
Note that while the above code works, it is replicating code before a given loop and inside the loop. If we add an extra pointer variable (e.g. bp), we can simplify the code a bit:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char string[] = "1,2,3;4,5,6;7,8,9";
char *token;
char *end;
int num;
int row_counter;
int column_counter;
int counter_position;
char *bp;
char *rows[100];
int square_ints[40][40];
row_counter = 0;
column_counter = 0;
bp = string;
while (1) {
token = strtok(bp,";");
bp = NULL;
if (token == NULL)
break;
rows[row_counter] = token;
row_counter++;
}
counter_position = row_counter;
for (row_counter = 0; row_counter < counter_position; row_counter++) {
bp = rows[row_counter];
if (bp == NULL)
break;
while (1) {
token = strtok(bp, ",");
bp = NULL;
if (token == NULL)
break;
num = strtol(token, &end, 10);
square_ints[row_counter][column_counter] = num;
printf("%d\n", square_ints[row_counter][column_counter]);
column_counter++;
}
}
return 0;
}
There is another similar function strsep that is considered by some to be "strtok done right". Here's a version that uses that function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char string[] = "1,2,3;4,5,6;7,8,9";
char *token;
char *end;
int num;
int row_counter;
int column_counter;
int counter_position;
char *bp;
char *rows[100];
int square_ints[40][40];
row_counter = 0;
column_counter = 0;
bp = string;
while (1) {
token = strsep(&bp,";");
if (token == NULL)
break;
rows[row_counter] = token;
row_counter++;
}
counter_position = row_counter;
for (row_counter = 0; row_counter < counter_position; row_counter++) {
bp = rows[row_counter];
if (bp == NULL)
break;
while (1) {
token = strsep(&bp,",");
if (token == NULL)
break;
num = strtol(token, &end, 10);
square_ints[row_counter][column_counter] = num;
printf("%d\n", square_ints[row_counter][column_counter]);
column_counter++;
}
}
return 0;
}

Why am I getting "undefined reference to" error?

The code below throws an error "undefined reference to 'poredi'".
Everything is defined in this single c file (aside from the c libs in include).
'poredi' is just a function, of which I define a prototype right below the typedef and then I impelement it lower in the file.
Looking at some of the similar questions I can say that it is compiled on Windows 10 using the MinGW C compiler through the CodeBlocks IDE without any additional arguments for compiling.
I am a total C noob so any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char ime[20];
char prezime[20];
char registracija[10];
int dRod;
int mRod;
int gRod;
char datumIzdavanja[10];
} vozac;
void poredi(vozac *vozaci, int linije);
int nlines(char *datoteka);
void napuni(FILE *ulaz, vozac *vozaci, int lines);
int main(){
int lines = nlines("Registar.in");
FILE *ulaz = fopen("Registar.in", "r");
int i;
FILE *izlaz = fopen("Najmladji.out", "w");
vozac *vozaci = calloc(lines,sizeof(vozac));
napuni(ulaz,vozaci,lines);
fclose(ulaz);
poredi(vozaci,lines);
for (i = 0; i < 5; i++){
printf("\n%s %s (%d.%d.%d)", vozaci[i].ime, vozaci[i].prezime, vozaci[i].dRod, vozaci[i].mRod, vozaci[i].gRod);
}
fclose(izlaz);
return 0;
}
int nlines(char *datoteka){
FILE *ulaz = fopen("Registar.in", "r");
int brojac = 0;
while (!feof(ulaz)){
char ch = fgetc(ulaz);
if (ch == '\n') brojac++;
}
fclose(ulaz);
return brojac;
}
void napuni(FILE *ulaz, vozac *vozaci, int lines){
int i;
char *linija = calloc(70, sizeof(char));
char *token;
char *token2;
char *znak;
char *znak2;
for (i= 0; i < lines; i++){
fgets(linija,70,ulaz);
token = strtok(linija," ");
strcpy(vozaci[i].prezime, token);
znak = strchr(vozaci[i].prezime,',');
*znak = 0;
token = strtok(NULL," ");
strcpy(vozaci[i].ime, token);
znak2 = strchr(vozaci[i].ime, ';');
*znak2 = 0;
token = strtok(NULL, " ");
strcpy(vozaci[i].registracija, token);
token = strtok(NULL, " ");
token2 = strtok(token, ".");
vozaci[i].dRod = atoi(token2);
token2 = strtok(NULL, ".");
vozaci[i].mRod = atoi(token2);
token2 = strtok(NULL, ".");
vozaci[i].gRod = atoi(token2);
token = strtok(NULL, " ");
strcpy(vozaci[i].datumIzdavanja, token);
}
void poredi(vozac *vozaci, int linije){
int sortirano = 1;
vozac buffer;
while (sortirano){
sortirano = 0;
for (i = 0; i < linije; i++){
if (vozaci[i].gRod > vozaci[i+1].gRod){
buffer = vozaci[i];
vozaci[i] = vozaci[i+1];
vozaci[i + 1] = buffer;
sortirano = 1;
}
else if (vozaci[i].gRod == vozaci[i + 1].gRod){
if (vozaci[i].mRod > vozaci[i + 1].mRod){
buffer = vozaci[i];
vozaci[i] = vozaci[i+1];
vozaci[i + 1] = buffer;
sortirano = 1;
}
else if (vozaci[i].mRod == vozaci[i + 1].mRod){
if (vozaci[i].dRod > vozaci[i + 1].dRod){
buffer = vozaci[i];
vozaci[i] = vozaci[i+1];
vozaci[i + 1] = buffer;
sortirano = 1;
}
}
}
}
}
}
}
You have poredi() defined inside napuni(). Nested functions is not valid in ISO C but gcc allows it as an extension. I doubt you actually intended to use nested functions but misplaced of braces. Basically remove one brace } from the end of your source file add it above the definition of poredi().
A better indentation would have help avoid such surprises.

Segmentation Fault with strlen

I am getting a segmentation fault error. When I comment out "wordlength = strlen(token);" it runs fine. I don't know why it the seg fault happens when I assign a strlen(token) just fine to an int a few lines before this one. I would appreciate any help possible.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define char_max 60
int main(int argc, char *argv[])
{
FILE *fp = fopen(argv[2],"r");
char **wordlist;
int row = 1;
int i;
char temp[100];
char *token;
int wordlength;
int lengthcounter;
wordlist = (char**)malloc(row*sizeof(char*));
for(i = 0; i < row; i++)
{
wordlist[i] = (char*)malloc(char_max*sizeof(char*));
}
while(fgets(temp, sizeof(temp), fp) != NULL)
{
lengthcounter = 0;
wordlength = 0;
token = strtok(temp, " ");
strcat(wordlist[row-1], token);
printf("%s\n", wordlist[row-1]);
lengthcounter = strlen(token);
while(token != NULL)
{
token = strtok(NULL, " ");
wordlength = strlen(token);
/*lengthcounter += wordlength;*/
}
printf("The lengthcounter is %d\n", lengthcounter);
}
free(wordlist);
fclose(fp);
return 0;
}
while(token != NULL)
{
token = strtok(NULL, " ");
wordlength = strlen(token);
/*lengthcounter += wordlength;*/
}
What happens in the last iteration of the loop when token is NULL? You pass it to strlen anyway.
Also, this is almost certainly wrong:
wordlist[i] = (char*)malloc(char_max*sizeof(char*));
You're allocating space for pointers, not characters. So why sizeof(char*)? Also, don't cast the return value of malloc. This is C, not C++.

Strtok using dynamic memory

I have the following code, and I need help getting and storing the last token. Right now the code tokenizes after every space, but when it gets to the end of my text file, it doesn't tokenize the last value. I'm pretty sure I need to have the token before the malloc statements, but when I add it in front, I get a seg fault. Does anybody know the issue? Initialized myStruct.extras = NULL above because of realloc; it is a char **.
token = strtok(fileArrayPTR[p],"X");
while (token!= NULL)
{
if (tempCounter == 0)
{
token = strtok(NULL," ");
myStruct.dimensions[1] = strtol(token,&ptr,10);
}else{
myStruct.extras = realloc(myStruct.extras,(extraCounter + 1) * sizeof(char *));
myStruct.extras[extraCounter] = malloc(strlen(token)+1);
strcpy(myStruct.extras[extraCounter],token);
token = strtok(NULL," ");
extraCounter++;
}
}
edit: forgot to put the incremented counter
This is (the second version of) the code from the question:
token = strtok(fileArrayPTR[p],"X");
while (token!= NULL)
{
if (tempCounter == 0)
{
token = strtok(NULL," ");
myStruct.dimensions[1] = strtol(token,&ptr,10);
}else{
myStruct.extras = realloc(myStruct.extras,(extraCounter + 1) * sizeof(char *));
myStruct.extras[extraCounter] = malloc(strlen(token)+1);
strcpy(myStruct.extras[extraCounter],token);
token = strtok(NULL," ");
extraCounter++;
}
}
This is not a self-contained program. We can improve it by:
Removing the structure.
Showing corresponding variable declarations.
Converting it into a simple main().
Those changes lead to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int extraCounter = 0;
char **extras = 0;
int tempCounter = 0;
char *ptr = 0;
char data[] = "10X16 something another-thing";
char *token = strtok(data, "X");
int dimension1 = strtol(token, &ptr, 10);
int dimension2 = -1;
while (token!= NULL)
{
if (tempCounter == 0)
{
token = strtok(NULL," ");
dimension2 = strtol(token, &ptr, 10);
tempCounter++;
}
else
{
extras = realloc(extras, (extraCounter + 1) * sizeof(char *));
extras[extraCounter] = malloc(strlen(token)+1);
strcpy(extras[extraCounter], token);
token = strtok(NULL, " ");
extraCounter++;
}
}
printf("Dimensions: %dx%d\n", dimension1, dimension2);
for (int i = 0; i < extraCounter; i++)
printf("%d: [[%s]]\n", i, extras[i]);
return 0;
}
And when run, it produces:
Dimensions: 10x16
0: [[16]]
1: [[something]]
2: [[another-thing]]
Is there a problem with that code? Yes, the code for the dimension2 doesn't reinvoke strtok(), so 16 is processed twice, once as a dimension and once as a string. Probably not what's wanted. Hence:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int extraCounter = 0;
char **extras = 0;
int tempCounter = 0;
char *ptr = 0;
char data[] = "10X16 something another-thing";
char *token = strtok(data, "X");
int dimension1 = strtol(token, &ptr, 10);
int dimension2 = -1;
while (token != NULL)
{
if (tempCounter == 0)
{
token = strtok(NULL, " ");
dimension2 = strtol(token, &ptr, 10);
tempCounter++;
}
else
{
extras = realloc(extras, (extraCounter + 1) * sizeof(char *));
extras[extraCounter] = malloc(strlen(token) + 1);
strcpy(extras[extraCounter++], token);
}
token = strtok(NULL, " ");
}
printf("Dimensions: %dx%d\n", dimension1, dimension2);
for (int i = 0; i < extraCounter; i++)
printf("%d: [[%s]]\n", i, extras[i]);
return 0;
}
Compilation:
gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Werror ss.c -o ss
Output:
Dimensions: 10x16
0: [[something]]
1: [[another-thing]]
Looks better...

Resources