all array elements are the same fgets in C? - c

So currently my program uses a hard-coded array like this:
char *array[] = {"array","ofran","domle","tters", "squar"}
Basically n strings of n length "an n*n grid. I then treat the values like a 2D array. So I will access array[y][x] and do comparison operations and math using the corresponding ASCII.
I wanted to allow text files of various sizes (n*n) (up to 32) be implemented in my program instead of hard coding it. But am having issues with using fgets.
My current function for getting and storing the file information looks like this:
char *array[32];
char buffer[32];
FILE *fp = fopen("textfile.txt","r");
int n = 0;
while(fgets(buffer, 32, fp)){
array[i] = buffer;
n++;
}
fclose(fp);
but all values of "array" are the same (they are the last string). So with the example values above. If I printed array[0] to array [4] I get
values from my code
squar
squar
squar
squar
squar
expected values:
array
ofran
domle
tters
squar

array[i] = buffer just assigns the very same pointer to all elements of array. You need dynamic memory allocation here:
char *array[32];
char buffer[32];
FILE *fp = fopen("textfile.txt","r");
int n = 0;
while(fgets(buffer, 32, fp)){
array[i] = strdup(buffer); // allocate memory for a new string
// containing a copy of the string in buffer
n++;
}
fclose(fp);
No error checking is done here for brevity. Also if the input file contains more than 32 lines you'll run into trouble.
if strdup does not exist on your platform:
char *strdup(const char *str)
{
char *newstring = malloc(strlen(str) + 1); // + 1 for the NUL terminator
if ( newstring )
strcpy(newstring, str);
return(newstring);
}
Again no error checking is done here for brevity.

Given this code:
char buffer[32];
How many buffer variables are there?
One.
So this code
array[i] = buffer;
points every char * element of array at the ONE buffer.
(One fix is to do #Jabberwocky posted in his answer - use strdup())

char *array[32];
char buffer[32];
....
while(fgets(buffer, 32, fp)){
array[i] = buffer;
....
Look at your variables: first one is array of 32 char* pointers, the second one is a 32 char array.
In while loop, you also just assign each & every element on the array to the same buffer. Do you see? While fgets just keeps re-freshing / updating that buffer with latest data.

Related

Passing char array to a function by reference when the size is not known

I am learning about pointers and char arrays and was wondering if you could help me find the optimal solution here. I want to avoid using malloc hence I chose to pass a char array to a function by reference where the values are getting filled.
In my main.c
char saved_networks[100]; // create an array to save data
readFile123(SPIFFS, "/wifi.txt",saved_networks);
Serial.print("saved networks=");
Serial.println(saved_networks);
And the function:
void readFile123(fs::FS &fs, const char *path, char* return_data)
{
int n=0;
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if (!file || file.isDirectory())
{
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available())
{
char c = file.read();
delayMicroseconds(100);
Serial.print(c);
//strcat(return_data, &c); //returns meditation error
return_data[n]=c;
n=n+1;
}
file.close();
}
In the program above, I create a char array size of 100 and pass it to the function. Inside a function, I read data inside my SPIFFS file system and then assing whatever string I found there to my char array. The code above works however I have 3 questions:
1. Why I cannot use strcat(return_data, &c);
The causes the cpu to crash and return an error:
Stack smashing protect failure!
abort() was called at PC 0x40137793 on core 1
ELF file SHA256: 0000000000000000
2. Why I cannot declare my char array as following : char* saved_networks;. If I do that, my microcontroller will crash and return an error:
Read from file: TGuru Meditation Error: Core 1 panic'ed (StoreProhibited). Exception was unhandled
3. What is the most optimal way to solve this problem? Since I do not know what will be the maximum size of the data that I read form SPIFFS, simply declaring it size of 100 may not be enough. Is there any way to declare it dynamically ? I assume the only way to do that is by using malloc? Is that true?
strcat(return_data, &c) is a <string> function, and as such, it expects actual strings and not char arrays.
In your example, you pass the address of a char, which can be interpreted as a char array of size 1.
What is the difference?
Well, strings are null terminated char arrays, which means their last valid element is a '\0', everything after that char is ignored by those str functions.
char* saved_networks; Declares a variable that can store an address of type char, you still have to allocate space! That space's address will then be stored in saved_networks.
You can try to find out how big the file is before reading it. Or you can incrementally read it.
Since you're using C++ you could also use std::string
Edit: when you pass the name of an array it is already a reference, so I'd say you're passing it correctly already, just need to be careful you don't exceed the space allocated (100 chars in this case).
To make it more clear, let me show you some examples of syntatic sugar:
char saved_networks[100];
saved_networks == &saved_networks[0]; // true
saved_networks[0] == *saved_networks; // true
saved_networks[50] == *(saved_networks + 50); // true
&saved_networks[50] == saved_networks + 50; // true
The +50 depends of the array type: in this case it means 50 bytes because chars are 1 byte each.
Edit 2:
"h" in reality is more similar to:
char const arr[2] = {'h', '\0'};
Which implies that " is used for strings and ' for chars!
This is important because str functions are expecting strings to be null terminated, or else there will be invalid memory accesses from infinite loops.
I think that's what you were missing and now you'll be able to better understand my first point.
Pass the length of the array:
size_t len = sizeof(saved_networks)/sizeof(*saved_networks);
char *saved_networks = readFile123(SPIFFS, "/wifi.txt",saved_networks, &len);
And then make sure that the length is not exceeded:
char *readFile123(fs::FS &fs, const char *path, size_t *lenp)
{
size_t chunksize = *lenp; // You can pass in chunksize you want to use
size_t n = 0;
size_t chunk_len = 0;
char *return_data = malloc(chunksize);
return_data[0]=0;
*lenp = 0;
...
while (file.available())
{
char c = file.read();
delayMicroseconds(100);
Serial.print(c);
return_data[n]=c;
n=n+1;
chunk_len++;
if (chunk_len == chunksize-1)
{
return_data = realloc(return_data, n+chunksize);
chunk_len = 0;
}
}
return_data[n]=0;
*lenp = n;
return return_data;
}

Tokenizing string from dynamic array into multiple lines in static 2D char array

I have a dynamic array that holds a string containing '\n' characters, so this string is made up of multiple lines. I'm trying to extract the lines and put them all into a 2D char array and I'm getting segmentation errors.
Here's my code:
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
input_lines = extractInput(MAX_LINE_LEN, input_file);
char inputLines_counted[lineCount_input][MAX_LINE_LEN];
char *t = strtok(input_lines, "\n");
for(i = 0; i < lineCount_input; i++) {
strcpy(inputLines_counted[i], t);
// printf("%s\n", inputLines_counted[i]);
t = strtok(NULL, "\n");
}
Upon creating the dynamic array, I use the extractInput(MAX_LINE_LEN, input_file) function to populate the input_lines array with a string containing multiple lines.
Here's the extract function:
char *extractInput(int len, FILE *file) {
char tmp[len];
char *pos;
char *input_lines = malloc(len*sizeof(char));
char *lines;
while(fgets(tmp, len, file)) {
// if((pos = strchr(tmp, '\n')) != NULL) {
// *pos = ' ';
// }
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
strcat(input_lines, tmp);
}
return input_lines;
}
Why am I getting segfaults here?
The function call
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
takes your current allocated memory block and expands it, if it can. you should check the return value of realloc, it may fail.
btw when you allocate memory in C, you always need to have space for the ending \0.
see what happens with this file
hello\n
world\n
The first fgets reads in hello\n into tmp.
you now do realloc even though it is unnecessary, input_lines is already pointing to a buffer that could hold the string
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
now with your realloc
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
you do strlen(input_lines) + len so you make the buffer strlen("hello\n") + len long.
but the important thing you need to notice is the following line
strcat(input_lines, tmp);
you have not initialized the memory that input_lines is pointing to, it can contain anything even \0's so your strcat could potentially put the string anywhere in the buffer and cause the error you describe.
Either do a memset or use calloc when you allocate the buffer.
If you use realloc you should keep track of the total size that you have allocated and how much you are using of it, before you copy into the buffer check if there is enough room. If not, add a certain number of bytes to the buffer.
I also noticed you read from the file line by line, then you concatenated the lines together to later use strtok to divide them again. It would be more efficient to return an array of lines.

How to copy a char array in C?

In C, I have two char arrays:
char array1[18] = "abcdefg";
char array2[18];
How to copy the value of array1 to array2 ? Can I just do this: array2 = array1?
You can't directly do array2 = array1, because in this case you manipulate the addresses of the arrays (char *) and not of their inner values (char).
What you, conceptually, want is to do is iterate through all the chars of your source (array1) and copy them to the destination (array2). There are several ways to do this. For example you could write a simple for loop, or use memcpy.
That being said, the recommended way for strings is to use strncpy. It prevents common errors resulting in, for example, buffer overflows (which is especially dangerous if array1 is filled from user input: keyboard, network, etc). Like so:
// Will copy 18 characters from array1 to array2
strncpy(array2, array1, 18);
As #Prof. Falken mentioned in a comment, strncpy can be evil. Make sure your target buffer is big enough to contain the source buffer (including the \0 at the end of the string).
If your arrays are not string arrays, use:
memcpy(array2, array1, sizeof(array2));
If you want to guard against non-terminated strings, which can cause all sorts of problems, copy your string like this:
char array1[18] = {"abcdefg"};
char array2[18];
size_t destination_size = sizeof (array2);
strncpy(array2, array1, destination_size);
array2[destination_size - 1] = '\0';
That last line is actually important, because strncpy() does not always null terminate strings. (If the destination buffer is too small to contain the whole source string, sntrcpy() will not null terminate the destination string.)
The manpage for strncpy() even states "Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated."
The reason strncpy() behaves this somewhat odd way, is because it was not actually originally intended as a safe way to copy strings.
Another way is to use snprintf() as a safe replacement for strcpy():
snprintf(array2, destination_size, "%s", array1);
(Thanks jxh for the tip.)
As others have noted, strings are copied with strcpy() or its variants. In certain cases, you could use snprintf() as well.
You can only assign arrays the way you want as part of a structure assignment:
typedef struct { char a[18]; } array;
array array1 = { "abcdefg" };
array array2;
array2 = array1;
If your arrays are passed to a function, it will appear that you are allowed to assign them, but this is just an accident of the semantics. In C, an array will decay to a pointer type with the value of the address of the first member of the array, and this pointer is what gets passed. So, your array parameter in your function is really just a pointer. The assignment is just a pointer assignment:
void foo (char x[10], char y[10]) {
x = y; /* pointer assignment! */
puts(x);
}
The array itself remains unchanged after returning from the function.
This "decay to pointer value" semantic for arrays is the reason that the assignment doesn't work. The l-value has the array type, but the r-value is the decayed pointer type, so the assignment is between incompatible types.
char array1[18] = "abcdefg";
char array2[18];
array2 = array1; /* fails because array1 becomes a pointer type,
but array2 is still an array type */
As to why the "decay to pointer value" semantic was introduced, this was to achieve a source code compatibility with the predecessor of C. You can read The Development of the C Language for details.
You cannot assign arrays, the names are constants that cannot be changed.
You can copy the contents, with:
strcpy(array2, array1);
assuming the source is a valid string and that the destination is large enough, as in your example.
it should look like this:
void cstringcpy(char *src, char * dest)
{
while (*src) {
*(dest++) = *(src++);
}
*dest = '\0';
}
.....
char src[6] = "Hello";
char dest[6];
cstringcpy(src, dest);
I recommend to use memcpy() for copying data.
Also if we assign a buffer to another as array2 = array1 , both array have same memory and any change in the arrary1 deflects in array2 too. But we use memcpy, both buffer have different array. I recommend memcpy() because strcpy and related function do not copy NULL character.
array2 = array1;
is not supported in c. You have to use functions like strcpy() to do it.
c functions below only ... c++ you have to do char array then use a string copy then user the string tokenizor functions... c++ made it a-lot harder to do anythng
#include <iostream>
#include <fstream>
#include <cstring>
#define TRUE 1
#define FALSE 0
typedef int Bool;
using namespace std;
Bool PalTrueFalse(char str[]);
int main(void)
{
char string[1000], ch;
int i = 0;
cout<<"Enter a message: ";
while((ch = getchar()) != '\n') //grab users input string untill
{ //Enter is pressed
if (!isspace(ch) && !ispunct(ch)) //Cstring functions checking for
{ //spaces and punctuations of all kinds
string[i] = tolower(ch);
i++;
}
}
string[i] = '\0'; //hitting null deliminator once users input
cout<<"Your string: "<<string<<endl;
if(PalTrueFalse(string)) //the string[i] user input is passed after
//being cleaned into the null function.
cout<<"is a "<<"Palindrome\n"<<endl;
else
cout<<"Not a palindrome\n"<<endl;
return 0;
}
Bool PalTrueFalse(char str[])
{
int left = 0;
int right = strlen(str)-1;
while (left<right)
{
if(str[left] != str[right]) //comparing most outer values of string
return FALSE; //to inner values.
left++;
right--;
}
return TRUE;
}
Well, techincally you can…
typedef struct { char xx[18]; } arr_wrap;
char array1[18] = "abcdefg";
char array2[18];
*((arr_wrap *) array2) = *((arr_wrap *) array1);
printf("%s\n", array2); /* "abcdefg" */
but it will not look very beautiful.
…Unless you use the C preprocessor…
#define CC_MEMCPY(DESTARR, SRCARR, ARRSIZE) \
{ struct _tmparrwrap_ { char xx[ARRSIZE]; }; *((struct _tmparrwrap_ *) DESTARR) = *((struct _tmparrwrap_ *) SRCARR); }
You can then do:
char array1[18] = "abcdefg";
char array2[18];
CC_MEMCPY(array2, array1, sizeof(array1));
printf("%s\n", array2); /* "abcdefg" */
And it will work with any data type, not just char:
int numbers1[3] = { 1, 2, 3 };
int numbers2[3];
CC_MEMCPY(numbers2, numbers1, sizeof(numbers1));
printf("%d - %d - %d\n", numbers2[0], numbers2[1], numbers2[2]); /* "abcdefg" */
(Yes, the code above is granted to work always and it's portable)
for integer types
#include <string.h>
int array1[10] = {0,1,2,3,4,5,6,7,8,9};
int array2[10];
memcpy(array2,array1,sizeof(array1)); // memcpy("destination","source","size")
You cannot assign arrays to copy them. How you can copy the contents of one into another depends on multiple factors:
For char arrays, if you know the source array is null terminated and destination array is large enough for the string in the source array, including the null terminator, use strcpy():
#include <string.h>
char array1[18] = "abcdefg";
char array2[18];
...
strcpy(array2, array1);
If you do not know if the destination array is large enough, but the source is a C string, and you want the destination to be a proper C string, use snprinf():
#include <stdio.h>
char array1[] = "a longer string that might not fit";
char array2[18];
...
snprintf(array2, sizeof array2, "%s", array1);
If the source array is not necessarily null terminated, but you know both arrays have the same size, you can use memcpy:
#include <string.h>
char array1[28] = "a non null terminated string";
char array2[28];
...
memcpy(array2, array1, sizeof array2);
None of the above was working for me..
this works perfectly
name here is char *name which is passed via the function
get length of char *name using strlen(name)
storing it in a const variable is important
create same length size char array
copy name 's content to temp using strcpy(temp, name);
use however you want, if you want original content back. strcpy(name, temp); copy temp back to name and voila works perfectly
const int size = strlen(name);
char temp[size];
cout << size << endl;
strcpy(temp, name);
You can't copy directly by writing array2 = array1.
If you want to copy it manually, iterate over array1 and copy item by item as follows -
int i;
for(i=0;array1[i]!='\0';i++){
array2[i] = array1[i];
}
array2[i]='\0'; //put the string terminator too
If you are ok to use string library, you can do it as follows -
strncpy ( array2, array1, sizeof(array2) );

Printing an array of characters

I have an array of characters declared as:
char *array[size];
When I perform a
printf("%s", array);
it gives me some garbage characters, why it is so?
http://www.cplusplus.com/reference/clibrary/cstdio/printf/
This url indicates printf takes in the format of: `int printf ( const char * format, ... );
#include <stdio.h>
#include <string.h>
#define size 20
#define buff 100
char line[buff];
int main ()
{
char *array[100];
char *sep = " \t\n";
fgets(line, buff, stdin);
int i;
array[0] = strtok(line, sep);
for (i = 1; i < size; i++) {
array[i] = strtok(NULL, sep);
if (array[i] == NULL)
break;
}
return 0;
}
You declare an array of characters like so:
char foo[size];
You seem to have it mixed up with char *, which is a pointer to a character. You could say
char *bar = foo;
which would make bar point to the contents of foo. (Or, actually, to the first character of foo.)
To then print the contents of the array, you can do one of the following:
// either print directly from foo:
printf("%s", foo);
// or print through bar:
printf("%s", bar);
Note, however, that C performs no initialization of the contents of variables, so unless you specifically set the contents to something, you'll get garbage. In addition, if that garbage doesn't happen to contain a \0; that is, a char with value 0, it will keep on outputting past the end of the array.
Your array is not initialized, and also you have an array of pointers, instead of an array of char's. It should be char* array = (char*)malloc(sizeof(char)*size);, if you want an array of char's. Now you have a pointer to the first element of the array.
Why are we making such a simple thing sound so difficult?
char array[SIZE];
... /* initialize array */
puts(array); /* prints the string/char array and a new line */
/* OR */
printf("%s", array); /* prints the string as is, without a new line */
The char in array after the end of what you want to be your string (ie. if you want your string to read "Hello" that would be the next char after the 'o') must be the terminating NUL character '\0'. If you use a C function to read input that would automatically be appended to the end of your buffer. You would only need to worry about doing it manually if you were individually writing characters to your buffer or something for some reason.
EDIT: As with pmg's comment, the '\0' goes wherever you want the string to end, so if you wanted to shorten your string you could just move it up closer to the front, or to have an empty string you just have array[0] = '\0';. Doing so can also be used to tokenise smaller strings inside a single buffer, just as strtok does. ie. "Part1\0Part2\0Part3\0". But I think this is getting away from the scope of the question.
ie. you wanted to store the first 3 chars of the alphabet as a string (don't know why anyone would do it this way but it's just an example):
char array[4];
array[0] = 'a';
array[1] = 'b';
array[2] = 'c';
array[3] = '\0';
printf("%s\n", array);
If you have something like char array[] = "Hello"; the '\0' is automatically added for you.
char *array[size];
array is not a char * with that, it's more like a char ** (pointer to an array of chars, with is similar to pointer to pointer to char).
If all you need is a C string, either:
char array[size];
and make sure you 0-terminate it properly, or
char *array;
and make sure you properly allocate and free storage for it (and 0-terminate it too).

Using strdup into malloc reserved space

I've never used malloc to store more than values but I have to use strdup to order the lines of an input file and I dont get a way to make it work.
I though using strdup() to get a pointer to each line and later, put each one into a space according to the number of lines reserved with malloc().
I dont know if I have to do it like reserved memory was an array to pointers, I mean using char** and later put each pointer to each strdup into reserved space.
I though something like this:
char **buffer;
char *pointertostring;
char *line; // line got using fgets
*buffer = (char*)malloc(sizeof(char*));
pointertostring = strdup(line);
I don't know what to do after that, I don't even know if this is correct, in that case, what should I do to store the pointer to the string in a position of buffer?
Regards
If I understand your requirement correctly. You'll have to do something like:
char **buffer;
char line[MAX_LINE_LEN]; // line got using fgets
int count; // to keep track of line number.
// allocate one char pointer for each line in the file.
buffer = (char**)malloc(sizeof(char*) * MAX_LINES);
count = 0; // initilize count.
// iterate till there are lines in the file...read the line using fgets.
while(fgets(line,MAX_LINE_LEN,stdin)) {
// copy the line using strdup and make the buffer pointer number 'count'
// point to it
buffer[count++] = strdup(line);
}
....
....
// once done using the memory you need to free it.
for(count=0;count<MAX_LINES;count++) {
free(buffer[count]);
}
....
....
Your buffer will only hold one pointer. You need something like:
char **buffer;
char *pString;
int linecount;
buffer = (char **)malloc(sizeof(char *)*MAXIMUM_LINES);
linecount = 0;
while (linecount < MAXIMUM_LINES) {
pString = fgets(...);
buffer[linecount++] = strdup(pString);
}

Resources