C语言个人笔记分享(2)

指针

认识指针

指针 == 地址

指针就像是拿着快递取件码去快递站取快递

变量访问的两种方式
  1. 变量名
  2. 地址

指针变量 == 存放地址的变量

指针变量存放地址

如何定义一个指针变量

*的标识作用:只产生在指针变量定义或者声明的时候

如何使用一个指针变量

*

变量访问的两种方式

为什么需要指针

练习:封装一个函数,实现;两个数的交换

练习:指针指向固定的区域(例如:单片机、armbootloader)

作业

 输入三个数a,b,c; 要求不管怎么输入,在输出的时候,a,b,c就是由大到小的顺序输出,用函数封装实现

通过指针引用数组

定义一个指针变量指向数组
指针变量保存了数组的首地址

指针增量和数组的关系

通过指针引用数组元素

下标法

a[0],a[1],a[2].......a[i]

指针法

偏移

偏移以后的内存还是指针,地址偏移一个字节数(int   or   char)

*p++

取内容

指针常量不可以++,指针变量可以++

只要是指针,就用8个字节表示地址

两种方法效率对比

练习1

函数封装数组初始化,遍历

练习2

将数组中的n个元素按逆序存放

#include <stdio.h>

void Input(int *p,int size)
{
    int i;
    for(i=0;i<size;i++)
    {
        printf("请输入第%d个数\n",i+1);
        scanf("%d",p++);
    }
}

void Output(int *p,int size)
{
    int i;
    for(i=0;i<size;i++)
    {
        printf("%d ",*p++);
    }
}

void Revers(int *p,int size)
{
    int i;
    int j;
    int cup;
    
    for(i=0;i<size/2;i++)
    {
        j = size-1-i;
        cup = *(p+i);
        *(p+i) = *(p+j);
        *(p+j) = cup;
    }
}

int main()
{
    int a[5];
    int size = sizeof(a)/sizeof(a[0]);
    
    Input(a,size);
    putchar('\n');
    Output(&a[0],size);
    putchar('\n');
    Revers(a,size);
    Output(&a[0],size);
    
    return 0;
}

指针和二维数组

二维数组 → 父数组+子数组 → 二维数组也是数组,只不过是数组元素也是数组(子数组)

思考问题1
a是谁的地址,a[0]又是什么,那*a或者*(a+0)呢

a表示父数组的地址a[0],*a表示子数组的地址

思考问题2
a[0]+1又是什么意思呢?

认知

总结

数组指针

int(*p)[4] → 指向一个含有4个元素的数组

输出二位数组任意行列的数

函数指针

定义“函数地址”

函数名就是地址         数组名就是地址

如何定义一个函数指针变量

使用函数指针
函数调用概念和变量一样

直接访问:变量名(函数名)        间接访问:指针(函数指针)

好用之处
根据程序运行过程的不同情况,调用不同的函数————java接口
练习

#include <stdio.h>

int getmax(int a,int b)
{
    return a>b ? a:b;
}

int getmin(int a,int b)
{
    return a<b ? a:b;
}

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

int dataReceive(int a,int b,int (*p)(int a,int b))
{
    int receive;
    
    receive = (*p)(a,b); 
    
    return receive;
}

int main()
{
    int a=10;
    int b=20;
    int x;
    int result;
    int (*p)(int a,int b);
    
    printf("输入1取最大值,输入2取最小值,输入3求和\n");
    scanf("%d",&x);
    switch(x)
    {
        case 1: p = getmax; break;
        case 2: p = getmin; break;
        case 3: p = getsum; break;
        default: puts("请输入正确的指令"); exit(-1); break;    
    }
    result = dataReceive(a,b,p);
    printf("结果:%d\n",result);
    
    return 0;
}

回调函数的底层逻辑
线程

int pthread_create(pthread_t *id,const pthread_attr_t *attr,   void*(*start_rtn)(void*),   void *restrict arg);

QT的信号与槽

指针数组

定义:注意和数组指针的区别,面试题会考

工作规则写法:*(p[i]),因为括号的优先级高

他是数组,数组的每一项都是一个指针变量

指针函数(返回指针值的函数)

概念

练习

作业

#include <stdio.h>

int* getposperson(int pos,int (*pstu)[4])
{
    int *p;
    p = (int *)(pstu + pos);
    return p;
}

int main()
{
    int scores[3][4] = 
    {
        {53,88,59,92},
        {86,85,70,95},
        {94,30,66,78},
    };
    int *ppos;
    int pos;
    
    printf("请输入学号:\n");
    
    scanf("%d",&pos);
    
    ppos = getposperson(pos,scores);
    
    int flag;
    for(int i=0;i<4;i++)
    {
        printf("%d ",*ppos++);
        if(*ppos < 60)
        {
            flag = 1;
        }
    }
    putchar('\n');
    
    if(flag == 1)
    {
        printf("%d号学生有不及格\n",pos);
    }
    else
    {
        puts("该学生没有课程不及格");
    }
    
    return 0;
}

二级(多级)指针

写法

int **p

差别

保存的是指针变量(int *p)的地址,也就是一级指针的地址

通过函数调用来修改调用函数指针指向的时候,就像是通过函数调用修改某变量的值的时候一样

二级指针不能简单粗暴指向二维数组

总结

中小公司大概率考题

字符串

定义介绍

换个角度来说,其实就是字符数组

定义的几种格式

和整型数组在储存上的区别

字符数组的存储方式

字符串和字符数组的区别就在于字符串结尾有一个 ’\0‘ 标志

sizeof和strlen的区别

strlen :计算有效字符的长度,不加入计算结束标志

p是一个char *,sizeof计算的时候,得出的是计算机用多少个字节来表示一个地址

动态开辟字符串(按需申请空间)

1、在函数调用结束后栈内数据被清理掉,回收内存;堆只有在函数结束后才释放

2、p = NULL;将指针p设置为NULL,即空指针。在C语言中,NULL是一个宏定义,表示一个空指针常量,通常用来表示指针不指向任何有效的内存地址。将指针设置为NULL可以用来表示指针当前不指向任何有效的对象或内存位置,这在程序中是一种常见的做法,以避免指针悬挂(dangling pointer)或野指针(wild pointer)的问题。

malloc函数 (void *malloc(sizeof_t size))

 1、在堆上面进行动态开辟的字符串(普通的数组、变量在栈上开辟空间)

2、一直while循环会耗尽堆上的数据资源,所以不用malloc要释放内存空间

3、分配所需的内存空间,并返回一个指向它的指针

4、sizeof_t相当于int型(unsigned int)的意思

5、size是开辟的空间大小

free (void free(void *ptr))

1、释放之前调用calloc、malloc、realloc所分配的内存空间

2、释放内存,防止内存泄漏

3、防止悬挂指针,也就是野指针

realloc (void *realloc(void *ptr,size_t size))

扩容

尝试重新调整之前调用的malloc或者calloc所分配的ptr所指向的内存块的大小

memset(void *memset (void *str,int c,size_t n))

几种字符串常用的API

输出字符串

puts ( );       printf ("%s",p);

获取字符串
scanf("%s",p);
gets

char * gets(char * str);

因为本函数可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值

计算长度

strlen

拷贝

#inclued  <string.>字符串拷贝函数的头文件

strcpy

char *strcpy(char *dest,const char *src);

src表示从哪里拷贝,dest表示拷贝到哪里去

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

char *mystrcpy(char *des,char *src)
{
    if(des == NULL || src == NULL)
    {
        return NULL;
    }
    char *back = des;
    while(*src != '\0')
    {
        *des = *src;
        des++;
        src++;
    }
    *des = '\0';
    
    return back;
}

char *mystrcpy2(char *des,char *src)
{
    if(des == NULL || src == NULL)
    {
        return NULL;
    }
    char *back = des;
    while(*src != '\0')
    {
        *des++ = *src++;
    }
    *des = '\0';
    
    return back;
}

char *mystrcpy3(char *des,char *src)
{
    if(des == NULL || src == NULL)
    {
        return NULL;
    }
    char *back = des;
    while((*des++ = *src++)!= '\0')
    *des = '\0';
    
    return back;
}

int main()
{
    char str[128] = {'\0'};
    char *p = "abcdefg";
    
    strcpy(str,p);
    puts(str);
    
    return 0;
}
strncpy

char *strncpy(char *dest,const char *src,int)

表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并且返回被复制后的dest

断言

assert

缺点:频繁调用会极大地影响程序的性能,增加额外的开销

拼接
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

char *mystrcat(char *des,char *src)
{
    assert(des != NULL && src != NULL);
    char *back = des;
    
    while(*des != '\0')
    {
        des++;
    }
    while((*des++ = *src++) != '\0');
    *des = '\0';
    
    return back;
}

char *mystrcat2(char *des,char *src)
{
    char *back = des;
    strcpy(des + strlen(des),src);
    
    return back;
}

int main()
{
    char str[128] = "tang shuai ";
    char *p = "shi ni ba ba";
    char *p2;
    
 p2 = mystrcat2(str,p);
    strcat(str,p);
    puts(str);
    puts(p2);
    
    return 0;
}

strcat  (char *strcat(char *dest,const char *src))

把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针

比较 (比较字节的大小)

查找子字符

strchr(char *strchr(const char *str,int c))

在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置

查找字串

strstr(char *strstr(char *str1, const char *str2))

返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL

字符串分割

strtok(char *strtok(char *str, const char *delim))

分解字符串 str 为一组字符串,delim 为分隔符

特别要注意分割处理后原字符串 str 会变,原字符串的改动是切分符原位置均更改为 '\0'

结构体

初识

为什么要用结构体

整型数,浮点型数,字符串是分散的数据表示

有时候我们需要用很多类型的数据来表示一个整体,比如学生信息

数组:数组是元素类型一样的数据集合

如果需要元素类型不同的数据集合,就要用到结构体了

定义一个结构体

它算是一个模板,一般不给赋具体的值,每一项在实际应用中并不是都要使用

成员列表(域表)

每个成员都是结构体中的一个域

声明

在声明的同时定义变量,但是尽量少用

初始化一个结构体变量并引用
初始化

引用

例题

例题:输入两个学生的名字,学号,成绩,输出成绩高的学生的信息

重点认识

结构体没什么特殊的,只是把变量藏在结构体里面,而内部的变量,以前学习的东西是通用的,只是“触达方式”不同

例题:输入两个学生的名字,学号,成绩,输出成绩高的学生的信息

结构体数组

练习:选票系统
#include <stdio.h>
#include <string.h>

struct XuanMin
{
    char name[32];
    int tickets;
};

int main()
{
    struct XuanMin toupiao[3];
    struct XuanMin max;
    
    int i;
    int length = sizeof(toupiao)/sizeof(toupiao[0]);
    int j;
    int feipiao = 0;
    int mark = 0;
    
    int total = 5;
    char tmpName[32];
    
    for(i=0;i<length;i++)
    {
        toupiao[i].tickets = 0;
        printf("请输入第%d个选民的名字:\n",i+1);
        scanf("%s",toupiao[i].name);
    }
    
    for(i=0;i<total;i++)
    {
        mark = 0;
        printf("你投票给谁:\n");
        memset(tmpName,'\0',sizeof(tmpName));
        scanf("%s",tmpName);
        for(j=0;j<length;j++)
        {
            if(strcmp(tmpName,toupiao[j].name) == 0)
            {
                toupiao[j].tickets++;
                mark = 1;
            }
        }
        if(mark == 0)
        {
            printf("弃票\n");
            feipiao++;
        }
    }
    for(i=0;i<length;i++)
    {
        printf("名字:%s,票数:%d\n",toupiao[i].name,toupiao[i].tickets);
    }
    max = toupiao[0];
    for(i=0;i<length;i++)
    {
        if(max.tickets < toupiao[i].tickets)
        {
            max = toupiao[i];
        }
    }
    printf("%s以%d票当选!!!\n废票%d张\n",max.name,max.tickets,feipiao);
    
    return 0;
}

结构体指针

概念

通过结构体指针访问结构体

通过结构体指针间接访问用 "-->"

结构体数组,指针,函数应用
#include <stdio.h>
#include <string.h>

struct XuanMin
{
    char name[32];
    int tickets;
};

int main()
{
    struct XuanMin toupiao[3];
    struct XuanMin *p = toupiao;
    struct XuanMin max;
    
    int i;
    int length = sizeof(toupiao)/sizeof(toupiao[0]);
    int j;
    int feipiao = 0;
    int mark = 0;
    
    int total = 5;
    char tmpName[32];
    
    for(i=0;i<length;i++)
    {
        toupiao[i].tickets = 0;
        printf("请输入第%d个选民的名字:\n",i+1);
        scanf("%s",p->name);
        p++;
    }
    p = toupiao;
    for(i=0;i<total;i++)
    {
        mark = 0;
        printf("你投票给谁:\n");
        memset(tmpName,'\0',sizeof(tmpName));
        scanf("%s",tmpName);
        for(j=0;j<length;j++)
        {
            if(strcmp(tmpName,p->name) == 0)
            {
                p->tickets++;
                mark = 1;
            }
            p++;
        }
        if(mark == 0)
        {
            printf("弃票\n");
            feipiao++;
        }
        p = toupiao;
    }
    p = toupiao;
    for(i=0;i<length;i++)
    {
        printf("名字:%s,票数:%d\n",p->name,p->tickets);
        p++;
    }
    max = toupiao[0];
    p = toupiao;
    for(i=0;i<length;i++)
    {
        if(max.tickets < p->tickets)
        {
            max = toupiao[i];
        }
        p++;
    }
    printf("%s以%d票当选!!!\n废票%d张\n",max.name,max.tickets,feipiao);
    
    return 0;
}

选民系统小综合

共用体/联合体

概念

有时候同一个内存空间需要存放类型的不同,不同类型的变量需要共享一块空间

与结构体的区别

1、结构体元素有各自单独空间 ;共用体元素共享空间,空间大小有最大类型确定

2、结构体元素互不影响 ;共用体赋值会导致覆盖

3、联合体的地址多大取决于联合体中数据的最大空间数

#include <stdio.h>



struct Test1

{

int data1;

char data2;

float data3;

};



union Test2

{

int data1;

char data2;

float data3;

};



int main()

{

struct Test1 t1;

struct Test1 *p0 = &t1;

union Test2 t2;

union Test2 *p1 = &t2;



printf("%d\n",sizeof(t1));

printf("%d\n",sizeof(t2));



printf("%p\n",&p0->data1);

printf("%p\n",&p0->data2);

printf("%p\n",&p0->data3);



printf("%p\n",&p1->data1);

printf("%p\n",&p1->data2);

printf("%p\n",&p1->data3);



putchar('\n');



printf("%p\n",&t1.data1);

printf("%p\n",&t1.data2);

printf("%p\n",&t1.data3);



printf("%p\n",&t2.data1);

printf("%p\n",&t2.data2);

printf("%p\n",&t2.data3);



return 0;

}

证明:

结构体在不同的地址空间内(独立空间)

共用体/联合体在同一空间地址下

注意数据覆盖

由于联合体的空间特殊性,上一个数据会被下一个数据覆盖掉

应用

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

struct Person
{
    int age;
    char name[32];
    char occupation;
    union
    {
        int class;
        char subject[12];
    }mes;
};

int main()
{
    int i;
    struct Person p[2];
    
    for(i=0;i<2;i++)
    {
        printf("请输入职业:;'t'代表老师,'s'代表学生\n");
        scanf("%c",&(p[i].occupation));
        if(p[i].occupation == 't')
        {
            printf("请输入老师教的科目:\n");
            scanf("%s",p[i].mes.subject);
            printf("请输入老师的名字:\n");
            scanf("%s",p[i].name);
        }
        else
        {
            printf("请输入学生的班级:\n");
            scanf("%d",&(p[i].mes.class));
            printf("请输入学生的名字:\n");
            scanf("%s",p[i].name);
        }
        getchar();
    }
    for(i=0;i<2;i++)
    {
        if(p[i].occupation == 't')
        {
            printf("老师教的科目是:%s\n老师的名字是:%s\n",p[i].mes.subject,p[i].name);
        }
        else
        {
            printf("学生的班级是:%d\n学生的名字是:%s\n",p[i].mes.class,p[i].name);
        }
    }
    
    return 0;
}

枚举类型

什么是枚举类型

如果一个变量只有规定的几种可能的值,比如星期一、二、三 ……

enum Weekday {Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};

怎么定义枚举类型

列表中的名字,可以自己定义,无需像变量一样去申请

C编译器把它当成常量处理,也称枚举常量

枚举变量
只限列表中的集中情况

值默认从0开始,枚举元素不能被赋值,虽然看着像变量

可以指定列表中枚举数的值

可直接忽略枚举类型名,直接定义枚举变量

例子

typedef关键字

给已有的变量类型换名字(OS:换一个简单的名字)
一般配合结构体,简单方便,不要每次都用struct开头
#include <stdio.h>

typedef unsigned int u8;
typedef int u16;
typedef int arr[10];

struct Test
{
    int data1;
    int data2;
};

typedef struct Test T;

typedef struct
{
    int data1;
    int data2;
}Demo;

void printInto(T t)
{
    printf("%d ",t.data1);
}

int main()
{
    arr a;
    a[0] = 10;
    printf("%d\n",a[0]);
    
    struct Test t1;
    t1.data1 = 100;
    printf("%d ",t1.data1);
    
    T t2;
    t2.data1 = 1000;
    printInto(t2);
    
    Demo d;
    d.data2 = 999;
    printf("%d ",d.data2);
    
    return 0;
}

案例
#include <stdio.h>

typedef struct
{
    int num;
    char name[32];
    char sex;
}Person,*pPerson;

void printInto(Person p)
{
    printf("%d号是:%s %c\n",p.num,p.name,p.sex);
}

void printInto2(pPerson pp)
{
    printf("%d号是:%s %c\n",pp->num,pp->name,pp->sex);
}

void printInto3(Person *p)
{
    printf("%d号是:%s %c\n",p->num,p->name,p->sex);
}

int main()
{
    Person p1 = {1,"小美",'g'};
    Person p2 = {2,"小帅",'m'};
    printInto(p1);
    printInto(p2);
    
    pPerson pp1 = &p1;
    pPerson pp2 = &p2;
    printInto3(pp1);
    printInto2(pp2);
    
    Person *pp3 = &p1;
    Person *pp4 = &p2;
    printInto3(pp3);
    printInto3(pp4);
    
    return 0;
}

 

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值