Как переменные складываются в стек в C?

#c

#c

Вопрос:

Для частей a) и b) обратитесь к следующему коду:

 void foo(int* p, int * q) { 
    p = q; 
    char* c = (char *) (p   2); 
    *c = 'x'; 
} 

int main() { 
    char c[] = {'h', 'e', 'l', 'l', 'o', ''};
    int i = 0; 
    int j = 0; 
    foo(amp;i, amp;j); 
    printf("%s", c);
} 
  

а) Что печатает основная функция? ____________________________

б) Если мы изменим объявление и инициализацию c во второй строке функции foo на:

 int* c = p   2;
  

и остальная часть кода остается прежней, что
теперь печатает основная функция?

Код написан на C, и ответ на a — xello, а ответ на b — x. Я хотел бы знать, как получился такой вывод и что произошло в стековой памяти

Комментарии:

1. Добро пожаловать в SO. Использование стека не определено стандартом. Компилятор может делать все, что ему нравится. Посмотрите на выходные данные ассемблера, чтобы увидеть, что было создано этим конкретным компилятором на этом конкретном оборудовании.

2. Арифметика указателей за пределами объекта памяти адресов недопустима и вызывает неопределенное поведение.

3. С изменениями для b) результат также зависит от последовательности.

4. Я не понимаю, как foo делает что-то полезное. Вы отправляете адреса i и j и что делаете?

5. Откуда вы взяли этот пример? Сожгите эти материалы!

Ответ №1:

Этот код просто глючит — он не имеет детерминированного вывода. Вы пытались запустить его самостоятельно? Если вы получили это задание в школе, то это ужасное задание, и тот, кто его дал, не должен преподавать C.

  • p = q; здесь p указывает на ячейку памяти int j .
  • p 2 здесь он выполняет арифметику указателей по int типу, 2*sizeof(int) удаляя байты, в конечном итоге выходя за пределы. Это неопределенное поведение (см. Это), и здесь может произойти что угодно, включая сбой программы или ловушку команд.
  • Программа уже потенциально готова, но для полноты картины давайте посмотрим на остальное. (char *) (p 2); означает, что этот неизвестный адрес преобразуется в символьный указатель.
  • *c = 'x'; Это отменяет ссылку на байт в недопустимом предполагаемом int местоположении. Какой байт он получает, возможно, будет зависеть от последовательности процессора, особенно в случае b).

Игнорируя ошибки неопределенного поведения, вам нужно знать конкретный ABI, компилятор и систему, чтобы даже попытаться предположить результат. На трех основных компиляторах x86 gcc, clang и icc, работающих в Linux, результат оказался hello следующим .