Подтвердить что ты не робот

C, скомпилированная с cygwin в Windows, работает с ошибкой сегментации под Linux. Является ли cygwin GCC "плохим"?

Для моего программирования класса 102 нас попросят предоставить код C, который компилируется и запускается под Linux. Мне не хватает свободного места на жестком диске для установки Linux вместе с Windows, поэтому я использую cygwin для компиляции моих программ.

Самая последняя программа, которую я должен был компилировать и запускать под Cygwin. Он отлично компилируется под Linux, но на полпути через выполнение возникает ошибка сегментации. Я объяснил это студенту-градиенту, который дает нам класс, и он сказал, что версия cygwin GCC позволяет скомпилировать и выполнить код sloppier.

Несколько ссылок, которые я нашел через google, не были окончательными. Один поток, который я нашел, сказал, что причиной сбоя seg в Linux является утечка памяти. Почему это не повлияет на версию cygwin?

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

Является ли версия GCC cygwin более "слабой" с кодом, который он компилирует? Если да, есть ли очевидные проблемы, которые нужно учитывать при кодировании? Существуют ли альтернативы для написания кода, который будет работать под Linux?

Edit

Спасибо за ответы. Я не был достаточно явным в своем оригинальном посте: что ошибка в моем коде была для меня значительной для меня (я совершенно новый для программирования и действительно зеленый, когда дело доходит до C). Мой TA подразумевал, что cygwin GCC является менее надежным компилятором, позволяющим работать с большим количеством sloppier-кода, чем тот, который найден в GNU/Linux. Я нашел это странным и так сделал поиск в Интернете, но не мог найти никаких ссылок на этот факт.

Больше, чем обвинять компилятор в сравнении с моим кодом, мне было интересно, что может быть причиной для запуска программы под Windows и сбоя в Linux. В этом отношении иллюстрируются ответы re: разные менеджеры памяти и компоновка кучи/стека под Windows/Linux.

Будет ли заключение, что cygwin GCC так же "хорош", как GNU/Linux ", и что основные операционные системы/просто удача в том, что моя багги-программа работает под одной, а не с другой, будет довольно правильной?

Что касается публикации исходного кода, это домашнее задание, поэтому я бы предпочел самому найти проблему, если это вообще возможно:)

Изменить 2

Я принял jalf-ответ, так как он говорит о том, что заставляет программу работать под Windows, а не под Linux, чего я действительно хотел узнать. Спасибо всем, кто внес свой вклад, они были очень интересными и информативными ответами.

Когда я нашел проблему и исправил ее, я загружу zip файл со всем исходным кодом этой нерабочей версии, если кому-то интересно узнать, что, черт возьми, я сделал:)

Изменить 3

Для тех, кто интересуется кодом, я нашел проблему, и это было действительно из-за указателей. Я пытался вернуть указатель из функции. Указатель, который я пытался вернуть, был объявлен внутри функции и был уничтожен после выполнения функции. Проблемный код прокомментирован в строках 22-24.

Не стесняйтесь высмеивать мой код.

/**
*  Returns array of valid searches based on current coordinate
*/
void determine_searches(int row, int col, int last_row, int last_col, int *active_search){
    // define coordinate categories and related valid search directions
    int Library0[] = {2, 3, 4, -1};
    int Library1[] = {4, 5, 6, -1};
    int Library2[] = {2, 3, 4, 5, 6, -1};
    int Library3[] = {0, 1, 2, 3, 4, 5, 6, 7, -1};
    int Library4[] = {0, 1, 2, -1};
    int Library5[] = {0, 6, 7, -1};
    int Library6[] = {0, 1, 2, 6, 7, -1};
    int Library7[] = {0, 1, 2, 3, 4, -1};
    int Library8[] = {0, 4, 5, 6, 7, -1};

    int * Library[] = { 
        Library0, Library1, Library2,
        Library3, Library4, Library5,
        Library6, Library7, Library8,
    };

    // declare (and assign memory to) the array of valid search directions that will be returned
    //int *active_search;
    //active_search = (int *) malloc(SEARCH_DIRECTIONS * sizeof(int));


    // determine which is the correct array of search directions based on the current coordinate
    // top left corner
        int i = 0;
    if(row == 0 && col == 0){
        while(Library[0][i] != -1){
            active_search[i] = Library[0][i];
            i++;
        }
    }
    // top right corner
    else if(row == 0 && col == last_col){
        while(Library[1][i] != -1){
            active_search[i] = Library[1][i];
            i++;
        }
    }
    // non-edge columns of first row
    else if(row == 0 && (col != 0 || col != last_col)){
        while(Library[2][i] != -1){
            active_search[i] = Library[2][i];
            i++;
        }
    }
    // non-edge coordinates (no edge columns nor rows)
    else if(row != 0 && row != last_row && col != 0 && col != last_col){
        while(Library[3][i] != -1){
            active_search[i] = Library[3][i];
            i++;
        }
    }
    // bottom left corner
    else if(row == last_row && col == 0){
        while(Library[4][i] != -1){
            active_search[i] = Library[4][i];
            i++;
        }
    }
    // bottom right corner
    else if(row == last_row && col == last_col){
        while(Library[5][i] != -1){
            active_search[i] = Library[5][i];
            i++;
        }
    }
    // non-edge columns of last row
    else if(row == last_row && (col != 0 || col != last_col)){
        while(Library[6][i] != -1){
            active_search[i] = Library[6][i];
            i++;
        }
    }
    // non-edge rows of first column
    else if((row != 0 || row != last_row) && col == 0){
        while(Library[7][i] != -1){
            active_search[i] = Library[7][i];
            i++;
        }
    }
    // non-edge rows of last column
    else if((row != 0 || row != last_row) && col == last_col){
        while(Library[8][i] != -1){
            active_search[i] = Library[8][i];
            i++;
        }
    }
    active_search[i] = -1;
}
4b9b3361

Ответ 1

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

Но да, проблема в том, что существует так много зависимых от платформы, а также в основном случайных факторов, влияющих на программу C. Виртуальная память означает, что иногда доступ к нераспределенной памяти будет работать, потому что вы нажмете неиспользуемую часть страницы, которая была выделена в какой-то более ранней точке. В других случаях это будет segfault, потому что вы нажмете на страницу, которая еще не была выделена для вашего процесса. И этого невозможно предсказать. Это зависит от того, где была выделена ваша память, была ли она на краю страницы или посередине? Это до ОС и диспетчера памяти, и какие страницы были выделены до сих пор, и...... Вы поняли эту идею. Различные компиляторы, разные версии одних и тех же компиляторов, разные ОС, различное программное обеспечение, драйверы или аппаратное обеспечение, установленные в системе, все может изменить, получаете ли вы segfault при доступе к нераспределенной памяти.

Что касается утверждения TA, что cygwin более "слабый", этот мусор по одной простой причине. Ни один компилятор не поймал ошибку! Если "родной" компилятор GCC был действительно менее слабым, это дало бы вам ошибку во время компиляции. Segfault не генерируются компилятором. Там не так много компилятора, чтобы убедиться, что вы получите segfault вместо программы, которая, по-видимому, работает.

Ответ 2

Я не хочу звучать грубо, но это, вероятно, ваш код, который плохой, а не компилятор.;) Проблемы, подобные этому, на самом деле более распространены, чем вы думаете, потому что разные ОС и компиляторы будут иметь разные способы организации ваших данных приложения в стеке и куче. Первый может быть особенно проблематичным, особенно если вы закончите перезаписывать память в стеке или ссылаетесь на свободную память, которую система решила использовать для чего-то еще. Таким образом, в основном, вы иногда можете уйти от него, но иногда ваше приложение задыхается и умирает. В любом случае, если это segfaults, это потому, что вы пытались ссылаться на память, которой вы не были допущены, так что это скорее "счастливое совпадение", которое оно не разбилось под другой системой/компилятором.

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

Изменить: Хорошо, теперь я понимаю, что вы имеете в виду... Я думал, что вы столкнулись с этой проблемой с помощью "X sucks, но Y отлично работает!" отношение, но, похоже, ваш ТП получил это.;)

В любом случае, здесь есть еще несколько советов для отладки таких проблем:

  • Посмотрите на арифметику указателя, ссылку/разыменование для возможного "doh!". ошибки. Любое место, где вы добавляете/вычитаете один (aka, fencepost errors), особенно подозрительны.
  • Прокомментировать вызовы malloc/free вокруг проблемной области и любые связанные области, в которых используются эти указатели. Если код перестает рушиться, вы направляетесь в правильном направлении.
  • Предполагая, что вы, по крайней мере, определили общую область, где ваш код разбивается, вставьте в него ранние заявления о возврате и найдите точку, где ваш код не сбой. Это может помочь найти область где-то между этой точкой и где ваш код действительно сбой. Помните, что подобный segfault может не обязательно произойти непосредственно в строке кода, где ваша ошибка.
  • Используйте инструменты отладки памяти, доступные в вашей системе.
    • В Unix проверьте это руководство для отладки памяти в unix и valgrind profiler (@Sol, спасибо, чтобы напомнить мне об этом)
    • В Visual Studio/Windows ваш good 'ol buddy CrtCheckMemory() подходит весьма удобно. Кроме того, прочитайте образцы отладки памяти CRT, поскольку они являются одной из лучших функций работы в VS. Часто, просто оставляя открытую вкладку памяти в VS, достаточно, чтобы диагностировать такие ошибки, как только вы запоминаете различные шаблоны.
    • В Mac OSX вы можете установить точку останова на malloc_error_break (либо из gdb, либо в Xcode), что приводит к ее отладчику, когда malloc обнаруживает повреждение памяти. Я не уверен, что это доступно в других вариантах Unix, но быстрый поиск в Google, похоже, указывает на то, что он только для Mac. Также для OSX существует довольно "экспериментальная" версия valgrind.

Ответ 3

Я ничего не слышал о специфике GCC в Cygwin, но в вашем случае было бы неплохо использовать параметр командной строки -Wall для gcc, чтобы показать все предупреждения, чтобы увидеть, найдет ли он что-нибудь, что может быть причиной segfault в вашем коде.

Ответ 4

У вас определенно есть ошибка где-то в вашем коде. Возможно, диспетчер памяти Windows является более слабым, чем менеджер памяти Linux. В Windows у вас могут быть плохие вещи с памятью (например, переписывание границ массива, утечки памяти, двойное освобождение и т.д.), Но это позволяет вам с этим справиться. Известную историю, связанную с этим, можно найти на http://www.joelonsoftware.com/articles/APIWar.html (найдите "SimCity" в этой (несколько длинной) статье).

Ответ 5

Cygwin-версия gcc может иметь другие флаги по умолчанию и измененные настройки (например, wchar_t составляет 2 байта), но я сомневаюсь, что она более "слабая" с кодом, и даже в этом случае ваш код не должен вылетать. Если это так, то, скорее всего, в вашем коде есть ошибка, которая должна быть исправлена. Например, ваш код может зависеть от определенного размера wchar_t или может выполнять код, который не гарантированно работает вообще, как запись в строковые литералы.

Если вы пишете чистый код, он запускается также в linux. В настоящее время я запускаю firefox и рабочий стол KDE, которые вместе состоят из миллионов строк на С++, и я не вижу, как эти приложения рушатся:)

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

Тем временем вы можете запустить свою программу в gdb, которая является отладчиком для Linux. Вы также можете скомпилировать все проверки mudflap и включить все предупреждения. mudflaps проверяет ваш код во время выполнения для различных нарушений:

[[email protected] cpp]$ cat mudf.cpp
int main(void)
{
  int a[10];
  a[10] = 3;  // oops, off by one.
  return 0;
}
[[email protected] cpp]$ g++ -fmudflap -fstack-protector-all -lmudflap -Wall mudf.cpp
[[email protected] cpp]$ MUDFLAP_OPTIONS=-help ./a.out
  ... showing many options ...
[[email protected] cpp]$ ./a.out 
*******                 
mudflap violation 1 (check/write): time=1234225118.232529 ptr=0xbf98af84 size=44
pc=0xb7f6026d location=`mudf.cpp:4:12 (main)'                                   
      /usr/lib/libmudflap.so.0(__mf_check+0x3d) [0xb7f6026d]                    
      ./a.out(main+0xb9) [0x804892d]                                            
      /usr/lib/libmudflap.so.0(__wrap_main+0x4f) [0xb7f5fa5f]                   
Nearby object 1: checked region begins 0B into and ends 4B after                
mudflap object 0x9731f20: name=`mudf.cpp:3:11 (main) int a [10]'                
bounds=[0xbf98af84,0xbf98afab] size=40 area=stack check=0r/3w liveness=3        
alloc time=1234225118.232519 pc=0xb7f5f9fd                                      
number of nearby objects: 1                                                     
*** stack smashing detected ***: ./a.out terminated                             
======= Backtrace: =========
....

Есть много проверок mudflap, которые вы можете сделать, и выше работает a.out, используя параметры по умолчанию. Другими инструментами, которые помогают для таких ошибок, являются valgrind, которые также могут помочь вам найти утечки или отключить одну ошибку, как описано выше. Установка переменной окружения "MALLOC_CHECK_" на 1 также приведет к печати сообщений о нарушениях. См. Man-страницу malloc для других возможных значений для этой переменной.

Чтобы проверить, где сбой вашей программы, вы можете использовать gdb:

[[email protected] cpp]$ cat test.cpp
int main() {
    int *p = 0;
    *p = 0;
}
[[email protected] cpp]$ g++ -g3 -Wall test.cpp
[[email protected] cpp]$ gdb ./a.out
...
(gdb) r
Starting program: /home/js/cpp/a.out

Program received signal SIGSEGV, Segmentation fault.
0x080483df in main () at test.cpp:3
3           *p = 0;
(gdb) bt
#0  0x080483df in main () at test.cpp:3
(gdb)

Скомпилируйте свой код с -g3, чтобы включить в него много отладочной информации, поэтому gdb может помочь вам найти точные строки, в которых сбой вашей программы. Все вышеупомянутые методы одинаково применимы для C и С++.

Ответ 6

Это почти наверняка ошибка указателя или переполнение буфера, возможно, неинициализированная переменная.

Неинициализированный указатель обычно ничего не указывает, но иногда он указывает на что-то; чтение из него или запись на него, как правило, приводят к сбою программы, но затем снова МОЖЕТ не быть.

Запись или чтение из свободной памяти - это одна и та же история; вы можете уйти с ним, а затем, возможно, нет.

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

Ответ 7

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

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

Ответ 9

Ошибка сегментации означает, что вы пытались получить доступ к памяти, которую вы не могли, что обычно означает, что вы пытались разыменовать нулевой указатель или вы дважды удалили память или получили дикий указатель. Есть две причины, по которым вы, возможно, оказались в курсе cygwin, а не Linux: либо это проблема с менеджерами памяти, либо вам повезло с одним из них, чем с другим. Это почти наверняка ошибка с вашим кодом.

Чтобы исправить это, посмотрите на использование указателя. Рассмотрим замену интеллектуальных указателей на raw указатели. Попробуйте выполнить поиск для удаления и обнулить указатель сразу после этого (безопасно пытаться удалить нулевой указатель). Если вы можете получить трещину в Linux, попробуйте получить трассировку стека через gdb и посмотреть, нет ли чего-то явно неправильного в строке. Изучите, как вы используете все указатели, которые не инициализируются. Если у вас есть доступ к инструменту отладки памяти, используйте его.

Ответ 10

Некоторые подсказки:

  • Отправьте свой код. Готов поспорить, вы получите хороший вклад, который сделает вас лучшим программистом.

  • Включите предупреждения с параметром -wall и исправьте все проблемы, о которых сообщается. Опять же, это может помочь сделать вас лучшим программистом.

  • Пройдите код с помощью отладчика. Помимо помощи вам понять, где проблема, это поможет сделать вас лучшим программистом.

  • Продолжайте использовать Subversion или другую систему управления исходным кодом.

  • Никогда не обвиняйте компилятор (или ОС или оборудование), пока не убедитесь, что задали проблему. Даже тогда будьте подозрительны к своему собственному коду.


GCC на Linux является исходным кодом, идентичным GCC на Cygwin. Различия между платформами существуют из-за уровня эмуляции Cygwin POSIX и базового API Windows. Возможно, дополнительные слои более прощающие, чем базовые аппаратные средства, но которые не учитываются.

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

Ответ 11

Ошибка сегментации является результатом доступа к памяти на несуществующем (или ранее освобожденном) адресе. Мне очень интересно то, что код не выполнялся под cygwin. Это может означать, что ваша программа использовала дикий указатель на адресное пространство некоторых других процессов и действительно могла его прочитать (вздох) или (скорее всего) код, который фактически вызвал segfault, не был достигнут, пока программа не была запущена под Linux.

Я рекомендую следующее:

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

Как только вы работаете под Linux (т.е. обстреляли ваш VPS), попробуйте работать со следующими программами:

  • GDB
  • Valgrind
  • Трассирование

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

Наконец, убедитесь, что -Wall передано в gcc, вы хотите, чтобы предупреждения передавались.