【c】菜鸟-C语言快速学习笔记及与Java的区别

目录

程序结构

  • c语言的程序基本结构

    • 预处理指令;主函数{变量定义等。。};函数定义
  • c语言main函数参数列表

    • int main(int argc, char *argv[]);
    • int main(int argc, char **argv);
    • argc (argument count):
      • 命令行参数的数量,包括程序名本身。因此,argc 至少为 1
    • argv (argument vector):
      • 一个指向字符串数组的指针,其中每个字符串是一个命令行参数。
      • 数组的第一个元素(即 argv[0])通常是程序的名称。接下来的元素是传递给程序的命令行参数。

基础语法

数据类型

  • c: 没有byte,有enum
  • java:有byte,没有enum
  • 注:enum类似字典的键值对,且是一个整型集合;而键值对是单个的
  • c不同数据类型的字节长度/小数点有效位:
    • 整型:char 1, short 2, int 2或4, long 4;
    • 浮点型:float 4/6, double 8/15, long double 16/19;

强制类型转换

  • c与java类似:(type_name) expression
  • c注意点:
    • 强制类型转换运算符的优先级大于运算符:先强转,再参与运算
    • 低转高:截断,舍弃超出位;
    • 常用的算术转换:char,short--int--unsigned int--long--unsigned long--long long--unsigned long long--float-double-long double

定义常量

  • c:
    • #define 预处理器定义,#define 常量名 常量值例如#define PI 3.14159
    • const关键字:const 数据类型 常量名 = 常量值;例如const int MAX_VALUE = 100;
    • 建议使用 const 关键字来定义常量: 具有类型检查和作用域的优势;
    • #define: 仅进行简单的文本替换,可能会导致一些意外的问题。
  • Java:
    • final关键字:final dataType NAME = XX;
    • 常量名一般全部字母大写
    • 还有其他方法

enum

  • 一种基本数据类型,是一组具有离散值的常量
  • 元素一般为int或unsigned int型
  • 定义格式:enum 枚举名 {枚举元素1,枚举元素2,……};
    • 第一个枚举成员的默认值为整型的 0,后续元素依次+1;
    • 中间可以指定不按顺序的常量值,指定元素后的仍然依次+1;
  • 定义枚举变量:
    • 另起一行定义:enum DAY day;
    • 跟在enum后定义: enum DAY{MON=1, TUE, WED, THU, FRI, SAT, SUN} day;
    • 同第二类但省略enum变量名: enum{MON=1, TUE, WED, THU, FRI, SAT, SUN} day;
  • 遍历:
    • 当元素连续时可以循环遍历;当不连续,即中间元素不按序赋值时,不能遍历
  • 类型:当变量定义为枚举类型时,赋值需要手动转换类型为枚举类型
    • int a = 1;
    • enum day weekend;
    • weekend = ( enum day ) a; //类型转换
  • 详见:菜鸟:C enum(枚举)

算术运算符

  • c/c++取余:两边都是整型,否则报错;
  • Java取余:允许小数

逻辑运算符

  • c语言:&&是逻辑运算符,含短路原理;&是位运算符,不短路;
  • Java:&和&&都表示与运算,单与不短路,双与短路;
  • c语言的&也可以作逻辑运算符,如元素是一个表达式,不短路
  • c语言不建议用单与作为逻辑运算符,建议用双与,因为当两侧含数字时,可能会做位运算导致结果有误

杂项运算符

  • c语言:sizeof()-变量字节大小, &+变量名-变量地址, *+指针名-指向指针地址对应的变量
    • sizeof():printf中对应的有%d也有%lu,返回值类型似乎不确定
  • 注:三元运算符? :同java

switch中表达式的类型

  • c:
    • 必须是整数类型(char、short、int或枚举),或者是能够隐式转换为整数类型的表达式。
  • Java:
    • 变量类型可以是: byte、short、int 或者 char。
    • 从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。

循环togo

  • java:没有togo
  • c: 无条件转移到同一函数内的被标记的语句
    • goto label;
    • ...
    • label: statement;
  • 注意:在任何编程语言中,都不建议使用 goto 语句

数组

数组声明

  • 需要指定元素的类型和元素的数量
  • 格式:type arrayName [ arraySize ];;
  • 示例:double balance[10];

数组长度

  • c:用 sizeof() :整个数组长度 除以 单个元素长度
    • int numbers[] = {1, 2, 3, 4, 5}; int length = sizeof(numbers) / sizeof(numbers[0]);
  • Java:
    • 数组.length,属性,返回值 int
    • 字符串.length(),方法,返回值 int
    • 集合.size(),方法,返回值 int

数组名

  • c:数组名表示数组的地址,即数组首元素的地址
    • 数组名会自动转换为指向数组首元素的指针。
    • 即可以直接将数组名用于指针运算
  • c: 数组名+1,arr + 1 = &(a[1])
    • arr+1就是将arr的值(地址)加上单个数组元素个长度
    • arr是二维数组时,arr+1表示加上整个一行的长度

数组元素获取

  • c: 与java类似,打印时%d等直接放在小括号里
    • 二维数组元素获取:printf("a[%d][%d] = %d\n", i,j, a[i][j] );
  • Java打印:用+和引号 把 直接打印和变量 连接起来输出

数组赋值

  • c: a 的大小比 a1 大,长度相同,大小不同
    • char a[]="runoob"; // 这样赋值之后在结尾会自动加上 '\0'
    • char a1[]={'r','u','n','o','o','b'}; // 这样赋值是整整好好的6个空间不会自动加上 '\0'
  • java: 未考证

二维数组的长度必须与可省略

  • c: 一维可省略,二维不可省略
    • 示例:double * MatrixMultiple(double a[][2], double b[][3]); /* 这才是正确的 */
    • 将二维数组当作参数的时候,必须指明所有维数大小或者省略第一维的,但是不能省略第二维或者更高维的大小
    • 设有数组 int a[m][n],如果要访问 a[i][j ]的值,编译器的寻址方式为。
    • &a[i][j]=&a[0][0]+i*sizeof(int)*n+j*sizeof(int); // 注意 n 为第二维的维数
  • java: 一维不能省略,二维可以省略
    • 示例:String[][] s = new String[2][];
  • 详细参考:【二维数组】java、c定义二维数组角标省略

动态数组

  • c: malloc, realloc等
    • 通过动态内存分配函数手动分配,并存储在堆上。
    • 使用 free 函数来释放内存;
    • realloc 函数:重新分配内存,并改变数组的大小;
    • void *malloc(size_t size)
      • 参数:size -- 内存块的大小,以字节为单位。
      • 返回值:返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
    • 示例:
      • int size = 5;
      • int *dynamicArray = (int *)malloc(size * sizeof(int)); // 动态数组内存分配
      • free(dynamicArray); // 动态数组内存释放
    • 注意:
      • 在使用动态数组时,需要检查内存分配是否成功(即 dynamicArray 是否为 NULL),以避免在内存分配失败时发生错误
    • 参考:菜鸟:C 语言静态数组与动态数组
  • java: ArrayList
    • 一种对象,可以任意伸缩数组长度
    • add(), remove(), size()等
    • 使用:ArrayLsit al = new ArrayList(); al.add("a"); ...
    • 注意:需要import java.util.ArrayList;

字符串

字符串的本质

  • c中的字符串,实际上是使用空字符 \0 结尾的一维字符数组。
    • har site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
    • \0 是用于标记字符串的结束。
    • C 编译器会在初始化数组时,自动把 \0 放在字符串的末尾
  • \0占位:
    • 需在给定字符数组的大小时在原有的字符串的字符数上加 1
    • char greeting[5] = { 'H', 'e', 'l', 'l', 'o' };--会报错
  • 空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符
    • \0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。

字符串相关函数

  • c中自带一部分字符串相关函数,可以直接使用
    • 示例:strcpy(str3, str1);/* 复制 str1 到 str3 */
  • 部分字符串函数对应的英文
    • strcmp: string compare
    • strcat: string catenate
    • strcpy: string copy
    • strlen: string length
    • strlwr: string lowercase
    • strupr: string upercase

字符串赋值:strcpy();

  • 结构体的成员是字符串时的赋值:
    • strcpy( Book1.title, "C Programming");

strlen 和 sizeof 求字符串长度的区别

  • sizeof: 变量的大小,不受字符 \0 影响;
    • 即无视\0,一视同仁,作为一个占位符计算
  • strlen: 字符串的长度,以 \0 作为长度判定依据。
    • 即字符串量身定制,为适配字符串,更“所见即所得”
  • 通常,sizeof比strlen取得的结果更长
    • char *str1 = "asdfgh"; //sizeof(str1) = 4; strlen(str1) = 6;
    • char str2[] = "asdfgh";//sizeof(str2) = 7; strlen(str2) = 6;
    • char str3[8] = {'a', 's', 'd'};//sizeof(str3) = 8; strlen(str3) = 3;
    • char str4[] = "as\0df";//sizeof(str4) = 6; strlen(str4) = 2;
    • 详细参考:利用 strlen 和 sizeof 求取字符串长度注意事项

函数

函数声明

  • 格式:return_type function_name( parameter list );
  • 其中,变量名不是必须的,例如这样也是对的:int max(int, int);
  • 可以在顶部进行函数声明,之后主函数中直接调用,而函数定义可以放在后面

函数调用

  • c: 传值调用、引用调用
  • Java:一般用传值调用
  • 传值调用:
    • 一般的函数调用方法,/*函数声明*/void swap(int x, int y);而调用: swap(a, b);
    • 不会在调用函数外对变量的值产生变化
  • 引用调用:
    • 形参为指向实参地址的指针,相当于对实参本身进行的操作
    • /* 函数声明 */void swap(int *x, int *y);而调用:swap(&a, &b);
    • 会对调用函数以外的变量产生变化
  • 指针定义格式:type *var_name;,即引用调用中的参数为指针参数

指针

Java没有指针

  • 没有指针
  • 用数组或链表等数据结构模拟指向性

c语言有指针

  • 指针:内存地址
  • 指针变量:用来存放内存地址的变量。
  • 定义指针:type *var_name;
    • type:应与指针所指向的类型一致
  • 一次定义多个指针
    • 星号要写在变量前:char * name , * sign;
  • 指针详细内容:
    • 太多了,此处仅概述

空指针

  • NULL 指针是一个定义在标准库中的值为零的常量
    • 如果printf,值为0x0
  • 如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯
  • 内存地址 0
    • 大多数操作系统上,内存地址 0 是操作系统保留的,程序不允许访问地址为 0 的内存。
    • 因此,内存地址 0 表明该指针不指向一个可访问的内存位置。
    • 但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西

指针的算术运算

  • 可以进行的四种算术运算:++、--、+、-
    • 运算时指针的变化:指向下一个元素的存储单元
  • 指针在递增和递减时跳跃的字节数:
    • 取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节
  • ptr++:
    • 指向地址 1000 的int指针,则运算后指向1004;
    • 指向地址 1000 的char指针,则运算后指向1001
  • 指针的递增递减:
    • 变量指针可以递增,而数组不能递增,因此,可以使用指针代替数组
  • 指针的比较:
    • 指针比较只有在指向同一个数组或同一内存块时才有意义,否则行为未定义
    • 相等比较 (== 和 !=):
      • 用于判断两个指针是否指向相同的内存位置。
    • 大小比较 (<, >, <=, >=):
      • 通常用于指针遍历数组或内存块时,判断一个指针是否在另一个指针之前或之后。

指针数组

  • 注意区别 指针数组 与 数组指针
  • 定义指针数组后,用*+数组元素的方式调用指针指向的变量
    • for(i=0; i<MAX; i++){ptr[i] = &var[i]; /* 赋值为整数的地址 */ }
    • printf("Value of var[%d] = %d\n", i, *ptr[i] );
  • 用一个指向字符的指针数组来存储一个字符串列表 不太懂

指向指针的指针

  • 声明
    • int **var;
  • 取值
    • V = 100; Pt1 = &V; Pt2 = &Pt1;
    • printf("**Pt2 = %d\n", **Pt2); \\输出:**Pt2 = 100

函数指针与回调函数

  • 传函数的用意
    • 当需要的操作细节不确定时,可以设为待选项;
    • 然后在具体场景中,进行选择,并将选中的函数作为参数传入。
    • 例如:菜鸟中的代码示例,数组元素的赋值方式待定;最终选择随机值赋值,并将随机赋值函数作为参数传入。
  • 来自知乎作者常溪玲的解说:

    你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。
    在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件

  • C 语言回调函数详解
  • 菜鸟:回调函数

结构体

结构体在Java和c

  • Java没有结构体
    • 对象有相似,但对象包括属性和方法;
    • 而结构体只有属性值,即变量定义。
  • c有结构体
    • 与enum有一定的相似
      • 结构体的结构:与enum类似,且都是存放一组数据
      • enum:管理一系列数值,且数据类型为int或unsigned int
    • 与java的对象有一定的相似
      • 都是针对一个多属性维度的对象
        • 如图书,有title、author等多个属性;
      • java的对象:内含属性和方法;
      • 结构体:只有属性值列表,可包含不同数据类型的变量;

结构体定义

  • 位置:
    • 定义位置:与函数/主函数并列定义
    • 结构体变量的使用:函数/main函数内
  • 结构体定义:
    • tag:结构体名称,首字母大写。
    • member-list:结构体成员变量,类似enum内部的数据值的名称。
    • variable-list:结构变量(类似对象的new,实体化)
    • 一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个
  • 初始化
    • 可以在定义时指定初始值
    • variable-list后直接...book = {"C 语言", "RUNOOB", "编程语言", 123456};
  • 两个结构体相互包含时:
    • 则需要对其中一个结构体进行不完整声明;
    • 即B先不完整声明;A声明包含B;B声明包含A
  • 指向结构体的指针
    • 定义指向结构的指针:struct Books *struct_pointer;
    • 赋值:地址符+结构体变量名 struct_pointer = &Book1;

访问结构成员

  • 结构体变量.结构成员
    • 定义结构体变量:struct Books Book1; /*声明 Book1,类型为Books*/
    • 使用结构体变量访问成员:strcpy( Book1.title, "C Programming");
    • 注意:结构体相当于抽象,结构体变量是一个具体的实体,所以访问时用的是结构体变量
  • 指针名->结构成员
    • 格式:struct_pointer->title;
    • 调用示例:printf( "Book title : %s\n", book->title);//book: 结构体指针变量名

结构体数组

  • 存放多个结构体的数组
    • 场景:每个学生有不同属性值,有10个学生的数据需要参加运算时

结构体大小

  • 结构体变量所占内存长度:其中最大字段大小的整数倍
  • 格式:sizeof(结构体变量名)

结构体内存大小对齐原则

  • 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
  • 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding)。
    • 即结构体成员的末地址减去结构体首地址(第一个结构体成员的首地址)得到的偏移量都要是对应成员大小的整数倍。
  • 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在成员末尾加上填充字节。
  • 【c】结构体内存对齐的规则

共用体

共用体在java和c

  • Java没有共用体
  • c有共用体

共用体的应用场景

  • 应用:
    • 在相同的内存位置存储不同的数据类型。且任何时候只能有一个成员带有值。
    • 节省内存;
    • 有两个很长的数据结构,不会同时使用时,比如一个表示老师,一个表示学生,
  • 通信中的数据包会用到共用体:
    • 不知道对方会发一个什么包过来,定义几种格式的包;
    • 收到包之后就可以直接根据包的格式取出数据。

共用体定义

  • 类似结构体定义:
  • 访问共用体
    • 共用体变量名称.共用体成员
    • 示例:printf( "data.str : %s\n", data.str);

共用体的大小

  • 共用体变量所占的内存长度:等于最长的成员变量的长度。
  • 格式:sizeof(共用体变量名)
  • 对齐原则

位域

位域是一种特殊的结构体成员

  • bit-field:"位域"或"位段"
  • 特殊点:按位对成员进行定义,指定其占用的位数
  • 可以节约空间,并使处理简便
  • 注意:与上面的共用体区分,位域是更接近结构体的

位域的定义

  • 格式:struct 位域结构名{ type [member_name]: width; };
  • type: 只能是三类整型 int,unsigned int,signed int
  • member_name: 成员的变量名
  • width: 成员占位的值,小于或等于指定类型的位宽度

位域的使用

  • 位域变量:位域变量名.位域成员名
  • 位域指针:位域指针变量名->位域成员名
  • 空域:
    • 无名位域,只用来作填充或调整位置,不能使用
    • 示例: a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占4位,c占4位
  • 当赋值超过位域定义的bit长度时:
    • 只保留对应的bit位的值,丢掉超出的位
    • 结构体定义为:struct{ unsigned int age : 3; } Age;
    • 赋值超过3bit:Age.age = 8; printf("Age.age : %d\n", Age.age);
    • 8 是 00001000,按照位域,对应 3 位的值是 000,所以打印结果是 0;
    • 9 是 00001001,按照位域,对应 3 位的值是 001,所以打印结果是 1;
    • 10 是 00001010,按照位域,对应 3 位的值是 010,所以打印结果是 2;

预处理器

c预处理器

  • C预处理器(C Preprocessor)简写为 CPP
  • 文本替换工具,指示编译器在实际编译之前完成所需的预处理
  • 以井号(#)开头,必须是第一个非空字符

预定义宏

  • 示例:printf("Date :%s\n", __DATE__ );//输出:Date :Jun 2 2012

预处理器运算符

  • 宏延续运算符(\)
    • #define定义宏;
    • 一个宏通常写在一个单行上,一行放不下时,\换行,作为延续;
  • 字符串常量化运算符(#)
    • 把一个宏的参数转换为字符串常量
    • 示例:#define message_for(a, b)\ printf(#a " and " #b ": We love you!\n")
  • 标记粘贴运算符(##)
    • 在宏定义中两个独立的标记被合并为一个标记("字面意义的合并")
    • 示例:#define tokenpaster(n) printf ("token" #n " = %d", token##n)
  • defined()运算符
    • 用来确定一个标识符是否已经使用 #define 定义过,并返回布尔值
    • 示例:#if !defined (MESSAGE)\ #define MESSAGE "You wish!"

参数化的宏

  • 使用参数化的宏来模拟函数
    • 即,在宏(#define)定义函数
    • 示例:#define square(x) ((x) * (x))
  • 定义规则:
    • 参数列表是括在圆括号内,且必须紧跟在宏名称的后边。
    • 宏名称和左圆括号之间不允许有空格;
    • 运算体部分:作为整体进行计算的每个单个部分,只有单个参数也必须括起来
    • 注意:因为预处理器都是文本逻辑,括号很重要,不能省略!
    • 示例:#define MAX(x,y) ((x) > (y) ? (x) : (y))

头文件

C 头文件

  • 扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。
  • 有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。
    • #include <>
      • 引用系统文件:编译器的类库路径里面的头文件。
    • #include ""
      • 引用用户头文件
      • 查找顺序:程序目录的相对路径中的头文件;编译器的类库路径的目录。
  • 标准库头文件

不同引用方式

  • 避免重复引用:
    • 引用前判断:#ifndef HEADER_FILE\ #define HEADER_FILE... #endif
  • 有条件引用:
    • 预处理器使用宏来定义头文件的名称
    • 格式:#define 自定义头文件名 头文件名.h
    • 示例:#define SYSTEM_H "system_1.h"\ ...
    • 使用:#include SYSTEM_H

typedef和define

相同点

  • 起个别名,用在后续的程序中
  • typedef:typedef unsigned char BYTE;
    • 则:BYTE b1, b2;相当于typedef unsigned char byte;
    • 按照惯例定义时使用大写字母;但小写也能通过
    • 为复杂的声明定义一个简单的别名,在回调函数中特别好用
  • #define: #define TRUE 1
    • 则:printf( "TRUE 的值: %d\n", TRUE);//输出:TRUE 的值: 1
    • 常见于 为常数定义常量名

不同点

  • 使用范围不同
    • typedef:仅限于为数据类型;
    • #define 可为类型,可为数值
  • 执行环节不同
    • #define:由预处理器执行,只是字面上的替换;
    • typedef:由编译器执行,更灵活
    • typedef char * STRING;:
      • STRING name, sign;相当于char * name , * sign;
    • #define STRING char *:
      • STRING name, sign;相当于char * name, sign;--只有name是指针

输入输出

getchar() & putchar()

  • int getchar(void)
    • 读取下一个可用的字符,并返回为一个整数。
    • 同一个时间内只会读取一个单一的字符。
  • int putchar(int c) :
    • 把字符输出到屏幕上,并返回相同的字符。
    • 同一个时间内只会输出一个单一的字符。

gets() & puts()

  • char *gets(char *s)
    • 从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF
  • int puts(const char *s)
    • 把字符串 s 和一个尾随的换行符写入到 stdout。
  • gets(): 可能会内存越界
    • fgets(): char *fgets(char *s, int n, FILE *stream);
    • 示例:fgets(str, sizeof(str), stdin);
      • str为数组首地址,sizeof(str)为数组大小,stdin表示我们从键盘输入数据。

scanf() 和 printf()

  • int scanf(const char *format, ...)
    • scanf()返回的值为:正确按指定格式输入变量的个数
  • int printf(const char *format, ...)

文件读写

文件读写相关的函数

  • fopen():新建或打开文件
    • FILE *fopen( const char *filename, const char *mode );
    • mode: r/r+读;w/w+写;a/a+追加写;
  • fclose():关闭文件
    • int fclose( FILE *fp );
  • fputc():把字符c写入文件
    • fputc(int c, FILE *fp)
  • fputs():把字符串 s 写入文件
    • int fputs( const char *s, FILE *fp );
  • fgetc():从文件读取单个字符
    • int fgetc( FILE * fp );
  • fgets():从文件读取一个字符串
    • char *fgets( char *buf, int n, FILE *fp );
    • 意为:读取n-1个字符,并存入缓冲区buf,并在最后追加一个null字符来终止字符串。
    • 提前终止:提前遇到'\n' 或文件的末尾 EOF时,会提前终止
  • fscanf():从文件读取一个字符串
    • int fscanf(FILE *fp, const char *format, ...)
    • 终止:遇到第一个空格和换行符时,它会停止读取
  • 二进制IO函数
    • fread(); fwrite()
  • 一个示例:

错误处理

  • c:errno.h头文件,errno、perror() 和 strerror()
    • perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
    • strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
  • Java:错误抛出

可变参数

可变参数的定义

  • 可变:指的是参数列表的参数个数是可变的
  • 头文件:stdarg.h
  • 定义格式:int func_name(int arg1, ...);
    • int arg1: 相当于一个sum,是省略号部分的参数个数之和
    • 应该是提前声明方便开辟空间。

可变参数的宏使用

  • va_list valist;
    • 创建一个 va_list 类型变量
  • 初始化可变参数列表:va_start(ap, last_arg):
    • 示例:va_start(valist, num);//num为函数的参数列表中第一个变量
    • ap 是 va_list 类型的变量,last_arg 是可变参数列表之前的参数。
    • 该宏将 ap 指向可变参数列表中的第一个参数(即不含num的第一个参数)。
  • 获取可变参数列表中的下一个参数:va_arg(ap, type)
    • 示例:sum += va_arg(valist, int);
    • ap 是 va_list 类型的变量,type 是下一个参数的类型。
    • 该宏返回类型为 type 的值,并将 ap 指向下一个参数。
  • 结束可变参数列表的访问:va_end(ap)
    • 示例:va_end(valist);
    • ap 是 va_list 类型的变量。该宏将 ap 置为 NULL。

绕过宏使用可变参数

  • 定义指针,指向num,并移动指针依次获取参数列表的值

内存管理

常用的内存管理函数

  • 头文件:stdlib.h
  • 初始化:
    • 首先,定义一个指针,指向未定义所需内存大小的字符;后续再根据需求来分配内存
    • 示例:char *description;
  • void *calloc(int num, int size); and void *malloc(int num);
    • 动态分配内存
    • calloc():分配指定个数指定大小的连续内存块;
    • malloc():分配指定大小的一块内存
    • 多用malloc而很少用calloc,因calloc对内存的写零操作大部分时间不需要
    • 示例:description = (char *)malloc( 30 * sizeof(char) );
  • void *realloc(void *address, int newsize);
    • 重新分配内存,把内存扩展到 newsize。
    • 示例:description = (char *) realloc( description, 100 * sizeof(char) );
  • void free(void *address);
    • 释放动态分配的内存空间
    • 示例:free(description);

其他常用函数和运算符

  • sizeof 运算符:
    • 数据类型或变量的大小(以字节为单位)
  • &运算符:
    • 变量的内存地址。
  • *运算符:
    • 指针所指向的变量的值。
  • memcpy() 函数:
    • 从源内存区域复制数据到目标内存区域。
  • memmove() 函数:
    • 类似于memcpy()函数,但它可以处理重叠的内存区域。

END

posted @ 2024-08-19 10:46  anliux  阅读(21)  评论(0编辑  收藏  举报