10天学会嵌入式技术之c语言-Day-10

第十四章 动态内存分配

14.1 c程序内存分配

动态内存分配指根据需要向系统申请所需大小的空间,由于未在声明部分定义其为变量
或者数组,不能通过变量名或者数组名来引用这些数据,只能通过指针来引用。
 

14.2 void指针(无类型指针)

(1)void 指针介绍

 C99 允许定义一个类型为 void 的指针变量,它可以指向任何类型的数据。

(2)void 指针作用

指针变量必须有类型,否则编译器无法知道如何解读内存块保存的二进制数据。但是,
有时候向系统请求内存的时候,还不确定会有什么类型的数据写入内存,需要要先获得内存
块,稍后再确定写入的数据类型。
这种情况下就可以使用 void 指针,它只有内存块的地址信息,没有类型信息,等到使
用该块内存的时候,再向编译器补充说明,里面的数据类型是什么。

(3)void 指针特点

        1)void 指针与其他所有类型指针之间是互相转换关系,任一类型的指针都可以转为
void 指针,而 void 指针也可以转为任一类型的指针。
        2)由于不知道 void 指针指向什么类型的值,所以不能用 * 运算符取出它指向的值
(解引用)。

(4)代码示例

#include<stdio.h>

int main()
{
    int num=42;
    double pi=3.1415;

//int指针转为void指针
void*vdPtr=&pi;//隐式类型转换

//void 指针转为int 指针并解引用
//int *inPtr=(int *)viPtr;// 显示类型转换
printf("整数值:%d \n", *intPtr);

// void 指针转换为 double 指针并解引用
// double *doublePtr = vdPtr; // 隐式类型转换
double *doublePtr = (double *)vdPtr; // 显式类型转换
printf("浮点数:%f \n", *doublePtr);

// void 指针不能解引用 会报错
// printf("%d", *viPtr);
// printf("%d", *vdPtr);

return 0;

}

整数值:42
浮点数:3.141590

(5)友情提示

        1)其他类型指针赋给 void 指针,使用隐式转换即可,因为 void 指针不包含指向的
数据类型的信息,通常是安全的。
        2)void 指针赋给其他类型指针,建议使用显式类型转换,这样更加安全,如果使用
隐式类型转换,有些编译器会触发警告。

14.3 内存分配相关函数

头文件 <stdlib.h> 声明了四个关于内存动态分配的函数:

(1)malloc() 函数

malloc() 函数用于分配一块连续的内存空间。

函数原型:

void *malloc(size_t size);

返回值说明:

如果内存分配成功,返回一个 void 指针,指向新分配内存块的地址;如果分配失败(例
如内存不足),返回一个空指针(NULL)。

参数说明:

size 是要分配的内存块的大小,以字节为单位。

代码示例 1:
动态分配整型数据的空间。

#include <stdio.h>
#inlude <stdlib.h>
cint main()
{
int *p;
p = (int *)malloc(sizeof(int));
}

// 判断是否分配成功
if (p == NULL)
{
    printf("内存分配失败\n");
    return 1; // 退出程序
}

// 使用解引用赋值并输出
*p = 120;
printf("p 指向的地址:%p \n", p);
printf("p 指向的值:%d", *p);

/ 释放分配的内存
    free(p);

    return 0;
}

p 指向的地址:00000053575ff838
p 指向的值:120

代码示例 2:
动态分配数组空间。

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

int main()
{
    int *p; // 定义整型指针
    int n = 5; // 定义数组长度

// 动态分配内存,将地址赋给指针 p
p = (int *)malloc(n * sizeof(int));

// 判断是否分配成功
if (p == NULL)
{
    printf("内存分配失败\n");
    return 1; // 退出程序
}

// 给数组元素赋值
for (int i = 0; i < n; i++)
{
    p[i] = i * 10;
}

// 释放分配的内存
    free(p);
    return 0;
}

p[0]=0
p[1]=10
p[2]=20
p[3]=30
p[4]=40

(2)calloc() 函数

calloc() 函数用于分配内存并将其初始化为零,它在分配内存块时会自动将内存中的每
个字节都设置为零。

函数原型:

void *calloc(size_t numElements, size_t sizeOfElement);

返回值说明:

如果内存分配成功,返回一个 void 指针,指向新分配内存块的地址;如果分配失败
(例如内存不足),返回一个空指针(NULL)。

参数说明:

        1)numElements 是要分配的元素的数量。
        2)sizeOfElement 是每个元素的大小(以字节为单位)。
代码示例:

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

int main()
{
    int *p; // 定义整型指针
    int n = 5; // 定义数组长度

    // 动态分配内存,将地址赋给指针 p
    p = (int *)calloc(n, sizeof(int));

    // 判断是否分配成功
    if (p == NULL)
    {
        printf("内存分配失败\n");
        return 1; // 退出程序
    }

// 输出数组的元素的值
for (int i = 0; i < n; i++)
{
    printf("p[%d]=%d \n", i, p[i]);
}

// 释放分配的内存
    free(p);
    return 0;
}

p[0]=0
p[1]=0
p[2]=0
p[3]=0
p[4]=0

(3)realloc() 函数

realloc() 函数用于重新分配 malloc() 或 calloc() 函数所获得的内存块的大小。

函数原型:

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

返回值说明:

返回一个指向重新分配内存块的指针。如果内存重新分配成功,返回的指针可能与原始
指针相同,也可能不同;如果内存分配失败,返回返回一个空指针(NULL)。
如果在原内存块上进行缩减,通常返回的原先的地址。

参数说明:

(        1)ptr 是要重新分配的内存块的指针。
            2)size 是新的内存块的大小(以字节为单位)。

代码示例:

注意,本案例中使用了<malloc.h>头文件中的_msize()函数,该函数可以获取指定内存
块的大小。

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

int main()
{
    // 声明指针
    int *b;

    // 分配内存
    b = malloc(sizeof(int) * 100);
    printf("b=%p, size:%zu \n", b, _msize(b));

    // 调整内存大小
    b = realloc(b, sizeof(int) * 2000);
    printf("b=%p, size:%zu \n", b, _msize(b));

    // 再次调整内存大小
    b = realloc(b, sizeof(int) * 200);
    printf("b=%p, size:%zu \n", b, _msize(b));

    // 释放内存
    free(b);
    return 0;
}

b=000001f5fc3f4770, size:400
b=000001f5fc3f47d0, size:8000
b=000001f5fc3f47d0, size:800

(4)free() 函数

如果动态分配的内存空间没有被正确释放,这种情况称为内存泄漏,内存泄漏会导致系
统中的可用内存逐渐减少,直到耗尽系统可用的内存资源。
free() 函数用于释放动态分配的内存,以便将内存返回给操作系统,防止内存泄漏。

函数原型:

void free(void *ptr);

返回值说明:
        没有有返回值。
参数说明:
ptr 是指向要释放的内存块的指针,ptr 必须是 malloc() 或 calloc() 动态分配的内存块地
址。

注意:

(1)分配的内存块一旦释放,就不应该再次操作已经释放的地址,也不应该再次使用free() 对该地址释放第二次。

(2)如果忘记调用 free()函数,会导致无法访问未回收的内存块,构成内存泄漏。

14.4 内存分配应用实例

动态创建数组,输入 5 个学生的成绩,再定义一个函数检测成绩低于 60 分的,输出不
合格的成绩。

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

//函数原型声明
void check(int *);

int main()
{
    int *p;
//在堆开辟一个5*4的空间
    p=(int*)malloc(5*sizeof(int));

    printf("请输入5个成绩:");
    for(i=0;i<5;i++)
    {
        scanf("%d,p+1");
    }
        
    check(p);
    free(p);//销毁堆区P指向的空间

    return0;
}

//定义函数
void check(int*p)
{
        printf("\n 不及格的成绩有:");
        for(int i=0;i<5;i++)
         {
            if(p[i]<60)
            {
                printf("%d",p[i]);
            }
    
        } 


}

14.5 内存分配的基本原则

         (1)避免分配大量的小内存块。分配堆上的内存有一些系统开销,所以分配许多小的
内存块比分配几个大内存块的系统开销大。
        (2)仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它,否则可能
出现内存泄漏。
        (3)总是确保释放已分配的内存。在编写分配内存的代码时,就要确定好在代码的什
么地方释放内存。

第十五章 文件操作

15.1 文件基本操作

文件,对我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用
的 word 文档,txt 文件,excel 文件...都是文件。文件最主要的作用就是保存数据,它既可以
保存一张图片,也可以保持视频,声音...
C 程序中,文件中数据的输入/输出操作以“流”的方式进行,可以看做是一种数据的流
动。

输入流(Input Stream):数据从数据源(例如文件、键盘等)到程序(内存)的
传输路径。

输出流(Output Stream):数据从程序(内存)到数据源(例如文件、屏幕等)
的传输路径。

15.2 C输入&输出

(1)当我们提到输入时,这意味着要向程序(内存)中写入一些数据。输入的数据来
源可以是从键盘中、从文件中或从网络中。C 语言提供了一系列内置的函数来读取给定的输
入,并根据需要写入到程序中。

(2)当我们提到输出时,这意味着要在屏幕上、打印机上或文件中显示(或保存)一
些数据。同样,C 语言提供了一系列内置的函数来输出数据到指定的载体上。

15.2.1标准文件

C 语言把所有的设备都当作文件,所以设备(比如显示器)被处理的方式与文件相同。
以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。

文件指针是访问文件的方式,我们会讲解如何从键盘获取输入值以及如何把结果输出到屏幕上。

15.2.2 scnaf()和printf函数

C 语言中的 I/O (输入/输出)通常使用 printf()和 scanf()两个函数,scanf()函数从标准
输入流 stdin 读取输入,printf()函数把输出写入到标准输出流 stdout。这两个函数我们在前面
的案例中已经大量使用。

案例演示:从键盘获取输入并将内容再输出

#include<stdio.h>

int main()
{
    //定义变量
    cahr str[100];
    int i;

     //获取输入
    printf("please input a string and a number:");
    scanf("%s %d",str,&i);
}

   //输出到屏幕终端
    printf("you entered:%s %d",str,&i);
    printf("\n");

    return 0;
}

15.2.3 getchar() & putchar() 函数

(1)getchar 函数

getchar() 函数用于从标准输入流(通常是键盘输入)读取一个字符。这个函数在同一
个时间内只会读取一个单一的字符。

函数原型:

int getchar(void);

返回值说明

返回一个整数值,表示读取的字符

(2)putchar 函数

putchar()函数用于将一个字符写入到标准输出流(通常是屏幕)。这个函数在同一个时
间内只会输出一个单一的字符。
 

函数原型

int putchar(int character);

参数说明:

character 是要写入的字符的 ASCII 码值。

(3)代码示例

#include<stdio.h>

int main( )
{
    int c;
    printf( "Enter a value :");

    c = getchar();
    printf( "\nYou entered: ");
    putchar(c);

    printf("\n");

    return 0;
}

15.2.4 gets()和 puts() 函数

(1)gets 函数

gets() 函数用于从标准输入流(通常是键盘)读取一行文本,并将其存储在一个字符数
组中(gets() 的参数),直到遇到换行符为止。

函数原型:

char *gets(char *str);

返回值说明:

返回一个指向存储在 str 中的字符串的指针

参数说明:

字符数组(字符串)的指针,用于存储读取的输入数据。

(2)puts 函数

puts() 函数用于将字符串输出到标准输出流(通常是屏幕),并自动添加换行符。它接
受一个字符串作为参数,然后将其显示在屏幕上。
函数原型

int puts(const char *str);

返回值说明:

返回成功写入的字符数,如果写入失败或出现错误,它会返回 特殊值 EOF((EOF 是
一个定义在 stdio.h 头文件中的常量,值通常为 -1))。

参数说明:

str 是要输出的字符串。

3)代码示例

#include <stdio.h>

int main()
{
    char str[20];
 
    printf("Enter a line of text :");
    gets(str);

    printf("You entered: ");
    // printf("%s", str);
    puts(str);

    return 0;
}

15.3 c文件读写

文件,无论是文本文件还是二进制文件,都以一系列字节的形式存在,C 语言提供了一
系列函数处理存储设备上的文件。

15.3.1 打开文件

使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件。该函数位于标准库的
stdio.h 头文件中

函数原型:

FILE *fopen( const char *filename, const char *mode );

返回值说明:

返回一个指向 FILE 结构的指针,该结构表示文件流,可以使用这个指针来进行文件
的读取和写入操作。如果打开文件失败,返回一个空指针(NULL)

参数说明:

(1)filename 是一个以字符串形式指定的文件名,表示要打开的文件的名称,可以包
括文件路径和文件名。
(2)mode 是一个以字符串形式指定的打开模式,表示打开文件的方式,可以是以下
值之一:

15.3.2 关闭文件

文件读写完毕后,一定要关闭文件,使用 fclose( ) 函数可以关闭文件。该函数位于标准
库的 stdio.h 头文件中。

函数原型:

int fclose(FILE *stream);

返回值说明:

返回一个整数值,通常为零(0),表示关闭操作成功。如果关闭失败,它返回特殊值 EOF
(EOF 是一个定义在 stdio.h 头文件中的常量,值通常为 -1)。

参数说明:stream 是一个指向 FILE 结构的指针,表示要关闭的文件流。

15.3.3 写入文件

(1)fputc 函数

fputc() 函数用于逐字符写入文件。位于标准库的 stdio.h 头文件中。

函数原型:

int fputc(int character, FILE *stream);

返回值说明:

返回一个整数值,通常是写入的字符的 ASCII 码值。如果写入成功,返回的值与输入
的 character 值相同;如果写入失败,它返回特殊值 EOF。

参数说明:

        1)character 是要写入的字符,通常以整数形式表示,即字符的 ASCII 码值。
        2)stream 是一个指向 FILE 结构的指针,表示要写入字符的文件流。

代码示例:

#include <stdio.h>

int main()
{
    // FILE *file = fopen("output.txt", "w"); // 从头写入
    FILE *file = fopen("output.txt", "a"); // 追加写入

    if (file != NULL)
    {
    // 使用 fputc 写入一个字符
    char ch = 'A';
    int result1 = fputc(ch, file);
        if (result1 != EOF)
        {
        printf("成功使用 fputc 写入字符 '%c',返回值:%d。\n", ch,  result1);
  
        }
    else
    {
        printf("使用 fputc 写入字符时出现错误。\n");
    }
        // 关闭文件
        fclose(file);
    }
    else
    {
    printf("打开文件时出现错误。\n");
    }

    return 0;
}

(2)fputs 函数

fputs() 函数用于将字符串写入文件。位于标准库的 stdio.h 头文件中。

函数原型

int fputs(const char *str, FILE *stream);

返回值说明:

返回一个整数值,如果写入成功,则返回非负整数(通常是成功写入的字符数,具体取
决于编译器),否则返回特殊值 EOF。

参数说明:
(1)str 是要写入的字符串,通常以 const char* 指针的形式传递。
(2)stream 是指向输出流的指针,通常是文件指针。
代码示例:

#include <stdio.h>

int main()
{
    FILE *file = fopen("output.txt", "w"); // 从头写入
    // FILE *file = fopen("output.txt", "a"); // 追加写入
if (file != NULL)
{
    // 使用 fputs 写入一个字符串
    const char *text = "fputs: Hello Shangguigu\n";
    int result2 = fputs(text, file);
    if (result2 != EOF)
    {
        printf("成功使用 fputs 写入字符串,返回值:%d。\n", result2);
    }
    else
    {
        printf("使用 fputs 写入字符串时出现错误。\n");
}
        // 关闭文件
        fclose(file);
}
else
{
    printf("打开文件时出现错误。\n");
}
    return 0;
}

(3)fprintf 函数

fprintf() 函数用于格式化写入文件。位于标准库的 stdio.h 头文件中。

函数原型:

int fprintf(FILE *stream, const char *format, ...);

返回值说明:

返回一个整数值,如果写入成功,则返回非负整数(通常是成功写入的字符数,具体取
决于编译器),否则返回特殊值 EOF。

参数说明:

(1)stream 是一个指向 FILE 结构的指针,表示要写入的文件流。
(2)format 是一个格式化字符串,类似于 printf() 函数中的格式化字符串。
(3)...表示可变数量的参数,根据格式化字符串中的格式占位符对应。
代码示例:

#include <stdio.h>

int main()
{
     FILE *file = fopen("output.txt", "w"); // 从头写入
     // FILE *file = fopen("output.txt", "a"); // 追加写入
     if (file != NULL)
      {
            // 使用 fputs 写入一个字符串
             const char *text = "fprintf: %s is studying in Shangguigu\n";
             char *name = "Alice";
             int result3 = fprintf(file, text, name);
             if (result3 != EOF)
             {
                    printf("成功使用 fprintf 写入字符串,返回值:%d。\n",
                    result3);
              }
              else
              {
                    printf("使用 fprintf 写入字符串时出现错误。\n");
               }
        // 关闭文件
        fclose(file);
        }
        else
        {
            printf("打开文件时出现错误。\n");
      }
    
      return 0;
}

15.3.4 读取文件

(1)fgetc 函数
fgetc() 函数用于从文件中逐字符读取。位于标准库的 stdio.h 头文件中。

函数原型:

int fgetc(FILE *stream);

返回值说明:

如果读取成功,它返回所读取字符的 ASCII 码值(0-255 之间的整数),如果到达文件
结束或发生错误,它返回特殊值 EOF。
参数说明:
stream 是一个指向 FILE 结构的指针,表示要写入字符的文件流。
代码示例:

#include <stdio.h>
    
int main()
{
    FILE *file = fopen("output.txt", "r"); // 打开文件以供读取
    if (file != NULL)
    {
    int ch; // 用于存储读取的字符
    // 使用 fgetc 逐字符读取文件
        while ((ch = fgetc(file)) != EOF)
        {
            printf("%c", ch); // 将字符显示在屏幕上
            }
        fclose(file); // 关闭文件
        }
    else
    {
        printf("Error opening the file.\n");
    }
    return 0;
}

2)fgets 函数

fgets() 函数从文件中逐行读取,遇到换行符即读取结束,读取的内容中包含换行符。
该函数位于标准库的 stdio.h 头文件中。
函数原型:
 

char *fgets(char *str, int num, FILE *stream);

返回值说明:
如果读取成功,它返回指向 str 的指针;如果到达文件结束或发生错误,它返回一个空
指针(NULL)。
参数说明:
(1)str 是一个指向字符数组的指针,用于存储读取的字符串。
(2)num 是要读取的最大字符数(包括字符串终止符 \0),通常是 str 的长度。
(3)stream 是一个文件流,通常是标准输入流(stdin)或其他文件流,用于指定从哪
里读取数据。
代码示例

#include <stdio.h>

int main()
{
  FILE *file = fopen("output.txt", "r"); // 打开文件以供读取
  if (file != NULL)
  {
     char buffer[256]; // 用于存储读取的字符串
    // 使用 fgets 逐行读取文件
    while (fgets(buffer, sizeof(buffer), file) != NULL)
    {
    printf("%s", buffer); // 将读取的字符串显示在屏幕上
    }
    fclose(file); // 关闭文件
    }
    else
    {
        printf("Error opening the file.\n");
  }
    return 0;
}

3)fscanf 函数

fscanf() 函数用于从文件中解析数据并存储到变量中,使用空白字符(空格、制表符、
换行符等)分隔内容赋值给不同的变量。该函数位于标准库的 stdio.h 头文件中。
 

函数原型:

返回值说明:
返回成功读取和分配的参数数目,如果没有成功读取任何参数,它返回 0;如果读取过
程中发生错误,它返回特殊值 EOF。
参数说明:
(1)stream 是一个指向 FILE 结构的指针,表示要从中读取数据的文件流。
(2)format 是一个格式化字符串,类似于 printf() 函数中的格式化字符串。
(3)... 表示可变数量的参数,根据格式化字符串中的格式指定要存储数据的变量。
代码示例

#include <stdio.h>

int main()
{
    FILE *file = fopen("example.txt", "r"); // 打开文件以供读取
    if (file != NULL)
    {
     // 变量
     char msg1[100], msg2[100], msg3[100];
     int num;
     // 读取
     fscanf(file, "%s %s %s %d", msg1, msg2, msg3, &num);
     // 输出
     printf("%s\n%s\n%s\n%d", msg1, msg2, msg3, num);
     // 关闭文件
     fclose(file);
     }
     else
     {
        printf("打开文件时出现错误。\n");
    }
   return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值