一、变量声明、定义与调用
extern int a, b, c; //声明
int a, b, c; //定义
a = 0; b = 1; c = 1; //赋值
a = b + c; //调用
二、&和*、地址和指针
1、存储空间都有相对应的地址,固定的地址对应着固定的存储空间。地址是一串数字,如0xc0200142(其字节长度与环境有关),访问该地址对应的存储空间里存的内容: *0xc0200142
2、&是 取 某内容所在存储空间的 地址
3、*是 指向 某地址对应的存储空间 里的内容
4、指针的类型 表示该指针对应的存储空间里存的内容 的类型
extern int *p; //声明指针 p
int *p = NULL; //定义指针p 指向空
p = &a; //赋值指针 取a所在存储空间的地址 赋值给p
if (p != NULL) //保险起见,是否成功
{
b = *p; //调用(使用)指针:取 地址p对应的存储空间里的内容 赋值给b, 此操作与b=a结果相同
//使用指针的作用体现在,当某内容占存储空间比较大时,调用其指针占用空间比较小
//比如某结构体占用空间50个字节,调用其指针占用4个字节
}
三、结构体和结构体指针
例一
1、声明一个结构体类型student
2、定义一个student类型的结构体stu_1
3、定义一个student类型的结构体指针p
4、取stu_1的地址赋值给p
5、给结构体stu_1的元素赋值
6、调用结构体本身 访问结构体的成员
7、通过结构体地址 访问结构体的成员
8、通过结构体指针 访问结构体的成员
#include <string.h>
#include <stdio.h>
#include <stdlib.b>
struct student //1、声明一个 结构体类型 student
{
long num;
char name[20];
char sex;
float score;
};
void main()
{
struct student stu_1; //2、定义一个 student结构类型的 结构体stu_1
struct student *p; //3、定义一个 student结构类型的 指针p
p = &stu_1; //4、取 stu_1的地址 赋值给p ,这时*p就指向stu_1了
//5、给 结构体stu_1的成员赋值
stu_1.num = 89101;
strcpy(stu_1.name, "Li Lin");
stu_1.sex = 'M';
stu_1.score = 89.5;
//6、调用结构体本身 访问结构体的成员
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n", stu_1.num, stu_1.name, stu_1.sex, stu_1.score);
//7、通过结构体地址 访问结构体的成员
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n", p->num, p->.name, p->.sex, p->.score);
//8、通过结构体指针 访问结构体的成员
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n", (*p).num, (*p).name, (*p).sex, (*p).score);
system("pause");
}
例二
#include <stdio.h>
#include <string.h>
struct Books //声明一个 结构体类型 Books
{
char title[50];
char author[50];
char subject[50];
int book_id;
}Book1;
void printBook( struct Books *book )
{
printf( "书标题 : %s\n", (*book ).title);
printf( "书作者 : %s\n", book ->author);
}
int main( )
{
/* Book1 元素赋值 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* 通过传 Book1 的地址来输出 Book1 信息 */
printBook( &Book1 );
return 0;
}
四、套娃与链表
1、typedef关键字
#define与 typedef
typedef仅限于为类型定义符号名称,#define不仅可以为类型定义别名,也能为数值定义别名。
typedef是由编译器执行解释的,#define语句是由预编译器进行处理的。
简而言之,#define 只是字面上的替换,由预处理器执行,#define A B 相当于打开编辑器的替换功能,把所有的B替换成A。
typedef unsigned char BYTE;
BYTE b1, b2; //unsigned char b1, b2;
2、套娃
1、typedef … Ex2:Ex2标识符 表示一个结构体类型
2、struct EX :定义一个 结构体类型EX ,注意成员d是 Ex结构类型的指针
3、typedef … EX:Ex标识符 表示Ex结构体类型
4、定义一个 Ex结构类型的结构体变量x,并给成员赋值
5、定义一个 Ex结构类型的 结构体指针px,并赋值,指向结构体x
6、定义一个 Ex结构类型的 结构体y
7、给 x的成员d(EX结构类型的结构体指针)赋值,指向结构体y
typedef struct{
int a;
short b
}Ex2; // 1、typedef ... Ex2
// Ex2标识符 表示 这个结构体类型
typedef struct EX{ // 2、struct EX :定义一个 结构体类型 EX
int a;
char b[3];
Ex2 c;
struct EX *d; //注意! 成员d是 Ex结构类型的 指针
}EX; //3、typedef ... EX
// Ex标识符 表示 Ex结构体类型
Ex x={10, "hi", {5, {-1,25} }, 0}; //4、定义一个 Ex结构类型的 结构体变量x 并给成员赋值
Ex *px = &x; //5、定义一个 Ex结构类型的 结构体指针px,并赋值 指向结构体x
Ex y; //6、定义一个 Ex结构类型的 结构体y
x.d = &y; //7、给 x的成员d(EX结构类型的指针)赋值,指向结构体y
3、套娃与访问成员:
px->d->a
px->d->b
px->d->c
px->d->c.a
px->d->c.b[1]
最后一个表达式的右值如下图中的小圆圈:
4、简单链表
#include<stdio.h>
#define NULL 0
struct student{
long num;
float score;
struct student *next;
};
void main()
{
struct student a,b,c;
struct student *head,*p;
a.num=10101;a.score=89.5;
b.num=10103;b.score=90;
c.num=10107;c.score=85;
head=&a;
a.next=&b;
b.next=&c;
c.next=NULL;
p=head;
do
{
printf("%ld %5.lf\n",p->num,p->score);
p=p->next;
}
while(p!=NULL);
}
运行结果为:
101010 89.5
10103 90.0
10107 85.0
5、创建(动态)链表
所谓动态链表是可以指定链表长度,动态分配存储空间。
1、创建三个LinkList类型的指针
2、malloc在内存动态存储区分配一个LNode类型、长度为sizeof(LNode)的连续空间(假设该空间叫做"盒子1"),然后返回该空间的首地址
3、将首地址赋值给L,此时L指向"盒子1"。
4、在这个函数中,L是整个链表的头指针,从开始被赋值就没再被改变过
5、L赋值给q,此时q和L都指向"盒子1"
6、创建新空间,p指向该空间。(假设该空间叫做"盒子2")
7、q->next 指向"盒子2",即 “盒子1->next” 指向 “盒子2”
8、p赋值给q,此时q和p都指向"盒子2"
9、在整个过程中,p负责开拓新结点(对应第6步),q负责连接新节点(第7步)然后指向当前尾节点(第8步)
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//注意!LinkList是 被关键字typedef修饰的标识符,表示LNode结构类型的指针
LinkList CreateList(int n);
void print(LinkList h);
int main()
{
LinkList Head=NULL;
int n;
scanf("%d",&n);
Head=CreateList(n);
printf("刚刚建立的各个链表元素的值为:\n");
print(Head);
printf("\n\n");
system("pause");
return 0;
}
LinkList CreateList(int n)
{
LinkList L,p,q; //1、创建三个LinkList类型的指针
int i;
/*2、malloc在内存动态存储区分配一个LNode类型、长度为sizeof(LNode)的连续空间
(假设该空间叫做"盒子1"),然后返回该空间的首地址*/
L=(LNode*)malloc(sizeof(LNode));
//3、将首地址赋值给L,此时L指向"盒子1"。
//4、在这个函数中,L是整个链表的头指针,从开始被赋值就没再被改变过
if(!L)return 0;
L->next=NULL;
q=L; //5、L赋值给q,此时q和L都指向"盒子1"
for(i=1;i<=n;i++)
{
//6、创建新空间,p指向该空间。(假设该空间叫做"盒子2")
p=(LinkList)malloc(sizeof(LNode));
printf("请输入第%d个元素的值:",i);
scanf("%d",&(p->data));
p->next=NULL;
q->next=p; //7、q->next 指向"盒子2",即"盒子1->next"指向"盒子2"
q=p; //8、p赋值给q,此时q和p都指向"盒子2"
//9、在整个过程中,p负责开拓新结点(对应第6步),q负责连接新节点(第7步)然后指向当前尾节点(第8步)
}
return L;
}
void print(LinkList h)
{
LinkList p=h->next;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
}
五、作为函数参数及优化
1、整个结构体作为传参
总价=数量*单价;
结构的一份拷贝作为参数传递给函数并被修改。然后一份修改后的结构拷贝从函数返回,所以这个结构被复制了两次。
例如将a传入函数过程,是将a复制一份赋值给函数的(局部)临时变量(参数),在函数执行过程中都基于此临时变量。在函数结束时会释放该临时变量丢失其数据,可以在函数结束前返回该临时变量,并用变量接收该临时变量。
如果传参所占存储空间大,那么使用指针代替是个好办法。
注:“结构体指针类型” 的 “类型”两字,表示该地址“所存储内容的类型"
假如磁盘控制器其中一个寄存器是如下定义的:
假如磁盘寄存器实在内存地址0xc0200142进行访问的,我们可以声明下面的指针变量
#define DISK_REGISTER ((struct DISK_REGISTER_FORMAT *)0xc0200142)
/*
** 告诉控制器从哪个扇区哪个磁道开始读取。
*/
DISK_REGISTER->sector = new_sector;
DISK_REGISTER->track = new_track;
DISK_REGISTER->command = READ;
/*
** 等待,直到操作完成(ready变量变成真)。
*/
while( !DISK_REGISTER->ready );
/*
** 检查错误
*/
if( DISK_REGISTER->error_occurred ) {
switch( DISK_REGISTER->error_code ) {
...
2、结构体地址作为传参
六、链表插入——指针
1、包含特殊情况
2、优化插入函数
3、一些事
七、链表倒置——指针
1、迭代法
在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。
也就是说,需要三个结构体指针,用于保存当前节点(curr)及其前节点(prev)、后节点(next)。
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* prev = NULL;
struct ListNode* curr = head;
while (curr) {
struct ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
2、递归法
递归版本稍微复杂一些,其关键在于反向工作。假设链表的其余部分已经被反转,现在应该如何反转它前面的部分?
struct ListNode* reverseList(struct ListNode* head) {
if (head == NULL || head->next == NULL) {
return head;
}
struct ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return newHead;
}