【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;
- 常量名一般全部字母大写
- 还有其他方法
- final关键字:
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] );
- 用一个指向字符的指针数组来存储一个字符串列表 不太懂
- 菜鸟:C 指针数组
指向指针的指针
- 声明
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的对象:内含属性和方法;
- 结构体:只有属性值列表,可包含不同数据类型的变量;
- 都是针对一个多属性维度的对象
- 与enum有一定的相似
结构体定义
- 位置:
- 定义位置:与函数/主函数并列定义
- 结构体变量的使用:函数/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个学生的数据需要参加运算时
- 场景:每个学生有不同属性值,有10个学生的数据需要参加运算时
结构体大小
- 结构体变量所占内存长度:其中最大字段大小的整数倍
- 格式:
sizeof(结构体变量名)
结构体内存大小对齐原则
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
- 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding)。
- 即结构体成员的末地址减去结构体首地址(第一个结构体成员的首地址)得到的偏移量都要是对应成员大小的整数倍。
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在成员末尾加上填充字节。
- 【c】结构体内存对齐的规则
共用体
共用体在java和c
- Java没有共用体
- c有共用体
共用体的应用场景
- 应用:
- 在相同的内存位置存储不同的数据类型。且任何时候只能有一个成员带有值。
- 节省内存;
- 有两个很长的数据结构,不会同时使用时,比如一个表示老师,一个表示学生,
- 通信中的数据包会用到共用体:
- 不知道对方会发一个什么包过来,定义几种格式的包;
- 收到包之后就可以直接根据包的格式取出数据。
共用体定义
- 类似结构体定义:
- 访问共用体
共用体变量名称.共用体成员
- 示例:
printf( "data.str : %s\n", data.str);
共用体的大小
- 共用体变量所占的内存长度:等于最长的成员变量的长度。
- 格式:
sizeof(共用体变量名)
- 对齐原则
- sizeof()求大小时,结果可能出乎意料,多多注意
- 参考:菜鸟:C 共用体的评论区底楼
位域
位域是一种特殊的结构体成员
- 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表示我们从键盘输入数据。
- fgets():
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);
andvoid *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()函数,但它可以处理重叠的内存区域。