前言
本周主要学习了函数,下面是对本周内容的总结,另外有专门的合集归纳整理同类问题的不同解决方案,另外一些函数接口也可以留存以后使用。
函数
实现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修饰的变量
- 为什么需要静态数据
- 全局变量在默认的情况下,对所有文件可见,为某些需要在各个不同文件和函数之间访问的数据提供操作 上的方便。
- static修饰的全局变量,只能在本文件使用,如果未被static修饰的全局变量,所有的文件都能使用,会出 现命名污染。
- 当我们希望一个函数退出后依然能保留局部变量的值,以便于下一次调用时还能用,静态局部变量可以帮 助实现这样的功能。
堆内存(重点,一定要掌握)
- 堆内存基本特征:
- 相比栈内存,堆的总大小仅受限于物理内存,在物理内存允许的范围内,系统对堆内存的申请不做限制。
- 相比栈内存,堆内存从下往上增长。
- 堆内存是匿名的,只能由指针来访问。
- 自定义分配的堆内存,除非开发者主动释放,否则永不释放,直到程序退出。
#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;
}
以上就是我对本周学习内容的总结,函数的很多内容都是重点,需要多练习多刷题巩固。