c语言关于链表和“->”符号

常规单链表的实现方式是定义一个结构体,结构体中定义一个存放数据的变量和一个指向下一个节点的指针。

在初次接触链表时,接触到了一个符号“->”,笔者在学习时,最开始常常把它和“.”傻傻分不清楚,对于这种状况,首先要搞清楚一点,结构体也是数据类型的一种,跟“int”"char"一样,定义一个结构体后结构体中的变量也就可以被这个结构体定义的变量访问。

结构体的作用即对一个东西进行封装,封装之后方便以后使用,使用了这个类型的变量都会具有这个结构的属性。

结构体变量访问结构体元素时:
例如a和b:
那么当a是指针变量时,a->b等价为(&a).b。
如果用“.”,只需要声明一个结构体,然后用结构体名加“.”加结构体元素名称就可以引用结构体中的元素了,因为计算机自动分配了结构体的内存,就像int a一样,(操作栈内存)。

用->的话必须声明一个结构体的指针,还要手动开启一个该结构体的内存,将结构体的返回的指针赋值给声明的结构体指针才能用->正确引用,否则内存中只分配了指针的内存,没有分配结构体的内存,想要的结构体实际上不存在,这时候用->引用自然会出错,因为没有结构体,所以也不存在结构体域。(操作堆内存)。

如下:

struct Data{
	int a,b,c;
};

struct Data *p;
struct Data A = {1,2,3};
p = &A;

上面中 A.a 和p->a 表示的是同一个意思。

“.”可以读作“的”“->”可以读作“指向结构体的”

补充:“->”与“.”功能一样,都是访问结构体变量,但箭头绝对没有现实生活中箭头的指向作用,它只表示用一个指针访问结构体变量,链表中节点的链接过程跟“->”没有关系,链表节点的链接是通过指针指向链接的,编程中表现为给指针变量赋值,实质是把后一个节点的首地址赋给前一个节点的pNext,箭头非指向,不注意这点会对理解链表产生困扰。

#include <stdio.h> #include <stdlib.h> typedef struct node { int val; struct node *prev, *next; } Node, List; List* init(void) { Node *s = (Node*) malloc(sizeof(Node)); s->val = -1, s->prev = s->next = s; return s; } void pushBack(List *head, int val) { Node *curr = (Node*) malloc(sizeof(Node)); curr->val = val, curr->prev = head->prev, curr->next = head; head->prev = head->prev->next = curr; } List* createList(size_t size, int val) { List *s = init(); for (int i = 0; i < size; ++i) pushBack(s, val); return s; } void traverse_n(List *head, size_t cnt) { printf("%d.", head->next->val); for (Node* curr = head->next->next; curr != head && cnt > 0; curr = curr->next, --cnt) printf("%d", curr->val); printf("\n"); } int main () { List *Rn = createList(550, 0); List *Sn = createList(550, 0); Rn->next->val = 2, Sn->next->val = 2; for (int n = 1; n <= 2000; ++n) { // R(n) = R(n) * n int carry = 0; for (Node *curr = Rn->prev; curr != Rn; curr = curr->prev) { int res = curr->val * n + carry; carry = res / 10; curr->val = res % 10; } // R(n) = R(n) / (2n + 1) carry = 0; for (Node *curr = Rn->next; curr != Rn; curr = curr->next) { int div = (n << 1) + 1; int res = curr->val + carry * 10; curr->val = res / div; carry = res - curr->val * div; } // S(n) += R(n) carry = 0; for (Node *curr1 = Sn->prev, *curr2 = Rn->prev; curr1 != Sn && curr2 != Rn; curr1 = curr1->prev, curr2 = curr2->prev) { int res = curr1->val + curr2->val + carry; curr1->val = res % 10; carry = res / 10; } } int cnt; scanf("%d", &cnt); traverse_n(Sn, cnt); return 0; } 请帮我详细讲解一下这个代码
最新发布
03-16
在 C 语言中,由于语法限制,我们通常不会直接编写这样的文法解析器。不过,我们可以用递归下降解析(Recursive Descent Parsing)的概念来设计一个函数式解析算法。这里我将给出一个基础的非终结符到 C 函数映射的示例,你可以通过链表或其他数据结构组合它们来模拟上下文无关文法的解析过程。 首先,为了简洁起见,我们将只列出关键的解析规则,并假设有一些辅助函数用于处理空格、结束标志等。实际实现时,你需要完整的词法分析错误处理。 ```c typedef struct { char symbol; // 非终结符 void (*parse)(void); // 解析函数 } GrammarRule; GrammarRule grammar[] = { {'E', parse_TE'}, // E -> TE' {'TE', parse_TE_prime}, // TE' -> +E | ~ {'T', parse_FT'}, // T -> FT' {'T_prime', parse_T_prime}, // T' -> T | ~ {'F', parse_PF'}, // F -> PF' {'F_prime', parse_F_prime}, // F' -> *F' | ~ {'P', parse_P}, // P -> (E) | a | b | ^ {'~', NULL} // 空符号,结束当前分支 }; void parse_start() { if (grammar[0].parse != NULL) grammar[0].parse(); } // 示例解析函数(实际需要根据文法规则扩展) void parse_TE() { // 实现 +E 的解析 } void parse_TE_prime() { // 实现 TE' 的选择逻辑 } // ... 依此类推,为每个非终结符定义相应的解析函数 int main() { parse_start(); // 开始解析 return 0; } ``` 请注意,这个例子非常简略,实际实现会涉及到状态机管理、错误检查栈操作。如果你想创建一个完整的 LR 或 LL(1) 解析器,可能需要使用更复杂的库,如 Flex Bison(也称为 Lex Yacc)。完成这样的项目是一个较大的工程,超出了这个问答的范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值