struct s{
int a;
char c;
};
int main()
{
    struct s b = {5,'a'};
    char *p =(char *)&b;
    *p = 45;
    printf("%d " , b.a);
    return 0;
}

Если *p является изменением любого character, чем он печатает ASCII значение символа, если *p изменился на любое string ("xyz"), чем он печатает 36. Почему это происходит?

Можете дать карту памяти структуры s и * p?

По моему мнению, структура структуры

s as z-> (****)(*) assuming 4 byte for int ., а при инициализации s он стал бы (0000000 00000000 00000000 00000101)(ASCII OF char a), а * p указывает на начальный адрес z. и * p - это указатель на символ, поэтому он будет хранить значение ASCII в каждом месте BYTE, т.е. в каждом * будет занято ASCII of char. Но теперь мы добрались до 45, поэтому z превратился бы в (45 0 0 5 )(ASCII of char a). Но это неправда, почему?

-1
user3805652 16 Июл 2014 в 11:27

7 ответов

Лучший ответ

Когда вы пишете в структуру через указатель char *, вы сохраняете 45 в первом байте структуры. Если вы используете реализацию Little-Endian, вы будете писать в младшую часть b.a. Если вы используете реализацию с прямым порядком байтов, вы будете писать на верхнем уровне b.a.

Вот визуализация того, что обычно происходит со структурой в реализации с 16-битными целыми числами до и после присваивания *p=45. Обратите внимание, что структура дополняется до кратного sizeof(int).

Little-Endian

Before:  a [05][00]  (int)5
         c [61]
           [  ]

After:   a [2d][00]  (int)45
         c [61]
           [  ]

Big-Endian

Before:  a [00][05]  (int)5
         c [61]
           [  ]

After:   a [2d][05]  (int)11525
         c [61]
           [  ]

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

Однако следующая строка вызывает неопределенное поведение по двум причинам:

printf("%d " , b.a);
  1. Вы изменяете часть b.a с помощью указателя другого типа. Это может дать b.a "представление прерывания", а чтение значения, содержащего представление прерывания, вызывает неопределенное поведение. (И нет, вы вряд ли когда-нибудь встретите представление ловушки (в целочисленном виде) в реальной жизни.)
  2. Вы вызываете вариативную функцию без объявления функции. Функции с переменным числом аргументов обычно имеют необычные способы передачи аргументов, поэтому компилятор должен знать об этом. Обычное исправление - #include <stdio.h>.

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

2
Community 20 Июн 2020 в 09:12

Ваша структура выглядит с прямым порядком байтов, как:

00000101 00000000 00000000 00000000 01100001

Поэтому p указывает на 5 и перезаписывает его. в printf 4 байта с прямым порядком байтов печатают 45.

Если бы вы попробовали это с прямым порядком байтов, результатом будет 754974725, потому что p указывает на сторону MSB int.

Простая тестовая программа, чтобы узнать, используете ли вы прямой или большой порядок байтов:

int main()
{
  int a = 0x12345678;
  unsigned char *c = (unsigned char*)(&a);
  if (*c == 0x78)
    printf("little-endian\n");
  else
    printf("big-endian\n");
  return 0;
}
2
mch 16 Июл 2014 в 09:23

Стандарт C гарантирует, что адрес первого члена структуры является адресом структуры. То есть в вашем случае

int* p =(int*)&b;

безопасное приведение. Но стандартного способа доступа к члену char по адресу структуры не существует. Это связано с тем, что в стандарте ничего не говорится о смежности последовательных элементов в памяти: на самом деле компилятор может или не может вставлять промежутки (так называемая упаковка структуры) между элементами в соответствии с набором микросхем.

Так что то, что вы делаете, по сути, не определено.

1
Bathsheba 16 Июл 2014 в 08:00

Потому что это

*p = 45;

Изменяет значение того, на что указывает p, на 45. И вы сделали p, указывающим на b.

0
Elliott Frisch 16 Июл 2014 в 07:38

Если вы хотите, чтобы указатели были членами структуры, а не массивом символов. попробуй это..

#include<stdio.h>

struct s{
int a;
char *c;
};

int main()
{
    struct s b = {5, "a"};

    printf("%d %s", b.a, b.c);
    return 0;
}
0
rm_beginners 16 Июл 2014 в 14:42

Попробуйте этот указатель на структуру.

#include<stdio.h>

    struct s{
    int a;
    char c[1];
    };

    int main()
    {
        struct s *p;
        struct s b = {5, 'a'};
        p = &b;

        printf("%d %s", p->a, p->c);
        return 0;
    }
0
rm_beginners 16 Июл 2014 в 14:56

char *p = (char*) &b; - в этой строке p указывает на начало структуры b как указатель на символ.

*p = 45; записывает 45 в область памяти b, к которой также может получить доступ b.a.

Когда вы печатаете printf("%d ", b.a);, вы распечатываете 45, хранящиеся в памяти стека, назначенной как a член struct b, вы получите 45.

Попробуйте отладить его самостоятельно, и вы увидите это в окне просмотра

-1
NirMH 16 Июл 2014 в 07:46