I have been working around with building a simple shell in C. I am wanting to add a history built in function, but I need to know how to do the following:
I have a global variable commanHistory which, I believe is a pointer to an array of characters? (correct me if I am wrong please).
char *commandHistory[MAX_COMMANDS][MAX_LINE_LENGTH + 1];
Within my read line function, I want to store the ith line in the ith row of commandHistory. Here is what I am doing:
char *lsh_read_line(void)
{
int bufsize = MAX_LINE_LENGTH;
int position = 0;
char *buffer = malloc(sizeof(char) * bufsize);
int c;
int i = 0;
if (!buffer) {
fprintf(stderr, "lsh: allocation error\n");
exit(EXIT_FAILURE);
}
while (1) {
// Read a character
c = getchar();
// If we hit EOF, replace it with a null character and return.
if (c == EOF || c == '\n') {
buffer[position] = '\0';
return buffer;
} else {
buffer[position] = c;
}
position++;
// If we have exceeded the buffer, reallocate.
if (position >= bufsize) {
bufsize += MAX_LINE_LENGTH;
buffer = realloc(buffer, bufsize);
if (!buffer) {
fprintf(stderr, "lsh: allocation error\n");
exit(EXIT_FAILURE);
}
}
}
commandHistory[i++][0] = buffer; // wanting to store command i in commandHistory (also only want to keep track of 10 at a time)
}
When I call my built in function I just get 10 (null) printing out from this:
int lsh_history(char **args)
{
int i;
for (i = 0; i < MAX_COMMANDS; i++) {
printf(" %s\n", commandHistory[i][0]);
}
}
EDIT: I am required to use a 2d array. This is the last part of building a shell that I am having trouble with. While I believe this can be done with a 1 dimensional array, I am following this part of instructions:
Internally, your shell should save the command history in a 2-dimensional array:
char commandHistory[MAX_COMMANDS][MAX_LINE_LENGTH + 1];
Each row of this table will store one command. Viewing the rows arranged in a circle, a data
structure similar to a queue can be constructed. Unlike a traditional queue, your command
history will never overflow — as we continue to add commands, older commands will simply
be overwritten.
Your commandHistory is actually a two-dimensinal array of strings/char pointers, while you want a one dimensional one. You should declare it like this:
char *commandHistory[MAX_COMMANDS];
and you don't need to worry about the string length at this point, since each command is allocated dynamically. And then to access the i'th string from this array you just need commandHistory[i], which is of type char *.
Update:
If you (or the instructor) insist on having commandHistory declared as static two dimensional array:
char commandHistory[MAX_COMMANDS][MAX_LINE_LENGTH + 1];
You should not allocate the buffer dynamically as you do, but copy the commands into corresponding place in your statically pre-allocated array commandHistory (i.e. instead of buffer[position]=.. do commandHistory[i][position] = ...).
You either need to make your command history an array of char*'s , i.e.:
char *commandHistory[MAX_COMMANDS];
and copy the buffer pointer into the history entry (be careful; the history now "owns" the malloced space)
An alternative would be to make it a 2D array of chars, but then you couldn't accept your overly long commands (the ones for which you realloced buffer).
Related
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;
}
I'm writing a program that should get its inputs from a text file by using input redirection in a function called GetInput. (The text file contains 10 words.) The code should then be able to print the contents of ListWord in the Print function.
This is what I have so far.
I keep on getting errors while trying to run this code. I tried to remove * before ListWord and the code works but it does not retain the word (string) that was stored in it. But removing * before ListWord does not make sense to me. What am I doing wrong?
void GetInput( char** ListWord)
{
int i=0;
char word[30]; //each word may contain 30 letters
*ListWord = malloc(sizeof(char*)*10); //there are 10 words that needs to be allocated
while(scanf("%s", word)==1) //Get Input from file redirection
{
*ListWord[i]= (char *)malloc(30+1);
printf("%s\n", word); //for checking
strcpy(*ListWord[i], word);
printf("%s\n", *ListWord[i]); //for checking
i++;
}
}
void Print(char *ListWord)
{
//print ListWord
int i;
for (i=0; i<10; i++)
{
printf("%s", ListWord[i]);
}
}
int main()
{
char * ListWord;
GetInput(&ListWord);
printf("%s\n", ListWord[0]);
Print(ListWord);
free(ListWord);
return 0;
}
(Note: This is a homework. Thank you and sorry if it's unclear)
Due to *operator precedence the expression *ListWord[i] doesn't do what you think it does. In fact you should be getting errors or warnings from the code you have.
The compiler thinks that *ListWord[i] means *(ListWord[i]), which is not right. You need to use (*ListWord)[i].
Unfortunately that's only the start of your problems. A bigger problem is that the pointer you pass to the function GetInput is not a pointer to what could become an array of strings, but a pointer to a single string.
For a dynamic allocated array of strings, you need a pointer to a pointer to begin with, and then emulate pass-by-reference on that, i.e. you need to become a three star programmer which is something you should avoid.
Instead of trying to pass in the array to be allocated as an argument, have the GetInput return the array instead. Something like
char **GetInput(void)
{
// Allocate ten pointers to char, each initialized to NULL
char **ListWords = calloc(10, sizeof(char *));
if (ListWords == NULL)
return NULL;
char word[31];
for (int i = 0; i < 10 && scanf("%30s", word) == 1; ++i)
{
ListWords[i] = strdup(word);
}
return ListWords;
}
The above code adds some security checks, so you will not go out of bounds of either the temporary array you read into, or the ListWords array. It also makes sure the ListWords array is initialized, so if you read less then 10 words, then the remaining pointers will be NULL.
Of course you need to change your main function accordingly, and also your Print function, because now it only takes a single string as argument, not an array of strings. You also of course need to free every single string in the array because freeing the array.
I have written a method in C that removes characters from a string
char* removeChars(char input[], char remove[])
{
int src, dst = 0;
int size = strlen(input);
bool flags[128];
char output[18]; // <- This should be output[size]!
int i;
for(i=0; i<128; i++) {
flags[i] = false;
}
int j=0;
while(remove[j] != '\0') {
flags[remove[j]] = true;
j++;
}
for(src=0; src<size; src++) {
if(flags[input[src]] != true) {
output[dst++] = input[src];
}
}
return output;
}
One of the issues that I am having is that when I attempt to set the size of the output array using size (i.e. char output[size]), The output array appears to always have zero elements. It only seems to work when I specify a specific size (like 18). Should I be using strcpy or malloc instead? If so, how should it be done?
char *output = malloc(sizeof(char) * (size+1));
If you want memory to be dynamically allocated using size.
Once you are done using this memory please free it.
free(output);
Your program has undefined behaviour because you are returning pointer to the first element of a local array that in general case will be destroyed after exiting the function.
So you have to allocate the array in the heap dynamically.
The other problem is that you do not append the output string with the terminating zero.
The function woild look simpler if you would remove characters in the source string that is "in place". In this case there is no any need in an additional array.
Also you could use standard function strchr declared in header <string.h> that to determine whether a given character in the source string is present in the string that contains characters to be removed.
In my application, we take in char values one at a time and we need to be able to but them into a string. We are assembling these strings one by one by putting the char values into a char array, then clearing the array. However the strings are each different lengths and we are unable to determine the size of the string. How can we change the sizes of the array to add more space as we need it?
Also, how can we print out the array?
If the array was dynamically allocated with malloc, you can resize it with realloc:
int array_size = 1024;
char *array = (char *) malloc(array_size);
int n = 0;
char c;
while ((c = getchar()) != EOF) {
array[n++] = c;
if (n >= array_size) {
array_size += 1024;
array = (char *) realloc(array_size);
}
}
array[n] = '\0';
For printing out the contents of the array, you can simply pass it to printf or puts:
printf("%s\n", array);
puts(array);
if you don'y know the size you are going to need and are adding one character at a time you can consider using a linked list. It can grow as much as you need it to. The disadvntages would be lookup is kind of slow, and if you need to free the memory, or clear it you would have to do this for each element, one at a time.
You can also take the dynamic array approach: allocate a certain size which you consider large enough and when that is 80% full, allocate a new buffer, twice as large and copy the contents of the old one in the new, larger one.
Context: I'm trying to do is to make a program which would take text as input and store it in a character array. Then I would print each element of the array as a decimal. E.g. "Hello World" would be converted to 72, 101, etc.. I would use this as a quick ASCII2DEC converter. I know there are online converters but I'm trying to make this one on my own.
Problem: how can I allocate an array whose size is unknown at compile-time and make it the exact same size as the text I enter? So when I enter "Hello World" it would dynamically make an array with the exact size required to store just "Hello World". I have searched the web but couldn't find anything that I could make use of.
I see that you're using C. You could do something like this:
#define INC_SIZE 10
char *buf = (char*) malloc(INC_SIZE),*temp;
int size = INC_SIZE,len = 0;
char c;
while ((c = getchar()) != '\n') { // I assume you want to read a line of input
if (len == size) {
size += INC_SIZE;
temp = (char*) realloc(buf,size);
if (temp == NULL) {
// not enough memory probably, handle it yourself
}
buf = temp;
}
buf[len++] = c;
}
// done, note that the character array has no '\0' terminator and the length is represented by `len` variable
Typically, on environments like a PC where there are no great memory constraints, I would just dynamically allocate, (language-dependent) an array/string/whatever of, say, 64K and keep an index/pointer/whatever to the current end point plus one - ie. the next index/location to place any new data.
if you use cpp language, you can use the string to store the input characters,and access the character by operator[] , like the following codes:
std::string input;
cin >> input;
I'm going to guess you mean C, as that's one of the commonest compiled languages where you would have this problem.
Variables that you declare in a function are stored on the stack. This is nice and efficient, gets cleaned up when your function exits, etc. The only problem is that the size of the stack slot for each function is fixed and cannot change while the function is running.
The second place you can allocate memory is the heap. This is a free-for-all that you can allocate and deallocate memory from at runtime. You allocate with malloc(), and when finished, you call free() on it (this is important to avoid memory leaks).
With heap allocations you must know the size at allocation time, but it's better than having it stored in fixed stack space that you cannot grow if needed.
This is a simple and stupid function to decode a string to its ASCII codes using a dynamically-allocated buffer:
char* str_to_ascii_codes(char* str)
{
size_t i;
size_t str_length = strlen(str);
char* ascii_codes = malloc(str_length*4+1);
for(i = 0; i<str_length; i++)
snprintf(ascii_codes+i*4, 5, "%03d ", str[i]);
return ascii_codes;
}
Edit: You mentioned in a comment wanting to get the buffer just right. I cut corners with the above example by making each entry in the string a known length, and not trimming the result's extra space character. This is a smarter version that fixes both of those issues:
char* str_to_ascii_codes(char* str)
{
size_t i;
int written;
size_t str_length = strlen(str), ascii_codes_length = 0;
char* ascii_codes = malloc(str_length*4+1);
for(i = 0; i<str_length; i++)
{
snprintf(ascii_codes+ascii_codes_length, 5, "%d %n", str[i], &written);
ascii_codes_length = ascii_codes_length + written;
}
/* This is intentionally one byte short, to trim the trailing space char */
ascii_codes = realloc(ascii_codes, ascii_codes_length);
/* Add new end-of-string marker */
ascii_codes[ascii_codes_length-1] = '\0';
return ascii_codes;
}