0

I'm doing a certain project for college, which consists in reading an input text, and generating a certain output, based in several commands. The focal point for grading is the efficiency, so dynamic memory allocation is the way to go, yet I'm really shaky in my knowledge of it.

Either way, the program compiled just fine, but when I ran it, it fast showed a segmentation fault, and I'm almost sure the reason is my poor handling of the memory. After I tried to diagnose it with gdc, I got this:

(gdb) run proj
Starting program: /home/dusk/Documents/proj proj
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
5
hello, I'm me

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a681c3 in _IO_vfscanf () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) where
#0  0x00007ffff7a681c3 in _IO_vfscanf () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff7a70a22 in __isoc99_scanf ()
from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00000000004009a4 in linelist (n=5) at proj.c:79
#3  0x000000000040134b in main () at proj.c:226

So apparently, the problem (well... the first problem) is in the linelist function, which is as follows:

/* Creates a list of strings (each being a line of the input)
implemented with pointers */

char **linelist(int n)
{
char **list;
list = calloc(n, MAX_STR*sizeof(char));
char *input;
int i;

for (i = 0; i < n; i++){
scanf("%s/n", input);
list[i] = input;
}
return list;
}

And this is the main function:

/*MAIN*/
int main(){
int linesnum = readlinesnum();
char **lines = linelist(linesnum);
char ***matrix = createmat(linesnum, lines);
char input;
fstruct ignore;
ignore.len = 0;
while (1){
    scanf("%c", &input);
    if (input == 'f'){
        ignore = f(ignore);
    }
    else if (input == 's'){
        s(lines, linesnum);
    }
    else if (input == 'l'){
        l(matrix, lines, linesnum, ignore);
    }
    else if (input == 'w'){
        w(matrix, lines, linesnum, ignore);
    }
    else if (input == 'h'){
        h(matrix, linesnum);
    }
    else if (input == -1){
        break;
    }
}
freememory(matrix, lines);
return 0;
}

The readlinesnum function seems to be working fine, so it's when I actually get to creating the list with the lines that things don't go well. I'd love to understand the exact reason why they don't, for I think any other problems I surely have within the rest of the code are related to this issue as well.

Thank you.

Dusk252
  • 187
  • 3
  • 7

3 Answers3

1

This line is the source of your troubles:

scanf("%s/n", input);

Not only is it supposed to be \n rather than /n (that's minor), but you're passing the current value of input to scanf when input is uninitialized. That's no good. For integers and such, you'd at least take the address of it before giving it to scanf:

scanf("%d", &my_number);

However, that's not applicable here: for %s, it tries to store the data into the memory you give it. You're giving it an uninitialized pointer, which may crash and in any case will never do what you want it to do.

You need to allocate memory for it yourself. For example, if you wanted to read a line of at most 200 characters, you'd do something like this:

char my_array[201];  /* don't forget to save space for the null byte */
scanf("%200s", my_array);

Then you're limiting the length of the line, though, and that's no good. Clearly we need another solution.

scanf, you may soon discover, is not a good tool for this job. We'll use fgets instead. The algorithm you'll need to implement is:

  1. Keep track of how much we've read so far. That'll be initialized to zero.
  2. Keep track of the current allocated size of the buffer. Initialize it to something reasonable.
  3. Allocate some memory of that size. (Don't forget to check for errors.)
  4. Call fgets, passing it &allocated_memory[read_so_far] for the buffer and amount_allocated - read_so_far for the length. The file should probably be stdin to do what you were doing previously, but it can be any readable file. (Again, don't forget to check for errors.)
  5. Update how much we've read so far.
  6. Check to see if we've read the whole line. Because fgets will preserve the newline if there is one and a newline will exist if we've read the whole line and we're not at the end of the file, we've read the whole line if there is a newline before the end of the string or we have reached end-of-file.
  7. If we have not read the whole line, double the current allocated size of the buffer and realloc the buffer. Don't forget to check for errors. Then go to step 4.
  8. If we reach here, we've finished reading the line. Optionally realloc the buffer so it fits the string exactly with no wasted space.

It's not that fun to implement but it's a useful thing to have around, so you might want to wrap it in a function. It's kind of a pain, but if you're using C, maybe you should get used to it.

icktoofay
  • 126,289
  • 21
  • 250
  • 231
0

For starters, your linelist function has a number of pretty serious errors.

First, you are calling calloc but not allocating enough space. What you want is an array of char * (pointers, which are either 4 or 8 bytes long), but you are allocating an array of characters. In short, replace sizeof(char) with sizeof(char *).

Then, you need to allocate space for every string that you are reading in.

Finally, you are writing into unallocated memory in your scanf statement.

In short, you need to do your homework on allocating memory, etc. I would also recommend that you debug your code by running it with Valgrind.

EmeryBerger
  • 3,897
  • 18
  • 29
  • Hmm... My line of thought on that was that I had a limit for the size of the strings it would read, which is defined as the constant `MAX_STR`. So multiplying that by `sizeof(char)`, would give me the max size of the string. And giving that I want an array of them of size `n`, that's what it should do. Am I wrong here? Also. I'll try Valgrind, and thanks a lot for the useful answer. – Dusk252 May 12 '13 at 01:28
  • All of the above are right, but that would be for a single array of characters that holds all the strings. Instead, you are presumably looking to have an array of array of characters (based on your "char **" declaration). If you want linelist to be usable as a vector, you need to allocate an array of pointers first, and then initialize each of those pointers to an appropriately-sized buffer. – EmeryBerger May 12 '13 at 16:20
0

In your function linelist this line:

scanf("%s/n", input);

is reading into unallocated memory which seems to be your immediate problem, in main though you are doing the correct thing. You are clearly not doing the correct things with calloc but it is not clear to me what you are trying to do from the code sample so not sure what the right solution is there. Although assuming you are trying to create a 2D array this previous thread should be helpful.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I was trying to create a list of strings by reading different lines of input: each line is one entry in the list. `n` is the number of lines, previously read from the input as well. `MAX_STR` is the max number of characters to be read in a line. – Dusk252 May 12 '13 at 01:34