easy c question: compare first char of a char array - c

How can I compare the first letter of the first element of a char**?
I have tried:
int main()
{
char** command = NULL;
while (true)
{
fgets(line, MAX_COMMAND_LEN, stdin);
parse_command(line, command);
exec_command(command);
}
}
void parse_command(char* line, char** command)
{
int n_args = 0, i = 0;
while (line[i] != '\n')
{
if (isspace(line[i++]))
n_args++;
}
for (i = 0; i < n_args+1; i++)
command = (char**) malloc (n_args * sizeof(char*));
i = 0;
line = strtok(line," \n");
while (line != NULL)
{
command[i++] = (char *) malloc ( (strlen(line)+1) * sizeof(char) );
strcpy(command[i++], line);
line = strtok(NULL, " \n");
}
command[i] = NULL;
}
void exec_command(char** command)
{
if (command[0][0] == '/')
// other stuff
}
but that gives a segmentation fault. What am I doing wrong?
Thanks.

Could you paste more code? Have you allocated memory both for your char* array and for the elements of your char* array?

The problem is, you do allocate a char* array inside parse_command, but the pointer to that array never gets out of the function. So exec_command gets a garbage pointer value. The reason is, by calling parse_command(line, command) you pass a copy of the current value of the pointer command, which is then overwritten inside the function - but the original value is not affected by this!
To achieve that, either you need to pass a pointer to the pointer you want to update, or you need to return the pointer to the allocated array from parse_command. Apart from char*** looking ugly (at least to me), the latter approach is simpler and easier to read:
int main()
{
char** command = NULL;
while (true)
{
fgets(line, MAX_COMMAND_LEN, stdin);
command = parse_command(line);
exec_command(command);
}
}
char** parse_command(char* line)
{
char** command = NULL;
int n_args = 0, i = 0;
while (line[i] != '\n')
{
if (isspace(line[i++]))
n_args++;
}
command = (char**) malloc ((n_args + 1) * sizeof(char*));
i = 0;
line = strtok(line," \n");
while (line != NULL)
{
command[i] = (char *) malloc ( (strlen(line)+1) * sizeof(char) );
strcpy(command[i++], line);
line = strtok(NULL, " \n");
}
command[i] = NULL;
return command;
}
Notes:
in your original parse_command, you allocate memory to command in a loop, which is unnecessary and just creates memory leaks. It is enough to allocate memory once. I assume that you want command to contain n_args + 1 pointers, so I modified the code accordingly.
in the last while loop of parse_command, you increment i incorrectly twice, which also leads to undefined behaviour, i.e. possible segmentation fault. I fixed it here.

Related

Where is the error? (C program won't print out the content of dynamic array)

The task was to read the first 20 lines from a specific file and format them to use only specific parts, the next step was to store those formatted strings in a dynamic array (char ** str | a pointer to a pointer), send it to a function and print it out with said function
Here is the main code:
int main(int argc, char* argv[]){
FILE* file = fopen("./passwd.txt", "r"); // open file
if (!file)
{
perror("Error opening file");
return 1;
}
char line [MAXCHARS];
int counter = 0;
char ** str;
str = malloc(20 * sizeof (char*));
while (fgets(line, MAXCHARS, file) && counter < 20) {
char * position;
if ((position = strchr(line,':'))){
char * end_char;
*position = 0; //setting here the value 0, will terminate the string line (first column)
if((position = strchr(++position,':')) && (end_char = strchr(++position,':'))){ //increment the position to skip the second column
*end_char = 0; //set the value 0 to end_char to terminate the string pointed by position
char * temp_str = "\0";
sprintf(temp_str, "{%d} - {%s}\n", atoi(position), line ); //concatenate line and userID into one string and store it into a temporary string
*(str + counter) = malloc(sizeof (char) * strlen(temp_str)); //set the memory space for the temp_string into the array
*(str + counter) = temp_str; //copy the string into the array
}
}
counter++;
}
printArray(str);
fclose(file);
if (line)
free(line);
return 0;
}
And here is the print function:
void printArray(char ** array){
for(int i = 0; i < 20; i++){
printf("%s",*(array+i));
free(*(array+i));
}
free(array);
}
I cannot find the error, the code compiles with
Process finished with exit code -1073741819 (0xC0000005)
So at least it compiles, and I think is just a problem in my pointers handling skills, but I'm not able to find the error.
Can someone help me?
there are 3 errors in your program :
use temp_str which haven't allocated.
char * temp_str = "\0";
sprintf(temp_str, "{%d} - {%s}\n", atoi(position), line );
save temp_str local pointer's address to str+counter and use the pointer after it went out of scope at printArray=> undefined behavior
line is not a pointer, can't use free
if (line)
{
free(line);
}
lets try this. https://godbolt.org/z/7KPfnTEMY I correct these points

Python-like array filling - C equivalent

I would like to know what would be the equivalent for filling an array like we do in Python, but in C.
Python example code:
arrayname=[0]*10
randomtextvar="test_text"
for i in range(10):
arrayname[i]=randomtextvar
for k in range(10):
print(arrayname[k]+" "+str(k)+ " position")
I haven't find an solution for this, I don't really understand how to set value to a string (char) array position (in C).
EDIT:
#include <stdio.h>
#include <string.h>
int main() {
int c = 0;
char arr[256];
fgets(arr, 256, stdin);
char spl[] = " ";
char *ptr = strtok(arr, spl);
while (ptr != NULL) {
ptr = strtok(NULL, spl);
// HERE I WANT TO ADD THE ptr value to a new array of strings
c++;
}
return 0;
}
You are doing strtok before the while loop and immediately at the start. So, you are trashing the first token on the line.
kaylum pointed out a simple way to save the strings into a fixed array using strdup.
But, I suspect you'd like something as flexible as what python is doing. So, the array of strings can be dynamically grown as you process many input lines using realloc.
Also, in addition, it's sometimes nice to have the last array element be NULL [just like argv].
Here's some refactored code. I've annotated it to explain what is going on:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char *ptr;
char *bp;
const char *spl = " \n";
char buf[256];
char **arr = NULL;
size_t arrcnt = 0;
size_t arrmax = 0;
// read in all input lines
while (1) {
// get next input line -- stop on EOF
ptr = fgets(buf,sizeof(buf),stdin);
if (ptr == NULL)
break;
// parse the current line
bp = buf;
while (1) {
// get next token on the current line
ptr = strtok(bp,spl);
bp = NULL;
// stop current line if no more tokens on the line
if (ptr == NULL)
break;
// grow the string array [periodically]
// NOTE: using arrmax cuts down on the number of realloc calls
if (arrcnt >= arrmax) {
arrmax += 100;
arr = realloc(arr,sizeof(*arr) * (arrmax + 1));
if (arr == NULL) {
perror("realloc/grow");
exit(1);
}
}
// add current string token to array
// we _must_ use strdup because when the next line is read any
// token data we had previously would get overwritten
arr[arrcnt++] = strdup(ptr);
// add null terminator just like argv -- optional
arr[arrcnt] = NULL;
}
}
// trim the array to the exact number of elements used
arr = realloc(arr,sizeof(*arr) * (arrcnt + 1));
if (arr == NULL) {
perror("realloc/trim");
exit(1);
}
// print the array
for (char **av = arr; *av != NULL; ++av)
printf("%s\n",*av);
// free the array elements
for (char **av = arr; *av != NULL; ++av)
free(*av);
// free the array
free(arr);
// reset counts and pointer
arrmax = 0;
arrcnt = 0;
arr = NULL;
return 0;
}

Dynamically allocated array of strings memory doesn't seem to be working

I'm trying to have this function read a sentence and store each word into an array of strings. I store the sentence in line:
char** storewords(char* line){
char** newArr = (char**) malloc(sizeof(char*));
free(word);
return newArr;
}
I later call this function and try to print each word in the array:
main {
// ........ //
//function to get a line into line//
}
void printArr(char** newArr) {
int i = 0;
}
}
I use while (newArr[i] != NULL) in order to tell the program to iterate through the array until encountering a NULL item.
The memory seems to be allocated weirdly, because when I print it out, some words don't seem to print sometimes but other times works perfectly.
For example, if I input "hello hi", I get:
newArr 0:
newArr 1: hi
and "hello my name is" will get:
newArr 0: hello
newArr 1: my
newArr 2: name
newArr 3:
but if I input something longer like "hello my name is stackoverflow", I get the correct results.
If I understand the idea behind your storewords correct, you want to:
a) Start with a single char pointer
b) Add an extra char pointer each time you call realloc
so that you can set the last char pointer to NULL so that it serves as an "end-of-array" marker.
If that is your intention, the problem is the realloc.
char** newArr = (char**) malloc(sizeof(char*)); // Now you have 1 char pointer
char* word;
int count = 0; // Notice that you (correctly) initialize count to zero
word = strtok(line, " ");
while (word != NULL) {
newArr = (char**) realloc(newArr, sizeof(char*) * (count + 1));
// The first time you execute the realloc, you want to
// increase the number of char pointers from 1 to 2.
// But count is zero so count + 1 is only 1.
// In other words, you only got 1 element but
// you wanted 2 elements.
So instead do:
newArr = (char**) realloc(newArr, sizeof(char*) * (count + 2));
as count is "2 behind" the number of elements you want.
BTW: Doing realloc directly into destination variable is bad. Always do it in two a temp variable. Check for NULL. If not NULL, assign the temp variable to the destination.
BTW: No need to cast the value returned by realloc and malloc
BTW: Remove free(word); It does nothing and you don't want to free anything here.
So with the above in mind, your function could be:
char** storewords(char* line){
char** tmp;
char** newArr = malloc(sizeof(char*));
char* word;
int count = 0;
word = strtok(line, " ");
while (word != NULL) {
tmp = realloc(newArr, sizeof(char*) * (count + 2));
if (tmp == NULL)
{
// realloc failed - add error handling - for now just exit
exit(1);
}
newArr = tmp;
newArr[count] = malloc(strlen(word) * sizeof(char) + 1);
// put the word in array
sscanf(word, "%s", newArr[count]);
count++;
word = strtok(NULL, " ");
}
newArr[count] = NULL; //set the last item in the array as null
return newArr;
}
while you play with string you should be always very careful. Here you are using pointer of pointer so use them like pointer rather use as array and mix and match. While you write a program, arrange the algorithm first how you are going to implement it step by step and write it down in a paper, which is call as procedural design as well. I have modified your program below see your mistakes here. Also see how I just use the pointer of pointers as pointer only but not like array. So I leave the code for printArr to you to change it from array to pointer so that you can enjoy it. GOOD LUCK!!
char** storewords(char* line){
char** newArr = (char**) malloc(sizeof(char*));
char* word;
int count = 1;
word = strtok(line, " ");
*newArr = word; //store the pointer of word at the first location
while (word != NULL) {
newArr = (char**) realloc(newArr, sizeof(char*) * (count + 1));
//newArr[count] = (char*)malloc(strlen(word) * sizeof(char) + 1);//you dont need this line at all
// put the word in array
//sscanf(word, "%s", newArr[count]);
word = strtok(NULL, " ");//get the next word
*(newArr+count)=word;//store the next word here
count++;
}
*(newArr+count) = NULL; //set the last item in the array as null
free(word);
return newArr;
}
void printArr(char** newArr) {
int i = 0;
while (newArr[i] != NULL) {
printf("newArr %d: %s\n", i, newArr[i]);
i++;
}
}
int main() {
// ........ //
//function to get a line into line//
char *line = malloc(100);
strcpy(line, "Hello world how are you!");
char** newArr = storewords(line);
printArr(newArr);
return 0;
}

C string not being filled in function call

I'm attempting to fill a few strings using a function, but the strings don't seem to be getting filled properly. The print statement is just 4 empty lines. BUT if I un-comment the char ** pls line, it prints all three strings properly even if I never use the variable pls anywhere. It also runs properly in debug mode without the pls variable existing. I'm not entirely sure what I did that it isn't happy about.
char * dataFile = (char *) calloc(64, sizeof(char));
//char ** pls = &dataFile;
char * queryFile = (char *) calloc(64, sizeof(char));
char * outFile = (char *) calloc(64, sizeof(char));
for(i = 1; i <argc; ++i)
{
char command[3];
char * iterator = argv[i];
command[0] = *iterator;
++iterator;
command[1] = *iterator;
++iterator;
command[2] = *iterator;
if(strcmp(command, "df=") == 0)
determineFileString(iterator, &dataFile);
else if(strcmp(command, "if=") == 0)
determineFileString(iterator, &queryFile);
else if(strcmp(command, "of=") == 0)
determineFileString(iterator, &outFile);
}
printf("%s\n%s\n%s\n", dataFile, queryFile, outFile);
void determineFileString(char * iterator, char ** file)
{
char * p = *file;
++iterator;
while(*iterator != '\0')
{
*p = *iterator;
++p;
++iterator;
}
*p = '\0';
}
You are calling strcmp but the first operand does not point to a string. A string is defined as some characters followed by a null terminator.
Your code will also cause undefined behaviour if an argv[i] string is shorter than 2 characters, because you always copy 3 characters out of it.
To fix this, either make command bigger and put a null terminator on the end, or use memcmp instead of strcmp. (But be careful with memcmp as it also causes UB if both objects are not at least as big as the size).
Here is a possible fix:
for(i = 1; i <argc; ++i)
{
if ( strlen(argv[i]) < 3 )
continue;
if ( memcmp(argv[i], "df=", 3) == 0 )
determineFileString(argv[i] + 3, &dataFile);
else if // etc.
}
BTW, the determineFileString function does not do any buffer size checking (it could buffer overflow). I'd suggest redesigning this function; perhaps it could do a length check and call realloc inside the function.

Allocating memory for a array to char pointer

The following piece of code gives a segmentation fault when allocating memory for the last arg. What am I doing wrong? Thanks.
int n_args = 0, i = 0;
while (line[i] != '\0')
{
if (isspace(line[i++]))
n_args++;
}
for (i = 0; i < n_args; i++)
command = malloc (n_args * sizeof(char*));
char* arg = NULL;
arg = strtok(line, " \n");
while (arg != NULL)
{
arg = strtok(NULL, " \n");
command[i] = malloc ( (strlen(arg)+1) * sizeof(char) );
strcpy(command[i], arg);
i++;
}
Thanks.
You don't reset the value of i after the for loop, so i is equal to n_args when you reach the bottom block. Trying to access command[i] at that point accesses uninitialized memory and segfaults.
The real lesson here is not to reuse variables in this manner without a good reason. Your code will be more robust and easier to read if you use something other than i in the middle for loop.
for (i = 0; i < n_args; i++)
command = malloc (n_args * sizeof(char*));
should become just
command = malloc (n_args * sizeof(char*))
because you just want to alloc an array of n_args elements, and
while (arg != NULL)
{
arg = strtok(NULL, " \n");
command[i] = malloc ( (strlen(arg)+1) * sizeof(char) );
strcpy(command[i], arg);
i++;
}
should become:
arg = strtok(NULL, " \n");
while (arg != NULL) {
command[i] = malloc ( (strlen(arg)+1) * sizeof(char) );
strcpy(command[i], arg);
i++;
arg = strtok(NULL, " \n");
}
to avoid strlen on a null pointer.
For a line comprising two arguments separated by a single space, n_args will be 1 rather than 2. This is probably not what you want.
I think you have a few funny things going on here (if I'm reading this correctly).
This block:
for (i = 0; i < n_args; i++)
command = malloc (n_args * sizeof(char*));
Should be this:
command = malloc (n_args * sizeof(char*));
No need to reallocate command over and over again.
As for the seg fault though, could be because you are re-using the i variable without resetting it to zero again first.
You're throwing away your first arg? Is that intentional? In case it isn't
int n_args = 1; /* We need one element more than spaces */
int i = 0;
while (line[i])
{
if (isspace(line[i++]))
n_args++;
}
command = malloc (n_args * sizeof(char*));
char* arg = NULL;
arg = strtok(line, " \n");
i = 0; /***** You forgot to reset that value, that was your segfault !!! */
while (arg)
{
command[i++] = strdup(arg); /* It does your malloc/strlen/strcpy */
arg = strtok(NULL, " \n");
}
You forgot to reset your i index, which reaches outside your allocated array in your code.
Try arranging this loop properly:
while (arg != NULL)
{
arg = strtok(NULL, " \n");
command[i] = malloc ( (strlen(arg)+1) * sizeof(char) );
strcpy(command[i], arg);
i++;
}
the line "arg=strtok..." does 2 things wrongs:
Skips the first argument.
Doesn't check the return code, so if arg==NULL then strlen(arg) will SEGFAULT.
Do this instead:
while (arg != NULL)
{
command[i] = malloc ( (strlen(arg)+1) * sizeof(char) );
strcpy(command[i], arg);
i++;
arg = strtok(NULL, " \n");
}
It is hard to figure out what you are trying to do.
It looks like you are looking at the number of spaces in the command line to see how many command line arguments you have. Are all of your command line args a single character?
The malloc is only reserving enough space for one character per arg.
If your args are just one char each:
command = malloc(strlen(line));
i = 0;
j = 0;
while(line[j]) {
if(!isspace(line[j])){
command[i++] = line[j];
}
j++;
}

Resources