【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));
复制代码

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

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


目录
相关文章
|
7天前
|
安全 程序员 编译器
【C语言基础】:深入理解指针(二)
【C语言基础】:深入理解指针(二)
【C语言基础】:深入理解指针(二)
|
6天前
|
算法 Java 程序员
面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性
【6月更文挑战第15天】面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性。封装可使用结构体封装数据和方法,如模拟矩形对象。继承则通过结构体嵌套实现静态继承。多态可通过函数指针模拟,但C不支持虚函数表,实现复杂。C语言能体现OOP思想,但不如C++、Java等语言原生支持。
26 7
|
7天前
|
C语言
【C语言基础】:深入理解指针(终篇)
【C语言基础】:深入理解指针(终篇)
|
7天前
|
存储 C语言 C++
【C语言基础】:深入理解指针(三)
【C语言基础】:深入理解指针(三)
|
7天前
|
存储 编译器 C语言
【C语言基础】:深入理解指针(一)
【C语言基础】:深入理解指针(一)
|
8天前
|
C语言
C语言的灵魂---指针(进阶)
C语言的灵魂---指针(进阶)
|
8天前
|
C语言 C++
【数据结构】C语言实现:栈(Stack)与队列(Queue)
【数据结构】C语言实现:栈(Stack)与队列(Queue)
|
21天前
|
C语言
C语言指针带代码
C语言指针带代码
23 3
|
23天前
|
C语言
C语言中返回指针值的函数
C语言中返回指针值的函数
23 0
|
15天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
14 1