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
1
Более конкретно ..
 – 
user3805652
16 Июл 2014 в 11:30
3
Кстати: это также зависит от того, работаете ли вы с маленьким или большим порядком байтов.
 – 
Jarod42
16 Июл 2014 в 11:35
1
Является ли основной вопрос вопросом, почему кажется, что целое целое пишется, когда вы пишете в char с * p = 45?
 – 
Martin G
16 Июл 2014 в 11:44
string("foo") наверняка не C.
 – 
diapir
16 Июл 2014 в 11:46
Это работает только с прямым порядком байтов, с прямым порядком байтов вывод будет 754 974 725
 – 
mch
16 Июл 2014 в 11:51

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 в 12:12
Как узнать, это Big или Little-Endian?
 – 
user3805652
16 Июл 2014 в 13:11
Вы находитесь на прямом порядке байтов, если результат равен 45
 – 
mch
16 Июл 2014 в 13:13
Да, но есть ли способ проверить это?
 – 
user3805652
16 Июл 2014 в 13:18
Я отредактировал тестовую программу на свой ответ. Но вы могли бы прочитать документацию вашего процессора
 – 
mch
16 Июл 2014 в 13:26
1
unsigned char имеет особые привилегии. Вы можете сохранить небольшое число, например 1, в int и проверить (через unsigned char *), является ли первый байт 0 или 1 не вызывая неопределенного поведения. Если это 0, это BE.
 – 
Nisse Engström
16 Июл 2014 в 13:27

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

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 в 13:23

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

int* p =(int*)&b;

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

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

1
Bathsheba 16 Июл 2014 в 12:00
Стандартный способ, вы правы, но практически на машине x86 указатели используются для доступа к началу пространства памяти структуры b.
 – 
NirMH
16 Июл 2014 в 11:56
Если я работаю над марсианским посадочным модулем (1/2 миллиона строк C), мне плевать на «практически», только на стандарт.
 – 
Bathsheba
16 Июл 2014 в 11:58

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

*p = 45;

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

0
Elliott Frisch 16 Июл 2014 в 11: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 в 18: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 в 18: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 в 11:46
p указывает на первый байт b, но b.a - это первые 4 байта b
 – 
mch
16 Июл 2014 в 11:44
- поскольку 45 занимает только 1 байт, то печать первых 4 байтов b дает тот же результат
 – 
NirMH
16 Июл 2014 в 11:46
Я не слежу за вашим обновленным вопросом - это слишком запутанно
 – 
NirMH
16 Июл 2014 в 11:55