проблемы с указателем в C? функция для упорядочения входных данных, возврата в виде строки и печати в основном - PullRequest
2 голосов
/ 18 января 2020

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

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

const char * entry()
{
    int n;
    int level;
    char habit1entry[6];
    char habit2entry[6];
    char habit3entry[6];
    for (int c = 0; c< 3; c++){
        printf("Habit #\n");
        scanf("%d", &n);
        printf("Level:\n");
        scanf("%d", &level);
        switch (n)
        {
            case 1:;
                sprintf(habit1entry, "|%d|%d|\n", n,level);
                printf("n = %d\n",n); 
                printf("%s\n",habit1entry);
                continue;
            case 2:;
                sprintf(habit2entry, "|%d|%d|\n", n,level);
                printf("n = %d\n",n);
                printf("%s\n",habit2entry);
                continue;
            case 3:;
                sprintf(habit3entry, "|%d|%d|\n", n,level);
                printf("n = %d\n",n);
                printf("%s\n",habit3entry);
                continue;
        }
    }
    strcat(habit2entry,habit3entry);
    printf("%s\n",habit2entry);
    strcat(habit1entry,habit2entry);
    printf("%s\n",habit1entry);
    char *fullEntry=habit3entry;
    printf("%s\n",fullEntry);

    return strdup(&fullEntry[0]);
}
int main(){
    const char * dataEntry = entry();
    //strcpy(dataEntry,entry());
    printf("Data:\n%s",dataEntry);
}

вот пример вывода (после правильной печати внутри корпуса переключателя) для входа 3 2 1 1 2 2: "

| 2 | 2 |

| 1 | 1 |
| 2 | 2 |
| 2 | 2 |
| ��
| 2 | 2 |
| ��
* Обнаружено разрушение стека *: ./a.out прекращено
Прервано (сброшено ядро) "

ps Извините, если все это звучит глупо, это мой первый проект C (и первый реальный пост переполнения стека, plz b gentl), возникающий в результате прыжков между java, python и clojure, и я хотел бы взять класс операционных систем, который позволяет вам начинать, не зная C, но ожидает, что вы его поднимите сам по себе и его труднодоступный материал, который объясняет C концепций в области, которая соответствует моим базовым знаниям и текущим ограничениям обучения с точки зрения времени, доступного для глубоких погружений с помощью объяснений, которые для меня в конечном итоге оказались либо безнадежно тайными c, невероятно конкретный случай c или чрезмерно упрощенный / красный недопустимые / бесполезные объяснения концепций программирования, которые я подобрал на других языках. Не хочу жаловаться или проговаривать, и, вероятно, полезно практиковаться в разных способах задавать вопросы и находить ответы на подобные проблемы, но кривая обучения для понимания подобных вещей (настройка файлов компилятора / json требовала тратить часы только чтобы обнаружить, что mcafee удалял мои exe-файлы, которые, как я убедился, были признаком вируса, только для того, чтобы остановить поведение после того, как я перезапустился для незначительного обычного windows обновления, и я понятия не имею, почему) иногда за пределами традиционной структуры больше похоже на стену, и я боюсь, что, возможно, мне следует пересмотреть свой подход, чтобы не тратить слишком много времени на то, чтобы биться головой о ряд очень крепких стен. Любой совет очень ценится.

Ответы [ 2 ]

0 голосов
/ 18 января 2020

Добро пожаловать в странный и чудесный мир C.

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

Способ написания вашей программы подготовлен для генерации переполнения стека. У вас есть три (очень маленьких) символьных массива, определенных в привычке стека, так что ваши sprintf наверняка взорвут ваш стек, если оба входа Habit и Level не меньше 10. Привычка в порядке, потому что ваш переключатель допускает только 1, 2 или 3. Ваш переключатель ничего не делает, если Habit - это что-то еще.

Примечание: sprintf на самом деле не является функцией, используемой в нашем мире, ориентированном на безопасность. snprintf - лучший выбор. Само по себе это не является проблемой, так как вы не передаете данные, предоставленные пользователем, но, тем не менее, это плохая привычка культивации.

Затем вы объединяете массивы ваших персонажей, фактически гарантируя нарушение стека, но давайте предположим, это работает; вы объединяете 2 и 3 в привычный ввод, а затем 1 и 2 в привычный ввод.

Затем вы создаете указатель на привычный ввод (не привычный ввод) и возвращаете дубликат.

Делая это, вы получаете выделение кучи мягким неясным образом. Вызываемый будет отвечать за освобождение этой памяти. Я всегда предпочитал явно mallo c памяти, а затем strcpy (или memcpy) данные в. Теперь, когда вы grep ваш код, вам нужно только искать mallo c. Кроме того, кто-то, использующий функцию, заметит mallo c, увидит, что вы вернулись к указателю и поймет, что освобождение его теперь станет его проблемой.

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

В вашем операторе switch я заметил, что за каждым из ваших ярлыков дел следует пустой оператор. точка с запятой в конце этой строки не обязательна: напишите "case 1:", а не "case 1 :;" Вы также используете продолжить в конце каждого блока. Это разрешено, но «перерыв» более уместен. В этом случае это будет иметь тот же эффект, но обычно у вас есть больше операторов после переключения. Теперь разница станет очевидной. Continue прыгнет прямо к вершине l oop, прорыв вырвется из переключателя и продолжит выполнение там.

Надеюсь, это даст вам некоторое представление.

Удачи.

0 голосов
/ 18 января 2020

Абстрагируясь от логи c программы, у вас там много проблем:

  1. Вы не предоставляете достаточно места для строк
  2. Ваш переключатель не очень связанные с вами для l oop
  3. Имена переменных не имеют значения для вас - но они имеют значение для программы. Будь осторожнее.
  4. наверное больше, но я уже забыл
#include <stdio.h>
#include <string.h>

const char * entry()
{
    int n;
    int level;
    char habit1entry[21] = "";
    char habit2entry[14] = "";
    char habit3entry[7] = "";
    for (int c = 1; c < 4; c++){
        printf("Habit #\n");
        scanf("%d", &n);
        printf("Level:\n");
        scanf("%d", &level);
        switch (c)
        {
            case 1:;
                sprintf(habit1entry, "|%d|%d|\n", n,level % 10);
                printf("n = %d\n",n); 
                printf("He1: %s\n",habit1entry);
                continue;
            case 2:;
                sprintf(habit2entry, "|%d|%d|\n", n,level % 10);
                printf("n = %d\n",n);
                printf("He2 = %s\n",habit2entry);
                continue;
            case 3:;
                sprintf(habit3entry, "|%d|%d|\n", n,level % 10);
                printf("n = %d\n",n);
                printf("He3 = %s\n",habit3entry);
                continue;
        }
    }
    strcat(habit2entry,habit3entry);
    printf("H2 + H3 = %s\n",habit2entry);
    strcat(habit1entry,habit2entry);
    printf("H1 + H2 = %s\n",habit1entry);
    char *fullEntry=habit1entry;
    printf("FE: %s\n",fullEntry);

    return strdup(fullEntry);
}
int main(){
    const char * dataEntry = entry();
    //strcpy(dataEntry,entry());
    printf("Data:\n%s",dataEntry);
}
...