Array of structs replacing values over itself - c

Ok so I have the below code and I am just pulling various things from a file and inputing them in an array of structs, it "seemingly" works initially, BUT when I go to printing it after it is done with the file it seemed to have replaced all of the courses and names with the very last vale, oddly this doesnt happen with the integers (grades), the grades do get inputed properly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
char *name;
char *course;
int grade;
};
void courseSort(struct student d[20], int size);
int main(void)
{
FILE* fp;
char* filename = "grades.csv";
char buffer[100];
char* name, *class;
char* del=",";
int grade, i, counter=0;
struct student d[20];
if((fp=fopen(filename, "r"))==NULL)
{
printf("unable to open %s\n", filename);
exit(1);
}
while(fgets(buffer, sizeof(buffer), fp) !=NULL)
{
name = strtok(buffer,del);
class=strtok(NULL,del);
grade = atoi(strtok(NULL,del));
d[counter].name=name;
d[counter].course=class;
d[counter].grade=grade;
printf("%s %s %d\n",d[counter].name,d[counter].course,d[counter].grade);
counter++;
}
printf("\n\n\n");
for(i=0;i<counter;i++)
printf("%s %s %d\n",d[i].name,d[i].course,d[i].grade);
courseSort(d,counter);
fclose(fp);
}
I am not sure what I am doing wrong :( it all seems straightforward but not sure why it just replaces everything with the latest one.

The strtok returns a pointer to the buffer and does not allocate memory. Since you do not copy the strings, you end up with lots of pointers pointing to the same buffer that is overwritten at each iteration of the loop.
To fix this, you need to change your loop to copy the strings using strdup:
while(fgets(buffer, sizeof(buffer), fp) != NULL)
{
d[counter].name = strdup(strtok(buffer, del));
d[counter].course = strdup(strtok(NULL, del));
d[counter].grade = atoi(strtok(NULL, del));
counter++;
}
Don't forget to return the allocated memory with free once you no longer need the strings:
for (i = 0; i < counter; i++) {
free(d[i].name);
free(d[i].course);
d[i].name = NULL;
d[i].course = NULL;
}
Note that strdup is part of POSIX1.2001 standard, not part of C89. If it is not available, you'll have to re-implement it yourself (quite easy):
char *my_strdup(const char *str) {
char *copy;
size_t len = strlen(str) + 1;
if (len == 0) return NULL;
copy = (char *)malloc(len);
if (copy == NULL) return NULL;
memcpy(copy, str, len);
return copy;
}

This is a simple misunderstanding of pointers and char arrays (strings). Here are a couple pages that explains them pretty well:
http://www.cplusplus.com/doc/tutorial/pointers/
http://www.cplusplus.com/doc/tutorial/ntcs/
In your case, you are setting your struct pointer values equal to the returned pointer from strtok. A lot of those string functions work by putting the result at a certain memory address and returning the pointer to it. The pointer returned is always the same, so all your struct values are going to point to the last result of the strtok call.
This is why you need strdup (String duplicate). Basically it takes the value at the address given and copies the contents into a new place in memory and returns the value.

The error is here.
d[counter].name=name;
replace with:
d[counter].name = strdup(name); /*don't forget to free this memory.*/
the issue for the courses is the same.

Related

cant find whos the owner of malloc - memory leak

I have a problem with a memory leak with temp_line.
This function read a whole text from a file and assign each word to a big linked list. I cant figure out who's the owner of temp_line whenever I exit this function, and whenever I am trying to replace temp_line with non-dynamic variable (like temp_line[1000]) every time I get a new line, its overwritten the data from the old line (and then I am getting a segmentation fault because of that).
So I really don't know how to solve it.
int fill(FILE *f, LinkedList *linkedlist) {
FILE *file = fopen((const char *)f, "r");
char line[MAX_LINE]; //MAX_LINE = 1000
while (fgets(line, MAX_LINE, file) != NULL) {
char *temp_line = malloc(MAX_LINE);
if (temp_line == NULL) {
fclose(file);
return false;
}
strcpy(temp_line, line);
read_line(linkedlist, temp_line);
}
fclose(file);
return true;
}
int read_line(LinkedList *linkedlist, char *line) {
char *word;
while (true) {
word = strtok(NULL, "\n ");
if (word == NULL) {
break;
}
add_node(linkedlist, word);
}
return true;
}
add_node adds the node to the linked list and returns the node.
There are multiple problems in the code:
FILE *file = fopen((const char *)f, "r"); with f defined as FILE *f is probably incorrect. If you are given a FILE *, just read from it.
strtok should be first called with the string, then with NULL until it returns NULL.
you insert nodes into the list from pointers into the middle of the block allocated by read_line. There is no way to determine the beginning of such a block, nor any way to determine how many pointers point into the block. Memory allocated and used this way cannot be freed. You should instead use strdup() to allocate individual copies of the words. These pointers can be later freed with free() in the del_node() function. Alternately, add_node() could make a copy of the string argument, which would be consistent with del_node() freeing this data.
regarding your question: Cannot find who is the owner of a malloc block... indeed it is the C programmers' responsibility to keep track of allocated memory. There is no way to test if a pointer is valid, not whether it points to an allocated block. You must design the program consistently so memory ownership can always be determined from context.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int read_line(LinkedList *linkedlist, char *line) {
char *word;
while ((word = strtok(line, "\n ")) != NULL) {
add_node(linkedlist, strdup(word));
line = NULL;
}
return true;
}
int fill(FILE *file, LinkedList *linkedlist) {
char line[MAX_LINE];
while (fgets(line, sizeof line, file) != NULL) {
read_line(linkedlist, line);
}
return true;
}
I am trying to replace temp_line with non-dynamic variable (like temp_line[1000])
temp_line[1000] is also called "automatic memory" because it is automatically deallocated at the end of its scope. What this means is temp_line would be deallocated on each iteration of the loop.
Perhaps the important thing to realize is strtok does not allocate any memory. word points at memory inside temp_line.
Let's say you did.
while (fgets (line, MAX_LINE, file) != NULL) {
char temp_line[MAX_LINE];
strcpy(temp_line, line);
read_line(linkedlist, temp_line);
}
On each loop temp_line will be allocated, passed into read_line, and then deallocated. The words you add to the linked list point at deallocated memory.
The question is who "owns" memory. Because it uses strtok, read_line assumes that it owns line and that line will not be deallocated. The safe thing to do is to change read_line so it copies each word from line.
int read_line(LinkedList *linkedlist, char *line) {
for(
char *word = strtok(line, "\n ");
word;
word = strtok(NULL, "\n ")
) {
// Duplicate the word so it no longer refers to line.
add_node(linkedlist, strdup(word));
}
return true;
}

C remove special characters from string

I am very new to C, and I have created a function that removes special characters from a string and returns a new string (without the special characters).
At first glance, this seemed to be working well, I now need to run this function on the lines of a (huge) text file (1 Million sentences). After a few thousand lines/sentences (About 4,000) I get a seg fault.
I don't have much experience with memory allocation and strings in C, I have tried to figure out what the problem with my code is, unfortunately without any luck.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char *preproccessString(char *str) {
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = malloc(sizeof(str));
// Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
// Changed from array to string for sake of simplicity
char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Initalize two integers
// i will be increased for every char in the string
int i = 0;
// j will be increased every time a new char is added to the result
int j = 0;
// Loop over the input string
while (str[i] != '\0') {
// l will be increased for every char in the allowed chars array
int l = 0;
// Loop over the chars in the allowed chars array
while (allowedCharsArray[l] != '\0') {
// If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
if (allowedCharsArray[l] == toupper(str[i])) {
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = toupper(str[i]);
j++;
}
l++;
}
i++;
}
return result;
}
Here is the rest of the program, I think the problem is probably here.
int main(int argc, char *argv[]) {
char const * const fileName = argv[1];
FILE *file = fopen(fileName, "r");
char line[256];
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preproccessString(line));
}
fclose(file);
return 0;
}
You have several problems.
You're not allocating enough space. sizeof(str) is the size of a pointer, not the length of the string. You need to use
char *result = malloc(strlen(str) + 1);
+ 1 is for the terminating null byte.
You didn't add a terminating null byte to the result string. Add
result[j] = '\0';
before return result;
Once you find that the character matches an allowed character, there's no need to keep looping through the rest of the allowed characters. Add break after j++.
Your main() function is never freeing the results of preprocessString(), so you might be running out of memory.
while (fgets(line, sizeof(line), file)) {
char *processed = preproccessString(line);
printf("%s\n", processed);
free(processed);
}
You could address most of these problems if you have the caller pass in the result string, instead of allocating it in the function. Just use two char[256] arrays in the main() function.
int main(int argc, char *argv[])
{
char const* const fileName = argv[1];
FILE* file = fopen(fileName, "r");
char line[256], processed[256];
while (fgets(line, sizeof(line), file)) {
processString(line, processed);
printf("%s\n", processed);
}
fclose(file);
return 0;
}
Then just change the function so that the parameters are:
void preprocessString(const char *str, char *result)
A good rule of thumb is to make sure there is one free for every malloc/calloc call.
Also, a good tool to keep note of for the future is Valgrind. It's very good at catching these kinds of errors.
There are some major issues in your code:
the amount of memory allocated is incorrect, sizeof(str) is the number of bytes in a pointer, not the length of the string it points to, which would also be incorrect. You should write char *result = malloc(strlen(str) + 1);
the memory allocated in preproccessString is never freed, causing memory leaks and potentially for the program to run out of memory on very large files.
you do not set a null terminator at the end of the result string
Lesser issues:
you do not check if filename was passed nor if fopen() succeeded.
there is a typo in preproccessString, it should be preprocessString
you could avoid memory allocation by passing a properly sized destination array.
you could use isalpha instead of testing every letter
you should cast the char values as unsigned char when passing them to toupper because char may be a signed type and toupper is undefined for negative values except EOF.
there are too many comments in your source file, most of which are obvious but make the code less readable.
Here is a modified version:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// transform the string in `str` into buffer dest, keeping only letters and uppercasing them.
char *preprocessString(char *dest, const char *str) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (isalpha((unsigned char)str[i])
dest[j++] = toupper((unsigned char)str[i]);
}
dest[j] = '\0';
return dest;
}
int main(int argc, char *argv[]) {
char line[256];
char dest[256];
char *filename;
FILE *file;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
filename = argv[1];
if ((file = fopen(filename, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preprocessString(dest, line));
}
fclose(file);
return 0;
}
The following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
properly checks for length of input string parameter
makes use of characteristic of strchr() also checking the terminating NUL byte
limits scope of visibility of local variables
the calling function is expected to properly cleaning up by passing the returned value to free()
the calling function is expected to check the returned value for NULL
informs compiler the user knows and accepts when an implicit conversion is made.
moves allowedCharsArray to 'file static scope' so does not have to be re-initialized on each pass through the loop and marks as 'const' to help the compiler catch errors
and now the proposed code: (note: edited per comments)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
char *preproccessString(char *str)
{
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = calloc( sizeof( char ), strlen(str)+1);
if( !result )
{
perror( "calloc failed" );
return NULL;
}
// Array of allowed chars
static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Loop over the input string
for( int j=0, i=0; str[i]; i++)
{
if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
{
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = (char)toupper(str[i]);
j++;
}
}
return result;
}
I think the problem is you are using malloc which allocates memory from the heap and since you are calling this function again and again you are running out of memory.
To solve this issue you have to call the free() function on the pointer returned by your preprocessString function
In your main block
char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);

Segfault in C program, malloc call

I am writing a program that takes a list of path ( environmental variable), splits the paths and prints it. When compiling it I get a segfault. The following is my output on GDB :
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400eb0 in dest (name=0x7fffffffbce0 "PATH") at executables.c:100
100 dest[i] = malloc(srclen+1);
On valgrind:
==21574== 1 errors in context 2 of 3:
==21574== Use of uninitialised value of size 8
==21574== at 0x400EB0: dest (executables.c:100)
==21574== by 0x400B5B: main (main.c:9)
This is my function:
char** dest(char *name){
int i=0;
char *vp;
const char s[2]=":";
char *token;
char **dest;
name[strlen(name)-1]='\0';
vp=getenv(name);
if(vp == NULL){
exit(1);
}
token =strtok(vp,s);
while( token != NULL ){
size_t srclen = strlen(token);
dest[i] = malloc(srclen+1);
strcpy(dest[i], token);
token = strtok(NULL, s);
i++;
}
dest[i]=NULL;
return dest;
}
And this is my main:
#include "executables.h"
int main(int argc, char **argv){
char *path;
char name[BUFSIZ];
printf("enter name of environment variable:\n");
fgets(name,BUFSIZ,stdin);
char **p=dest(name);
int j=0;
while(p[j]!=NULL){
printf("%s\n",p[j]);
j++;
}
return(0);
}
Use strdup(). Saves steps (accounts for
'\0' too). You have to allocate some memory before hand for the approach you're using. Otherwise you might want a linked list and allocate packets instead of using the array pattern. When you say dest[i] = <ptr value> you're indexing to an offset of unallocated memory and storing something there, so it's a segvio.
#include <string.h>
#define MAXTOKENS 10000
char **get_dest(char *name) {
// Since dest has to be exposed/persist beyond this function all
// need dynamically allocate (malloc()) rather than stack allocate
// of the form of: char *dest[MAXTOKENS].
char *dest = malloc(MAXTOKENS * sizeof (char *)); // <--- need to allocate storage for the pointers
char *vp;
if ((vp = getenv(name)) == NULL)
exit(-1); // -1 is err exit on UNIX, 0 is success
int i = 0;
char *token = strtok(vp, ":");
while (token != NULL) {
dest[i] = strdup(token); // <=== strdup()
token = strtok(NULL, ":");
i++;
}
// dest[i] = NULL; // Why are you setting this to NULL after adding token?
return dest;
}
It's better if main() takes care of passing a proper null-terminated string to the get_dest() function because main is where the finicky fgets() is handled. Generally you want to do things locally where it makes the most sense and is most relevant. If you ever took your get_dest() function and used it somewhere where the strings were not read by fgets() it would just be a wasted step to overwrite the terminator there. So by initializing the char array to zeroes before fgets() you don't have to worry about setting the trailing byte to '\0'.
And finally probably not good to have your function name dest the same name as the variable it returns dest. In some situations having multiple symbols in your program with the same name can get you into trouble.
#include "executables.h"
int main(int argc, char **argv) {
char *path;
char name[BUFSIZ] = { 0 }; // You could initialize it to zero this way
printf("enter name of environment variable:\n");
// bzero(name, BUFSIZ); //... or you could initialize it to zero this way then
fgets(name, BUFSIZ, stdin);
char **p = get_dest(name);
int j = 0;
while(p[j] != NULL) {
printf("%s\n", p[j]);
j++;
free(p[j]); // like malloc(), strdup'd() strings must be free'd when done
}
free(p);
return 0;
}
dest[i] = malloc(srclen + 1);
You need to allocate memory for the pointer to char pointers (dest) as well as each char pointer stored in dest. In the code you provided, neither step is taken.
From the manpage of getenv:
Notes
...
As typically implemented, getenv() returns a pointer to a string
within the environment list. The caller must take care not to modify
this string, since that would change the environment of the process.
Your code violates that rule:
vp=getenv(name);
...
token =strtok(vp,s);
This is an illegal memory write operation.

using strcpy for copying string into the element at index retrieved with atoi

Here's the code, which is supposed to execute the first command in history when "history 1" is entered:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[])
{
int i=0; int j=0; int k=0;
char inputString[100];
char *result=NULL;
char delims[] = " ";
char historyArray[100][100] = {0};
char *tokenArray[100][100] ;
do
{
j = 0;
printf("hshell>");
gets(inputString);
strcpy (historyArray[k], inputString);
k++;
// Break the string into parts
result = strtok(inputString, delims);
while (result!=NULL)
{
//result2 = result;
strcpy(tokenArray[j], result);
//puts(result);
j++;
result= strtok(NULL, delims);
//puts(tokenArray[j]);
}
//j = 0;
puts(tokenArray[0]);
puts(tokenArray[1]);
if (strcmp(tokenArray[0], "exit") == 0)
{
return 0;
}
else if (strcmp(tokenArray[0], "history") == 0)
{
if (j>1)
{
strcpy (result,historyArray[atoi(tokenArray[j-1])]);
}
else
{
//print history array
for (i=0; i<k;i++)
printf("%i. %s\n", i+1, historyArray[i]);
}
}
else
{
printf("Command not found\n");
}
}while (1);
}
However, it crashes. When in debugging, I noticed two things: - the array (tokenArray) address is out of bounds and - Access Violation (Segmentation Fault). You can see the errors in the images below.
What am I missing? What am I doing wrong?
The reason why you are dealing with segmentation fault is because you are trying to copy a string into the memory that has not yet been allocated. You have defined result as a char* and just assigned NULL to it, so trying to copy string into it is wrong:
char *result = NULL;
// ...
strcpy(result, historyArray[atoi(tokenArray[j-1])]);
You need to allocate some memory, that result will point to. Then strcpy can be used to copy string into this memory. You can either use malloc to allocate it dynamically or you can define result as an temporary variable with automatic storage duration (i.e. char result[100];).
Also note that
char *tokenArray[100][100];
defines a two-dimensional array of pointers to char. But what you actually need in this case is an array of strings, so you need to get rid of * just like #cnicutar has pointed out.
And one more note:
strcpy(result,historyArray[atoi(tokenArray[j-1])]);
is quite dangerous thing to do, because when atoi fails, you are trying to access the element out of array bounds, which produces undefined behavior, thus I recommend you doing something like this:
char tokenArray[100][100] = {0};
int index;
char indexString[100] = "8";
if (sscanf(indexString, "%d", &index) == 1) // integer successfully retrieved
{
strcpy(tokenArray[index], "some string");
printf("%s", tokenArray[8]);
}
You probably meant char tokenArray[100][100]; which creates 100 tokens with 100 characters each in 1 token.
writing char *tokenArray[100][100] literally means tokenArray is an array of 100 arrays, which contain 100 char *. But each of those char * points to a random addresses if it is not assigned a proper address.
You are getting a segmentation violation error because one of the char * contains an address which you cannot access.

from static array assignment to array from file

I have this piece of code outside the main function
mystr * arrstr[] = {
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};
I modified it so that it can read the values from a text file. for this purpose i modified this working code to read line from file into array named string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int i=0,j;
char* string[100];
char line[100];
FILE *file;
file = fopen("patt", "r");
while(fgets(line, sizeof(line), file)!=NULL) {
printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return 0;
}
so the final code is now something like this.
..
..
char *getpatterns(const char *filename) {
int i=0;
char* string[100];
char line[100];
FILE *file;
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file)!=NULL) {
//printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return(string);
}
mystr * arrstr[] = getpatterns("patt");/*{
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};*/
..
..
But i get errors like this.
example1.c: In function ‘getpatterns’:
example1.c:43:2: warning: return from incompatible pointer type [enabled by default]
example1.c:43:2: warning: function returns address of local variable [enabled by default]
example1.c: At top level:
example1.c:45:1: error: invalid initializer
make: *** [example1.o] Error 1
Here line 45 is this line
mystr * arrstr[] = getpatterns("patt");/*{
Please suggest corrective action.
The first warnings are that you are trying to return a char ** as a char * (which is not a good idea), and that you are returning a local variable which is deallocated when the function returns (also not a good idea). The last is telling you that you can't use function calls in initializers of global variables in C (you can do some of that in C++, though I'm not convinced you can do this one).
Fixing it will take some rethinking. You need the function to return allocated memory, or you need to pass the memory to the function. And you'll have to change the type of the global variable. And you'll need to know how many entries there are in the array, somehow.
mystr **arrstr = 0; // Either
mystr *arrstr[100]; // Or
On the whole, I'd probably go with memory allocation and the 'either' declaration:
mystr **arrstr = 0;
char **getpatterns(const char *file)
{
char **array = 0;
...code similar to yours that allocates entries in the array...
...include space for a null pointer to mark the end of the list of strings...
return(array);
}
int main(void)
{
arrstr = getpatterns("patt");
...
}
(Another 'cheat' mechanism would use static char *string[100]; in getpatterns(); you still have to fix the return type and the type of the global variable.)
I tried these but, errors were not resolved: ...
It's impossible to tell exactly what was wrong without your code. However, the code below works for me. The source code was in a file gp.c; the source code prints itself, and releases the memory. Checked under valgrind with a clean bill of health.
Note that your original code did not allocate enough space for the strings it was copying (because you retained the newline read by fgets() — but you were at least using fgets() and not gets(), which is very important). This code uses memmove() — it could use memcpy() instead since there's guaranteed to be no overlap, but memmove() always works and memcpy() doesn't necessarily work when the source data overlaps the target data. It knows how long the string is, so the copy function doesn't need to test for whether the character being copied is a NUL '\0'. The code carefully ensures that there's a null pointer at the end of the list of pointers; that's how you know when you've reached the end of the list of strings. The code also works when gp.c is an empty file.
The algorithm using three items num_xxx, max_xxx, and xxx is a typical way to handle incremental allocation. It typically over-allocates slightly; if you're concerned about the space, you could use strings = realloc(strings, (num_strings+1) * sizeof(*strings)); max_strings = num_strings + 1; at the end of the loop to release the extra space. The + 1 is to allow for the null pointer. By roughly doubling the size allocated each time you allocate, you avoid quadratic behaviour compared with incrementing by one each time.
Notice too that the code carefully avoids losing the allocated space if the realloc() fails. You should 'never' use space = realloc(space, new_size); to avoid losing your pointer. The code carefully avoids dereferencing null pointers, and simply stops reading when there is a memory shortage.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **getpatterns(const char *filename);
char **getpatterns(const char *filename)
{
size_t num_strings = 0;
size_t max_strings = 0;
char **strings = 0;
FILE *file = fopen(filename, "r");
if (file != 0)
{
char line[4096];
while (fgets(line, sizeof(line), file) != NULL)
{
if (max_strings == 0 || num_strings >= max_strings - 1)
{
size_t new_num = max_strings * 2 + 2;
char **new_space = realloc(strings, new_num * sizeof(*new_space));
if (new_space == 0)
break;
strings = new_space;
max_strings = new_num;
}
size_t len = strlen(line); /* Includes '\n' at end */
strings[num_strings] = (char*)malloc(len);
memmove(strings[num_strings], line, len - 1);
strings[num_strings][len] = '\0';
strings[++num_strings] = 0; /* Null terminate list of strings */
}
fclose(file);
}
return(strings);
}
int main(void)
{
char **data = getpatterns("gp.c");
char **argp = data;
if (argp != 0)
{
/* Print data */
while (*argp != 0)
puts(*argp++);
/* Free space */
argp = data;
while (*argp != 0)
free(*argp++);
free(data);
}
return(0);
}

Resources