0
votes

I am getting a segmentation fault when I call my getField(char *line, int field) function in my while loop and I'm not sure why. I'm trying to pass a line to the function and a column number so that I can grab specific columns from each line in a csv file and print them to the screen. Thanks for input.

void getField(char *line, int field);

int main(int argc, char *argv[]) {

  if(argc < 3) {
    fprintf(stderr, "Too few arguments \"%s\".\n", argv[0]);
  }

  if(atoi(argv[1]) < 1) {
    fprintf(stderr, "First argument must be >= 1 \"%s\".\n", argv[1]);
  }
  FILE *fp = fopen(argv[2], "r");
  if(fp == NULL)
    fprintf(stderr, "Cannot open file %s\n", argv[0]);
  char buf[80];
  while(fgets(buf, 80, fp) != NULL) {
    getField(buf, atoi(argv[1]);  // seg fault is happening here
  }

  return 0;
}

void getField(char *line, int field) {
  printf("here2");
  //char *ln = line;
  int column = field - 1;
  int idx = 0;
  while(column) {
    //printf("here");
    if(line[idx] == ',') field--;
    idx++;
  }

  for(int j = idx; ; ++j) {
    if(line[j] == ',') break;
    printf("%s", line[j]);
  }
  printf("\n");
  printf("%d", idx);
}
4
Exactly which line is the segfault happening in? - merlin2011
Can you run gdb and post the backtrace for the seg fault? - rw.liang1
In your 1st test you should stop the program, there is not point in continuing if there are not enough arguments. - Jabberwocky
@merlin2011 it's happening before the call to getField(). It's not even calling it. - StarCute
In the while loop while(column) ... you are looping till column does not become zero. But you are never updating column. - jyotesh

4 Answers

4
votes

One obvious error is that you have an infinite loop here, and you will eventually access illegal memory.

while(column) {
     //printf("here");
    if(line[idx] == ',') field--;
    idx++;
}

You are not modifying column at all, so your loop cannot possibly end. column will not update itself when you update field, so you will have to update it if you want it to update.

while(column) {
     //printf("here");
    if(line[idx] == ',') field--;
    idx++;
    column = field - 1;
}

Note on debugging segfaults using printf.

The function printf prints to stdout and stdout likes to buffer output. This means that sometimes if you try to find a segfault by moving a print statement down your code until it fails to print, you will misunderstand where the segfault it happening. In particular, a printf line that appears before the line that actually contains the segfault may not print even if you might expect it to.

If you want to use this strategy (instead of gdb), you can force it to print by using fflush(stdout); immediately after your debugging printf.

1
votes
while(column) {
        //printf("here");
        if(line[idx] == ',') column--;  // Changed field-- to column--
                idx++;
}
0
votes

In following line:

printf("%s", line[j]);

you are using the %s format specifier but you are passing a char as argument.

You probably want this (%c format specifier fot printing a char):

printf("%c", line[j]);
0
votes

You are accessing out of bounds of the array in the function getField because the while loop never exits. This invokes undefined behaviour and most likely program crash due to segfault which is what is happening in your case. I suggest the following changes to your program.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void getField(char *line, int field);

int main(int argc, char *argv[]) {
  if(argc < 3) {
    fprintf(stderr, "Too few arguments \"%s\".\n", argv[0]);
    return 1; // end the program
  }

  if(atoi(argv[1]) < 1) {
    fprintf(stderr, "First argument must be >= 1 \"%s\".\n", argv[1]);
    return 1; // end the program
  }

  FILE *fp = fopen(argv[2], "r");
  if(fp == NULL) {
    fprintf(stderr, "Cannot open file %s\n", argv[0]);
    return 1; // end the program
  }

  char buf[80];
  while(fgets(buf, 80, fp) != NULL) {
    getField(buf, atoi(argv[1]));  // seg fault is happening here
  }

  return 0;
}

void getField(char *line, int field) {
  int len = strlen(line);
  char temp[len + 1];
  strcpy(temp, line);

  int count = 0;
  char ch = ',';
  char *p = temp;
  char *q = NULL;
  while(count < field - 1) {
    q = strchr(p, ch);
    if(q == NULL) {
      printf("error in the value of field\n");
      return;
    }
    count++;
    p = q + 1;
  }
  q = strchr(p, ch);
  if(q != NULL)
    *q = '\0';
  else 
    temp[len-1] = '\0';

  printf("%s\n", p);
}