如果可以进行以下假设,是否可以恢复链表的头节点。
为了更好地说明,请使用以下程序,该程序可以在默认配置下至少在Ubuntu / WSL下使用gcc成功编译。
程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
typedef struct node
{
int val;
struct node *next;
} node_t;
node_t *head = NULL;
unsigned long start_address = 0;
unsigned long end_address = 0;
node_t *getLastNode()
{
node_t *iter = head;
for (; iter->next != NULL; iter = iter->next)
;
return iter;
}
void addToLinkedList(int value)
{
node_t *data = malloc(sizeof(node_t));
data->val = value;
data->next = NULL;
if (head == NULL)
head = data;
else
getLastNode()->next = data;
}
void createLinkedList()
{
// Add 10 nodes to the linked list
int start_val = 0x10101010;
for (int i = 1; i <= 10; i++)
addToLinkedList(start_val * i);
}
void printLinkedList()
{
printf("Head pointer of Linked List : %p\n", head);
for (node_t *iter = head; iter != NULL; iter = iter->next)
printf("%p -> value = %X, next = %p \n", iter, iter->val, iter->next);
printf("\n");
}
void resetHeadPtr()
{
// Lets make head point to the last node
head = getLastNode();
}
void findHeapBoundary()
{
// Code inspired from https://unix.stackexchange.com/a/251769/152334
char mapsFilename[1024];
char line[256];
char area[1024];
sprintf(mapsFilename, "/proc/%d/maps", getpid());
FILE *pMapsFile = fopen(mapsFilename, "r");
while (fgets(line, 256, pMapsFile) != NULL)
{
// Dirty hack to get the heap start and end address
sscanf(line, "%08lx-%08lx%*[^[]%s\n", &start_address, &end_address, area);
if (strcmp(area, "[heap]") == 0)
break;
}
fclose(pMapsFile);
printf("Heap memory start address : %p\n", (int *)start_address);
printf("Heap memory end address : %p\n", (int *)end_address);
}
node_t *findPointerInMemory()
{
for (int *ptr = (int *)start_address; ptr < (int *)(end_address - sizeof(node_t)); ptr++)
{
if (((node_t *)ptr)->next == head)
return (node_t *)ptr;
}
return NULL;
}
void recoverHeadPtr()
{
node_t *ptr = findPointerInMemory();
if (ptr == NULL)
{
printf("Cannot find %p in heap memory\nStopping Search\n\n", head);
return;
}
printf("Found %p at %p\n", head, ptr);
head = ptr;
recoverHeadPtr();
}
int main(int argc, char const *argv[])
{
createLinkedList();
printf("Original Linked List Contents\n*****************************\n");
printLinkedList();
resetHeadPtr();
printf("Linked List Contents after reset\n********************************\n");
printLinkedList();
findHeapBoundary();
recoverHeadPtr();
printf("Recovered Linked List Contents\n******************************\n");
printLinkedList();
return 0;
}
输出
Original Linked List Contents
*****************************
Head pointer of Linked List : 0x1db6010
0x1db6010 -> value = 10101010, next = 0x1db6030
0x1db6030 -> value = 20202020, next = 0x1db6050
0x1db6050 -> value = 30303030, next = 0x1db6070
0x1db6070 -> value = 40404040, next = 0x1db6090
0x1db6090 -> value = 50505050, next = 0x1db60b0
0x1db60b0 -> value = 60606060, next = 0x1db60d0
0x1db60d0 -> value = 70707070, next = 0x1db60f0
0x1db60f0 -> value = 80808080, next = 0x1db6110
0x1db6110 -> value = 90909090, next = 0x1db6130
0x1db6130 -> value = A0A0A0A0, next = (nil)
Linked List Contents after reset
********************************
Head pointer of Linked List : 0x1db6130
0x1db6130 -> value = A0A0A0A0, next = (nil)
Heap memory start address : 0x1db6000
Heap memory end address : 0x1dd7000
Found 0x1db6130 at 0x1db6110
Found 0x1db6110 at 0x1db60f0
Found 0x1db60f0 at 0x1db60d0
Found 0x1db60d0 at 0x1db60b0
Found 0x1db60b0 at 0x1db6090
Found 0x1db6090 at 0x1db6070
Found 0x1db6070 at 0x1db6050
Found 0x1db6050 at 0x1db6030
Found 0x1db6030 at 0x1db6010
Cannot find 0x1db6010 in heap memory
Stopping Search
Recovered Linked List Contents
******************************
Head pointer of Linked List : 0x1db6010
0x1db6010 -> value = 10101010, next = 0x1db6030
0x1db6030 -> value = 20202020, next = 0x1db6050
0x1db6050 -> value = 30303030, next = 0x1db6070
0x1db6070 -> value = 40404040, next = 0x1db6090
0x1db6090 -> value = 50505050, next = 0x1db60b0
0x1db60b0 -> value = 60606060, next = 0x1db60d0
0x1db60d0 -> value = 70707070, next = 0x1db60f0
0x1db60f0 -> value = 80808080, next = 0x1db6110
0x1db6110 -> value = 90909090, next = 0x1db6130
0x1db6130 -> value = A0A0A0A0, next = (nil)
背景
我的一位朋友在接受采访时被问到以下问题。 “如果给出最后一个节点,你能找到单个链表的头指针吗?”。他回答“不”,即使面试官对答案并不完全满意,他也得到了这份工作。这让我开始思考,是否有可能这样做。
所以,真正的问题是。
答案 0 :(得分:5)
正确答案是:
不干净并且尝试这样做对于糟糕的设计来说将是一个冒险的解决方法 如果你确实试图在内存中搜索任何内容的地址(如果它是有问题的指针,那么它不会保证只包含地址),那么你可能会发现任何一块似乎意外包含的内存一个看起来像有问题的地址的数字 如果你继续使用它,假设它是指针,你就会引发各种各样的问题 如果你反复这样做以倒退单链表,那么你几乎可以保证找到至少一个废话。
简而言之:否。
一个简单的“号”太短了,可能因此而让审查员皱眉头。
答案 1 :(得分:0)
在标准C中,它是不可能的。
访问堆而不是通过malloc给出的指针(或那些指针的副本等)将获得未定义的行为。
但是,如果您了解分配器并且可以遍历已分配的内存块的结构,则可以找到候选列表节点。其中一人将成为首脑。