【C语言数据结构2】--指针的简单复习

简介: 指针作为C语言的重点,在数据结构中也发挥了重要作用,对指针的理解很有助于我们实现各种数据结构。所以我觉得有必要来复习一下指针的内容。本次复习是假设读者已经对指针有了大致的了解,知道取地址符、间接寻找符等运算符的作用,知道指针的运算等。这次复习是对一些细节内容的复习,而且是针对数据结构学习到的内容复习。

前言

指针作为C语言的重点,在数据结构中也发挥了重要作用,对指针的理解很有助于我们实现各种数据结构。所以我觉得有必要来复习一下指针的内容。本次复习是假设读者已经对指针有了大致的了解,知道取地址符、间接寻找符等运算符的作用,知道指针的运算等。这次复习是对一些细节内容的复习,而且是针对数据结构学习到的内容复习。

一、字符串的指针

字符串实际上就是字符数组,我们这里说字符串的指针,也就是字符数组的指针。

1.1、字符串的两种表示

我们知道字符串有两种表示方式,我们可以用字符数组表示,也可以用字符指针表示,如下:

char *sentence1 = "Do not go gentle into that good night!";
char sentence2[] = "Do not go gentle into that good night!";
复制代码

上面两句我们都可以正常输出,也可以使用下标运算符[],也可以用间接寻址符*,我们看如下代码:

//直接输出两个字符串
printf("%s\n", sentence1);
printf("%s\n", sentence2);
//使用下标运算符
printf("%c", sentence1[0]);
printf("%c", sentence2[1]);
//使用间接寻址符(指针运算符)
printf("%c", *(sentence1+2));
printf("%c", *(sentence2+3));
复制代码

输出结果如下:

Do not go gentle into that good night!
Do not to gentle into that good night!
Do n
复制代码

可以看出每条代码都顺利执行了,那么这两种方式有区别吗?

1.2、字符指针和字符数组的区别

实际上,我们使用指针方式定义的是一个字符串常量,是不可修改的,而用数组定义的是字符串变量,我们分别用sentence1和sentence2测试如下代码:

//修改字符串内容
sentence1[2] = '#';
//输出修改后的内容
printf("sentence1 : %s\n", sentence1);
复制代码

其中sentence1执行时异常退出了,而sentence2执行结果如下:

sentence2 : Do#not to gentle into that good night!
复制代码

可以看到字符串修改成功了。

二、动态分配内存的函数

在我们实现数据结构的时候,经常需要动态的分配内存,动态分配内存的方法在malloc.h库中,我们看看下面几个方法。

方法名称 函数原型 方法作用
malloc void *malloc(unsigned long size); 向系统申请size大小的内存,返回一个无类型指针
calloc void *calloc(unsigned int n , unsigned int size); 向系统申请n*size大小的内存,返回一个无类型指针
realloc void* realloc(void* pointer, unsigned int new_size); 对p指向的内存进行扩展,返回一个无类型指针
free void free(void *p); 释放p指向的内存

在具体说这些函数之前,我们再来温习一下堆栈。

2.1、堆栈

我们先看看什么是堆栈:

:由系统自动分配,速度较快。但程序员是无法控制的。

:是由程序员分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

像我们平时写代码,如下语句:

int a = 10;
char ch = 'a';
float f = 1.4;
复制代码

上面这些,都是申请在栈中的变量。而什么上面讲到的动态分配内存函数分配的,则是分配在堆中的变量。

2.2、malloc函数和free函数

使用该函数时,传入一个无符整形变量size,该函数将向系统堆存储区申请size大小的内存,并返回该内存区的首地址的无类型指针。当申请失败(内存不足)时,返回NULL,我们看如下代码:

int *p = malloc(sizeof(int)*5);
复制代码

我们分配了5个int大小的内存,返回的为无类型指针,很多教程上会将它强转为需要的类型,实际上是不需要的(如果是考试还是需要强转)。

因为是在堆中申请的内存,所以需要我们手动释放:

free(p)
复制代码

2.3、calloc函数和realloc函数

我们先看看calloc,如下代码:

int *p = calloc(5, sizeof(int));
复制代码

用法上和malloc有异曲同工之处,我们也不过多的讲解了。

然后是我们的realloc函数,这个是用法比较丰富的一个函数,在使用这个函数时,我们需要事先分配一块内存:

int *p = malloc(sizeof(int)*5);
复制代码

然后我们对这块原有的内存进行扩展:

p = realloc(p, sizeof(int)*10);
复制代码

该函数会返回一个无类型的指针,如果p后连续的地址充足,则返回原来的p,否则分配一块新地址。我们看如下代码:

#include <stdio.h>
#include <malloc.h>
int main(){
    int *p = malloc(sizeof(int)*5);
    //刚创建时,输出地址
    printf("before realloc p = %p\n", p);
    //将p指向的内存区域扩展到sizeof(int)*10
    p = realloc(p, sizeof(int)*10);
  //第一次扩展后输出地址
    printf("after realloc 10 p = %p\n", p);
  //将p指向的内存区域扩展到sizeof(int)*1000
    p = realloc(p, sizeof(int)*1000);
  //第二次扩展后输出地址
    printf("after realloc 1000 p = %p\n", p);
  //释放内存
    free(p);
    return 0;
}
复制代码

输出结果如下:

before realloc p = 00AB1658
after realloc 10 p = 00AB1658
after realloc 1000 p = 00AB3E38
复制代码

可以看到,第一次扩展因为内存充足,所以在原来的地址扩展,第二次内存不足,所以新申请了一块内存。

如果内存不足,扩展新地址的过程中,原地址中的内容也会复制。

除此之外,realloc函数还可以用来缩小:

p = realloc(p, sizeof(int))
复制代码

我们还可以用realloc来释放内存:

realloc(p, 0);
p = NULL;
复制代码

三、指针的初始化

指针的初始化方式是多样的,我们可以使用动态分配内存的方式初始化,也可以使用取地址符,还可以使用直接赋值的方式,我们一个一个来了解一下。

3.1、使用取地址符

这是我们常使用的一种方式,我们先定义好一个变量,并为它初始化,然后我们使用取地址符将这个变量的地址赋值给一个指针变量。人们看如下代码:

int a = 10;
int *p = &a;
复制代码

在我们最初学习指针时,我们就学了这种方式,这个方法也是比较常用的,就不再细说了。

3.2、直接赋值

除了用取地址符,我们可以用直接赋值的方式,即我们已知一个地址,将该地址赋值给指针变量。

int *p = 1001;
复制代码

这种方式一般不会使用,因为我们通常情况下不需要自己指定特定的地址,让电脑直接分配就可以了。

3.3、用指针赋值

指针变量有基本类型变量的大部分操作,赋值操作当然也是有的,我们先创建一个指针变量并初始化,然后将该指针赋值给另一个指针变量。

int x = 10;
int *p1 = &x;
int *p2 = p1;
复制代码

这种方式我们也时常会使用到,但是对于这种方式,我们更多的是认为这是指针的**=运算**。

3.4、动态初始化

所谓动态初始化就是我们上面说到的,使用动态分配内存的函数,手动的分配内存。代码如下:

int *p = malloc(sizeof(int));
复制代码

这种方式的特点就是分配的内存不会自己销毁,需要程序员手动销毁。

到此我们就将指针的指针简单复习了一遍,更多的内容需要读者自己在之前的学习中寻找。


目录
相关文章
|
26天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
38 1
|
23天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
76 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
23天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
48 9
|
23天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
42 7
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
27天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
27天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
96 3
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
27天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
37 1
|
7月前
|
C语言
C语言---指针进阶
C语言---指针进阶
53 0