第四周课程学习总结【关于函数的部分内容】

前言

本周主要学习了函数,下面是对本周内容的总结,另外有专门的合集归纳整理同类问题的不同解决方案,另外一些函数接口也可以留存以后使用。

函数

实现MyStrcat()、MyStrcpy() 、MyStrncpy() 、MyStrlen()接口

MyString.h

#include <stdbool.h>

/*
* MyStrcpy : 字符串拷贝
*/

bool MyStrcpy(char *dest, const char *src);
bool MyStrncpy(char *dest, const char *src, size_t n);

/*
* MyStrcat : 字符串拼接
*/

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

/*
* MyStrlen : 计算字符串有效长度
*/
int MyStrlen(const char *src);

MyString.c

#include <stdio.h>
#include <stdbool.h>

// 字符串拷贝

bool MyStrcpy(char *dest, const char *src)
{
    if(dest == NULL || src == NULL)
        return false;
    while(*src) // 只要没到0则继续遍历
   {
        *(dest++) = *(src++);
   }
    *dest = '\0';
    
    return true;
}

bool MyStrncpy(char *dest, const char *src, size_t n)
{
    if(dest == NULL || src == NULL)
        return false;
    int i;
    for(i = 0; i < n && src[i] != '\0'; i++)
   {
        dest[i] = src[i];
   }
    for(;i < n; i++)
   {
        dest[i] = '\0';
   }
    return true;
}

// 字符串合并

char *MyStrcat(char *dest, const char *src)
{
    if(dest == NULL || src == NULL)
        return NULL;
    // 定位dest末尾

    while(*dest)
   {
        ++dest;
   }  
    
    while(*src)
	{
        *dest++ = *src++; 
   	}
    *dest = '\0';
    return dest;
}

// 字符串长度

int MyStrlen(const char *src)
{
    if(src == NULL)
        return -1;
    
    int len = 0;
    while(*src++ != '\0')
   {
        len++;
   }
    return len;
}

main.c

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

int main(int argc, char const *argv[])
{
    char src[5] = "jack";
    char dest[10] = "ken";
    // 字符串拷贝
    //MyStrcpy(dest,src);
    //MyStrncpy(dest, src, 3);
    //printf("dest : %s\n",dest);

    MyStrcat(dest,src);
    printf("dest:%s\n",dest);
    printf("dest len : %d\n",MyStrlen(dest));
    return 0;
}

编译

gcc main.c MyString.c
./a.exe

gcc 编译器编译原理

1.将c文件进行预编译,作用是将头文件里面的内容进行展开,就是将头文件的内容拷贝到main函数上面

 gcc main.c -o main.i -E

2.生成汇编程序,作用是将c文件编译成更加底层的编程语言汇编程序

 gcc main.i -o main.s -S

3.将汇编程序生成未链接的可执行程序

 gcc main.s -o main.o -c

4.将未连接的可执行程序生成已链接的可执行程序

 gcc main.o -o main.exe

typedef 给变量取别名,加分号
define 给常量取别名,不加分号

考点:宏定义与typedef的区别

在预编译时,宏会进行宏替换

typedef是在编译时转换

C进程内存布局

在这里插入图片描述

静态数据(面试必考)

  • c语言中,静态数据有两种:
    • 全局变量:定义在函数外的变量
    • 静态局部变量:定义在函数内部,且被static修饰的变量
  • 为什么需要静态数据
    1. 全局变量在默认的情况下,对所有文件可见,为某些需要在各个不同文件和函数之间访问的数据提供操作 上的方便。
    2. static修饰的全局变量,只能在本文件使用,如果未被static修饰的全局变量,所有的文件都能使用,会出 现命名污染。
    3. 当我们希望一个函数退出后依然能保留局部变量的值,以便于下一次调用时还能用,静态局部变量可以帮 助实现这样的功能。

堆内存(重点,一定要掌握)

  • 堆内存基本特征:
    • 相比栈内存,堆的总大小仅受限于物理内存,在物理内存允许的范围内,系统对堆内存的申请不做限制。
    • 相比栈内存,堆内存从下往上增长。
    • 堆内存是匿名的,只能由指针来访问。
    • 自定义分配的堆内存,除非开发者主动释放,否则永不释放,直到程序退出。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    // 申请堆空间,可以存放100个char类型的数据

    char *str = malloc(sizeof(char)*100);
    // str指向字符串常量,写法异常

    //str = "jack";

    // 将字符串进行拷贝

    memcpy(str,"jack",5);
    str[2] = 'g';
    printf("%s\n",str);
    return 0;
}

【malloc、calloc、realloc】

在这里插入图片描述

函数指针(重点)

int func(int a, int b);
// 函数指针,指向的函数类型为返回值为int 参数为(int,int)的函数
int (*pfunc)(int a, int b);

#include <stdio.h>

typedef int int32_t;
// 给函数指针该别名,方便使用,增加指针的易用性

// 此时fptr就相当于void (*fptr)(int *a, int *b)的类型
typedef void (*fptr)(int *a, int *b);// 类似于 int类型 int a = 10

typedef int (*fMaxPtr)(int,int);// 函数指针的参数可以只写变量类型

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

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

int main(int argc, char const *argv[])
{
    int a = 10 , b = 20;
    // 定义函数指针指向swap
	// 注意pfunc指针的类型与所指向的函数类型一致
	// 将函数名去掉,剩下的部分为函数的类型

    void (*pfunc)(int *a, int *b) = swap;
    printf("%p\n",swap);
    printf("%p\n",pfunc);
    // 2. 将函数指针改别名
	fptr p = swap;
    printf("%p\n",p);
    // 通过函数指针执行swap函数
	//p(&a,&b);
	pfunc(&a,&b);
    printf("%d,%d\n",a,b);
    // 定义函数指针指向max函数,实现比较最大值
	fMaxPtr fmax = max;
    printf("max = %d\n",fmax(19,29));
    return 0;
}

函数指针数组

#include <stdio.h>

typedef int (*pfunc[2])(int, int);

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

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

int main(int argc, char const *argv[])
{
    int a = 10, b = 20;
    
    //定义函数指针类型
    //int (*p[2])(int, int) = {max,min}; 
    pfunc p = {max,min};
    printf("%d\n",p[0](a,b));
    printf("%d\n",p[1](a,b));
    
    return 0;
}

回调函数

在这里插入图片描述

回调函数实现冒泡排序见冒泡合集~~

内联函数 inline

  • 内联的特性 :以空间换时间

  • 当编译器发现某段代码有inline关键字的时候就会将这段代码插入到当前的位置,加快运行效率,但是也会消耗 一定的运行空间

  • 什么时候用inline

  • 函数需要频繁被调用,代码最好不要超过5行

  • inline注意事项

    内联函数在头文件实现,其它函数不要在头文件实现

    函数声明和函数实现都需要添加关键字inline,如果函数声明没有添加extern 和 inline 关键字,会报错

    练习 : 通过内联函数实现获取两个数的最大值

    #ifndef _INLINE_H
    #define _INLINE_H
    #include <stdio.h>
    
    // 内联函数需要extern声明,并加上inline关键字
    
    extern inline int max(int,int);
    
    #endif
    
    #include "inline.h"
    
    inline int max(int a, int b)
    {
        return a > b ? a : b;
    }
    
    int main(int argc, char const *argv[])
    {
        printf("%d\n",max(10,5));
        return 0;
    }
    
    

递归函数(自己调用自己)

  • 递归概念:如果一个函数内部,包含了对自身的调用,则该函数称为递归函数。
自己调用自己,注意需要有结束条件,否则会出现内存溢出(段错误)
什么时候用到递归  : 需要满足有规律递减条件,递减到某个程度是可以退出的

 func()
 {
 func()
 }
#include <stdio.h>

void func(int n)
{
    // 递归退出条件

    if(n == 6)
        return;
    func(++n);
    printf("%d\n",--n);
}

int main(int argc, char const *argv[])
{
    int n = 1;
    func(n);
    return 0;
}
//阶乘
#include <stdio.h>

int func(int n)
{
    // 退出条件

    if(n == 1)
        return 1;
    return func(n-1)*n;
}

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

//字符串翻转
#include <stdio.h>

void reverseToStr(void)
{
    char ch = getchar();
    if(ch == '\n')
        return ;
    reverseToStr();
    printf("%c",ch);
}

int main(int argc, char const *argv[])
{
    reverseToStr();
    return 0;
}

以上就是我对本周学习内容的总结,函数的很多内容都是重点,需要多练习多刷题巩固。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值