指针

C指针与地址(基础认知)

在C语言的学习过程中,常理不清指针(即一个变量的地址)和指针变量(专门用来存放另一变量的地址(指针)的变量)的关系,因此通过程序打印地址运行结果,来分析加强理解。老师在教授过程当中常将两个概念混在一起说,很多人听不明白,某个小学生多要理解几遍这段话。

前言

一、代码示例

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
int main()
{
int a=88,b=60 ;
int *p;
p=&a;//指向a的地址
printf("%p\n",&a);
printf("%#p",p);//以十六进制的形式输出
printf (" %p ",*p);
printf(" %p\n",&p);
printf("%d",p);
printf (" %d ",*p);
printf(" %d\n",&p);
p--;//指针移动指向b的地址
printf("%p\n",&b);
printf("%#p",p);
printf (" %p ",*p);
printf(" %p\n",&p);
printf("%d",p);
printf (" %d ",*p);
printf(" %d\n",&p);
return 0;
}

2.运行结果

代码如下:

1
2
3
4
5
6
000000000061FE1C
0X000000000061FE1C 0000000000000058 000000000061FE10
6422044 88 6422032
000000000061FE18
0X000000000061FE18 000000000000003C 000000000061FE10
6422040 60 6422032

总结

在指针*p=&a初始化后,p等同于&a即a的地址,在运行过程中可以代换。%#p是输出位0X开开头的16进制数。因此printf(“%p\n”,&a);    printf(“%#p”,p);本质是一样的。*p则是通过指针变量p存储的a的地址,间接获取a的值。*p=a,对数值%p等同于对数值以十六进制位输出。%d对p,*p,&p则是以十进制形式分别输出地址,值,地址。

p–,由于p是整型指针,p–减去int的4个字节刚好移动到b。

【注】1.这里发现后定义的变量b的地址要小于a的地址,这是因为c语言中,先定义的数据先入栈,在栈的底部(不分配内存),声明结束后,b在栈顶,所以b先出栈,先为b分配内存。因此b的地址小于a的地址。
   2.数据的地址位数和数据能存储的位数无关。学习阶段曾存在疑问(输出的地址是16位,int是4字节,即16进制的4位代表一个字节(实际应该是一个字节等于2位16进制),一个字节等于八位二进制,但八位二进制不等于四位16进制?)通过交流请教得知有关计算机组成原理,地址位数只取决于系统,所有类型的指针所占长度相同。

指针知识框架(详解)

具体内容请单击此链接,某个小学生可以待会再看,上面看完直接看结构体

结构体

如何声明一个结构体类型?

例子:形如下列代码的形式为结构体模板(struct template),仅是构造一个数据类型,如同构造int类型代表了什么样的数据类型,但未定义结构体变量,编译器不会为此分配内存。

1
2
3
4
5
6
struct student
{
long ID;
char Name[10];
int score[4];
}

如何定义一个结构体变量?

1.先定义结构体类型再定义结构体变量名

1
2
3
4
5
6
7
8
9
struct student
{
long ID;
char Name[10];
int score[4];
};
struct student stu1; //合法的√
struct stu1; //非法的×
student stu1; //非法的×

2.在定义结构体类型的同时定义变量

1
2
3
4
5
6
struct student
{
long ID;
char Name[10];
int score[4];
}stu1;

3.直接定义结构体变量(不指定结构体标签)

不写struct (结构体标签)的标签,相当于没有给该结构体类型起名字,不推荐使用,会导致不一致和重复的问题,不能用于声明函数的形参。

1
2
3
4
5
6
struct 
{
long ID;
char Name[10];
int score[4];
}stu1;

为结构体类型定义名字

1.用结构体标签来标识结构体类型

struct (结构体标签)

2.用typedef给数据类型定义一个别名

typedef即使某个词何某个词为同义词
如下面示例中STUDENTstruct student是同义词

类型1

使用typedef为已存在的类型定义一个别名,并未声明新的类型。这种定义方法结构体标签不可以省略。

1
2
3
4
5
6
7
struct student
{
long ID;
char Name[10];
int score[4];
};
typedef struct student STUDENT;

使用typedef成为同义词后,可以直接用STUDENT stu1定义结构体变量

类型2

1
2
3
4
5
6
typedef struct student
{
long ID;
char Name[10];
int score[4];
}STUDENT;

类型3

结构体标签可以省略

1
2
3
4
5
6
typedef struct student
{
long ID;
char Name[10];
int score[4];
}STUDENT;

分割线


typedef与链表的联系

typedef的作用是为已有的数据类型定义一个新名字,其主要目的是为了我们在使用时能用这个更加清晰简单的新名字,还有一个目的就是为了简化变量的声明。

样式1

1
2
3
4
5
6
struct node
{
int data; // 节点的数据域
struct node *next; // 节点的指针域
};
struct node n; // 定义一个单个节点

样式2

1
2
3
4
5
6
7
struct node
{
int data; // 节点的数据域
struct node *next; // 节点的指针域
};
typedef struct node NODE; // 把struct node型的结构体用别名NODE表示
NODE n; // 定义一个单个节点

样式3

1
2
3
4
5
6
typedef struct node 
{
int data; // 节点的数据域
struct node *next; // 节点的指针域
}NODE;
NODE n; // 定义一个单个节点

分割线


单链表的实际运用

单链表结构定义

参考文章编写结构体中的LNode与*LinkList编写

1
2
3
4
5
typedef int DateType;
typedef struct node{//链表节点的结构定义
DateType date;//数据域
struct StuNode *next;//指针域
}LinkNode,*LinkList;//单链表的结构定义

初学时往往不理解LinkNode,LinkList的意义。
对于这个结构体来说,LinkNode和
LinkList其实都是结构体struct node的别名,只不过类型不同。

  • LinkNode是一个普通的结构体名,相当于将结构体类型struct node 重命名为LinkNode;
  • *LinkList是一个指针类型,相当于将struct node * 重命名为LinkList

本质上LinkNodeLinkList都只是为了简化代码省略struct node部分,它们仍然是用来表示数据类型的别名。
实际使用中运用这些别名来定义变量,如LinkList L; 等价于struct node *L;

参考陆加壹编写

1.当函数参数为LinkList L时,意味着只改变或操作List的内容,而不需要改变L这个指针
如查找操作Status GetElem(LinkList L,int i,ElemType)

2.当参数为LinkList &L时,意味着需要改变或操作L这个指针本身
如初始化操作(初始化单链表,需要给L分配内存空间,即需要改变L)

1
2
3
4
5
Status InitList(LinkList &L)
{
L=(LinkList)malloc(sizeof(struct LNode));
......
}

&其实是C++的知识,其在此处的作用是使函数运行后可以改变实参。
&详解请见c++引用|菜鸟教程

当参数为LinkList L时,意味着需要改变或操作L这个指针指向的LinkList类型的指针(此处的L可以理解为指向前两点中的L的指针)
此时给头结点分配储存空间时要这样写(*L)=(LinkList)malloc(sizeof(struct LNode));
L前面要加
,表示L所指向的那个指针
该情况属于二级指针即指向指针的指针
参考星辰编写

某个小学生能不能看到这?知识点看完,找个实际例子对照着理解下