c语言指针指北

c语言指针简介

稍后补充



案例一

在c语言程序中,可以使用指向字符型数据的指针(char*)表示字符串。

例子如下:

#include <stdio.h>
int main(int argc, char **argv)
{
    char *test = "Hello";
    printf("%s\n", test);
    printf("%d", sizeof(test));
}



程序输出结果如下:
Hello
8

案例二

在使用c语言实现二叉树的基本操作时,也很考验对于指针的操作熟练程度。


本例子使用二叉链表作为二叉树的数据结构。

#include <stdio.h>
#include <stdlib.h>

typedef char ElemType;

typedef struct BinaryTree
{
    ElemType data;
    struct BinaryTree *left_child;
    struct BinaryTree *right_child;
} BinaryTree;

typedef BinaryTree TreeNode;

下面是测试程序。
int main(int argc, char **argv)
{
    BinaryTree *a, *b, *c, *d, *e, *f, *g;
    /* 非内置的数据类型需要手动申请内存空间 */
    a = (BinaryTree*)malloc(sizeof(BinaryTree));
    b = (BinaryTree*)malloc(sizeof(BinaryTree));
    c = (BinaryTree*)malloc(sizeof(BinaryTree));
    d = (BinaryTree*)malloc(sizeof(BinaryTree));
    e = (BinaryTree*)malloc(sizeof(BinaryTree));
    f = (BinaryTree*)malloc(sizeof(BinaryTree));
    g = (BinaryTree*)malloc(sizeof(BinaryTree));

    a->data = 'A';
    a->left_child = b; a->right_child = c;
    b->data = 'B';
    b->left_child = NULL; b->right_child = NULL;
    c->data = 'C';
    c->left_child = NULL; c->right_child = NULL;
    d->data = 'D';
    d->left_child = e; d->right_child = f;
    e->data = 'E';
    e->left_child = NULL; e->right_child = NULL;
    f->data = 'F';
    f->left_child = NULL; f->right_child = NULL;

    g = a;
    printf("将节点a赋值给节点g\n");
    printf("节点g的值为 %c\n", g->data);
    printf("节点g的左孩子的值为 %c\n", g->left_child->data);
    printf("节点g的右孩子的值为 %c\n", g->right_child->data);
    
    return 0;
}

上述程序构造了一个简单的二叉树1
						A
					  /   \
					 B	   C

以及二叉树2
						D
					  /   \
					 E	   F

以及一个空二叉树G
						G
					  /   \
					NULL  NULL

然后将指针a赋值给指针g。


以上程序的运行结果如下:

将节点a赋值给节点g
节点g的值为 A        
节点g的左孩子的值为 B
节点g的右孩子的值为 C

说明:
将a的地址赋值给g后
a指向的左右孩子也是g指向的左右孩子


接着,再在测试案例中添加一小段代码,看看程序运行过程中虚拟内存的变化。

	/*
	 * 在主函数中修改为以下代码
	 */
	printf("操作前\n");
    printf("节点a的虚拟内存地址为 %d\n", a);
    printf("节点g的虚拟内存地址为 %d\n", g);

    g = a;
    printf("将节点a赋值给节点g\n");
    printf("节点g的值为 %c\n", g->data);
    printf("节点g的左孩子的值为 %c\n", g->left_child->data);
    printf("节点g的右孩子的值为 %c\n", g->right_child->data);
    /*
     * 输出结果显示将a的地址赋值给g后
     * a指向的左右孩子也是g指向的左右孩子
     */
    printf("节点a的虚拟内存地址为 %d\n", a);
    printf("节点g的虚拟内存地址为 %d\n", g);


测试程序运行结果如下:

操作前
节点a的虚拟内存地址为 1394704
节点g的虚拟内存地址为 1395184
将节点a赋值给节点g
节点g的值为 A
节点g的左孩子的值为 B
节点g的右孩子的值为 C
节点a的虚拟内存地址为 1394704
节点g的虚拟内存地址为 1394704

可以看出,将指针a复制给指针g就是将a的内存地址赋值给g。
因此,a所指向的左右孩子也是g所指向的左右孩子。

接着,再尝试使用free()函数释放内存。
再上述代码之后,添加如下代码片段。

	free(g);
    printf("free节点g\n");
    printf("节点g释放内存后的虚拟内存地址为%d\n", g);
    printf("节点g的值为 %d\n", g->data);
    printf("节点a的虚拟内存地址为 %d\n", a);
    printf("节点a的左孩子的地址原来为(即节点b的地址) %d, 现在为%d\n", b, a->left_child);
    printf("节点a的右孩子的地址为 %d\n", a->right_child);


运行结果如下:

操作前
节点a的虚拟内存地址为 7817232
节点g的虚拟内存地址为 7817712
将节点a赋值给节点g
节点g的值为 A
节点g的左孩子的值为 B
节点g的右孩子的值为 C
节点a的虚拟内存地址为 7817232
节点g的虚拟内存地址为 7817232
free节点g
节点g释放内存后的虚拟内存地址为7817232
节点g的值为 -16
节点a的虚拟内存地址为 7817232
节点a的左孩子的地址原来为(即节点b的地址) 7817312, 现在为
7799120
节点a的右孩子的地址为 -17891602

可以看出,free(g)但是没有将指针g置为空指针(即令 g = NULL)之后,g的虚拟内存地址不变,但是g的值变化为内存块上的随机值。

并且,对指针g释放内存影响到了原先赋值给其的指针a
指针a的虚拟内存地址不变,(情况和指针g相同)
但是左右孩子的地址跟着被修改。
并且当你试图输出a的左右孩子的值时(如下图)

	printf("节点a的左孩子的值为 %c", a->left_child->data);
    printf("节点a的右孩子的值为 %c", a->right_child->data);

程序在运行过程中会报错(内存错误)
说明释放指针g的内存时,a的内存也跟着被释放。

原因如下:
指针g和指针a 指向的是同一块地址(同一块堆空间)

所以,在本案例开始的将指针a赋值给g时其实是浅拷贝

如果需要指针g和指针a在拷贝后相互独立,需要使用深拷贝
即将a指向的数据的所有属性逐个赋值给g指向的数据。

另外,若在对指针g进行free()操作后对其赋值为空指针,即

	free(g);
    g = NULL;

之后的内容与不对其置空基本一致,只是置空之后g的地址为0
由于仍然是浅拷贝,指针a仍然被释放内存,因此结果大部分一致(除了g的地址改变)。

注意:

在对指针使用**free()**的时候,千万要注意free()只能释放之前调用过malloc,calloc,realloc这三个函数返回的指针ptr所指向的内存空间。
也就是说,free和malloc,calloc,realloc需要成对出现。否则会有意外发生。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值