Linux及C语言高级[6](C语言指针)

258 阅读5分钟

“这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

1指针

📍下述的概念在使用的时候统称为指针。

  • 地址:内存单元的编号
  • 指针:存放地址的内存单元就叫做指针
  • 指针变量:存储地址的变量,就叫做指针变量

📢指针指向的那一块内存就是四个字节

1.1指针的定义格式

  • char *p; //字符指针
  • short *q; //短整型指针
  • int *t; //整型指针
  • float *w; //浮点类型的指针
  • void *l; //实例如下
  • //char a = 100; l = (void *)&a;
  • //int b = 200; l = (void *)&b;
  • char *:指针的类型
  • p :指针变量,赋的值是地址
  • [* :在定义指针的时候,这个*只是表示p是一个指针变量,
  • 除此之外没有其他含义]
  • &p :取p指针变量的地址
  • 指针内存: 在32位操作系统上指针都是占用的4个字节
  • 指针的类型:表示指向内存单元中数据的类型。

1.2指针的用法

int a = 10;

int b;

int *p; //这里的*只是标识p是一个指针变量

p = &a;

*p = 100; //==>向p指向的内存单元中写入100的值

b = *p; //==>读取p指向的内存单元中的数据,并赋值给b;

1.3指针的经典用法

void swap(int a,int b)  //不能交换
{
    a ^=b;
    b ^=a;
    a ^=b;
}
void swap1(int *a,int *b) //可以交换
{
    *a ^=*b;
    *b ^=*a;
    *a ^=*b;

}

> > 📝练习1:实现函数mystrcpy

#include <stdio.h>
void mystrcpy(char *p,char *q) 
{
    while(*p){
    *q++ = *p++;
    }
    *q = '\0';
}
int main(int argc, char const *argv[])
{
    char a[50] = {0};
    char b[50] = {0};
    printf("input:");
    scanf("%[^\n]",a);
    mystrcpy(a,b);
    printf("b=%s\n",b);
    return 0;
}

> > 📝练习2:请输入一个字符串,将字符串中的空格删除

#include <stdio.h>

void mystrcpy(char *p,char *q)
{
    while(*p){
        *q++ = *p++;
    }
    *q = '\0';
}

void DeleteSpace(char *a)
{
    char *tmp;
    while(*a){
        if(*a != ' '){
            a++;
            continue;
        }else{
            tmp = a;
            while(*a == ' ' && *a !='\0')a++;
            mystrcpy(a,tmp);
            a = tmp;
            if(*a=='\0')continue;
        }
        a++;      
    }
}

void DeleteSpace1(char *a) //改进
{
    char *tmp;
    while(*a){
        if(*a == ' '){
            tmp = a;
            while(*a == ' ')a++;
            mystrcpy(a,tmp);
            a = tmp;
            if(*a=='\0')continue;
        }
        a++;
    }
}

int main(int argc, const char *argv[])
{
    char a[50] = {0};
    printf("input string > ");
    scanf("%[^\n]",a);
    DeleteSpace(a);
    printf("a = %s\n",a);
    return 0;
}

> > 📝练习3:请输出一个字符串"this is a book",使用指针

#include <stdio.h>
#include <string.h>

void swap(char **a,char **b)
{
    while(*a < *b){ 
    **a = **a^**b;
    **b = **a^**b;
    **a = **a^**b;
    (*a)++;
    (*b)--;
    }
    return;
}

void change_word(char *p)
{
    char *a,*b,*c;
    int len = strlen(p);
    a = p;
    b = p + len -1;
    swap(&a,&b);
    a =p;
    b =p;
    while (*b){ //这里为何为*b
        while((*b != ' ') && *b){
            b++;
        }
        c = b--;
        swap(&a,&b);
        if (*c == '\0')break;
        a = b = c+1; 
    }
    printf("%s\n",p);
}

int main(int argc, char const *argv[])
{
    char s[100];
    printf("请输入字符串:");
    scanf("%[^\n]",s);
    change_word(s);
    return 0;
}

1.4什么是野指针?

  • 在定义完指针之后,没有对指针赋值,这个指针是随机值,此时这个指针就是野指针。

//全局变量野指针为0

如何规避野指针?char *p = NULL;

2指针数组

  • 指针数组:它本身是一个数组,数组中的成员是指针

  • 定义格式如下: char *p[3]; int *t[3];

[3]:有三个成员
p :是有三个char *成员的数组
p[0] = "hello";   //将字符串的首地址赋给 p[0]指针 
p[1] = "list";,
p[2] = "tttt";
char *:数组中成员的类型

在定义主函数时,使用快捷键 main + tab键,括号内的含义

命令行:
./a.out hello world ttt 
-----------------------------------------
int main(int argc, const char *argv[])
             |                 |
     命令行参数的个数  指针数组,表示是哪个成员

argc = 4;
argv[0] = "./a.out"; //对应字符串的首地址
argv[1] = "hello"
argv[2] = "world"
argv[3] = "ttt"

3二级指针

  • 二级指针:指向指针的指针就是二级指针

  • 定义格式: char **p; int **q;

**:只是一个标识符号(在定义变量的时候加的**)

p :是一个二级指针

char**:它是指针的类型

  • 如何使用二级指针?
int a=10;
int *p = &a; 
int **q = &p; 

关系图.png

📝练习:地址加加后的含义?

p++   : p向后移动了四个字节0x11223348
*p++  : 取出10的值,然后执行p++ ,p=0x11223348
(*p)++: 将a内存中的值修改为11
q++   : q向后移动四字节0xaabbcce2,p是没有改变的
*q++  : 取出p的地址,然后q向后移动四字节
(*q)++: p向后移动了四个字节0x11223348
**q++ :  取出a中10的值,然后q向后移动4字节
(**q)++:将a内存中的值修改为11
void  my_malloc(char **s)  
{  
    *s=(char*)malloc(100);  
}  
 
void  main()  
{  
    char  *p=NULL;  
    my_malloc(&p);
    //do something
    if(p)
    free(p);  
} 

二级指针的用法,大佬的讲解

4数组指针

  • 📢数组指针:它是一个指针,它是一个行指针

  • 定义格式:

(char [3]) *p;

(char [3]) a[4];

char (*p)[3]; //有括号的后读括号 ,类型是 char (*)[3]

int (*t)[3];

📑上述的p和t都占4字节,因为他们都是指针

p++ 移动 sizeof(char) * 3 t++ 移动 sizeof(int) * 3

📍实例:

char a[4][3] = {1,2,3,4,5,6,7,8,9,10,11,12};

char (*p)[3];

p = a;

📌注:以后涉及到数组指针的时候,就把它转化成二维数组

 **a[i][j]<===>*(a[i]+j)<==>*(*(a+i)+j)**
 **p[i][j]<===>*(p[i]+j)<==>*(*(p+i)+j)**

📝练习: short a[5][4],(*p)[4]=a;数组a的首地址为100

*(p+2)+3 = &a[2][3]等于?; ☞122

5指针函数

  • 指针函数:返回值是指针的函数就是指针函数

char *func(void);

char * strcat(char *dest, const char *src) //strcat返回拼接后的字符串的首地址

6函数指针

  • 函数指针:它是一个指针,这个指针指向函数

  • 定义格式: char (*func)(int a,int b);

  • 如何使用?

int add(int a,int b)
{
    return (a+b);
}

int main(int argc, const char *argv[])
{
    int (*func)(int ,int ) = add;
    printf("sum = %d\n",func(100,200));
    return 0;
}

🔎func是一个指向有两个int参数和一个int类型返回值的函数指针。

func = add;

add是函数的名字,本身就是函数的首地址可以直接给func赋值

📝练习:函数指针作为函数的参数?(回调函数)

int add(int a,int b)
{
    return (a+b);
}

int sub(int a,int b)
{
    return (a-b);
}

void show(int a,int b,int (*func)(int ,int )) 
{
    printf("sum = %d\n",func(a,b));
}

int main(int argc, const char *argv[])
{					
    show(100,200,add);//📢add就是函数add的地址
    show(100,200,sub);
    return 0;
}

7函数指针数组

  • 函数指针数组:它是一个数组,数组中存放的是函数指针

  • 定义格式: int (*func[2])(int ,int);

func[0] :存放的int (*)(int,int)的函数指针

func[1] :存放的int (*)(int,int)的函数指针

int (*func[2])(int ,int) = {add,sub};   
   // func[0] = add;
   // func[1] = sub;
        
    printf("sum = %d\n",func[0](100,200));
    printf("sub = %d\n",func[1](100,200));

8结构体指针数组,结构体数组指针

8.1结构体指针数组

typedef struct{
    char name[20] ;
    int age;
    char sex; 
    int score;
    struct aa t;
}stu_t;
	
stu_t *p[3];  //结构体指针数组
p[0] = malloc(sizeof(stu_t));
p[1] = malloc(sizeof(stu_t));
p[2] = malloc(sizeof(stu_t));

8.2结构体数组指针

stu_t (*p)[3]; //它是一个指针,指向一行。
stu_t arr[4][3];
p = arr;