Странное поведение при возврате строки с помощью C

Я новичок в C, и моя программа показалась мне очень странной:

Вот код:

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

char * p(char arg[] , char sym[] , int i , bool rv) {
    char head[i],w[i];
    strncpy(head,arg,i); head[i] = "\0";
    int l;
    for (l = 0 ; l <= (int)(strlen(sym) / i) ; l++) {
        strncpy(w,sym+l*i,i); w[i] = "\0";
        if (strcmp(head,w) == 0) {
            if (rv) { return head; } else {
                char v[strlen(arg) - i];
                strcpy(v,arg+i);
                v[strlen(arg)-i] = "\0";
                return v;
            };
        };
    };
    return arg;
}

int main() {
    printf(p("/parameter","+-\/",1,false));
    getch();
}

Проблема ГЛАВНЫЙ заключается в том, что возвращаемое значение функции - это либо «строка» случайно сгенерированных кодов, либо просто ничего.

Ожидалось, что он вернет / для return h; и parameter для return v;.

Другая проблема заключается в том, что при компиляции программы НЕ обнаруживается ошибок, но появляется множество предупреждений о том, что function returns address of local variable и assignment makes integer from pointer without a cast.

С другой стороны, return arg; очень мирный и не выдает ошибок. (Попробуйте изменить мои коды в p("/parameter","+-\/",1,false), если не верите.) Что я сделал не так?


Использование функции:

p("argument_passed_to_check","symbols_accepted to_be_at_the_front","separate_for_each_i_characters","return_header_instead_of_parameter")

ожидаемый результат:

p("-argue","/\\-",1,false) возвращает argue

p("/help","me",1,false) возвращает /help

p("/help","me",1,true) возвращает (null)

p("--parameter","--++",2,false) возвращает parameter

p("--parameter","--++",2,true) возвращает --


Резюме того, в чем я прошу помощи:

  • За исключением return arg, другие возвращаемые части выглядят странно: return head; выдает случайные символы; return v; вообще ничего не возвращает. Как я могу позволить им работать в соответствии с ожидаемыми результатами?

  • Почему есть эти предупреждения?

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

StoryTeller - Unslander Monica 24.12.2018 13:40
h и v - локальные переменные. Их не существует, когда функция возвращает значение, поэтому не возвращайте их.
Mark Tolonen 24.12.2018 13:46

Считайте предупреждения ошибками. Заменить "\ 0" на '\ 0'

manoliar 24.12.2018 13:55

Спасибо, но могу ли я спросить @MarkTolonen, означает ли это, что я должен сделать h и v глобальными для решения проблемы?

Lai Yan Hui 24.12.2018 13:59

Лай Янь Хуэй, просмотрите плохой код head[i] = "\0";. Этот код пытается назначить указатель на char.

chux - Reinstate Monica 24.12.2018 14:10

Учитывая определение char head[i], как вы думаете, где head[i] = ...; `помещает назначенные данные?

Andrew Henle 24.12.2018 14:12

@chux Спасибо, но manoliar сказал об этом.

Lai Yan Hui 24.12.2018 14:15

@AndrewHenle Извините, я не понимаю, что вы имеете в виду ... Я новичок в этом, извините.

Lai Yan Hui 24.12.2018 14:17

Сколько элементов в head[]? Какой индекс у первого?

Andrew Henle 24.12.2018 14:20
int i символов (элементов) ; индекс первого из них равен 0.
Lai Yan Hui 24.12.2018 14:33

@StoryTeller: Не следует рассматривать именованные объекты как самодокументирующиеся. Имена не могут, не будучи чрезмерно длинными, содержать предварительные условия, постусловия или другую важную информацию, особенно если условия включают отношения с другими элементами. Нет ничего плохого в простых именах объектов, используемых простым знакомым образом, сопровождаемых пояснительными примечаниями и условиями, например, x < y или p указывает на символы n, оканчивающиеся нулем. Не лучше функция, наполненная многочисленными повторами PointerToNCharactersTerminatedByANull[i] = PointerToNCharacterTerminatedByANull[j];.

Eric Postpischil 24.12.2018 16:18

@EricPostpischil - Ваш встречный пример экстраполирует мой комментарий намного дальше, чем я предполагал. Я подозреваю, что это сделано намеренно, чтобы доказать свою точку зрения, но следует отметить, что я нигде не рекомендовал кодифицировать OP в названиях хорошо известных идиом (например, что такое строка в C). Я рекомендую, чтобы имя кодировало цель, которую оно служит для выполнения задачи функции, если короткое имя достаточно, фантастика. Однако в случае OP их выбор из одного имени персонажа не подходит. Как видно по серьезному пагубному влиянию удаления комментария на ясность их опубликованного кода.

StoryTeller - Unslander Monica 24.12.2018 16:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
138
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. Поскольку head определяется как char head[i], его последним элементом является head[i-1]. Попытка получить доступ к head[i] имеет поведение, не определенное стандартом C.

  2. Поскольку w определяется как char w[i], его последним элементом является w[i-1]. Попытка получить доступ к w[i] имеет поведение, не определенное стандартом C.

  3. Поскольку v определяется как char v[strlen(arg) - i], его последним элементом является v[strlen(arg) - i - 1]. Попытка получить доступ к v[strlen(arg) - 1] имеет поведение, не определенное стандартом C.

  4. Поскольку w определен внутри заключенного в фигурные скобки блока операторов без extern или static, у него есть автоматическая продолжительность хранения, связанная с блоком, поэтому он существует только пока функция является блочной. Когда выполняется оператор return, w перестает существовать (в абстрактной машине Си). Оператор return w; пытается вернуть указатель на первый элемент w (потому что в этом случае массив автоматически преобразуется в указатель на свой первый элемент). Когда этот оператор return выполняется, указатель становится недействительным.

  5. Поскольку v определен внутри заключенного в фигурные скобки блока операторов без extern или static, у него есть автоматическая продолжительность хранения, связанная с блоком, поэтому v существует только во время выполнения оператора. Когда выполняется return v;, выполнение блока заканчивается, и возвращенный указатель становится недействительным.

  6. head[i] - это символ, а "\0" - это строка, содержащая один символ, поэтому head[i] = "\0"; - неправильное присвоение. Строка будет преобразована в указатель на ее первый элемент, что приведет к попытке назначить указатель на char. Это нарушение ограничения, и ваш компилятор должен выдать предупреждение об этом. Та же проблема возникает в w[i] = "\0"; и v[strlen(arg)-i] = "\0";. Правильный код будет head[i] = '\0'; (если размер head фиксирован для включения элемента head[i]).

Средства правовой защиты включают:

  • Определите, чтобы каждый массив был достаточно большим, чтобы в него можно было записать все элементы.
  • Чтобы вернуть строки, созданные внутри функции, либо динамически выделите для них пространство (как в случае с malloc), либо создайте строки внутри массивов, предоставленных вызывающей стороной, либо используйте массивы со статической продолжительностью хранения. Если вы используете первый вариант, динамически создаваемые массивы, вы должны предусмотреть освобождение пространства (например, когда вызывающий объект передаст их free, когда они будут выполнены). Вам следует избегать использования массивов со статической продолжительностью хранения, поскольку они имеют ограниченное и проблемное использование (например, тот факт, что для каждого определения существует только один такой массив, но функция может вызываться несколько раз вызывающими, каждый из которых хочет свои собственные отдельные данные) .

Спасибо за руководство ~ (^ u ^)

Lai Yan Hui 24.12.2018 14:20

Но я хотел бы спросить, почему char head[i] может возвращать случайные символы?

Lai Yan Hui 24.12.2018 14:20

@LaiYanHui: Когда вы объявляете char head[i], компилятор C выделяет i байта памяти, которая будет использоваться для head. В таком случае вы обязаны использовать только эти байты i. Когда вы пытаетесь использовать head[i], который находится вне массива, компилятор вас не останавливает. Часто это приводит к тому, что ваша программа обращается к памяти за пределами массива, отведенного для head, и это может нарушить работу вашей программы различными способами. Иногда могут возникнуть другие результаты, так как оптимизация компилятором может неожиданно преобразовать вашу программу, и любое поведение, не определенное стандартом, может иметь какой-либо результат.

Eric Postpischil 24.12.2018 14:26

Большое спасибо ~

Lai Yan Hui 24.12.2018 14:33

Другие вопросы по теме