C语言 13 指针进阶 壹

简介: C语言 13 指针进阶 壹

头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <math.h>


内存单元有编号 编号 = 地址 = 指针

指针就是个变量,用来存放地址,地址唯一标识一块内存空间

地址/指针/指针变量的大小是4 / 8个字节(32位/64位平台)

指针类型决定了指针加一减一的步长 指针解引用操作的权限

指针的运算


1.字符指针

把一个字符的地址赋值给指针

字符串表达式的值是首字符的地址

int main()
{
  char ch = 'w';
  char* pc = &ch;
  char arr[] = "abcdef";
  const char* p = "abcdef";
  printf("%p\n", p);
  char* p1 = arr;//数组的地址
  char* p2 = &arr[0];//数组首元素的地址
  printf("%p\n", p1);//000000906378FAD4
  printf("%p\n", p2);//000000906378FAD4
  printf("%c\n", "abcdef"[3]);//d 想象成地址,访问数组中下标为3的元素
  //首字符a的地址赋给了p
  return 0;
}
int main()
{
  char str1[] = "hello bit.";
  char str2[] = "hello bit.";
  const char* str3 = "hello bit.";//const+变量:该变量里的数据只能被访问,不能被修改,意味只读
  const char* str4 = "hello bit.";
  if (str1 == str2)//not same 两个数组地址不相同
    printf("same\n");
  else
    printf("not same\n");
  if (str3 == str4)//same 两个常量字符串比较 常量字符串不能被修改 内容相同时,只会保存一份
    printf("same\n");
  else
    printf("not same\n");
  if (&str3 == &str4)//no
    printf("same\n");
  else
    printf("not same\n");
  return 0;
}

字符串≈数组

常量字符串只能保存一份  地址不同 但是只保存一份常量字符串 所以两字符串很像


2.指针数组——是数组、


存放在数组中的元素是指针类型

整形数组是存放整形的数组

字符数组是存放字符的数组

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

存放在数组中的元素是指针类型的


int* arr[5]存放整形指针的数组


char* ch[6]存放字符指针的数组

int main()
{
  int a = 1;
  int b = 2;
  int c = 3;
  int d = 4;
  //不会这样使用
  int* arr[] = { &a,&b,&c,&d };
  return 0;
}

用指针数组模拟二维数组的

int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int arr2[] = { 2,3,4,5,6 };
  int arr3[] = { 3,4,5,6,7 };
             //int* int* int*
  int *arr[] = { arr1,arr2,arr3 };
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 5; j++)
    {
      printf("%d ", arr[i][j]);//通过访问数组下标来访问每一个元素
    }
    printf("\n");
  }
  return 0;
}
int main()
{
  //指针数组的应用场景之一
  const char* arr[5] = { "hello bit ","hehe ","zezeze ","ijij ","666 " };
  for (int i = 0; i < 5; i++)
  {
    printf("%s\n", arr[i]);
  }
  return 0;
}

指针数组可以维护多个数组

指针数组是一种数组形式的存在


3.数组指针是指针,指向数组的指针

 指针数组是数组,是存放指针的数组

int main()
{
  int a = 10;
  int* p1 = &a;
  char ch = 'c';
  char* p2 = &ch;
  float f = 3.14;
  float* p3 = &f;
  return 0;
}

数组名的理解 : 数组名是数组首元素的地址

2个例外

sizeof(数组名)表示整个数组,计算整个数组大小,单位字节

& 数组名,这里的数组表示整个数组,表示整个数组的大小

p = &arr

arr & arr & arr[0] 取地址相同 它们的值是一样的

int main()
{
  int arr[10];
  printf("arr:  %p\n", arr);
  printf("arr+1:%p\n", arr+1);
  printf("\n");
  printf("&arr:  %p\n", &arr);//它们的值相同
  printf("&arr+1:%p\n", &arr+1);//它们的值相同
  printf("\n");
  printf("&arr[0]:  %p\n", &arr[0]);
  printf("&arr[0]+1:%p\n", &arr[0]+1);
  //指针类型决定了指针+1,到底+几个字节
  return 0;
}

前两个加一是类型不同 跳过字节

第三个跳过整个数组元素的字节

数组指针的创建方式 : int(*p)[10] = &arr p是用来存放数组的地址的 是数组指针

int main()
{
  int arr[10] = { 0 };
  //数组指针的写法:
  int (*p)[10] = &arr;//p是用来存放数组的地址的,p就是数组指针
  //eg、1
  char* arr1[5];
  char* (*pc)[5] = &arr1;
  //eg、2
  int* arr2[7];
  int* (*p)[7] = &arr2;
  return 0;
}

数组指针的用处 :

int main()
{
  //法1:
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int(*p)[10] = &arr;
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", (*p)[i]);
  }
  return 0;
}
int main()
{
  //法2: 一维数组正确访问方式:
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int* p = arr;
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", p[i]);
  }
  return 0;
}

遍历二维数组中的每一个元素

void print(int arr[3][5], int r, int c)
 //       (int (*P)[5],int r,int c) 二维数组传参 形参的部分写的是指针
{
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 5; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
  print(arr, 3, 5);//数组名,行数,列数 二维数组传参
  return 0;//arr是二维数组的数组名,是首元素的地址,
 //二维数组首元素的地址是第一行的地址,第一行的地址是第一行首元素的地址
}

数组传参本质是传指针

void print(int *arr, int sz)//形式参数指针的形式 本质是指针
 //void print(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[] = { 1,2,3,4,5,6,7,8,9,10 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  print(arr, sz);
    //一维数组传参
  return 0;
}
int arr[5];//arr是一个能够存放5个整形数据的数组
int* parr1[10];//parr1是一个数组,数组10个元素,每个元素的类型是int*
int(*parr2)[10];//parr2是一个数组指针,该指针指向数组,指向的数组有10个元素,每个元素的类型是int
int(*parr3[10])[5];//parr3是一个数组,是存放数组指针的数组,存放的这个数组指针,指向的数组有5个元素,每个元素是int类型

替换二维数组传参 形参使用二维数组的形式

二维数组行可以省略 列不能省略 一维数组可以省略

一维数组传参用指针传参更好理解

二维数组数组名是第一行的地址 第一行的地址是第一行一维数组的地址

二维数组传参 形参的部分传给指针



4.数组参数和指针参数

把数组传参给指针

一维数组 : 数组传参 形参的部分可以写成数组形式



数组传参时 数组的大小随意

数组传参的本质是传递数组首元素的地址

数组传参 形参可以传为指针形式

void test(int arr[])
{}y
void tst(int arr[10])
{}y
void test(int* arr)
{}y
void test2(int *arr[20])
{}y
void test2(int **arr)//传过去一级指针的地址,所以可以写成二级指针
{}y
数组名表示首元素的地址 每个元素都是int * 类型地址 数组名表示首元素的地址是int * 地址
二维数组传参
void test(int arr[3][5])
{}y
void test(int arr[][])
{}N 行可以省略,列不可以省略
void test(int arr[][5])
{}y
void test(int *arr)
{}N
void test(int* arr[5])
{}N 要么写成指针,要么写成数组
void test(int (*arr)[5])
{}y
void test(int **arr)
{}N
二维数组传参 : 行可以省略 列不能省略
数组名传参传地址传的是第一行的地址

一级指针传参

void print(int* p, int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *(p + i));
  }
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int* p = arr;
  int sz = sizeof(arr) / sizeof(arr[0]);
  //一级指针p,传给函数
  print(p, sz);
  return 0;
}

二级指针用来接收一级指针的地址

一级指针传参

传地址和传指针一样 形参部分写成一级指针便可

一级指针用来接收地址

看类型的地址 同一类型的指针都可以传参


当一个函数的参数部分为一级指针的时候,函数能接收什么参数

void test(int* p)
{
}
int main()
{
  int a = 10;
  int* ptr = &a;
  int arr[5];
  test(arr);//传整形一维数组的数组名
  test(&a);//传整型变量的地址
  test(ptr);//传整形指针
}

二级指针传参

先给一个一级指针变量

再把一级指针的地址传给二级指针

二级指针传参只能用二级指针的地址传参

 

#include <stdio.h>
void test(int** ptr)
{
  printf("num = %d \n", **ptr);
}
int main()
{
  int n = 10;
  int* p = &n;
  int** pp = &p;
  //二级指针变量等于一级指针变量的地址
  //当一个二级指针变量int **p接收,可以传一个一级指针变量地址,可以传一个二级指针变量
  //还可以传一个数组首元素的地址  如:int* arr[6]
  test(pp);
  test(&p);
  return 0;
}

数组指针 : 指向数组的指针 存放的是数组的地址 取地址数组名就是数组的地址 &数组名就是数组的地址

函数指针 : 指向函数的指针 存放的是函数的地址 取地址函数名就是函数的地址 &函数名是函数的地址吗

 

int Add(int x, int y)
{
  return x + y;
}
int main()
{
 //&函数名和函数名代表的都是函数的地址
  int a, b;
  printf("请您输入两个值\n");
  scanf("%d%d", &a, &b);
  int c = Add(a, b);
  printf("%p\n", &Add);
  printf("%p\n", Add);
  printf("%d", c);
  int (*pf1)(int, int) = Add;
  int (*pf2)(int, int) = &Add;
//int* pf2(int,int);函数的声明
  //由此可见类型相同,两种写法皆可
  int ret = (*pf2)(2, 3);
  int ret1 = Add(2, 3);
  printf("%d\n", ret);
  printf("%d\n", ret1);
  return 0;
}

函数名也是函数的地址

与数组指针写法非常类似

函数指针也是一种指针 是函数的地址 需要一个函数的变量

//代码1
int main()
{
  //void(*)()函数指针类型
  //int a=(int)3.14;
  //下面的代码是在调用0地址处的函数,这个函数没有参数,返回类型是void
  ( *(void (*)( ))0)();
//返回类型void 强转 调用函数无参数
}
//代码2:
int main()
{
    //对一个类型重新起名
    //typedef unsigned int uint
    //类型重命名 复杂类型 重命名
  void(* signal(int, void(*)(int)))(int);
  //void(*)(int) signal(int, void(*)(int));//不支持
    //signal函数 两参数:int类型 函数指针类型 参数是int,返回类型是void
  //这个代码是一次函数声明,声明的是signal函数,
  //signal函数参数有两个,第一个是int类型,第二个是函数指针类型,该类型是void(*),
  //signal函数的返回类型也是函数指针类型,该类型是void(*)(int)
  //该函数指针指向的函数,参数是int,返回类型是void
  return 0;
}


代码2太复杂,如何优化:

typedef void(*pfun_t)(int);

pfun_t signal(int, pfun_t);


typedef unsigned int uint

类型重命名 复杂类型 重命名

 

typedef unsigned int uint;//类型重命名
int main()
{
  uint a;
  unsigned int b;
  return 0;
}


目录
相关文章
|
1月前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
40 1
|
1月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
49 0
|
1月前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
18 2
|
1月前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
1月前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
1月前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
1月前
|
C语言
C语言指针(3)
C语言指针(3)
14 1
|
1月前
|
C语言
C语言指针(2)
C语言指针(2)
15 1