Ошибка сегментации после ввода в строку из структуры - PullRequest
0 голосов
/ 18 октября 2018

так как в вопросе говорится, что я получаю ошибку сегментации каждый раз, когда пытаюсь ввести имя для клиента.Эта программа компилируется и работает, пока не дойдет до части имени клиента.Я не уверен, что проблема с malloc.Может ли кто-нибудь показать мне, что я делаю не так?Я пытался выяснить это некоторое время безуспешно.Спасибо

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

#define END_OF_STRINGS  '\0' 
#define NEWLINE         '\n'    
#define MAX_CUSTOMERS   100     
#define MIN_CUSTOMERS   2     
#define MAX_NAME_LENGTH 20    
#define DB_ALLOC_ERR    1  
#define QUIT            0  

struct customer
{
    char  *p_last_name[MAX_NAME_LENGTH + 1];
    float amount_owed;
    int   priority;
};

void print_instructions();
int  number_of_customers();
void get_accounts(struct customer *p_customer_start, int
    customer_amount);
void clean_names(struct customer *p_customer_start, int
    customer_amount);
void sort_names(struct customer *p_customer_start, int
    customer_amount);
void print_results(struct customer *p_customer_start, int
    customer_amount);

int main()
{
    struct customer *p_customer;
    int    customer_amount;


    while (print_instructions(), (customer_amount =
        number_of_customers()) != QUIT)
    {
        if ((p_customer = (struct customer *)malloc(sizeof(*p_customer) *
            MAX_NAME_LENGTH)) == NULL)
        {
            printf("\nError #%d occurred in main()", DB_ALLOC_ERR);
            printf("\nCannot allocate memory for database of customer ");
            printf("\naccountable records");
            printf("\nThe program is aborting");
            exit  (DB_ALLOC_ERR);
        }

        get_accounts (p_customer, customer_amount);
        clean_names  (p_customer, customer_amount);
        sort_names   (p_customer, customer_amount);
        print_results(p_customer, customer_amount);

        printf("%c", NEWLINE);

        free(p_customer);
    }

    return 0;
}

void print_instructions()
{
    printf("\n\nThis program allows you to input customers which owe");
    printf("\nyou money (your accounts receivable), and manage these");
    printf("\naccounts in a database.  You will enter the following:");
    printf("\n   Customer last name (1-20 characters)");
    printf("\n   Amount the customer owes (to the exact cent)");
    printf("\n   Customer priority (1=VIP, 2=Important, 3=Regular)");
    printf("\nFrom 2 to 100 customers may be processed.");
    return;
}

int number_of_customers()
{
    int user_choice;

    printf("\n\nGet the customers for the database");
    printf("\n--------------------------------------------------");

    do
    {
        printf("\nHow many customers do you have (%d to %d, %d=quit): ", MIN_CUSTOMERS, MAX_CUSTOMERS, QUIT);
            scanf ("%d", &user_choice);
    } while ((user_choice < MIN_CUSTOMERS ||
        user_choice > MAX_CUSTOMERS) && user_choice != QUIT);
    return user_choice;
}

void get_accounts(struct customer *p_customer_start, int
    customer_amount)
{
    struct customer *p_customer;

    for (p_customer = p_customer_start; (p_customer - p_customer_start)
        < customer_amount; p_customer++)
    {
        printf("\nCustomer number %d", (int)(p_customer -
            p_customer_start + 1));
        printf("\n   Enter the customer's last name: ");
        scanf ("%20s", p_customer->p_last_name[MAX_NAME_LENGTH + 1]);
        getchar();
        do
        {
            *p_customer->p_last_name[MAX_NAME_LENGTH] = getchar();
            p_customer->p_last_name[MAX_NAME_LENGTH]++;
        } while (!NEWLINE);
        p_customer->p_last_name[MAX_NAME_LENGTH + 1] = END_OF_STRINGS;
        printf("\n  Enter the amount owed: ");
        scanf ("%f", &p_customer->amount_owed);
        do
        {
            printf("\n  Enter the customer's priority (1-3): ");
            scanf ("%d", &p_customer->priority);
        } while (p_customer->priority < 1 || p_customer->priority > 3);
    }
    return;
}

void clean_names(struct customer *p_customer_start, int
    customer_amount)
{
    char   *p_fast = p_customer_start->p_last_name[MAX_NAME_LENGTH],
        *p_slow = p_customer_start->p_last_name[MAX_NAME_LENGTH];

    if (tolower(*p_fast))
        *p_slow++ = toupper(*p_fast);
    while (*p_fast != END_OF_STRINGS)
    {
        if (!isspace(*p_fast) || isalpha(*p_fast))
            *p_slow++ = tolower(*p_fast);
        p_fast++;
    }
    *p_slow = END_OF_STRINGS;
    return;
}

void sort_names(struct customer *p_customer_start, int
    customer_amount)
{
    char   *p_inner[MAX_NAME_LENGTH],
        *p_outer[MAX_NAME_LENGTH],
        temp[MAX_NAME_LENGTH];

    for (p_outer[MAX_NAME_LENGTH] = p_customer_start ->
        p_last_name[MAX_NAME_LENGTH]; (p_outer - p_customer_start ->
            p_last_name)
        < customer_amount; p_outer[MAX_NAME_LENGTH]++)
    {
        for (p_inner[MAX_NAME_LENGTH] = p_outer[MAX_NAME_LENGTH + 1];
            (p_inner - p_customer_start ->
                p_last_name) < customer_amount; p_inner[MAX_NAME_LENGTH]++)
        {
            if (strcmp(p_outer[MAX_NAME_LENGTH],
                p_inner[MAX_NAME_LENGTH]))
            {
                temp[MAX_NAME_LENGTH] = *p_outer[MAX_NAME_LENGTH];
                *p_outer[MAX_NAME_LENGTH] = *p_inner[MAX_NAME_LENGTH];
                *p_inner[MAX_NAME_LENGTH] = temp[MAX_NAME_LENGTH];
            }
        }
    }
    return;
}

void print_results(struct customer *p_customer_start, int
    customer_amount)
{
    char   last_name[MAX_NAME_LENGTH];
    float  amount_owed = p_customer_start->amount_owed;

    printf("\n  Here is the accounts receivable customer database");
    printf("\n=====================================================");
    printf("\n   Customer Name         Amount        Priority");
    printf("\n--------------------    ---------    -------------");
    printf("\n          %s         $    %.2f   ", last_name,
        amount_owed);

    switch (p_customer_start->priority)
    {
    case 1:
        printf("1 (VIP)");
        break;

    case 2:
        printf("2 (Important)");
        break;

    case 3:
        printf("3 (Regular)");
        break;
    }
    printf("\n\n******* End Of Customer Database Processing *******");
    return;
}

1 Ответ

0 голосов
/ 19 октября 2018

Я полагаю, что начало вашей проблемы здесь:

struct customer
{
    char  *p_last_name[MAX_NAME_LENGTH + 1];
    float amount_owed;
    int   priority;
};

с этим кодом вы создаете 21 указатели на символ.

То, что вы хотите, это указатель символав пространство, которое будет содержать MAX_NAME_LENGTH + 1 символов

Поэтому вы хотели бы что-то вроде:

struct customer
{
   char  last_name[MAX_NAME_LENGTH + 1];
   float amount_owed;
   int   priority;
};

Я также изменил p_last_name на просто last_name, чтобы на глаз это читалось более логично, но вы можете называть это как хотите, но сказать, что p_last_name означает, что это указатель, который не нужен, и он плохо читается

При объявлении или определении переменных вы читаете справа налево, тогда это будет массив из-за [], который 21 большой, называется last name, и это массив типа данных char.

Теперь с C дело в том, что массивы и указатели имеютчто-то общее, или часто может быть перепутано ... потому что они технически одно и то же.Любой определяемый вами массив, который, в свою очередь, выделяет пространство в памяти, является не более чем указателем на начало массива , вот и все!

, когда вы делаете что-то вроде last_name[7]тогда 7 - это количество прыжков с начала массива, которое в вашем случае всегда известно как last_name.Размер перехода зависит исключительно от типа данных массива, когда он был определен.В вашем случае это char, что составляет 1 байт, поэтому скачок last_name[7] будет на 7 байт дальше, чем last_name указывает на

For example if the contents in memory where `last_name` points to is    abcdefghijklmnopqrst
  • , тогда char last_name[MAX_NAME_LENGTH + 1]; будетопределить переменную с именем last_name, которая технически является символьным указателем на непрерывный фрагмент памяти, который составляет MAX_NAME_LENGTH + 1 байт из-за типа данных char , и это указатель на начало этого фрагмента памяти.
  • *last_name - это то же самое, что и last_name[0], которое задерживает указатель символа last_name , так что оно возвращает содержимое памяти, равное a
  • *(last_name+2) совпадает с last_name[2], то есть c

Кроме того, в

int main()
{
   struct customer *p_customer;
   int    customer_amount;

этот оператор struct customer *p_customer; создает один указательназывается p_customer , это указатель, который будет указывать на некоторый кусок памяти (еще не произошел), который имеет тип данных struct customer, который определен выше.Хорошо, чтобы там.Затем в

if ((p_customer = (struct customer *)malloc(sizeof(*p_customer) * MAX_NAME_LENGTH)) == NULL)

, где вы используете malloc, чтобы зарезервировать часть памяти для того, что вы делаете, вы действительно делаете sizeof( a pointer )

то, что выдолжно быть (struct customer *) malloc( sizeof( struct customer )) в дополнение к правильному определению 21-байтового массива символов, называемого last_name в struct customer.

. Он должен логически считываться на английском , часто справаналево, если нет, то заподозрить проблему.Также при компиляции учитесь использовать -W, он может быть вашим другом и предупреждать вас о подобных проблемах.

ваш исходный код, вероятно, не выделяет или резервирует достаточно большой кусок памяти для количества набираемых вами символов.в магазине в p_last_name.

...