Кто-нибудь может сказать мне, почему я ошибаюсь в этой простой C-программе? - PullRequest
4 голосов
/ 24 марта 2010

Я продолжаю ломать сег после того, как заканчиваю свой первый цикл for, и я не знаю почему. Файл, который я сканирую, состоит из 18 строк и 18 строк. Я думаю, что проблема в том, как я неправильно использую двойной указатель, называемый выбором, но я не знаю точно, почему. Я только пытаюсь отсканировать строки длиной менее 15 символов, поэтому я не вижу проблемы. Может кто-нибудь, пожалуйста, помогите.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 100

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

   char* string = malloc( 15*sizeof(char) );
   char** picks = malloc(15*sizeof(char*));
   FILE* pick_file = fopen( argv[l], "r" );
   int num_picks;


   for( num_picks=0 ; fgets( string, MAX_LENGTH, pick_file ) != NULL ; num_picks++ )
     {
       scanf( "%s", picks+num_picks );
     }
   //this is where i seg fault
   int x;
   for(x=0; x<num_picks;x++)
     printf("s\n", picks+x);
}

Ответы [ 7 ]

9 голосов
/ 24 марта 2010

picks является указателем на указатель: это означает, что объекты, на которые он указывает, сами являются указателями.

Когда вы делаете это:

char** picks = malloc(15*sizeof(char*));

Вы заставляете picks указывать на блок из 15 указателей - что хорошо, насколько это возможно (хотя, поскольку вы хотите читать в 18 строках, вам действительно нужно 18 вместо 15). Это означает, что picks указывает на блок переменных в памяти следующим образом:

| picks (char **) | --------> | picks[0] (char *)  | ----> ?
                              | picks[1] (char *)  | ----> ?
                              | picks[2] (char *)  | ----> ?
                              | ...                |
                              | picks[14] (char *) | ----> ?

Как видите, эти 15 char * указатели теперь не инициализированы - они ни на что не указывают. Вы не можете начать использовать их, пока не выделите для них немного памяти - вам нужно сделать что-то вроде этого:

int i;
for (i = 0; i < 15; i++)
{
    picks[i] = malloc(15);
}

Теперь , после этого раскладка памяти выглядит следующим образом:

| picks (char **) | --------> | picks[0] (char *)  | ----> | picks[0][0] (char)  |
                                                           | picks[0][1] (char)  |
                                                           | ...                 |
                                                           | picks[0][14] (char) |

                              | picks[1] (char *)  | ----> | picks[1][0] (char)  |
                                                           | ...                 |

                              | ...                |

... и теперь у вас есть места для хранения всех тех символов, которые вы хотите прочитать.

5 голосов
/ 23 марта 2010
  1. string выделяется только достаточно памяти для хранения одного символа (sizeof(char)). Если вы хотите сохранить больше символов, вам нужно умножить sizeof(char) на размер строки, которую вы хотите сохранить, плюс один для нуля в конце.

  2. Вместо:

    char** picks = malloc(15*sizeof(char));
    

    Вы хотите сделать это:

    char** picks = malloc(15*sizeof(char*));
    

    Каждый элемент массива picks должен быть достаточно большим, чтобы содержать указатель.

3 голосов
/ 23 марта 2010

Прежде всего, в C строки хранятся как байтовые (char) массивы и должны быть выделены. В этой ситуации строка, используемая для чтения файла, должна быть выделена для символов MAX_LENGTH + 1 (+1 для ограничителя строки, \ 0):

char* string = malloc( (MAX_LENGTH+1) * sizeof(char) );

Это выделит достаточно памяти для строки максимальной длины: MAX_LENGTH.

Другая проблема заключается в том, что массив указателей char **picks не выделен для хранения 18 строк, которые вы ожидаете прочитать:

Он должен быть выделен для 15 указателей на символы (char *), которые также должны быть размещены в первом цикле.

int main( int argc,char *argv[] )
{
   ...
   char* string = malloc( (MAX_LENGTH+1) * sizeof(char) );
   char** picks = malloc(15*sizeof(char *));
   FILE* pick_file = fopen( argv[l], "r" );
   int num_picks;

   for( num_picks=0 ; fgets( string, MAX_LENGTH, pick_file ) != NULL ; num_picks++ )
     {
       printf("pick a/an %s ", string );
       //--- allocate the char array to store the current string
       picks[num_picks] = malloc (15 * sizeof(char));
       sscanf(string, "%s", picks[num_picks] );
     }

   for(int x=0; x<num_picks;x++)
     printf("%s\n", picks[x]);
}

Вы также должны проверить возвращаемое значение malloc (), и вы можете проверить, действительно ли содержимое файла соответствует ожидаемому и не содержит больше строк или строк длиннее 15 символов.


Также scanf () читает стандартный ввод, я заменил его на sscanf () и добавил пропущенный знак «%» во второй printf ().

1 голос
/ 23 марта 2010
  • Местоположение, на которое указывает string, выделяет место только для одного символа, но вы пытаетесь прочитать до MAX_LENGTH символов в нем;
  • Местоположение, на которое указывает picks, выделяет место только для 15 char * указателей, но вы, очевидно, хотите хранить указатели на 18 строк;
  • Местоположение, на которое указывает picks, выделено, но никогда не инициализируется. Вам нужно заставить эти 15 (или 18) указателей на самом деле указать на что-то сами, прежде чем передать их scanf.

В этом случае вообще нет необходимости в динамическом размещении - вы можете делать с массивами все, что хотите:

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

#define MAX_LENGTH 100
#define MAX_LINES 18

int main(int argc, char *argv[])
{
    char string[MAX_LENGTH];
    char picks[MAX_LINES][MAX_LENGTH];
    FILE *pick_file = NULL;
    int num_picks;

    if (argc > 1)
        pick_file = fopen(argv[1], "r");

    if (pick_file == NULL)
        return 1;

    for (num_picks = 0; num_picks < MAX_LINES && fgets(string, MAX_LENGTH, pick_file) != NULL; num_picks++)
    {
        printf("pick a/an %s ", string);
        scanf("%s", picks[num_picks]);
    }

    int x;
    for (x = 0; x < num_picks; x++)
        printf("%s\n", picks[x]);

    return 0;
}
0 голосов
/ 24 марта 2010

Вы получаете ошибку сегмента здесь.

scanf( "%s", picks+num_picks );

вместо этого сделайте это,

for( num_picks=0 ; fgets( string, MAX_LENGTH, pick_file ) != NULL ; num_picks++ )
 {
   pics[num_picks] = (char* )malloc(100);
   scanf( "%s", picks+num_picks );
 }

Проблема в том, что вы выделили **pics для хранения 15 строк, но вы читаете строки без выделения места для этих строк. убедитесь, что вы всегда читаете выделенные указатели.

0 голосов
/ 24 марта 2010

Вы выделяете массив из пятнадцати char* для picks:

char** picks = malloc(15*sizeof(char*));

Теперь picks имеет достаточно места для хранения пятнадцати char*, но вы на самом деле никогда не помещаете внутрь указатели, которые бы указывали на память, которая должна содержать прочитанные символы.

Вам нужно выделить память для этих строк, которые будут прочитаны с scanf. В настоящий момент scanf просто записывает данные туда, куда указывают указатели в picks (которые не были инициализированы, чтобы указывать на где-либо полезное).

0 голосов
/ 23 марта 2010

Это потому что в строке:
for( num_picks=0 ; fgets( string, MAX_LENGTH, pick_file ) != NULL ; num_picks++ )
MAX_LENGTH равно 100, но вы читаете в переменную string, которая может содержать только отдельный символ?

...