@Isalamon По сути, все, что хранится в сыром виде в хранилище (первичном или вторичном) компьютера, называется «данными». Адрес других данных также является данными, поэтому указатель хранит данные.
аписании проекта я столкнулся со странной проблемой.
Это минимальный код, который мне удалось написать, чтобы воссоздать проблему. Я намеренно храню фактическую строку вместо чего-то другого, с достаточным количеством выделенного пространства.
// #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h> // For offsetof()
typedef struct _pack{
// The type of `c` doesn't matter as long as it's inside of a struct.
int64_t c;
} pack;
int main(){
pack *p;
char str[9] = "aaaaaaaa"; // Input
size_t len = offsetof(pack, c) + (strlen(str) + 1);
p = malloc(len);
// Version 1: crash
strcpy((char*)&(p->c), str);
// Version 2: crash
strncpy((char*)&(p->c), str, strlen(str)+1);
// Version 3: works!
memcpy((char*)&(p->c), str, strlen(str)+1);
// puts((char*)&(p->c));
free(p);
return 0;
}
Приведенный выше код сбивает меня с толку:
С участиемgcc/clang -O0
, обеstrcpy()
а такжеmemcpy()
работает на Linux / WSL, аputs()
ниже дает все, что я вошел.С участиемclang -O0
на OSXкод вылетает сstrcpy()
.С участиемgcc/clang -O2
или же-O3
на Ubuntu / Fedora / WSL, кодсбой (!!) вstrcpy()
, покаmemcpy()
работает хорошо.С участиемgcc.exe
в Windows код работает хорошо независимо от уровня оптимизации.Также я нашел некоторые другие черты кода:
(Это выглядит как)минимальный вход для воспроизведения аварии составляет 9 байтов (включая нулевой терминатор), или1+sizeof(p->c)
, При такой длине (или дольше) сбой гарантирован (Дорогой я ...).Даже если я выделю дополнительное пространство (до 1 МБ) вmalloc()
это не помогает Вышеуказанное поведение не меняется вообще.strncpy()
ведет себя точно так же, даже с правильной длиной, указанной в третьем аргументе.Указатель, похоже, не имеет значения. Если член структурыchar *c
изменяется вlong long c
(или жеint64_t
), поведение остается прежним. (Обновление: уже изменилось).Сообщение о сбое не выглядит обычным. Много дополнительной информации дается вместе.
Я перепробовал все эти компиляторы, и они не имели никакого значения:
GCC 5.4.0 (Ubuntu / Fedora / OS X / WSL, все 64-битные)GCC 6.3.0 (только Ubuntu)GCC 7.2.0 (Android, norepro ???) (это GCC изC4droid)Clang 5.0.0 (Ubuntu / OS X)MinGW GCC 6.3.0 (Windows 7/10, оба x64)Кроме того, эта пользовательская функция копирования строк, которая выглядит в точности как стандартная, хорошо работает с любой конфигурацией компилятора, упомянутой выше:
char* my_strcpy(char *d, const char* s){
char *r = d;
while (*s){
*(d++) = *(s++);
}
*d = '\0';
return r;
}
Вопросов:Почемуstrcpy()
провал? Как это может?Почему происходит сбой только при включенной оптимизации?Почему неmemcpy()
потерпеть неудачу независимо от-O
Уровень ??* Если вы хотите обсудить нарушение доступа к элементам структуры, пожалуйста, не стесняйтесьВот.
Частьobjdump -d
Вывод аварийного исполняемого файла (на WSL):
Постскриптум Сначала я хочу написать структуру, последний элемент которой является указателем на динамически выделенное пространство (для строки). Когда я пишу структуру в файл, я не могу написать указатель. Я должен написать фактическую строку. Поэтому я пришел к такому решению: принудительно хранить строку вместо указателя.
Также, пожалуйста, не жалуйтесь наgets()
, Я не использую его в своем проекте, но приведенный выше пример кода.