Storing details into C Structure - c

I'm writing the C program to find the function and calculate the number of lines of a function in C file and I am storing it into a structure. I've given my codes below.
#include <stdio.h>
#include <string.h>
#define SIZE 1024
struct fundetails
{
int nooflines;
char *funcname;
}s[20];
char *ffname(char *line)
{
int i=1,j=0;
char *dt;
char name[SIZE];
strtok(line,"(");
dt = strchr(line,' ');
if(dt[i] == '*')
i++;
while(dt[i] != '\0')
{
name[j]=dt[i];
i++;
j++;
}
name[j] ='\0';
return name;
}
int main(int argc, char **argv)
{
if(argc < 2)
{
printf("Give the filename \n");
printf("Usage: %s filename\n", argv[0]);
return -1;
}
int i, lines =0, funlines =0,count =0, fn =0, flag =0, size=0,emptyflag=0;
char c[SIZE],b[SIZE];
char *fname;
FILE *fd;
fd = fopen(argv[1],"r");
while(fgets(c,SIZE,fd))
{
emptyflag=0;
lines++;
size = strlen(c);
if(size == 1 && (strcmp(c,"\n"))== 0)
emptyflag=1;
for(i=0;i<size;i++)
{
while( c[i] =='\t' || c[i] == ' ')
{
i++;
}
if( c[i] == '{')
{
count++;
if(flag)
{
if(!emptyflag)
funlines++;
else
emptyflag=0;
}
if(count == 1)
{
fn++;
printf("Function %d is Started..............\n", fn);
flag = 1;
fname=ffname(b);
printf("Function name is:%s\n",fname);
}
break;
}
else if( c[i] == '}')
{
count--;
if(!count)
{
flag = 0;
printf("No of lines in the function %d is: %d\n", fn, funlines);
printf("Function %d is finished..........\n", fn);
s[fn-1].nooflines=funlines;
s[fn-1].funcname=fname;
funlines = 0;
}
else
{
if(!emptyflag)
funlines++;
else
emptyflag=0;
}
break;
}
else if(flag)
{
if(!emptyflag)
funlines++;
else
emptyflag=0;
break;
}
}
strcpy(b,c);
}
printf("FUN_NAME\tNO_OF_LINES\n");
for(i=0;i<fn;i++)
{
printf("%s\t\t%d\n",s[i].funcname,s[i].nooflines);
}
return 0;
}
It produces warning as try.c:26:2: warning: function returns address of local variable [enabled by default]. And It produces output as given below.
Function 1 is Started..............
Function name is:fundetails
No of lines in the function 1 is: 2
Function 1 is finished..........
Function 2 is Started..............
Function name is:dhahira
No of lines in the function 2 is: 1
Function 2 is finished..........
Function 3 is Started..............
Function name is:add
No of lines in the function 3 is: 3
Function 3 is finished..........
Function 4 is Started..............
Function name is:sub
No of lines in the function 4 is: 9
Function 4 is finished..........
Function 5 is Started..............
Function name is:main
No of lines in the function 5 is: 13
Function 5 is finished..........
FUN_NAME NO_OF_LINES
main 2
main 1
main 3
main 9
main 13
I'm storing function name and no of lines in same loop.WQhile i1m running it in GDB,for each time of
s[fn-1].nooflines=funlines;
s[fn-1].funcname=fname;
above line,the number of line is storing in a structure correctly.But not in the case of
function name.
Problem:I don't understand that why is it working properly for line and not working for function name? is it because of that warning? Please guide me, Thanks.

In ffname(), name[] is local, it's pushed to stack when execute the function. After ffname() return, the stack is popped, that means the memory seized by name[] got released, system could reuse the memory, but before the memory is reused, data is still there. That's why sometime it works and sometime not. Also, that's why you get warning.
You should define funcname in the struct as array rather than pointer. Because when funcname, you always point funcname to same name[], while name[] is written in each loop, so that at last you print same name 5 times.
After you change funcname to array, you should use strcpy to copy name:
strcpy(funcname, name); // this is right way when funcname is array
Rather than:
funcname = name;

First make the struct
struct fundetails
{
int nooflines;
char funcname[128];
}s[20];
then fix the returning value of the func ffname: you can't return a pointer to data that will disappear going out of scope (in this case, the function's end). As cheap quick modification, just turn your
char name[SIZE];
into
static char name[SIZE];
Then
strcpy(s[fn-1].funcname, fname);
instead of your
s[fn-1].funcname=fname;
The output would be like
FUN_NAME NO_OF_LINES
fundetails
2
ffname 15
main 82
I haven't checked how you identify functions but it seems too much naive. (The answer may not fix all the problem of your code; e.g. there could be path in your code that brings to the use of fname before it being assigned?...)

Related

Cannot append elements in struct array properly

This is my first post here. I am making a program in C that handles Joker results (it's a game like Powerball). Below I include only the code that matters for my question. First, you input 1 to the program so that it reads the previous results file. I will include the file so that you can run it as well.Afterwards you input 3 so that you insert a result in the form of
id;day/month/year;num1;num2;num3;num4;num5;joker
After that you input 99 and you see the full result array.
The problem is that the first 2 results that are appended to the array(resArr) are displayed properly, but all the following appends are stored with pseudorandom numbers. Any clue why my code works only for 2 repetitions?
The file: link
#include <stdio.h>
#include <stdlib.h>
typedef struct results
{
int id,date[3],num[5],joker;
}Results;
Results *read()
{
FILE *fp=fopen("joker.csv","r");
Results *temp=(Results *)malloc(sizeof(Results));
Results *result=(Results *)malloc(sizeof(Results));
int i=0,size=1;
while(!feof(fp))
{
char *s=(char *)malloc(50*sizeof(char));
fgets(s,50,fp);
sscanf(s,"%d;%d/%d/%d;%d;%d;%d;%d;%d;%d",&result[i].id,&result[i].date[0],&result[i].date[1],&result[i].date[2],&result[i].num[0],&result[i].num[1],&result[i].num[2],&result[i].num[3],&result[i].num[4],&result[i].joker);
temp=(Results *)realloc(result,(++size)*sizeof(Results));
if (temp) result=temp;
else
{
result=NULL;
break;
}
i++;
}
fclose(fp);
return result;
}
int findLength()
{
FILE *fp=fopen("joker.csv","r");
int len,i=0;
while(!feof(fp))
{
char *s=(char *)malloc(50*sizeof(char));
fgets(s,50,fp);
i++;
}
fclose(fp);
len=i-1;
return len;
}
void eisagogi(Results *resArr,int *len)
{
Results result;
printf("id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker\n");
scanf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d",&result.id,&result.date[0],&result.date[1],&result.date[2],&result.num[0],&result.num[1],&result.num[2],&result.num[3],&result.num[4],&result.joker);
resArr=(Results *)realloc(resArr,(*len+1)*sizeof(Results));
resArr[*len]=result;
*len=*len+1;
}
void showResults(Results *resArr,int len)
{
int i;
for (i=0;i<len;i++)
{ printf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d\n",resArr[i].id,resArr[i].date[0],resArr[i].date[1],resArr[i].date[2],resArr[i].num[0],resArr[i].num[1],resArr[i].num[2],resArr[i].num[3],resArr[i].num[4],resArr[i].joker);
}
}
int menuChoose()
{
int choice;
printf("Load results 1\n");
printf("Append result 3\n");
printf("Result array 99\n");
printf("Exit 0\n");
scanf("%d",&choice);
return choice;
}
int main()
{
Results *resArr=(Results *)malloc(sizeof(Results));
int choice,len;
while(1)
{
choice=menuChoose();
switch(choice)
{
case 1:
resArr=read();
len=findLength();
break;
case 3:
eisagogi(resArr,&len);
break;
case 99:
showResults(resArr,len);
break;
case 0:
exit(0);
break;
}
}
return 0;
}
I changed your feof to fgets
char s[50];
while (fgets(s, sizeof(s), fp) != 0) {
and now it seems that you can add 3 results and display them.
#include <stdio.h>
#include <stdlib.h>
typedef struct results {
int id, date[3], num[5], joker;
} Results;
Results *read() {
FILE *fp = fopen("joker.csv", "r");
Results *temp = (Results *) malloc(sizeof(Results));
Results *result = (Results *) malloc(sizeof(Results));
int i = 0, size = 1;
char s[50];
while (fgets(s, sizeof(s), fp) != 0) {
sscanf(s, "%d;%d/%d/%d;%d;%d;%d;%d;%d;%d", &result[i].id, &result[i].date[0], &result[i].date[1],
&result[i].date[2], &result[i].num[0], &result[i].num[1], &result[i].num[2], &result[i].num[3],
&result[i].num[4], &result[i].joker);
temp = (Results *) realloc(result, (++size) * sizeof(Results));
if (temp) result = temp;
else {
result = NULL;
break;
}
i++;
}
fclose(fp);
return result;
}
int findLength() {
FILE *fp = fopen("joker.csv", "r");
int len, i = 0;
while (!feof(fp)) {
char *s = (char *) malloc(50 * sizeof(char));
fgets(s, 50, fp);
i++;
}
fclose(fp);
len = i - 1;
return len;
}
void eisagogi(Results *resArr, int *len) {
Results result;
printf("id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker\n");
scanf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d", &result.id, &result.date[0], &result.date[1], &result.date[2],
&result.num[0], &result.num[1], &result.num[2], &result.num[3], &result.num[4], &result.joker);
resArr = (Results *) realloc(resArr, (*len + 1) * sizeof(Results));
resArr[*len] = result;
*len = *len + 1;
}
void showResults(Results *resArr, int len) {
int i;
for (i = 0; i < len; i++) {
printf("%d;%d/%d/%d;%d;%d;%d;%d;%d;%d\n", resArr[i].id, resArr[i].date[0], resArr[i].date[1], resArr[i].date[2],
resArr[i].num[0], resArr[i].num[1], resArr[i].num[2], resArr[i].num[3], resArr[i].num[4],
resArr[i].joker);
}
}
int menuChoose() {
int choice;
printf("Load results 1\n");
printf("Append result 3\n");
printf("Result array 99\n");
printf("Exit 0\n");
scanf("%d", &choice);
return choice;
}
int main() {
Results *resArr = (Results *) malloc(sizeof(Results));
int choice, len;
while (1) {
choice = menuChoose();
switch (choice) {
case 1:
resArr = read();
len = findLength();
break;
case 3:
eisagogi(resArr, &len);
break;
case 99:
showResults(resArr, len);
break;
case 0:
exit(0);
break;
}
}
return 0;
}
Test
Load results 1
Append result 3
Result array 99
Exit 0
3
id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker
1768;18/12/2016;11;28;5;9;31;1
Load results 1
Append result 3
Result array 99
Exit 0
3
id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker
1769;18/12/2016;11;28;5;9;31;2
Load results 1
Append result 3
Result array 99
Exit 0
3
id;dd/mm/yyyy;num1;num2;num3;num4;num5;joker
1770;18/12/2016;11;28;5;9;31;3
Load results 1
Append result 3
Result array 99
Exit 0
1
...
1768;18/12/2016;11;28;5;9;31;1
1769;18/12/2016;11;28;5;9;31;2
1770;18/12/2016;11;28;5;9;31;3
Load results 1
Append result 3
Result array 99
Exit 0
Your I/O strategy is one part fragile and one part flat wrong.
In the first place, while(!feof(fp)) is always wrong. As the answers to the linked question explain in some detail, the feof() function reports on whether end-of-file has already been detected on the specified stream. It cannot report on whether the next attempt to read will encounter EOF, so you need to watch for that on each read. In this case, you would want to verify that fgets() does not return NULL, as #DacSaunders already recommended:
char s[50];
while (fgets(s, sizeof(s), fp) != NULL) {
...
(The integer literal 0 also serves as a null pointer constant, but I find it clearer to use NULL.) Note also that Dac changed your s to an automatic array instead of having it dynamically allocated. That's not only easier to manage, but it cleans up the memory leak that your original code exhibited from dynamically allocating 50 bytes for each line and never freeing them. That also allows you to use sizeof(s) to get the capacity of s -- that works for arrays, but not for pointers.
In the second place, you do not check your inputs.
You should verify that each fgets() in fact reads a whole line (as can be determined by looking for a newline in the data read), for the next read will pick up where the last left off, and that could be in the middle of a line.
You should verify that each sscanf() in fact matches every expected field (by checking its return value), for if it does not do so then some or even all of the fields you are trying to populate will be uninitialized. You might have such a failure if the input is malformed, or even if an input line is just too long.
Bonus tip: you have another memory leak, in that you allocate memory for temp in its declaration, but you never free it, nor even use it. Just because you declare a pointer does not mean you have to allocate memory.
Thanks a lot! I repeated Dac's change in findLength as well and I also used temp() to free temp in read().
Was this bug happening because of memory leaks?

Command Line Argument

I am having trouble figuring out how to make this code work. What is suppose to is depending on the arguments giving from the command line, it suppose to print out a greeting.
int main (int argc, char *argv[]) {
double testscore;
if (argc == 2) {
printf("Hello, Mr.%s.\n", argv[1]);
}
else if (argc == 3 && argc == testscore) {
testscore = atof(argv[2]);
printf("Hi, Mr.%s, your score is %.1f\n", argv[1], testscore);
}
else {
printf("My name is %s %s.\n", argv[1], argv[2]);
}
}
If someone puts only their last name, then the terminal will print out...
Hello, Mr. last_name
...because they only put in one argument. This works fine.
The part where I am stuck on is when the command line arguments given are == 3. If 3 arguments are given then either the terminal is suppose to print out...
Hi, Mr. last_name, your test score is test_score
...or...
My name is first_name last_name.
If I put in the command line arguments only the last name and test score (Smith 3.4) then it prints out (example using the last name Smith) then it prints out...
My name is Smith 3.4
However, it does work for putting in the first name and last name (John Smith). This gives...
My name is John Smith.
I do not want the answer, I just want what I am doing wrong and hints on how to fix it.
I do not want the answer, I just want what I am doing wrong and hints on how to fix it.
Problem 1: You are using testscore variable before it is being initialized.
Problem 2: You are not performing error handling with atof. I would suggest to use strtod(). You can perform some error handling with it to know that the third argument is a float or not. You can also create your own implementation of atof() which will convert and report error in conversion, if any.
Hint: Try to first check that the number of arguments passed to the c program. After that, try to convert third argument to float using strtod() or your own implementation. If it successfully converts, assign the result of float convrsion to test_score and print last_name and testscore. If not, then consider third argument as last_name and print first_name and last_name.
Your problem is with this line:
else if (argc == 3 && argc == testscore) {
In fact, when argc == 3, then you want to check if argv[2] is a numeric argument.
else if ( (argc==3) && (is_numeric_arg(argv[2])==1)) {
A possible implementation would be:
int is_numeric_arg(char* arg)
{
int isInt = 0;
int isFloat = 0;
int isChar = 0;
char* currChar;
int i = 0;
currChar = arg;
isInt = 1;
while (*currChar != '\0')
{
if(*currChar < '0' || *currChar > '9')
{
if(*currChar == '.' && isInt == 1)
{
isInt = 0;
isFloat = 1;
}
else
{
isInt = 0;
isChar = 1;
}
}
currChar++;
}
if (isChar == 1){ return 0; } // argument is a string
else { return 1; } // argument is a int or float
}
int main (int argc, char *argv[]) {
double testscore;
if (argc == 2) {
printf("Hello, Mr.%s.\n", argv[1]);
}
else if ( (argc==3) && (is_numeric_arg(argv[2])==1)) {
testscore = atof(argv[2]);
printf("Hi, Mr.%s, your score is %.1f\n", argv[1], testscore);
}
else {
printf("My name is %s %s.\n", argv[1], argv[2]);
}
}
I did not test the code and there is probably a better way to check that the argument from the command line is "numeric".
Got it the answer guys. To check it without using another function, it would be
....
else if ( argc==3 && sscanf(argv[2], "%f", testscore)
{
testscore = atof(argv[2]);
printf("Hi, Mr.%s, your score is %.1f\n", argv[1], testscore);
}
...

Storing values of file into array leads to weird behaviour

Let's say I've got the file
5f2
3f6
2f1
And the code:(The printf should print the second numbers (i.e 2,6, and 1) but it doesn't
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, char * argv[])
{
FILE *ptr;
char str[100];
char * token;
int a, b, i;
int arr[4];
if(argc > 1)
{
ptr = fopen(argv[1],"r");
if(ptr == NULL)
{
exit(1);
}
}
else
{
exit(1);
}
//And I'm looking to parse the numbers between the "f" so..
while(fgets(str,100,ptr) != NULL)
{
token = strstr(str,"f");
if(token != NULL)
{
a = atol(str); // first number
b = atol(token+1); // second number
arr[i] = b; // store each b value (3 of em) into this array
}
i++;
printf("Values are %d\n",arr[i]); //should print 2,6 and 1
}
}
I've tried to move the printf outside the loop, but that seems to print an even weirder result, I've seen posts about storing integers from a file into an array before, however since this involves using strstr, I'm not exactly sure the procedure is the same.
int i,j=0;
while(fgets(str,sizeof(str),file) != NULL)
{
size_t n = strlen(str);
if(n>0 && str[n-1] == '\n')
str[n-1] = '\0';
i = str[strlen(str)-1] - '0'; /* Convert the character to int */
printf("%d\n",i);// Or save it to your int array arr[j++] = i;
}
Just move to the last character as shown and print it out as integer.
PS: fgets() comes with a newline character you need to suppress it as shown
You are never initializing i, then you are reading into arr[i] (which just happens to not crash right there), then increment i (to "undefined value + 1"), then print arr[i] -- i.e., you are writing to and reading from uninitialized memory.
Besides, your FILE * is ptr, not file. And you should get into the habit of using strtol() instead of atol(), because the former allows you to properly check for success (and recover from error).

C Array struct, trying to access data, but is coming up with the same for all arrays

So, my goal is to create a linear search, but i have got that down pat, I am having one problem with accessing strings from the struct, that i have stored using a txt file, so in linearSearch() I tried doing this:
printf("Name: %s \n", q.name[i]);
printf("Data: %d \n", q.data[i]);
The data would be perfect but name would just print out the same name for every array which would be the last item that I put into the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char* name[10];
int data[10];
}Word;
//int bubblesort (Word word);
void linearSearch(char* name, Word q);
int main (int argc, const char *argv[]){
Word q;
char username[9]; /* One extra for nul char. */
int score;
int i = 0;
FILE *ifp, *ofp;
ifp = fopen("Data.txt", "r");
while (fscanf(ifp, "%s %d", &username, &score) == 2) {
q.name[i] = username;
printf ("Name: %s, I = %d \n", q.name[i], i);
q.data[i] = score;
printf ("Data: %d, I = %d \n", q.data[i], i);
i++;
}
linearSearch("Matt", q);
return EXIT_SUCCESS;
}
void linearSearch(char* name, Word q){
int i = 0;
int foundIt = 0;
int numNames = sizeof(&q.name);
while ((foundIt == 0) && (i <= numNames)){
printf("Name: %s \n", q.name[i]);
printf("Data: %d \n", q.data[i]);
if ((strcmp(name, q.name[i]) != 0)){
i = i + 1;
} else {
foundIt = 1;
}
}
if (foundIt == 1){
printf("Name found at position %d", i);
} else {
printf("Required person not found");
}
}
This happens because of the code
q.name[i] = username;
You cannot assign the value of an array using = operator. Here, you're assigning the address of username to every q.name[i]. So, the last value of username is reflected throughout the array.
What you actually need is to use malloc() to allocate memory and then strcpy() to copy the string contents.
Otherwise, you can also make use of strdup().
Either way, don't forget to free() the allocated ememory once you're done using them.
I can see that you declared char username[9], so I assume your names should be at most 8 characters long. You should :
read with : fscanf(ifp, "%8s %d",&username, &score) == 2 : the & is useless in front of an array (it decays nicely to a pointer), but you should limit size of input - ok , your problem does not come from there
use a 2D char array for Word.name instead of an array of pointers. That way your memory is already allocated and you can safely strcpy to it :
typedef struct {
char name[10][9];
int data[10];
}Word;
then :
strcpy(q.name[i], username); /* safe because qname[i] and username are both [9] */
The rule here is always control that you do not risk a buffer overrun when writing in char arrays/
An alternative way would be to do dynamic allocation through strdup but in that case you should free it.

scanf catches char type string with \0 and bunch of zeros after in C

I'm working on enumerations in C and can't find the source of problem in the following example that, the output is always "Sorry!":
#include <stdio.h>
#include <stdlib.h>
typedef enum
{
summer, winter, fall, spring
} season;
void printPhrase (season s);
int main(void)
{
printf("What's your prefered season? ");
char seasonHold[10], seasonHold2[10];
scanf("%s", seasonHold);
for (int n = 0; n < strlen(seasonHold); n++)
{
if (n != '\0')
seasonHold2[n] = seasonHold[n];
}
printPhrase (*seasonHold2);
return 0;
}
void printPhrase (season s)
{
if (s == summer)
printf("It's hot out!\n");
else if (s == fall)
printf("It's getting cooler!\n");
else if (s == winter)
printf("Should be really cold!\n");
else if (s == spring)
printf("It should be lovely outside!\n");
else
printf("Sorry!\n");
}
The problem is whatever input I enter, there's always one output: Sorry!
Thanks.
Also, this can solve the matter:
I could manage it by changing main function into following:
int main(void)
{
printf("What's your prefered season? ");
char seasonHold[10];
scanf("%s", seasonHold);
if (seasonHold[0] == 's')
printPhrase (summer);
else if (seasonHold[0] == 'f')
printPhrase(fall);
else if (seasonHold[1] == 'p')
printPhrase(spring);
else if (seasonHold[0] == 'w')
printPhrase(winter);
return 0;
}
Enums are like constant integers. Here: summer=0, winter=1,...
seansonhold is a char*. By dereferencing it you get a char. This char will then be converted to a 'season' type because char->int does not give compiler errors.
So you basically test here if the first byte of your char array is equal to 0,1,2..
If you are sure seasonHold is null-terminated (it will be here), you can use a pointer and while loop to accomplish what you want:
char *ptr = seasonHold;
n = 0;
while (*ptr++) { /* same as saying while (*ptr++ != '\0') */
seasonHold2[n] = seasonHold[n]; /* could also do: seasonHold2[n] = *ptr; */
n++;
}
seasonHold2[n] = 0; /* null-terminate */
Additionally, if you would like to dynamically allocate seasonHold2, you can simply declare it as a pointer, include string.h and use strdup to copy seasonHold to seasonHold2, e.g.:
#include <string.h>
...
char *seasonHold2;
...
seasonHold2 = strdup (seasonHold);

Resources