Почему при смене подписи main возникает ошибка segfault?

Я пытаюсь разобраться в C и написал эту программу, которая отображает килобайт моей оперативной памяти в случайном месте. Вот код, и он отлично работает:

#include <stdio.h>

int main(){
    char *mem;
    for(int i =0; i < 1024; i++){
        mem++;
        printf("%c", *mem);
    }
    return 0;
}

После этого я внес следующие изменения в свой код и получаю ошибки segfaults при каждом запуске своей программы:

#include <stdio.h>


// Just added this signature
int main(int argc, char *argv[]){
    char *mem;
    for(int i =0; i < 1024; i++){
        mem++;
        printf("%c", *mem);
    }
    return 0;
}

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

$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

4 ответа

Решение

Оба ваших фрагмента вызывают неопределенное поведение при попытке

  1. Выйти за пределы (mem++;без распределения)
  2. использовать неинициализированные значения (доступ к *mem)

с текущей версией.

Помните, что указатели волшебным образом не наследуют (или не приобретают) память, вам нужно вообще указать указатель на что-то действительное.

Значение mem не определено (не инициализировано), но не случайно. Если перед вызовом main вызываются другие функции времени выполнения C, то слот стека используется mem может иметь действительный указатель внутри него. Добавление параметров к основным изменениям, какой слот используется, и изменение поведения. Это может означать, что код не вылетает, хотя он не является правильным.

Вам нужно инициализировать mem, Я думаю, вы пытаетесь просто прочитать случайную память, но это не разрешено. Например, вы можете пытаться прочитать память, которая используется другим процессом, или вы можете пытаться прочитать какой-то адрес, который даже не существует на вашем компьютере.

Изменяя подпись для main, вы изменили значение случайного мусора в mem начать с. То, как это работает, вероятно, заключается в том, что mem берет случайное значение из некоторого регистра. Когда вы изменили подпись функции, argc а также argv вместо этого используют эти регистры. Therefor mem получает другое значение регистра мусора из значения стека мусора. В любом случае, вы не должны пытаться следовать указателю мусора.

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

Вы никогда не инициализируете memпоэтому его содержимое не определено. Когда вы пытаетесь либо увеличить его с ++ или разыменовав указатель, вы получите неопределенное поведение.

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

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