详解C语言—进阶指针(一)

目录

指针简介:

1.字符指针

 例题:

2.指针数组

3.数组指针

 数组指针有什么用呢? 

辨析: 

4.数组传参和指针传参

一维数组传参: 

二维数组传参 :

5.函数指针

定义

调用 

 有趣的例子


指针简介:

  1. 指针变量就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针的大小是固定的4个或8个字节,分别对应32位和64位平台。
  3. 指针是有类型的,指针的类型决定了指针加减整数的步长和指针解引用操作的时候的权限。
  4. 指针可以运算。

1.字符指针

顾名思义,指向字符的指针,即char*。  

int main()
{
    //指向单个字符
    char ch='w';
    char *pc = &ch;
    printf("%c\n", *pc);
    //指向字符串
    char* p="hello CSDN!";
    printf("%s\n", *p);
    return 0;
}

 需要注意:指针变量p指向字符串hello CSDN!的首地址,也就是字符h的地址

 例题:

#include <stdio.h>
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和str2是两个不同的数组,创建时分别开辟不同的内存块,与其储存的内容没有关系。str3和str4指向的是同一个常量字符串,C/C++会把常量字符串存储到单独的内存区域,当  几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。

2.指针数组

  • 指针数组是存放指针的数组,储存的元素类型是指针。
  • 指针数组—本质是数组—是存放指针的数组
  • char* arr[5];—存放字符指针的数组
  • int* arr2[6];—存放整型指针的数组

 

3.数组指针

  •  数组指针本质是指针!!! 
  • 字符指针—指向字符的指针—存放字符的地
  • char ch = ‘a';
  • char* pc= &ch;
  • 整形指针—指向整型的指针—存放整形的地址
  • int num = 100;
  • int* p    = &num;
  • 数组指针—是指向数组的指针—存放数组的地址
int main()
{
	int arr[10] = { 0 };
	int (*p)[10] = &arr; //p用来存放数组的地址,p就是数组指针

	char* arr2[5];
	char* (*pc)[5] = &arr2;	//指向char*类型

	int arr3[] = { 1,2,3 };
	int(*p3)[3] = &arr3;//数组指针[]不能为空
	return 0;
}

 int  (*p)[10]; p首先与*结合,声明p是一个指针变量,然后指向的是一个大小为10个整型的数组。[ ]的优先级高于*,所以(*p)的( )不能省略。

 数组指针有什么用呢? 

void print_arr2(int (*arr)[5], int row, int col)
{
    int i = 0;
    for(i=0; i<row; i++)
   {
        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);
    return 0;
}

 

数组指针可以用来作为接收数组的参数

二维数组传参,传递的是首元素的地址,二维数组相当于每个元素都是一维数组的一维数组,传递的实际就是就是第一行的地址。

辨析: 

4.数组传参和指针传参

数组传参时:

  1. 传递的是首元素地址
  2. 一维数组传参,传递的是第一个元素的地址
  3. 二维数组传参,传递的是第一行的地址
  4.  数组传参的时候,形式可以写成数组也,可以写成指针

一维数组传参: 

 

以上形式传参都是正确的,数组传参本质是传递了数组首元素的地址,传参形式写成数组和指针都可以,建议写成指针。

二维数组传参 :

 

5.函数指针

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}

 

函数名和函数名的地址本质一样,都是函数的地址。 

定义

接下来我们学习如何定义函数指针。

int Add(int x, int y)
{	
    return x + y;
}

定义一个含有两个参数的Add函数,

int (*pf1)(int, int) = Add;
int (*pf1)(int x , int y) = Add;

在C或C++中,函数指针的参数名不是强制要求的,因此两种方式都可以用来定义一个函数指针,但在实际使用时,通常会省略参数名 

        pf1就是函数指针变量 ,*和pf1结合,说明pf1是指针类型,后面紧跟着的括号表示指针pf1指向函数的参数,只需写出对应类型即可。

        等于号后面可以写&Add也可以直接写Add,效果相同。

调用 

int ret = (* pf1)(2, 3);
int ret1=  pf1(2, 3);
int ret1=  Add(2, 3);

 调用时,以上三种效果相同。

第一种:通过(* pf1)调用函数Add,紧跟着的括号为传入函数的参数。

第二种与第三种相同。

解释:因为pf1指向函数Add,也就是储存Add的地址即pf1 == Add,正常调用函数时一般都直接写函数名加参数,即Add(2, 3);  所以我们可以将Add替换成pf1或直接写成Add,结果是一样的。

 有趣的例子

下面的代码是在调用0地址处的函数,这个函数没有参数,返回类型是void。 

( *(void (*)()) 0 )();

这样看更清楚一些 :

(  *(  void (*)()  ) 0 )  ();
  • void (*)() 是函数指针类型。
  • ( void (*)( ) ) 将“0”强制类型转换成函数指针类型。
  • *( void (*)( ) )0   *号 解引用函数指针,调用地址为0处的函数,
  • 这个函数没有参数,所以后面为( )什么都没有的括号。
  • 0地址处的函数被声明为返回 void 类型。因此,无论这个函数在地址0处做了什么,它都不会返回任何值(void),所以整个表达式的返回类型是 void

 我们在再来看一个例子:

void (* signal(int , void(*)(int) ) ) (int);
  • 这个代码是一次函数声明
  • 声明的是signal函数
  • signal函数有两个参数:
  • 第一个是int类型,第二个是函数指针类型,该指针指向的函数参数是int,返回类型是void;
  • signal函数的返回类型也是函数指针类型,该类型是void(*)(int),该函数指针指向的函数参数是int,返回类型是void。

 下面让我们进入C语言—进阶指针(二)的学习吧!!!

猜你喜欢

转载自blog.csdn.net/m0_73800602/article/details/132761704