Вызов isalpha вызывает ошибку сегментации

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

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

int main(int argc, char *argv[])
{
    printf("TEST");

    for (int k=0; k<(strlen(argv[1])); k++)
    {
        if (!isalpha(argv[1])) {
            printf("Enter only alphabets!");
            return 1;
        }
    }

    return 0;
}

я понял, что именно эта строка вызывает проблему

if (!isalpha(argv[1])) {

и заменить argv[1] С argv[1][k] решает проблему.

однако мне довольно любопытно, что программа приводит к ошибке сегментации даже без печати TEST. Я также ожидаю isalpha функция неправильно проверить, если нижний байт char* указатель argv[1], но, похоже, это не так. У меня есть код для проверки количества аргументов, но он не показан здесь для краткости.

что здесь происходит?

4 ответов


в общем, довольно бессмысленно обсуждать, почему неопределенное поведение приводит к тому или иному результату.

но, возможно, не вредно попытаться понять, почему что-то происходит, даже если это вне спецификации.

есть реализации isalpha что использовать простой массив для поиска всех возможных unsigned char значения. В этом случае значение, переданное как параметр, используется в качестве индекса в массиве. В то время как реальный символ ограничен 8 битами, целое число-нет. Этот функция принимает int в качестве параметра. Это должно позволить войти EOF а также, который не вписывается в unsigned char.

если вы передаете адрес как 0x7239482342 в вашу функцию, это далеко за пределами конца указанного массива, и когда процессор пытается прочитать запись с этим индексом, она падает с края мира. ;)

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

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


  1. printf не была слита
  2. неявное преобразование из указателя на целое число это должно было создать по крайней мере диагностику времени компиляции для нарушения ограничений произвел число, которое было вне диапазона для isalpha. isalpha будучи реализованным как таблица поиска, означает, что ваш код получил доступ к таблице за пределами, поэтому неопределенное поведение.
  3. почему вы не сделать диагностику, может быть в одном часть из-за как isalpha реализован как макрос. На моем компьютере с Glibc 2.27-3ubuntu1, isalpha определяется как

    # define isalpha(c)     __isctype((c), _ISalpha)
    # define __isctype(c, type) \
        ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
    

    макрос содержит неудачный бросок в int в нем, который заставит замолчать вашу ошибку!


одна из причин, почему я публикую этот ответ после стольких других, заключается в том, что вы не исправили код, она по-прежнему страдает от неопределенного поведения с учетом расширенных символов и char подписывается (что обычно происходит на x86-32 и x86-64).

правильный аргумент, чтобы дать isalpha и (unsigned char)argv[1][k]! C11 7.4:

во всех случаях аргумент-это int, значение которого должно быть представлено как unsigned char или должно равняться значению макроса EOF. если аргумент имеет любое другое значение, то поведение неопределено.


Я нахожу довольно любопытным, что программа приводит к ошибке сегментации даже без теста печати

printf не печатает мгновенно, но записывает во временной буфер. Завершите строку с \n Если вы хотите сбросить его до фактического выхода.

и замена argv[1] на argv[1] [k] решает проблему.

isalpha предназначен для работы с одиночными символами.


прежде всего, соответствующий компилятор должен дать вам диагностическое сообщение здесь. Не допускается неявное преобразование из указателя в int параметр that isalpha ожидает. (Это является нарушением правил простой уступки, 6.5.16.1.)

Что касается того, почему "тест" не печатается, это может быть просто потому, что stdout не смывается. Вы можете попробовать добавить fflush(stdout); после printf и посмотреть, решает ли это проблему. В качестве альтернативы добавьте ленту\n В конце строка.

в противном случае компилятор может переупорядочить выполнение кода до тех пор, пока нет побочных эффектов. То есть, разрешено выполнять весь цикл до printf("TEST");, пока он печатает TEST прежде чем он потенциально печатает "Enter only alphabets!". Такая оптимизация, вероятно, вряд ли произойдет здесь, но в других ситуациях они могут произойти.