Return pointer to array of strings C - c

I am trying to return an array of strings that are taken from a file. The file looks like this (first line is number of words, and every new line is word). I get some weird output in main part when functions are over. I want to return pointer to array of strings. Note that some part of code that uses printing is for checking my program.
Here is the function that allocates memory:
char *generisiProstor(int n) {
return (char*)malloc(n*sizeof(char[20]));
}
This is function for taking words from rijeci.txt and should return pointer to array of strings that contains the words:
char* ucitajRijeci(int n) {
char i;
char *rijeci;
static const char filename[] = "rijeci.txt";
FILE *file;
file = fopen(filename, "r");
if (file != NULL)
{
char line[20];
int n;
fscanf(file, "%d", &n);
rijeci = generisiProstor(n);
if (rijeci == NULL) {
return NULL;
}
int i = -1;
fgets(line, 20, file); //skipping first line witch is integer and not needed
while (fgets(line, 20, file) != NULL)
{
printf("%s\n", line); //normal output
i++;
strcpy(rijeci + i, line);
printf("%s\n", rijeci + i); //normal expected output
}
for (i = 0; i < n; i++) {
printf("%s\n", rijeci + i); //wrong output
}
}
return rijeci;
}
Main
int main()
{
static const char filename[] = "rijeci.txt";
FILE *file;
file = fopen(filename, "r");
char *rijeci;
int i;
if (file != NULL)
{
char line[20];
int n;
fscanf(file, "%d", &n);
rijeci = ucitajRijeci(n);
printf("Here is the array: ");
for (i = 0; i < n; i++) {
printf("%s ", rijeci+i); //wrong output
}
}
return 0;
}

Here you have to use 2-dimensional array (char ** instead of char *). Since you are returning 2-d array you have to declare rijeci as char **rijeci;
Return types of both functions should be also char **.
Change rijeci + i to rijeci[i].
Proper code indentation.
Try this modified code. This will work :-
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* generisiProstor */
char **generisiProstor(int n)
{
char **c; // making 2-d array
c = (char **)malloc(n * sizeof(char *));
for (int i = 0; i < n; i++)
{
c[i] = (char *)malloc(20 * sizeof(char));
}
return c;
}
/* ucitajRijeci */
char **ucitajRijeci(int n)
{
char **rijeci; // change to char **
static const char filename[] = "rijeci.txt";
FILE *file;
file = fopen(filename, "r");
if (file != NULL)
{
char line[20];
int n;
fscanf(file, "%d", &n);
rijeci = generisiProstor(n);
if (rijeci == NULL)
{
return NULL;
}
int i = -1;
fgets(line, 20, file); //skipping first line witch is integer and not needed
while (fgets(line, 20, file) != NULL)
{
printf("%s\n", line); //normal output
i++;
strcpy(rijeci[i], line);
printf("%s\n", rijeci[i]); //changed to rijeci[i]
}
for (i = 0; i < n; i++)
{
printf("%s\n", rijeci[i]); //changed to rijeci[i]
}
}
return rijeci;
}
/* main() */
int main()
{
static const char filename[] = "rijeci.txt";
FILE *file;
file = fopen(filename, "r");
char **rijeci; // change to char **
int i;
if (file != NULL)
{
char line[20];
int n;
fscanf(file, "%d", &n);
rijeci = ucitajRijeci(n);
printf("Here is the array: ");
for (i = 0; i < n; i++)
{
printf("%s ", rijeci[i]); //changed to rijeci[i]
}
}
return 0;
}

The first problem you encounter is here:
char *generisiProstor(int n) {
return (char*)malloc(n*sizeof(char[20]));
}
You want an array of char pointers, but you return a char pointer, or an array of char.
This part should be:
char **generisiProstor(int n) {
return (char**)malloc(n*sizeof(char[20]));
}
The same problem comes with char *rijeci, you are declaring it as a string or a char pointer.
You should declare it like this char **rijeci (you might want it to be char *(rigeci[20]) in this context) so this will be an array of strings.
If I get your code right another problem might come from this part:
while (fgets(line, 20, file) != NULL)
{
printf("%s\n", line); //normal output
i++;
strcpy(rijeci + i, line);
printf("%s\n", rijeci + i); //normal expected output
}
Earlier in the code, you allocate memory for n words. Here you are reading the line, placing it into line. So when you read the first line i is 0, but you increment it before copying it, so your array has its first occurence unset and you are writing the last word on unallocated memory.
This part should be:
while (fgets(line, 20, file) != NULL)
{
printf("%s\n", line); //normal output
strcpy(rijeci + i, line);
i++
printf("%s\n", rijeci + i); //normal expected output
}

If you want to return a pointer to a char array of size 20 you have to declare the function as following:
char (*generisiProstor(int n))[20]
{
return malloc(n*sizeof(char[20]));
}
The variable which holds the pointer to the arrays is declared as:
char (*rijeci)[20];
rijeci[i] is of type char[20] and you can write your strings there.

Do you know the definitions of array and string?
I'll give them to you, as given in the 2011 C-standard:
An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. […]
A string is a contiguous sequence of characters terminated by and including the first null character. […]
Thus, an array is a type derived from a complete object-type, but a string is not a type but a data-structure.
You are very cast-happy. Are you sure forcing the compiler to believe you without cause is a good habit to get into? Also prefer sizeof expr over sizeof (TYPE), as it's harder to get wrong initially or out-of-sync when refactoring later.
Consider reading "Do I cast the result of malloc?".

You're allocating the correct amount of memory, but you need to change how you use it. malloc() can only return a "flat" array of characters, so the return value from generisiProstor() is a simple pointer to the first character in the whole array.
The reason it's working initially is that each string overwrites the tail end of the previous string, so when you do the print outs during the read in loop, they show correctly. But even so, the payload of your rijeci array is completely corrupt by the time you've finished reading.
One possible solution is to use a struct to hold your words:
struct Rijec
{
char rijec[20];
};
and then change generisiProstor(int n) to be this:
struct Rijeci *generisiProstor(int n)
{
return malloc(n * sizeof(struct Rijec));
}
Note that the cast is not needed in C, and indeed should be avoided.
Then, you'll need to change the top of ucitajRijeci() to look like this:
struct Rijec *ucitajRijeci(int n)
{
struct Rijec *rijeci;
...
and in all cases where you're using rijeci + i change that to rijeci[i].rijec.
The net result of this is that when you use i to index a word in the rijeci array, the offset will now be correct.

Related

Novice C question: Working with a variable-length array of variable-length strings?

I probably got an easy one for the C programmers out there!
I am trying to create a simple C function that will execute a system command in and write the process output to a string buffer out (which should be initialized as an array of strings of length n). The output needs to be formatted in the following way:
Each line written to stdout should be initialized as a string. Each of these strings has variable length. The output should be an array consisting of each string. There is no way to know how many strings will be written, so this array is also technically of variable length (but for my purposes, I just create a fixed-length array outside the function and pass its length as an argument, rather than going for an array that I would have to manually allocate memory for).
Here is what I have right now:
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
const char terminator = '\0';
if ((file = popen(in, "r")) == NULL) {
return 1;
}
for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
if (pclose(file)) {
return 2;
}
return 0;
}
and I call it with
#define N 128
int main(void)
{
const char* buffer[N];
const char cmd[] = "<some system command resulting in multi-line output>";
const int code = exec(cmd, buffer, N);
exit(code);
}
I believe the error the above code results in is a seg fault, but I'm not experienced enough to figure out why or how to fix.
I'm almost positive it is with my logic here:
for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
What I thought this does is:
Get a mutable reference to out (i.e. the head pointer)
Save the current stdout line to buffer (via fgets)
Append a null terminator to buffer (because I don't think fgets does this?)
Overwrite the data at head pointer with the value from step 3
Move head pointer strlen(buffer) bytes over (i.e. the number of chars in buffer)
Continue until fgets returns NULL or head pointer has been moved beyond the bounds of out array
Where am I wrong? Any help appreciated, thanks!
EDIT #1
According to Barmar's suggestions, I edited my code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
if ((file = popen(in, "r")) == NULL) return 1;
for (size_t i = 0; i < n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; i += 1) out[i] = buffer;
if (pclose(file)) return 2;
return 0;
}
#define N 128
int main(void)
{
const char* buffer[N];
const char cmd[] = "<system command to run>";
const int code = exec(cmd, buffer, N);
for (int i = 0; i < N; i += 1) printf("%s", buffer[i]);
exit(code);
}
While there were plenty of redundancies with what I wrote that are now fixed, this still causes a segmentation fault at runtime.
Focusing on the edited code, this assignment
out[i] = buffer;
has problems.
In this expression, buffer is implicitly converted to a pointer-to-its-first-element (&buffer[0], see: decay). No additional memory is allocated, and no string copying is done.
buffer is rewritten every iteration. After the loop, each valid element of out will point to the same memory location, which will contain the last line read.
buffer is an array local to the exec function. Its lifetime ends when the function returns, so the array in main contains dangling pointers. Utilizing these values is Undefined Behaviour.
Additionally,
for (int i = 0; i < N; i += 1)
always loops to the maximum storable number of lines, when it is possible that fewer lines than this were read.
A rigid solution uses an array of arrays to store the lines read. Here is a cursory example (see: this answer for additional information on using multidimensional arrays as function arguments).
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINES 128
#define MAX_LINE_LENGTH 512
int exec(const char *cmd, char lines[MAX_LINES][MAX_LINE_LENGTH], size_t *lc)
{
FILE *stream = popen(cmd, "r");
*lc = 0;
if (!stream)
return 1;
while (*lc < MAX_LINES) {
if (!fgets(lines[*lc], MAX_LINE_LENGTH, stream))
break;
(*lc)++;
}
return pclose(stream) ? 2 : 0;
}
int main(void)
{
char lines[MAX_LINES][MAX_LINE_LENGTH];
size_t n;
int code = exec("ls -al", lines, &n);
for (size_t i = 0; i < n; i++)
printf("%s", lines[i]);
return code;
}
Using dynamic memory is another option. Here is a basic example using strdup(3), lacking robust error handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **exec(const char *cmd, size_t *length)
{
FILE *stream = popen(cmd, "r");
if (!stream)
return NULL;
char **lines = NULL;
char buffer[4096];
*length = 0;
while (fgets(buffer, sizeof buffer, stream)) {
char **reline = realloc(lines, sizeof *lines * (*length + 1));
if (!reline)
break;
lines = reline;
if (!(lines[*length] = strdup(buffer)))
break;
(*length)++;
}
pclose(stream);
return lines;
}
int main(void)
{
size_t n = 0;
char **lines = exec("ls -al", &n);
for (size_t i = 0; i < n; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}

C - Why does char array only return last value that's put into it?

I'm writing in C. I'm trying to read lines from a text file, parse the line, and put some info into an array of strings. When I test my code, every value in the array seems to be the last value inserted. What causes this?
int r;
char *users[51]; //given no more than 50 users
for (r = 0; r < 51; r++) {
int n = 15; //arbitrary guess at length of unknown usernames
users[r] = malloc((n + 1) * sizeof(char));
}
FILE *fp;
fp = fopen(argv[1], "r");
char *username;
int counter = 0;
char line[100];
while (fgets(line, 100, fp) != NULL) {
username = strtok(line, ":");
users[counter] = username;
printf("%s\n", username);
printf("%s\n", users[counter]);
//counter increase for later
counter += 1;
strtok is a very confusing function:
it modifies the array it receives a pointer to
it returns a pointer to an element of this array.
it keeps an internal state which makes it non-reentrant and non thread-safe.
Hence username points inside line. You store this pointer into users[counter]. At the end of the loop, all entries in users point to the same array that has been overwritten by every call to fgets().
You should duplicate the contents of the array with strdup():
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[) {
char *users[51]; //given no more than 50 users
int r;
FILE *fp;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
char line[100];
int counter = 0;
while (counter < 50 && fgets(line, 100, fp) != NULL) {
char *username = strtok(line, ":");
if (username != NULL) {
users[counter] = strdup(username);
//counter increase for later
counter += 1;
}
}
users[counter] = NULL;
...
}
You put a sensible value into each entry in the array:
users[r] = malloc((n+1) * sizeof(char));
But then you overwrite it with a nonsensical value (a pointer into line):
users[counter] = username;
What you presumably wanted to do was copy the string pointed to by username into the space allocated at users[counter]. The strcpy function can do this.
I would just like to add to David's answer that you should also check the username string is null terminated before using strcpy(). I can't remember if strtok() null terminates but you shouldn't rely on it anyway.

How can I store an array in a MD array, and return that array from function?

I have a method that takes in a list of files, and gets data about the files. The format of those files is returned as two 1D arrays. I want to combine those into a single 2D array.
The files only return with a type array, and a data array.
Here is the method in question. I'm successfully getting the data I need out of the files and into the 1D array, but when I try to add them to the 2D array, it runs (but gives a warning that char * and char *[][] aren't equivalent).
char *ParseFiles(char *listOfFiles[32])
{
char *allFileData;
char *tmpArray[32][32];
allFileData = tmpArray;
for(int i = 0; i < listOfFiles[i]; i++)
{
int j = 0;
// Get the File Type
char *currentFileType = GetFileType(listOfFiles[i], strlen(listOfFiles[i]));
if(currentFileType)
{
// Warning char * and char *[][] are not equivalent.
tmpArray[i][j] = currentFileType;
printf("File %d is %s\n", i, currentFileType);
//printf("File %d is %s\n", i, &tmpArray[i][j]);
//free(currentFileType);
}
//Get all File Data
// Warning char * and char *[][] are not equivalent.
char *currentFileData = GetFileData(listOfFiles[i]);
j = j + 1;
if(currentFileData)
{
tmpArray[i][j] = currentFileData;
printf("File Data:\n%s\n", currentFileData);
//free(currentFileType);
}
}
return allFileData;
}
When I try to output the tmpArray[][] values in the printf statements that are commented out, I get unexpected characters (unexpected to someone who is making a mistake) ?]p
Is the problem based on the warning of the two char arrays not being the same type, or is it because I'm trying to insert the single dimensional array into a multi dimensional array incorrectly?
The exact warning I am getting is warning: incompatible pointer types assigning to 'char*' from 'char *[32][32]'
Below are my other two methods, and my main:
char *GetFileType(char *filePath, int size)
{
char *ret = malloc(size);
char currentLine[100];
FILE *file = fopen(filePath, "r");
while(fgets(currentLine, sizeof(currentLine), file) != NULL)
{
if(strstr(currentLine, "test"))
{
ret = "test";
}
else if(strstr(currentLine, "production"))
{
ret = "production";
}
}
if(!ret)
{
return NULL;
}
return ret;
}
char *GetFileData(char *filePath)
{
char *buffer = NULL;
size_t size = 0;
FILE *file = fopen(filePath, "r");
// Get the buffer size
fseek(file, 0, SEEK_END);
size = ftell(file);
// Reset the position of the stream
rewind(file);
buffer = malloc((size + 1) * sizeof(*buffer));
// Read the entire file into the buffer
fread(buffer, size, 1, file);
buffer[size] = '\0';
return buffer;
}
int main (void)
{
char *listOfFiles[32] = {"file1.txt", "file2.txt", "file3.txt"};
ParseFiles(listOfFiles);
printf("Press ENTER key to Continue");
getchar();
return 0;
}
The problem isn't where you say it is, it is here instead:
char *allFileData;
char *tmpArray[32][32];
allFileData = tmpArray;
The type of tmpArray is char *[32][32]. The type of allFileData is char *. The assignment is simply not valid.
And there's one even worse thing happening: You returning allFileData which is pointing to the local variable tmpArray. The variable tmpArray will go out of scope once the function returns, and the pointer you return will become invalid immediately. Either pass the array in as an argument, or allocate it dynamically inside the function.

Split file with commas, storing values in an 2-d array

Currently working on how to split a .csv file with ",". Then creating a
2-D array to store the Alphabet and the number together. As it stands, the code below outputs: "a,,,,,,,,,,,,,,,,,,,". Also, what is the appropriate data type to declare the 2-D array since the values would be Char and int? Furthermore, I know this is a duplicate question because I've not found previous questions helpful. A simple explanation would be great and appreciated, explanation on how to split the file with this piece code would be perfect "%*[^,]" if possible. Thanks in advance.
Sample contents of the .csv file below.
A,1
B,2
C,3
.....
The program:
char single;
/* char array[26][2]; I was thinking the 2-d array would be declared like that. */
while ((single = fgetc(fpointer)) != EOF)
{
fscanf(fpointer,"%*[^,]");
printf("%c",single);
}
fclose(fpointer);
............................................................
edit code: With strtok() and fgetc()
............................................................
//char single;
char s[26] = ",";
char *token;
char str[100];
while (fgets(str,100,fpointer))
{
while((token = strtok(NULL, s)) != NULL)
{
printf(" %s\n", token);
}
}
fclose(fpointer);
typedef struct
{
char charVal;
int intVal;
}SplitValue;
SplitValue result[50];
int count = 0;
FILE *myFile = NULL;
fopen_s(&myFile, "mycsvfile.csv", "r");
char single[100];
if (myFile != NULL)
{
while (fgets(single, 100, myFile) != NULL)
{
// store the first char value
result[count].charVal = single[0];
// store the int value as string
char intval[25];
int i = 0;
for (i = 2; single[i] != '\n'; ++i)
{
intval[i - 2] = single[i];
}
intval[i-2] = 0;
// convert the string to int, either using atoi or sscanf
result[count].intVal = atoi(intval);
// get ready for the next item
count++;
}
fclose(myFile);
}
if (count)
{
for (int i = 0; i < count; ++i)
{
printf("Char value: %c and int value: %d\n", result[i].charVal, result[i].intVal);
}
}
Hope this helps!
Try the following solution, considering comments from DYZ and RoadRunner. Hope it helps somehow.
#include <stdio.h>
#include <stdlib.h>
typedef struct charIntPair {
char alpha;
int value;
} charIntPair_t;
#define MAX_ALPHABET_LENGTH 26
charIntPair_t myAlphabet[MAX_ALPHABET_LENGTH];
int alphabetLength = 0;
int main() {
FILE *fp = fopen("mycsvfile.csv","r");
if (!fp)
return 1; // File could not be opened.
char line[100];
for (alphabetLength=0; alphabetLength < MAX_ALPHABET_LENGTH && fgets(line,100,fp); alphabetLength++) {
int elementsRead = sscanf (line,"%c,%d",
&myAlphabet[alphabetLength].alpha,
&myAlphabet[alphabetLength].value);
if (elementsRead < 2) // not a valid char/int-combination?
break;
}
for (int i=0; i<alphabetLength; i++) {
printf("element %d is (%c,%d)\n", i, myAlphabet[i].alpha, myAlphabet[i].value);
}
return 0;
}
It expects that the character is the first element in a line and that it is immediately followed by a ,. The number may have spaces upfront. The following input yields the following output:
A,1
B,2
C, 3
D,15
E,17
=>
element 0 is (A,1)
element 1 is (B,2)
element 2 is (C,3)
element 3 is (D,15)
element 4 is (E,17)

C - Storing Values from text into Arrays

I'm trying to store different values that are taken from a file line by line. The lines in the text file read as something shown below
100000,player1,long title name
300000,someotherplayer,another long title name
45512845,thisplayer,one more long title name
I want to store each value that is comma separated into three different arrays, (int)number, (str)player_name, (str)title_name.
I have some code below, but it doesn't compile.
ptr_file=fopen("text.txt", "r");
char buffer[1000];
int line;
line = 0;
while(fgets(buffer, sizeof(buffer), ptr_file) != NULL){
char number[line]=strtok(buffer, ",");
char player_name[line]=strtok(NULL, ",");
char title_name[line]=strtrok(NULL, ",");
}
Can someone give me some advice on this?
So, there are a couple of issues with your code,
You open the file in mode "o" which I'm not really sure what it is, I suspect you want "r"
strtok returns a char * which you cannot assign to a char[].
One the second run through the loop you will overwrite the data in buffer.
I would do something like this:
struct player {
int number;
char player_name[64];
char title_name[256];
};
int main(void) {
FILE *ptrfile=fopen("text.txt", "r");
char buffer[1000];
int line;
struct player players[16];
line = 0;
if(ptrfile==NULL) return 0;
while(fgets(buffer, sizeof(buffer), ptrfile) != NULL){
if(strcmp(buffer, "") == 0) return 0;
char *number=strtok(buffer, ",");
char *player_name=strtok(NULL, ",");
char *title_name=strtok(NULL, ",");
players[line].number=atoi(number);
strcpy(players[line].player_name, player_name);
strcpy(players[line].title_name, title_name);;
line++;
}
fclose(ptrfile);
return 0
}
function strtok return a pointer, so it should be
char* p = strtok(...)
Check the reference here
This is something I did that was similar to what you seem to be doing. The problem you will find is that you want to make each value into a char* but you have to malloc each one then you can connect this char* into the array. It would also just be easier to do that with the numbers to then turn them into int later on.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *msg[100];
char temp[100];
int length, i;
int num = 0;
while((scanf("%s", &temp[0]) != EOF))
{
length = strlen(temp);
msg[num] = malloc((length+1 )* sizeof(char));
strcpy(msg[num], temp);
num++;
}
printf("There are %d words in the this input.\n", num);
for(i = 0; i < num; i++)
{
printf("%s\n", msg[i]);
}
return 0;
}
The thing with the malloc is that you will have to have each one unique because the words are all different sizes. I know this example isn't exactly what your doing but it will get you in the right direction.

Resources