征服C语言指针系列(2)

简介: 征服C语言指针系列(2)

一.指针相关类型

1.字符指针

1.形式及介绍:

int main()
{
  const char* pstr="hello world";
  printf("%s\n",pstr);//打印出hello world
  return 0;
}

含义:把字符串"hello world"的首字符的地址即h的地址放在了字符指针pstr中,而不是将字符串"hello world"直接放入字符指针pstr中

2.面试题

int main()
{
  char str1[] = "hello bit.";
  char str2[] = "hello bit.";
  const char* str3 = "hello bit.";
  const char* str4 = "hello bit.";
  if (str1 == str2)
    printf("str1 and str2 are same\n");
  else
    printf("str1 and str2 are not same\n");
  if (str3 == str4)
    printf("str3 and str4 are same\n");
  else
    printf("str3 and str4 are not same\n");
  return 0;
}

最后打印的是:

str1 and str2 are not same

str3 and str4 are same

因为:

1.str1与str2是两个不同的数组,只不过是两者初始化的内容相同而已,但是两者所开辟的空间并不相同,因此两者"not same"

2.str3与str4指向的是同一个常量字符串,C/C++会把常量字符串存储到一个单独的内存区域,当几个指针指向同一个字符串时,它们实际指向的是同一块内存,因此两者"same"

但是用相同的常量字符串去初始化不同数组的时候就会开辟出不同的内存块,因此str1与str2 “not same”

2.指针数组

1.形式及介绍

int* arr1[10];//arr1为指针数组,数组中存放的是int*类型的指针
char* arr2[4];//一级字符指针的数组
char* *arr3[5];//二级字符指针的数组

2.应用实例1:模拟实现二维数组

那么指针数组有什么应用实例呢?

下面我们来看一下它的第一个应用实例:即模拟实现二维数组

int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int arr2[] = { 2,3,4,5,6 };
  int arr3[] = { 3,4,5,6,7 };
  int* arr[3] = { arr1,arr2,arr3 };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

3.应用实例2:动态开辟二维数组

//动态开辟的二维数组
//1.开辟一个一维的指针数组
//2.开辟row个一维的数组,让指针数组中的指针指向相应的一维数组
//释放时先释放后开辟的那些一维数组,然后才能释放第一个指针数组
int** maze=(int**)malloc(sizeof(int*)*row);
//开辟的指针数组,用int** 来接收指针数组的首地址,可以分解为int* *,后面那个*表示这个变量是一个指针,前面那个变量表示这个数组的元素类型是int*的指针类型
for(int i=0;i<row;i++)
{
  maze[i]=(int*)malloc(sizeof(int)*col);
  //动态开辟一维数组,并用指针数组分别指向
}
//二维数组的输入
for(int i=0;i<row;i++)
{
    for(int j=0;j<col;j++)
    {
        scanf("%d",&maze[i][j]);
    }
}

3.数组指针

1.形式及介绍

int (*p)[10];
//指向一个数组的指针,该数组的元素类型为int,所含元素个数为10
//原因:
//[]的优先级高于*号,所以必须加上()来保证p先和*结合成为指针类型
//此时p的类型为int(*)[10],即去掉p后即为指针的类型

2.使用:接收二维数组传参

void print_arr2(int(*arr)[5], int row, int col)
{
  int i = 0;
  for (i = 0; i < row; i++)
  {
    int j = 0;
    for (j = 0; j < col; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
  print_arr2(arr, 3, 5);
  //数组名arr表示二维数组首元素的地址
  //但是二维数组的首元素是二维数组的第一行
  //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
  //可以用数组指针来接收
  return 0;
}

3.小小总结

学习了指针数组和数组指针之后,让我们来一起回顾一下:

int arr[5]://整型数组,数组大小为5个元素,数组元素类型为int类型
int* parr1[10];//指针数组,数组大小为10个元素,数组元素类型为int*类型
int (*parr2)[10];//数组指针,指针指向一个数组,这个数组大小为10个元素,每个元素的类型是int
int (*parr3[10])[5]//指针数组,数组大小为10个元素,每个元素的类型都是数组指针,这个指针指向一个数组,数组的大小为5个元素,返回类型为int

二.数组传参

1.一维数组传参

void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{
 int arr[10] = {0};
 int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}
1.ok,一维数组传参,参数用一维数组来接收
2.ok,一维数组传参,参数用一维数组来接收
3.ok,arr代表数组首元素地址,可以用int*类型的指针来接收
并通过指针进行加减整数运算来遍历整个数组中的全部元素
4.ok,arr2是指针数组,用指针数组来接收完全可以
5.ok,arr2是指针数组,其中的每个元素都是int*类型,所以用二级指针来接收完全可以

2.二维数组传参

void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}
1.ok,二维数组传参,用二维数组接收
2.err,用二维数组作形参时,函数形参只能省略第一个[]中的数字
因为对于一个二位数组来说,可以不知道有多少行
但是必须知道每一行有多少元素即有多少列才可以正确地确定出该二维数组的形状
3.ok,同上
4.err,int* 类型的指针只能存储整型数据与一维数组,不能存储二维数组
5.err,因为实参为二维数组,所以必须只能用二维数组或数组指针来接收,
也就是无法用指针数组来接收
实参类型为int[][5],形参类型为int*[5],不相同,无法接收
6.ok,数组指针,每个指针指向的数组:大小为5个元素,每个元素的类型为int类型,每个指针刚好可以表示这个二维数组的一行
实参类型为int[][5],形参类型为int(*)[5]即int[][5],所以可以接收
7.err,二维数组不能用二级指针来接收,二级指针是存放一级指针地址的指针,不能表示二维数组
实参类型为int[][5],形参类型为int** ,不相同,无法接收

三.指针传参

1.一级指针传参

当一个函数的参数部分为一级指针的时候,该函数能够接受什么参数?
方法:看实参类型与形参类型是否相同?
例如:以字符型指针为例
void test(char* p)
{}
char ch = 'a';
char* ptr = &ch;
char arr[] = "abcdef";
test(&ch);
test(ptr);
test(arr);
//这三种均可以

2.二级指针传参

当一个函数的参数部分为二级指针的时候,该函数能够接受什么参数?
方法:看实参类型与形参类型是否相同?
数组传参:形参可以写成数组形式,也可以写成指针形式
指针传参:形参只能写成指针形式
void test(char** p)
{}
char ch = 'a';
char* cp = &ch;
char** cpp = &cp;
char* p1[10];
test(cpp);
test(&cp);
test(p1);
//这三种均可以

四.函数指针

1.形式及介绍

void test(int a,int b);
void (*pf)(int,int);
此时pf就叫做函数指针,该指针指向test函数,返回值类型为void类型,参数为两个int类型的形参

2.两个有趣的代码


1.(*(void(*)())0)();
首先考虑我们要以0为突破点
所以我们摘出0那一部分的参数
1=(void(*)())0
(*1)()
注意:
void(*p)()-p是函数指针
void(*)()是函数指针类型
又因为void(*)()为:函数指针类型,该类型所指向的函数返回值为void,无参数
也就是说在这里:
1.把0强制类型转换为一个函数指针类型
2.然后调用0地址处的函数

2.void(*signal(int,void(*)(int)))(int);
首先我们还是把里面的东西(signal)先摘出来
1=signal(int,void(*)(int))
void(*1)(int)
又因为对于一个函数来说:
去掉其函数名跟形参部分,最终剩下的部分就是函数的返回值类型
所以这是一个函数signal,signal的返回值类型是一个函数指针,void(*)(int)类型
参数:第一个为int类型,第二个为一个函数指针:void(*)(int)类型
该题可以简化为下列形式:
typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
小小复习typedef:
typedef unsigned int uint;
typedef int* ptr_t;
typedef int(*parr_t)[10];//此时parr_t是一个数组指针类型
typedef int (*pf_t)(int, int);//此时pf_t是函数指针类型

五.函数指针数组

1.形式及介绍

先把函数指针的形式写出来
void test(int a,char b);
void (*pf)(int,char);//函数指针
void (*pf[10])(int,char);//函数指针数组

2.实例应用:计算器

1.未简化版(未应用函数指针数组)

int Add(int x, int y)
{
  return x + y;
}
int Sub(int x, int y)
{
  return x - y;
}
int Mul(int x, int y)
{
  return x * y;
}
int Div(int x, int y)
{
  return x / y;
}
void menu()
{
  printf("***********************************\n");
  printf("********0.exit     1.add    *********\n");
  printf("********2.sub      3.mul    *********\n");
  printf("********       4.div    *********\n");
  printf("***********************************\n");
}
void test04()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 0;
  //函数指针数组
  int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      printf("请输入两个操作数:");
      scanf("%d%d", &x, &y);
      ret = Add(x, y);
      printf("ret=%d\n", ret);
      break;
    case 2:
      printf("请输入两个操作数:");
      scanf("%d%d", &x, &y);
      ret = Sub(x, y);
      printf("ret=%d\n", ret);
      break;
    case 3:
      printf("请输入两个操作数:");
      scanf("%d%d", &x, &y);
      ret = Mul(x, y);
      printf("ret=%d\n", ret);
      break;
    case 4:
      printf("请输入两个操作数:");
      scanf("%d%d", &x, &y);
      ret = Div(x, y);
      printf("ret=%d\n", ret);
      break;
    case 0:
      printf("退出计算器\n");
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (input);
}

2.简化版

void test04 ()
{
  int input = 0;
  int x = 0;
  int y = 0;
  int ret = 0;
  //函数指针数组
  int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    if (input >= 1 && input <= 4)
    {
      printf("请输入两个操作数:");
      scanf("%d%d", &x, &y);
      ret = pfArr[input](x, y);
      printf("ret=%d\n", ret);
    }
    else if (input == 0)
    {
      printf("退出计算器\n");
    }
    else
    {
      printf("输入错误,请重新输入\n");
    }
  } while (input);
}

六.指向函数指针数组的指针

1.形式及介绍

指向函数指针数组的指针是一个指针,该指针指向一个数组,数组中的所有元素都是一个函数指针
int main()
{
  //int (*pf)(int, int);//函数指针
  int (*pfArr[])(int, int) = { Add,Sub,Mul,Div };//函数指针数组
  int(*(*ppfArr)[])(int, int);//指向函数指针数组的指针
  //int* pArr[] = { 1,2,3,4 };指针数组
  //int(*p)[];数组指针
}

以上就是征服C语言指针系列(2)的全部内容,希望能对大家带来帮助,谢谢大家!

相关文章
|
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
|
1月前
|
编译器 C语言
【c语言】指针就该这么学(2)
本文详细介绍了指针与数组的关系,包括指针访问数组、一维数组传参、二级指针、指针数组和数组指针等内容。通过具体代码示例,解释了数组名作为首元素地址的用法,以及如何使用指针数组模拟二维数组和传递二维数组。文章还强调了数组指针与指针数组的区别,并通过调试窗口展示了不同类型指针的差异。最后,总结了指针在数组操作中的重要性和应用场景。
19 0