Указатели адреса расположения - PullRequest
0 голосов
/ 21 февраля 2019

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

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

int main(){
    char str[] = "hmmmm..";
    const char * const ptr1[] = {"to be","or not to be","that is the question"};
    char *ptr2 = "that is the qusetion";

    (&ptr2)[3] = str;

    strcpy(str,"(Hamlet)");
    for (int i = 0; i < sizeof(ptr1)/sizeof(*ptr1); ++i){
        printf("%s ", ptr1[i]);
    }
    printf("\n");
    return 0;
}

Позже, после изученияОтветы, стало ясно, что ячейка (& ptr2) [3] была идентична ячейке памяти в & ptr1 [2], поэтому вывод программы: to be or not to be (Hamlet)

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

Обратите внимание, я не имею в виду переменные массива, поэтому все элементы в массиве должны быть в последовательности.

Ответы [ 4 ]

0 голосов
/ 21 февраля 2019
char *ptr2 = some initializer;

(&ptr2)[3] = str;

Когда вы оцениваете & ptr2, вы получаете адрес памяти, где хранится указатель, который указывает на этот инициализатор.

Когда вы делаете (&ptr2)[3]=something, вы пытаетесь написать 3*sizeof(void*) местоположений дальше отрасположение ptr2, адрес строки.Это неверно и почти наверняка завершится с ошибкой сегментации.

0 голосов
/ 21 февраля 2019

В этом выражении:

(&ptr2)[3] = str;

ptr2 было определено с char *ptr2 внутри main.При таком определении компилятор отвечает за предоставление хранилища для ptr2.Компилятору разрешается использовать для этого любое хранилище, которое он хочет - это может быть до ptr1, это может быть после ptr1, это может быть близко, это может быть далеко.

Тогда &ptr2принимает адрес ptr2.Это разрешено, но мы не знаем, где этот адрес будет по отношению к ptr1 или чему-либо еще, потому что компилятору разрешено использовать любое хранилище, которое он хочет.

, поскольку ptr2 является char *, &ptr2 - указатель на char *, также известный как char **.

Затем (&ptr2)[3] пытается сослаться на элемент 3 массива char *, который находится в &ptr2.Но там нет массива в модели вычислений Си.Там есть только один char *.Когда вы пытаетесь сослаться на элемент 3 массива, когда нет элемента 3 массива, поведение не определяется стандартом C.

Таким образом, этот код является плохим примером.Похоже, автор теста неправильно понял C, и этот код не иллюстрирует то, что предполагалось.

0 голосов
/ 21 февраля 2019

Программа обычно загружается в память с такой структурой: стек, файлы Mmap, куча, BSS (неинициализированные статические переменные), сегмент данных (инициализированные статические переменные) и текст (скомпилированный код)

YouВы можете узнать больше здесь: https://manybutfinite.com/post/anatomy-of-a-program-in-memory/

В зависимости от того, как вы объявите переменную, она перейдет в одно из мест, упомянутых ранее.

Компилятор упорядочит переменные сегмента BSS и Data, как онпожелания по компиляции, поэтому обычно нет шансов.Ни одна из кучных переменных (ОС получит блок памяти, который лучше соответствует выделенному пространству)

В стеке (который является структурой LIFO) переменные помещаются одна над другой, так что если у вас есть:

int a = 5;
int b = 10;

Можно сказать, что a и b будут располагаться один за другим.Таким образом, в этом случае вы можете сказать.

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

В вашем коде ptr1 - это массив массивов символов, поэтому он будет следовать исключению, которое я сказал.

Фактически, выполните следующее упражнение:

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

int main(){
    const char * const ptr1[] = {"to be","or not to be","that is the question"};
    for (int i = 0; i < 3; i++) {
            for (int j = 0; j < strlen(ptr1[i]); j++) 
            printf("%p -> %c\n", &ptr1[i][j], ptr1[i][j]);
        printf("\n");
    }

}

, и вы увидитеадрес памяти и его содержимое!

Хорошего дня.

0 голосов
/ 21 февраля 2019

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

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

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

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