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)
. Но это неправда, почему?
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);
- Вы изменяете часть
b.a
с помощью указателя другого типа. Это может датьb.a
"представление прерывания", а чтение значения, содержащего представление прерывания, вызывает неопределенное поведение. (И нет, вы вряд ли когда-нибудь встретите представление ловушки (в целочисленном виде) в реальной жизни.) - Вы вызываете вариативную функцию без объявления функции. Функции с переменным числом аргументов обычно имеют необычные способы передачи аргументов, поэтому компилятор должен знать об этом. Обычное исправление -
#include <stdio.h>
.
Неопределенное поведение означает, что может произойти что угодно, например, печать неправильного значения, сбой вашей программы или (что самое худшее) выполнение именно того, что вы ожидаете.
unsigned char
имеет особые привилегии. Вы можете сохранить небольшое число, например 1
, в int
и проверить (через unsigned char *
), является ли первый байт 0
или 1
не вызывая неопределенного поведения. Если это 0
, это BE.
Ваша структура выглядит с прямым порядком байтов, как:
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;
}
Стандарт C гарантирует, что адрес первого члена структуры является адресом структуры. То есть в вашем случае
int* p =(int*)&b;
безопасное приведение. Но стандартного способа доступа к члену char
по адресу структуры не существует. Это связано с тем, что в стандарте ничего не говорится о смежности последовательных элементов в памяти: на самом деле компилятор может или не может вставлять промежутки (так называемая упаковка структуры) между элементами в соответствии с набором микросхем.
Так что то, что вы делаете, по сути, не определено.
b
.
Потому что это
*p = 45;
Изменяет значение того, на что указывает p, на 45. И вы сделали p, указывающим на b.
Если вы хотите, чтобы указатели были членами структуры, а не массивом символов. попробуй это..
#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;
}
Попробуйте этот указатель на структуру.
#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;
}
char *p = (char*) &b;
- в этой строке p
указывает на начало структуры b
как указатель на символ.
*p = 45;
записывает 45 в область памяти b
, к которой также может получить доступ b.a
.
Когда вы печатаете printf("%d ", b.a);
, вы распечатываете 45, хранящиеся в памяти стека, назначенной как a
член struct b
, вы получите 45.
Попробуйте отладить его самостоятельно, и вы увидите это в окне просмотра
p
указывает на первый байт b
, но b.a
- это первые 4 байта b
b
дает тот же результат
Похожие вопросы
Новые вопросы
c
C - это язык программирования общего назначения, используемый для системного программирования (ОС и встраиваемых), библиотек, игр и кроссплатформенности. Этот тег следует использовать с общими вопросами, касающимися языка C, как это определено в стандарте ISO 9899 (последняя версия 9899: 2018, если не указано иное, а также для запросов, специфичных для версии, с c89, c99, c11 и т. Д.). C отличается от C ++ и не должен сочетаться с тэгом C ++ без разумной причины.
string("foo")
наверняка не C.