【C】指针与结构体---初阶

简介: 【C】指针与结构体---初阶

💻前言

🍁这篇博客总结指针与结构体的基本知识和用法,较为基础,这篇博客可以让你搞明白这些知识,并可以初步在一些场景下应用。

🍁之后还会更新对于结构体和指针部分的深入理解与应用。

🍁对于这部分内容一些更为基础的介绍和理解可以看我的另一篇博客找到对应部分 C语言入门——带你从0开始 。

💻一.指针

1.指针是什么!

指针理解的2个要点:

  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址,口语中说的指针通常指的是指针变量,指针变量用来存放地址(指针)。

结合以下进行理解:

计算机当中的内存,

52b1d32249d44b0f94a9bce6e969424c.png

指针变量:

我们可以通过&(取地址操作符)取出变量的内存(其实是地址),把地址可以存放到一个变量中,这个变量就是指针变量

#include<stdio.h>
int main()
{
  int a = 10;
  /*a是整型变量,占用4个字节的内存空间*/
  int* pa = &a;
   /*这里我们对变量a,取出它的地址,可以使用&操作符。
   a变量占用4个字节的空间,
  这里是将a的4个字节的第一个字节的地址存放在p变量中,
   p就是一个指针变量。*/
  return 0;
}

指针(指针变量)是用来存放地址的(存放在指针中的值都被当成地址处理),地址是唯一标示一块地址空间的;

指针的大小取决于这个地址的大小,在32位平台是4个字节,在64位平台是8个字节。

2. 指针类型与其意义

我们都知道,变量有不同的类型,整形,浮点型等;同样的指针也是有类型的。

指针变量的类型是要与其解引用之后所指向的变量相对应的,如:

int num = 10;
p = &num;

这里的p就是一个指针变量,我们这里给出其相对应的类型

int* p = &num

指针的定义方式是: type + *

char * 类型的指针是为了存放 char 类型变量的地址。
short * 类型的指针是为了存放 short 类型变量的地址。
int * 类型的指针是为了存放 int 类型变量的地址。

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

🌸下面刨析指针类型的意义,结合代码分析;

对比第一组:

52b1d32249d44b0f94a9bce6e969424c.png

改变了四个字节的内容!

52b1d32249d44b0f94a9bce6e969424c.png

对比第二组:

52b1d32249d44b0f94a9bce6e969424c.png

只改变了一个字节的内容。

52b1d32249d44b0f94a9bce6e969424c.png

结论1:
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
如果是int的指针,解引用访问4个字节。
如果是char
的指针,解引用访问1个字节。
推广到其他类型类似。

再看下面代码的效果!52b1d32249d44b0f94a9bce6e969424c.png

结论2:
指针的类型决定了指针±1操作的时候,跳过几个字节。
也就是类型决定了指针的步长。

有人可能还有一个疑问,当类型不同但它们访问的权限是一样的,对它们解引用可不可以得到相同的效果?

52b1d32249d44b0f94a9bce6e969424c.png

52b1d32249d44b0f94a9bce6e969424c.png

对比可以发现当类型相对应的时候,可以实现改变a的值,当不对应时就出问题了,所以区分不同的指针类型还是非常重要的!

3.野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的),也就是对指针解引用其指向的空间不在给程序分配的范围内。

🌸 野指针成因

指针未初始化

52b1d32249d44b0f94a9bce6e969424c.png2. 指针越界访问

52b1d32249d44b0f94a9bce6e969424c.png

  1. 指针指向的空间释放

52b1d32249d44b0f94a9bce6e969424c.png

🌸 如何规避野指针

  1. 指针初始化(赋为空指针)
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

52b1d32249d44b0f94a9bce6e969424c.png

4. 指针运算

🌸指针±整数
上面在解释指针类型的意义时进行的就是指针±整数 的运算,不局限于1,可以±更大的数,但要时刻注意出现野指针的情况!
🌸指针-指针

52b1d32249d44b0f94a9bce6e969424c.png

指针-指针求出的是俩个指针之间元素的个数!52b1d32249d44b0f94a9bce6e969424c.png

要注意指针 - 指针得有意义才行,也就是俩个指针在同一片空间内,否则计算是没有意义的,比如下面这个错误示范,它的计算结果毫无意义。

52b1d32249d44b0f94a9bce6e969424c.png    

用指针 - 指针的方式实现模拟strlen函数!

int my_strlen(char* str)
{
  char* start = str;
  while (*str != '\0')
  {
    str++;
  }
  return (str - start);
}
int main()
{
  int len = my_strlen("abcdef");
  printf("%d\n", len);
  return 0;
}

🌸指针的关系运算

#define _CRT_SECURE_NO_WARNINGS 1
#define N_VALUES 5
int main()
{
  float values[N_VALUES];
  float* vp;
  //指针+-整数;指针的关系运算
  for (vp = &values[0]; vp < &values[N_VALUES];)
  {//将数组中的元素都赋为0
    *vp++ = 0;
  }
  return 0;
}

<b这里将代码做出修改如下:

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}

实际上修改后的代码在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5. 通过指针访问数组

把数组名当成地址存放到一个指针中,我们使用指针来访问一个数组。

52b1d32249d44b0f94a9bce6e969424c.png

看下面俩种写法其实是等价的!

#include<stdio.h>
//写法一
void test(int* p, int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *(p + i));
  }
}
//写法二
void test(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
    //arr[i]--> *(arr+i)
  }
}
int main()
{
  int arr[10] = {0};
  test(arr, 10);
  return 0;
}

6. 二级指针

指针变量也是变量,是变量就有地址,而指针变量的地址再存放在指针变量中,这就是二级指针

52b1d32249d44b0f94a9bce6e969424c.png

*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa ;**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .52b1d32249d44b0f94a9bce6e969424c.png

7. 指针数组

存放指针的数组就是指针数组。

int* arr3[5];
arr3是一个数组,有五个元素,每个元素是一个整形指针。

代码实例:

52b1d32249d44b0f94a9bce6e969424c.png

下面用指针数组模拟实现一个二维数组!

52b1d32249d44b0f94a9bce6e969424c.png

8. 对于指针类型 type*中 * 和type的理解

一级指针

比如:int* pa = &a;

这里的 * 表明p是一个指针变量,而int表明对pa解引用后,* pa指向的的对象是int类型的。

二级指针

比如:int** ppa = &pa;

从左往右数,第二颗 * 表明ppa是一个指针变量,而而int * 表明对ppa解引用后,* ppa指向的的对象是 int * 类型的。

如果往后延伸还有三级、四级指针等…同样这样理解,但这些n级指针一般是用不到的。

💻二.结构体

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。结构体用来描述一个复杂对象,比如一个人的各种信息、一本书的各项说明…

1. 结构体的声明、定义、初始化

🌸结构的声明

struct tag
{
 member-list;
}variable-list;

这里的struct tag就是声明的一个结构体类型!
例如描述一个人的信息:

struct Peo
{
  char name[20];//姓名
  char tele[12];//电话
  char sex[5];//性别:女/男/保密
  int high;//身高
}p1, p2;//p1和p2是两个全局的结构体变量
       //分号不能丢

🌸 结构成员的类型

结构的成员可以是变量、数组、指针,甚至是其他结构体。

struct Peo
{
  char name[20];
  char tele[12];
  char sex[5];
  int high;
}p3, p4;
struct St
{
  struct Peo p;//结构成员为结构体
  int num;
  float f;
};

🌸 结构体变量的定义和初始化

struct Point
{
  int x;
  int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = { x, y };
struct Stu        //类型声明
{
  char name[15];//名字
  int age;      //年龄
};
struct Stu s = { "zhangsan", 20 };//初始化
struct Node
{
  int data;
  struct Point p;
  struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化

2. 结构体成员的访问

🌸结构体变量访问成员

结构变量的成员是通过点操作符(.)访问的。
点操作符接受两个操作数。

52b1d32249d44b0f94a9bce6e969424c.png🌸结构体指针访问指向变量的成员
有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针;可以通过对指针解引用再使用 . 操作符,或者使用 -> 操作符!52b1d32249d44b0f94a9bce6e969424c.png

3. 结构体传参

#include<stdio.h>
struct S
{
  int data[1000];
  int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
  printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
  printf("%d\n", ps->num);
}
int main()
{
  print1(s);  //传结构体
  print2(&s); //传地址
  return 0;
}

其实在我们写代码的过程中,一般采用的是print2函数这种写法;

函数传参的时候,参数是需要压栈的,如果这里有疑惑,可以看一看我的另一篇博客 关于函数栈帧

如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

所以哦,结构体传参的时候,一般传结构体的地址。


目录
相关文章
|
10月前
初阶指针详解
初阶指针详解
|
4月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
317 13
|
4月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
85 4
|
10月前
|
存储 编译器 C语言
C语言初阶⑦(指针初阶)知识点+笔试题(上)
C语言初阶⑦(指针初阶)知识点+笔试题
58 0
|
6月前
|
存储 Go
Go: struct 结构体类型和指针【学习笔记记录】
本文是Go语言中struct结构体类型和指针的学习笔记,包括结构体的定义、成员访问、使用匿名字段,以及指针变量的声明使用、指针数组定义使用和函数传参修改值的方法。
|
7月前
|
存储 C语言
指针与结构体
指针与结构体
57 0
|
8月前
|
编译器 C语言
【C语言初阶】指针篇—下
【C语言初阶】指针篇—下
|
8月前
|
存储 C语言
【C语言初阶】指针篇—上
【C语言初阶】指针篇—上
|
9月前
|
算法 Java 程序员
面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性
【6月更文挑战第15天】面向对象编程(OOP)通过对象组合构建软件,C语言虽是过程式语言,但可通过结构体、函数指针模拟OOP特性。封装可使用结构体封装数据和方法,如模拟矩形对象。继承则通过结构体嵌套实现静态继承。多态可通过函数指针模拟,但C不支持虚函数表,实现复杂。C语言能体现OOP思想,但不如C++、Java等语言原生支持。
85 7
|
9月前
结构体\结构体指针
结构体\结构体指针
48 3