这里讲一讲函数的可变参数列表和字符串和字符以及结构体联合体和动态内存分配
一、可变参数列表
C 语言允许定义参数数量可变的函数,这称为可变参数函数(variadic function)。这种函数需要固定数量的强制参数(mandatory argument),后面是数量可变的可选参数(optional argument)。
这种函数必须至少有一个强制参数。可选参数的类型可以变化。可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定。
C 语言中最常用的可变参数函数例子是 printf()和 scanf()。这两个函数都有一个强制参数,即格式化字符串。格式化字符串中的转换修饰符决定了可选参数的数量和类型。
对于每一个强制参数来说,函数头部都会显示一个适当的参数,像普通函数声明一样。参数列表的格式是强制性参数在前,后面跟着一个逗号和省略号(...),这个省略号代表可选参数。
可变参数函数要获取可选参数时,必须通过一个类型为 va_list 的对象,它包含了参数信息。这种类型的对象也称为参数指针(argument pointer),它包含了栈中至少一个参数的位置。可以使用这个参数指针从一个可选参数移动到下一个可选参数,由此,函数就可以获取所有的可选参数。va_list 类型被定义在头文件 stdarg.h 中。
当编写支持参数数量可变的函数时,必须用 va_list 类型定义参数指针,以获取可选参数。在下面的讨论中,va_list 对象被命名为 argptr。可以用 4 个宏来处理该参数指针,这些宏都定义在头文件 stdarg.h 中:
void va_start(va_list argptr,lastparam);
宏 va_start 使用第一个可选参数的位置来初始化 argptr 参数指针。该宏的第二个参数必须是该函数最后一个有名称参数的名称。必须先调用该宏,才可以开始使用可选参数。
type va_arg(va_list argptr, type);
展开宏 va_arg 会得到当前 argptr 所引用的可选参数,也会将 argptr 移动到列表中的下一个参数。宏 va_arg 的第二个参数是刚刚被读入的参数的类型。
void va_end(va_list argptr);
当不再需要使用参数指针时,必须调用宏 va_end。如果想使用宏 va_start 或者宏 va_copy 来重新初始化一个之前用过的参数指针,也必须先调用宏 va_end。
voidva_copy(va_list dest,va_list src);
宏 va_copy 使用当前的 src 值来初始化参数指针 dest。然后就可以使用 dest 中的备份获取可选参数列表,从 src 所引用的位置开始。
// 函数add() 计算可选参数之和
#include <stdio.h>
#include <stdarg.h>
// 参数:第一个强制参数指定了可选参数的数量,可选参数为double类型
// 返回值:和值,double类型
double add(int n,...)
{
int i=0;
double sum=0.0;
va_list argptr;
va_start(argptr,n);// 初始化argptr
for(i=0;i<n;++i)// 对每个可选参数,读取类型为double的参数,
sum+= va_arg(argptr,double);// 然后累加到sum中
va_end(argptr);
return sum;
}
二、字符串和字符
字符串相关的函数后面讲
三、 结构体联合体
结构体定义:
第一种:只有结构体定义
struct stuff{
char job[20];
int age;
float height;
};
第二种:附加该结构体类型的“结构体变量”的初始化的结构体定义
//直接带变量名Huqinwei
struct stuff{
char job[20];
int age;
float height;
}Huqinwei;
也许初期看不习惯容易困惑,其实这就相当于:
struct stuff{
char job[20];
int age;
float height;
};
struct stuff Huqinwei;
第三种:如果该结构体你只用一个变量Huqinwei,而不再需要用
struct stuff yourname;
去定义第二个变量。
那么,附加变量初始化的结构体定义还可进一步简化出第三种:
struct{
char job[20];
int age;
float height;
}Huqinwei;
把结构体名称去掉,这样更简洁,不过也不能定义其他同结构体变量了——至少我现在没掌握这种方法。
结构体变量及其内部成员变量的定义及访问:
绕口吧?要分清结构体变量和结构体内部成员变量的概念。
就像刚才的第二种提到的,结构体变量的声明可以用:
struct stuff yourname;
其成员变量的定义可以随声明进行:
struct stuff Huqinwei = {"manager",30,185};
也可以考虑结构体之间的赋值:
struct stuff faker = Huqinwei;
//或 struct stuff faker2;
// faker2 = faker;
打印,可见结构体的每一个成员变量一模一样
如果不使用上边两种方法,那么成员数组的操作会稍微麻烦(用for循环可能好点)
Huqinwei.job[0] = 'M';
Huqinwei.job[1] = 'a';
Huqinwei.age = 27;
Huqinwei.height = 185;
结构体成员变量的访问除了可以借助符号".",还可以用"->"访问(下边会提)。
struct stuff
{ char job[20];
int age;
float height;
struct stuff *p;
}Huqinwei;
注意这里的结构体自引用中的指针只能写struct stuff *p不能写成Huqinwei *p,
联合:
union中的变量共用一段内存
四、 动态内存分配
malloc free
数组越界编译器不会检查出来,所以要小心
头文件:#include <stdlib.h>
malloc() 函数用来动态地分配内存空间
void* malloc (size_t size);
【参数说明】size 为需要分配的内存空间的大小,以字节(Byte)计。
【函数说明】malloc() 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。如果希望在分配内存的同时进行初始化,请使用 calloc()函数。
【返回值】分配成功返回指向该内存的地址,失败则返回 NULL。
由于申请内存空间时可能有也可能没有,所以需要自行判断是否申请成功,再进行后续操作。
如果 size 的值为 0,那么返回值会因标准库实现的不同而不同,可能是 NULL,也可能不是,但返回的指针不应该再次被引用。
注意:函数的返回值类型是 void *,void 并不是说没有返回值或者返回空指针,而是返回的指针类型未知。所以在使用 malloc() 时通常需要进行强制类型转换,将 void 指针转换成我们希望的类型,例如:
char*ptr=(char*)malloc(10);// 分配10个字节的内存空间,用来存放字符
动态内存分配举例:
#include<stdio.h>/* printf, scanf, NULL */
#include<stdlib.h>/* malloc, free, rand, system */
int main()
{
int i,n;
char*buffer;
printf("输入字符串的长度:");
scanf("%d",&i);
buffer=(char*)malloc(i+1);// 字符串最后包含 \0
if(buffer==NULL)exit(1);// 判断是否分配成功
// 随机生成字符串
for(n=0;n<i;n++)
buffer[n]=rand()%26+'a';
buffer[i]='\0';
printf ("随机生成的字符串为:%s\n",buffer);
free(buffer);// 释放内存空间
system("pause");
return 0;
}
运行结果:
输入字符串的长度:20
随机生成的字符串为:phqghumeaylnlfdxfirc
该程序生成一个指定长度的字符串,并用随机生成的字符填充。字符串的长度仅受限于可用内存的长度。
网友评论