I have a 'Segmentation Problem' while printing parsed parts of a String - c

I am writing a simple Shell for school assignment and stuck with a segmentation problem. Initially, my shell parses the user input to remove whitespaces and endofline character, and seperate the words inside the input line to store them in a char **args array. I can seperate the words and can print them without any problem, but when storing the words into a char **args array, and if argument number is greater than 1 and is odd, I get a segmentation error.
I know the problem is absurd, but I stuck with it. Please help me.
This is my parser code and the problem occurs in it:
char **parseInput(char *input){
int idx = 0;
char **parsed = NULL;
int parsed_idx = 0;
while(input[idx]){
if(input[idx] == '\n'){
break;
}
else if(input[idx] == ' '){
idx++;
}
else{
char *word = (char*) malloc(sizeof(char*));
int widx = 0; // Word index
word[widx] = input[idx];
idx++;
widx++;
while(input[idx] && input[idx] != '\n' && input[idx] != ' '){
word = (char*)realloc(word, (widx+1)*sizeof(char*));
word[widx] = input[idx];
idx++;
widx++;
}
word = (char*)realloc(word, (widx+1)*sizeof(char*));
word[widx] = '\0';
printf("Word[%d] --> %s\n", parsed_idx, word);
if(parsed == NULL){
parsed = (char**) malloc(sizeof(char**));
parsed[parsed_idx] = word;
parsed_idx++;
}else{
parsed = (char**) realloc(parsed, (parsed_idx+1)*sizeof(char**));
parsed[parsed_idx] = word;
parsed_idx++;
}
}
}
int i = 0;
while(parsed[i] != NULL){
printf("Parsed[%d] --> %s\n", i, parsed[i]);
i++;
}
return parsed;
}

In your code you have the loop
while(parsed[i] != NULL) { ... }
The problem is that the code never sets any elements of parsed to be a NULL pointer.
That means the loop will go out of bounds, and you will have undefined behavior.
You need to explicitly set the last element of parsed to be a NULL pointer after you parsed the input:
while(input[idx]){
// ...
}
parsed[parsed_idx] = NULL;
On another couple of notes:
Don't assign back to the same pointer you pass to realloc. If realloc fails it will return a NULL pointer, but not free the old memory. If you assign back to the pointer you will loose it and have a memory leak. You also need to be able to handle this case where realloc fails.
A loop like
int i = 0;
while (parsed[i] != NULL)
{
// ...
i++;
}
is almost exactly the same as
for (int i = 0; parsed[i] != NULL; i++)
{
// ...
}
Please use a for loop instead, it's usually easier to read and follow. Also for a for loop the "index" variable (i in your code) will be in a separate scope, and not available outside of the loop. Tighter scope for variables leads to less possible problems.
In C you shouldn't really cast the result of malloc (or realloc) (or really any function returning void *). If you forget to #include <stdlib.h> it could lead to hard to diagnose problems.

Also, a beginner might find the -pedantic switch helpful on your call to the compiler. That switch would have pointed up most of the other suggestions made here. I personally am also a fan of -Wall, though many find it annoying instead of helpful.

Related

Using gdb--still can't find malloc error

I've looked at previous posts about this and they didn't help me locate my problem... To keep it short I'm making a function should read a text file line by line (and yes, I do realize there are many posts like this). But when I run my program through CMD, it's giving me this error:
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_realloc (oldmem=0x10011, bytes=1) at malloc.c:2999
2999 malloc.c: No such file or directory.
I'm pretty sure I wrote out my malloc/realloc lines correctly. I've tried finding alot of posts similar to this, but none of the solutions offered are helping. If you have any post suggestions that maybe I missed, please let me know. Regardless, here are my functions:
char* read_single_line(FILE* fp){
char* line = NULL;
int num_chars = 0;
char c;
fscanf(fp, "%c", &c);
while(!feof(fp)) {
num_chars++;
line = (char*) realloc(line, num_chars * sizeof(char));
line[num_chars -1] = c;
if (c == '\n') {
break;
}
fscanf(fp, "%c", &c);
}
if(line != NULL) {
line = realloc(line, (num_chars+1) * sizeof(char));
line[num_chars] = '\0';
}
return line;
}
void read_lines(FILE* fp, char*** lines, int* num_lines) {
int i = 0;
int num_lines_in_file = 0;
char line[1000];
if (fp == NULL) {
*lines = NULL;
*num_lines = 0;
} else {
(*lines) = (char**)malloc(1 * sizeof(char*));
while (read_single_line(fp) != NULL) {
(*lines)[i] = (char*)realloc((*lines)[i], sizeof(char));
num_lines_in_file++;
i++;
}
*lines[i] = line;
*num_lines = num_lines_in_file;
}
}
I would really appreciate any help--I'm a beginner in C so hear me out!!
char line[1000];
:
while (read_single_line(fp) != NULL) {
:
}
*lines[i] = line;
This doesn't look at all right to me. Your read_single_line function returns an actual line but, other than checking that against NULL, you never actually store it anywhere. Instead, you point the line pointer to line, a auto-scoped variable which could contain literally anything (and, more worrying, possibly no terminator character).
I think you should probably store the return value from read_single_line and use that to set your line pointers.
By the way, it may also be quite inefficient to expand your buffer one character at a time. I'd suggest initially allocating more bytes and then keeping both that capacity and the bytes currently in use. Then, only when you're about to use beyond your capacity do you expand, and by more than one. In pseudo-code, something like:
def getLine:
# Initial allocation with error check.
capacity = 64
inUse = 0
buffer = allocate(capacity)
if buffer == null:
return null
# Process each character made available somehow.
while ch = getNextChar:
# Expand buffer if needed, always have room for terminator.
if inUse + 1 == capacity:
capacity += 64
newBuff = realloc buffer with capacity
# Failure means we have to release old buffer.
if newBuff == null:
free buffer
return null
# Store character in buffer, we have enough room.
buffer[inUse++] = ch
# Store terminator, we'll always have room.
buffer[inUse] = '\0';
return buffer
You'll notice, as well as the more efficient re-allocations, better error checking on said allocations.
while (read_single_line(fp) != NULL) {
(*lines)[i] = (char*)realloc((*lines)[i], sizeof(char));
num_lines_in_file++;
i++;
}
*lines[i] = line;
There are more errors then lines in this short fragment. Let's go over them one by one.
while (read_single_line(fp) != NULL)
You read a line, check whether it's a null pointer, and throw it away instead of keeping it around to accumulate it in the lines array.
(*lines)[i] = (char*)realloc((*lines)[i], sizeof(char));
You are trying to realloc (*lines[i]). First, it does not exist beyond i==0, because (*lines) was only ever allocated to contain one element. Second, it makes no sense to realloc individual lines, because you are (should be) getting perfect ready-made lines from the line reading function. You want to realloc *lines instead:
*lines = realloc (*lines, i * sizeof(char*));
Now these two lines
num_lines_in_file++;
i++;
are not an error per se, but why have two variables which always have the exact same value? In addition, you want them (it) be before the realloc line, per usual increment-realloc-assign pattern (you are using it in the other function).
Speaking of the assign part, there isn't any. You should insert one now:
(*lines)[i-1] = // what?
The line pointer you should have saved when calling read_single_line, that's what. From the beginning:
char* cur_line;
int i = 0;
*lines = NULL;
while ((cur_line=read_single_line(fp)) != NULL)
{
++i;
*lines = realloc (*lines, i * sizeof(char*));
(*lines)[i-1] = cur_line;
}
*num_lines = i;
The last one
*lines[i] = line;
is downright ugly.
First, lines is not an array, it's a pointer pointing to a single variable, so lines[i] accesses intergalactic dust. Second, you are trying to assign it an address of a local variable, which will cease to exist as soon as your function returns. Third, what is it doing outside of the loop? If you want to terminate your line array with a null pointer, do so:
}
*num_lines = i;
++i;
*lines = realloc (*lines, i * sizeof(char*));
(*lines)[i-1] = NULL;
But given that you return the number of lines, this may not be necessary.
Disclaimer: none of the above is tested. If there are any bugs, fix them!

Tokenizing user input in C (store in **arg)?

I'm attempting to write a simple shell like interface, that takes in a users input (by char) and stores it via a pointer to a pointer* (exactly how argv works). Here's my code:
char input[100];
char **argvInput;
char ch;
int charLoop = 0;
int wordCount = 0;
argvInput = malloc(25 * sizeof(char *));
while((ch = getc(stdin))) {
if ((ch == ' ' || ch == '\n') && charLoop != 0) {
input[charLoop] = '\0';
argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char));
argvInput[wordCount] = input;
charLoop = 0;
wordCount++;
if (ch == '\n') {
break;
}
} else if (ch != ' ' && ch != '\n') {
input[charLoop] = ch;
charLoop++;
} else {
break;
}
}
If I loop through argvInput via:
int i = 0;
for (i = 0; i < wordCount; i++)
printf("Word %i: %s\n", i, argvInput[i]);
All of the values of argvInput[i] are whatever the last input assignment was. So if I type:
"happy days are coming soon", the output of the loop is:
Word 0: soon
Word 1: soon
Word 2: soon
Word 3: soon
Word 4: soon
I'm at a loss. Clearly each loop is overwriting the previous value, but I'm staring at the screen, unable to figure out why...
This line is your bane:
argvInput[wordCount] = input;
Doesn't matter that you allocate new space, if you're going to replace the pointer to it with another one (i.e. input).
Rather, use strncpy to extract parts of the input into argvInput[wordCount].
argvInput[wordCount] = input; is only making the pointer of argvInput[wordCount] point to the memory of input instead of copy the content of input into the new allocated memory. You should use memcpy or strcpy to correct your program.
After the pointer assignment the memory status looks like the image below. The memory allocated by malloc((charLoop + 1) * sizeof(char));, which are the grey ones in the graph, could not be accessed by your program anymore and this will lead to some memory leak issue. Please take care of that.
I suggest printing your argvInput pointers with %p, instead of %s, to identify this problem: printf("Word %i: %p\n", i, (void *) argvInput[i]);
What do you notice about the values it prints? How does this differ from the behaviour of argv? Try printing the pointers of argv: for (size_t x = 0; x < argc; x++) { printf("Word %zu: %p\n", x, (void *) argv[x]); }
Now that you've observed the problem, explaining it might become easier.
This code allocates memory, and stores a pointer to that memory in argvInput[wordCount]: argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char)); (by the way, sizeof char is always 1 in C, so you're multiplying by 1 unnecessarily).
This code replaces that pointer to allocated memory with a pointer to input: argvInput[wordCount] = input; ... Hence, all of your items contain a pointer to the same array: input, and your allocated memory leaks because you lose reference to it. Clearly, this is the problematic line; It doesn't do what you initially thought it does.
It has been suggested that you replace your malloc call with a strdup call, and remove the problematic line. I don't like this suggestion, because strdup isn't in the C standard, and so it isn't required to exist.
strncpy will work, but it's unnecessarily complex. strcpy is guaranteed to work just as well because the destination array is allocated to be large enough to store the string. Hence, I recommend replacing the problematic line with strcpy(argvInput[wordCount], input);.
Another option that hasn't been explained in detail is strtok. It seems this is best left unexplored for now, because it would require too much modification to your code.
I have a bone to pick with this code: char ch; ch = getc(stdin); is wrong. getc returns an int for a reason: Any successful character read will be returned in the form of an unsigned char value, which can't possibly be negative. If getc encounters EOF or an error, it'll return a negative value. Once you assign the return value to ch, how do you differentiate between an error and a success?
Have you given any thought as to what happens if the first character is ' '? Currently, your code would break out of the loop. This seems like a bug, if your code is to mimic common argv parsing behaviours. Adapting this code to solve your problem might be a good idea:
for (int c = getc(stdin); c >= 0; c = getc(stdin)) {
if (c == '\n') {
/* Terminate your argv array and break out of the loop */
}
else if (c != ' ') {
/* Copy c into input */
}
else if (charLoop != 0) {
/* Allocate argvInput[wordCount] and copy input into it,
* reset charLoop and increment wordCount */
}
}

realloc not working for 2-D pointer array

In the following code, I ask the user to give me some strings. I make a 2-D pointer char array, so that I read the input with pointer string which points to the start of a string of length 50. My problem is that I keep crashing after the input of the first string.. and I assume that my problem has to do with the realloc. I am not used to it.. can you please help to figure out what is happening?? I tried to debug with netbeans, but didn't manage to see anything interesting, since it doesn't give feedback for the new addresses made from realloc!!
Here is the code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char *str,**string,buffer[50],temp[2];
int i,j,q,size,counter;
size=10;
string=(char**) calloc(size,sizeof(char*));
for (i=0; i<size; i++) string[i]=(char*) malloc(50*sizeof(char));
printf("\nGimme strings, terminate input with x");
i=0;
gets(string[i]);
temp[0]=120;//x
temp[1]='\0';
size=0;
while(strcmp(string[i],temp)!=0)
{
string=realloc(string,size*sizeof(char**));
i++;
gets(string[i]);
size++;
counter++;
}
return 0;
}
I want to make the table of pointers bigger with this realloc.
string=realloc(string,size*sizeof(char**));
i++;
gets(string[i]);
size++;
After you call realloc to enlarge string, the new portion contains no valid pointers. So when you call gets, you're passing it a pointer you failed to initialize.
Also, that size=0; is totally broken.
realloc does not initialize the allocated memory with zeros, in addition you forgot to initialize the newly allocated string pointers.
Consider to move up i++ and size++ within the while loop.
Code Review
initialize all your variables
char *str = NULL,**string = NULL,buffer[50] = {0},temp[2] = {0};
int i = 0,j = 0,q = 0,size = 10,counter = 0;
do not cast what is returned from malloc/calloc and use {} when possible for clarity
string=calloc(size,sizeof(char*));
for (i=0; i<size; i++)
{
string[i]=malloc(50*sizeof(char));
}
When reading from the keyboard do not use gets, use fgets() since you can specify the max size to read.
printf("\nGimme strings, terminate input with x");
char input[256];
fgets(input,sizeof(input),stdin); // another varname, will explain below
With newer compilers you can declare variables where you need them instead of decl at top of function.
char temp={'x','\0'}; // 120;//x
setting size=0 here seems a bit strange
size=0;
it is better to keep what the user inputs in a separate buffer (input)
then if it is not "x" copy it into your string array so instead of
while(strcmp(string[i],temp)!=0)
{
string=realloc(string,size*sizeof(char**));
i++;
gets(string[i]);
size++;
counter++;
}
e.g.
while (fgets(input,sizeof(input),stdin) != NULL && input[0] != 'x')
{
string[i] = calloc(1,strlen(input)+1); // add a byte for \0
strncpy(string[i],input,strlen(input)-1); // not copying ending \n
if ( ++i == size ) // a new chunk needed
{
char *newstring = realloc((size + 10)*sizeof(char*), string );
if ( newstring != NULL )
{
string = newstring;
size += 10;
}
}
}

how to put char * into array so that I can use it in qsort, and then move on to the next line

I have lineget function that returns char *(it detects '\n') and NULL on EOF.
In main() I'm trying to recognize particular words from that line.
I used strtok:
int main(int argc, char **argv)
{
char *line, *ptr;
FILE *infile;
FILE *outfile;
char **helper = NULL;
int strtoks = 0;
void *temp;
infile=fopen(argv[1],"r");
outfile=fopen(argv[2],"w");
while(((line=readline(infile))!=NULL))
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if(temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
} else {
helper=temp;
}
while (ptr != NULL) {
strtoks++;
fputs(ptr, outfile);
fputc(' ', outfile);
ptr = strtok(NULL, " ");
helper[strtoks-1] = ptr;
}
/*fputs(line, outfile);*/
free(line);
}
fclose(infile);
fclose(outfile);
return 0;
}
Now I have no idea how to put every of tokenized words into an array (I created char ** helper for that purpose), so that it can be used in qsort like qsort(helper, strtoks, sizeof(char*), compare_string);.
Ad. 2 Even if it would work - I don't know how to clear that line, and proceed to sorting next one. How to do that?
I even crashed valgrind (with the code presented above) -> "valgrind: the 'impossible' happened:
Killed by fatal signal"
Where is the mistake ?
The most obvious problem (there may be others) is that you're reallocating helper to the value of strtoks at the beginning of the line, but then incrementing strtoks and adding to the array at higher values of strtoks. For instance, on the first line, strtoks is 0, so temp = realloc(helper, (strtoks)*sizeof(char *)); leaves helper as NULL, but then you try to add every word on that line to the helper array.
I'd suggest an entirely different approach which is conceptually simpler:
char buf[1000]; // or big enough to be bigger than any word you'll encounter
char ** helper;
int i, numwords;
while(!feof(infile)) { // most general way of testing if EOF is reached, since EOF
// is just a macro and may not be machine-independent.
for(i = 0; (ch = fgetc(infile)) != ' ' && ch != '\n'; i++) {
// get chars one at a time until we hit a space or a newline
buf[i] = ch; // add char to buffer
}
buf[i + 1] = '\0' // terminate with null byte
helper = realloc(++numwords * sizeof(char *)); // expand helper to fit one more word
helper[numwords - 1] = strdup(buffer) // copy current contents of buffer to the just-created element of helper
}
I haven't tested this so let me know if it's not correct or there's anything you don't understand. I've left out the opening and closing of files and the freeing at the end (remember you have to free every element of helper before you free helper itself).
As you can see in strtok's prototype:
char * strtok ( char * str, const char * delimiters );
...str is not const. What strtok actually does is replace found delimiters by null bytes (\0) into your str and return a pointer to the beginning of the token.
Per example:
char in[] = "foo bar baz";
char *toks[3];
toks[0] = strtok(in, " ");
toks[1] = strtok(NULL, " ");
toks[2] = strtok(NULL, " ");
printf("%p %s\n%p %s\n%p %s\n", toks[0], toks[0], toks[1], toks[1],
toks[2], toks[2]);
printf("%p %s\n%p %s\n%p %s\n", &in[0], &in[0], &in[4], &in[4],
&in[8], &in[8]);
Now look at the results:
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
As you can see, toks[1] and &in[4] point to the same location: the original str has been modified, and in reality all tokens in toks point to somewhere in str.
In your case your problem is that you free line:
free(line);
...invalidating all your pointers in helper. If you (or qsort) try to access helper[0] after freeing line, you end up accessing freed memory.
You should copy the tokens instead, e.g.:
ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);
Obviously, you will need to free each element of helper afterwards (in addition to helper itself).
You should be getting a 'Bad alloc' error because:
char **helper = NULL;
int strtoks = 0;
...
while ((line = readline(infile)) != NULL) /* Fewer, but sufficient, parentheses */
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if (temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
}
This is because the value of strtoks is zero, so you are asking realloc() to free the memory pointed at by helper (which was itself a null pointer). One outside chance is that your library crashes on realloc(0, 0), which it shouldn't but it is a curious edge case that might have been overlooked. The other possibility is that realloc(0, 0) returns a non-null pointer to 0 bytes of data which you are not allowed to dereference. When your code dereferences it, it crashes. Both returning NULL and returning non-NULL are allowed by the C standard; don't write code that crashes regardless of which behaviour realloc() shows. (If your implementation of realloc() does not return a non-NULL pointer for realloc(0, 0), then I'm suspicious that you aren't showing us exactly the code that managed to crash valgrind (which is a fair achievement — congratulations) because you aren't seeing the program terminate under control as it should if realloc(0, 0) returns NULL.)
You should be able to avoid that problem if you use:
temp = realloc(helper, (strtoks+1) * sizeof(char *));
Don't forget to increment strtoks itself at some point.

C: Reading in strings to a structure member using a do...while loop

I'm having a problem understanding how to read in a string to a structure member array. I have a structure called 'customer' and a member called 'char last_name[20]'. I prompt the user to enter in his last name and that last name is to be stored in the 'last_name[20]' variable. The condition is that I have to use a do...while loop.
Here's the code:
void get_customer_info(struct customer *p_customer_start, int customer_num)
{
struct customer *p_customer;
for (p_customer = p_customer_start; (p_customer - p_customer_start) <
customer_num; p_customer++)
{
printf("\nCustomer number %d: ", (p_customer - p_customer_start) + 1);
while (getchar() != NEW_LINE);
printf("\n Enter the customer's last name: ");
// *THIS PART IS THE PROBLEM*
do
{
p_customer->last_name = getchar();
p_customer->last_name++;
} while (*p_customer->last_name != NEW_LINE);
}
return;
}
Problem is, with that algorithm last_name[0] does not get checked, it moves to 'last_name[1]' before it gets checked for a new line. And yes, a do...while construct must be used (this is for a class).
I appreciate anyone's thoughts.
I think you may have much bigger problems there than you realise, with the attempted manipulation of an array address :-)
You can probably avoid all these problems with:
int i = 0;
do {
p_customer->last_name[i++] = getchar();
} while (p_customer->last_name[i-1] != NEW_LINE);
p_customer->last_name[i] = '\0';
Keep in mind that this is still open to buffer overflow problems (as was your original solution) since entering a name like "Pasquale Voldemort Fortescue del Mor" is going to blow out your 20-character array.
There are ways to fix that but it probably doesn't matter for classwork that much (it will in the real world but that comes with experience):
int i = 0;
do {
p_customer->last_name[i] = getchar();
if (i < sizeof (p_customer->last_name) - 1) // NEVER got to 20
i++;
} while (p_customer->last_name[i-1] != NEW_LINE);
p_customer->last_name[i] = '\0';
If you really want a pointer version of it, that's easy:
char *p = p_customer->last_name;
do {
*p = getchar();
if (p != &(p_customer->last_name[sizeof(p_customer->last_name) - 1])
p++;
} while (*(p-1) != NEW_LINE);
*p = '\0';
Ok, the solution with NO INDEXES would be;
char *pointer;
/* other code here */
pointer = p_customer->last_name
do {
*pointer = getchar();
pointer += sizeof( char );
} while ( *(pointer - sizeof( char ) )!= NEW_LINE );
if you want to be sure not to go out of array bound.......simply do not use do while =) (which I always reccomend, as you must read lots of line before understanding the loop condition (and in a multinested function this is't a real readibility problem
The code you posted won't compile. You mentioned that last_name is an array, but you can't assign to or increment arrays, so these two lines are invalid:
p_customer->last_name = getchar();
p_customer->last_name++;
Try doing something like this, which will get rid of this error as well as solve the problem of checking if the first character is a newline:
int i = 0;
char ch;
while ((ch = getchar()) != NEW_LINE)
p_customer->last_name[i++] = ch;
p_customer->last_name[i] = '\0'; // Don't forget the null terminator

Resources