Seg Fault с isdigit () в C?

У меня есть этот код:

#include <ctype.h> 

char *tokenHolder[2500];

for(i = 0; tokenHolder[i] != NULL; ++i){

         if(isdigit(tokenHolder[i])){ printf("worked"); }

Где tokenHolder содержит ввод токенов char из пользовательского ввода, который был токенизирован с помощью getline и strtok. Я получаю ошибку seg при попытке использовать isdigit на tokenHolder, и я не знаю, почему.


person user2757849    schedule 28.02.2014    source источник
comment
tokenHolder[i] != '\0' не имеет смысла, если tokenHolder является массивом char *. Это то же самое, что и tokenHolder[i] != NULL.   -  person Dietrich Epp    schedule 28.02.2014
comment
@DietrichEpp Я сделаю это редактирование, спасибо за объяснение.   -  person user2757849    schedule 28.02.2014


Ответы (3)


Поскольку tokenHolder представляет собой массив char *, когда вы индексируете tokenHolder[i], вы передаете char * в isdigit(), а isdigit() не принимает указатели.

Вероятно, вам не хватает второго цикла, или вам нужно:

if (isdigit(tokenHolder[i][0]))
    printf("working\n");

Не забывайте о новой строке.

Ваш тест в цикле тоже нечетный; обычно вы пишете «нулевой указатель» как 0 или NULL, а не как '\0'; это просто вводит людей в заблуждение.

Кроме того, вам нужно обратить внимание на предупреждения компилятора, которые вы получаете! Не публикуйте код, который компилируется с предупреждениями, или (по крайней мере) указывайте, что это за предупреждения, чтобы люди могли видеть, что говорит вам компилятор. Вы должны стремиться к нулевым предупреждениям с компилятором, настроенным на суетливость.

Если вы пытаетесь проверить, что все значения в массиве токенов являются числами, вам нужна функция test_integer(), которая пытается преобразовать строку в число и позволяет узнать, используются ли при преобразовании все данные в строке (или вы можете разрешить начальные и конечные пробелы). Ваша спецификация проблемы не совсем ясна, что вы пытаетесь сделать со строковыми токенами, которые вы нашли с помощью strtok() и т. д.

Что касается того, почему вы получаете дамп ядра:

Код макроса isdigit() часто примерно

#define isdigit(x) (_Ctype[(x)+1]&_DIGIT)

Когда вы предоставляете указатель, он обрабатывается как очень большое (положительное или, возможно, отрицательное) смещение массива из (обычно) 257 значений, и поскольку вы обращаетесь к памяти за пределами границ, вы получаете ошибку сегментации. +1 позволяет передать EOF в isdigit(), когда EOF равно -1, что является обычным значением, но не является обязательным. Макросы/функции, такие как isdigit(), принимают либо символ unsigned char, обычно в диапазоне 0..255, следовательно, либо EOF в качестве допустимых входных данных.

person Jonathan Leffler    schedule 28.02.2014

Вы объявляете массив указателя на char, а не простой массив только char. Вам также необходимо инициализировать массив или присвоить ему некоторое значение позже. Если вы читаете значение члена массива, который не был инициализирован или не назначен, вы вызываете неопределенное поведение.

char tokenHolder[2500] = {0};

for(int i = 0; tokenHolder[i] != '\0'; ++i){

     if(isdigit(tokenHolder[i])){ printf("worked"); }

С другой стороны, вы, вероятно, упускаете из виду предупреждения компилятора о том, что ваш код может быть неправильным. isdigit ожидает int, а char * не совместим с int, поэтому ваш компилятор должен был сгенерировать для этого предупреждение.

person dreamlax    schedule 28.02.2014

Вам нужно/хотите ввести свой ввод в unsigned char перед передачей его в isdigit.

if(isdigit((unsigned char)tokenHolder[i])){ printf("worked"); }

В большинстве типичных схем кодирования символы за пределами диапазона USASCII (например, любые буквы с умляутами, ударениями, гравюрами и т. д.) будут отображаться как отрицательные числа в типичном случае, когда char является знаком.

Что касается того, как это вызывает ошибку сегмента: isdigit (наряду с islower, isupper и т. д.) часто реализуется с использованием таблицы битовых полей, и когда вы вызываете функцию, значение, которое вы передаете, используется в качестве индекса в таблице. Отрицательное число заканчивается попыткой индексации (хорошо) за пределами таблицы.

Хотя сначала я этого не заметил, у вас также есть проблема, потому что tokenHolder (вероятно) не тот тип, который вы ожидали/планировали использовать. Судя по остальной части кода, вы действительно хотите определить его как:

char tokenHolder[2500];
person Jerry Coffin    schedule 28.02.2014
comment
Это подавляет предупреждение компиляции, но по-прежнему передает указатель, преобразованный в беззнаковый символ, в isdigit(). - person Jonathan Leffler; 28.02.2014
comment
Если вы не возражаете, я спрошу, почему char становится отрицательным, должен ли компилятор обрезать значение из-за разных типов? Извините, если я не понимаю, я новичок в C. - person user2757849; 28.02.2014
comment
@ user2757849: код макроса isdigit() часто примерно равен #define isdigit(x) (_Ctype[(x)+1]&_DIGIT). Когда вы предоставляете указатель, он обрабатывается как очень большое (положительное или, возможно, отрицательное) смещение массива из 257 значений. (+1 позволяет передать EOF в isdigit(), когда EOF равно -1, что обычно, но не обязательно.) - person Jonathan Leffler; 28.02.2014
comment
@ user2757849: Вроде того. US ASCII использует 7 бит, поэтому в 8-битном типе все они обычно отображаются как положительные. Чтобы расширить это, они обычно используют восьмой бит, который (если рассматривать его как число со знаком) дает отрицательное значение. - person Jerry Coffin; 28.02.2014