C 语言教程 在线

1559C 内存管理

对于 void 指针,GNU 认为 void *char * 一样,所以以下写法是正确的:

description = malloc( 200 * sizeof(char) );

但按照 ANSI(American National Standards Institute) 标准,需要对 void 指针进行强制转换,如下:

description = (char *)malloc( 200 * sizeof(char) );

同时,按照 ANSI(American National Standards Institute) 标准,不能对 void 指针进行算法操作:

void * pvoid;
pvoid++; //ANSI:错误
pvoid += 1; //ANSI:错误
// ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。

int *pint;
pint++; //ANSI:正确

更多内容参考:C 语言中 void* 详解及应用

1558C 可变参数

C 函数要在程序中用到以下这些宏:

void va_start( va_list arg_ptr, prev_param ); 
type va_arg( va_list arg_ptr, type ); 
void va_end( va_list arg_ptr );

va_list: 用来保存宏va_start、va_arg和va_end所需信息的一种类型。为了访问变长参数列表中的参数,必须声明 va_list 类型的一个对象,定义: typedef char * va_list;

va_start: 访问变长参数列表中的参数之前使用的宏,它初始化用 va_list 声明的对象,初始化结果供宏 va_arg 和 va_end 使用;

va_arg: 展开成一个表达式的宏,该表达式具有变长参数列表中下一个参数的值和类型。每次调用 va_arg 都会修改用 va_list 声明的对象,从而使该对象指向参数列表中的下一个参数;

va_end: 该宏使程序能够从变长参数列表用宏 va_start 引用的函数中正常返回。

va 在这里是 variable-argument(可变参数) 的意思。

这些宏定义在 stdarg.h 中,所以用到可变参数的程序应该包含这个头文件。

下面我们写一个简单的可变参数的函数,改函数至少有一个整数参数,第二个参数也是整数,是可选的。函数只是打印这两个参数的值。

#include <stdio.h>;  
#include <string.h>;  
#include <stdarg.h>;  

/* ANSI标准形式的声明方式,括号内的省略号表示可选参数 */  
int demo(char *msg, ... )  
{  
    va_list argp;                    /* 定义保存函数参数的结构 */  
    int argno = 0;                    /* 纪录参数个数 */  
    char *para;                        /* 存放取出的字符串参数 */                                      
    va_start( argp, msg );          /* argp指向传入的第一个可选参数,      msg是最后一个确定的参数 */  
    
    while (1) 
    {  
        para = va_arg( argp, char *);                 /*      取出当前的参数,类型为char *. */  
        if ( strcmp( para, "/0") == 0 )  
                                                      /* 采用空串指示参数输入结束 */  
            break;  
        printf("Parameter #%d is: %s/n", argno, para);  
        argno++;  
    }  
    va_end( argp );                                   /* 将argp置为NULL */  
    return 0;  
}

int main( void )  
{  
    demo("DEMO", "This", "is", "a", "demo!" ,"333333", "/0");  
}    

从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:

  • 1)首先在函数里定义一个 va_list 型的变量,这里是 arg_ptr,这个变量是指向参数的指针。
  • 2)然后用 va_start 宏初始化变量 arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。
  • 3)然后用 va_arg 返回可变的参数,并赋值给整数 j。va_arg 的第二个参数是你要返回的参数的类型,这里是int型。
  • 4)最后用 va_end 宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。如果函数有多个可变参数的,依次调用 va_arg 获取各个参数。

1557C 可变参数

// 64 位机器用 8 字节对齐, 32 位 4 位对齐
#ifdef X64
#defin t long long
#else
#define t int
#endif

//VA_LIST套宏中可以使用,用来改变INTSIZEOF中t的类型
//固定参数详见
void test(int a, double b, char* c)
{
    char *p = (char*)&a;
    //因为&a = void 类型 需要转换,void * =&a 不需要转换但是使用时要转换
    printf("%p %p %p\n", &a, &b, &c);
    //观察地址变化
    printf("%p %s",(p+8),*(char**)(p+8+8));//64位机器时加8内存大小8字节对齐
    return;
}

//可变参数实验
void test1(char* s,char *st,...)
{
    char *ppt =(char*)&s;
    //printf("%p %p %p %p,",ppt,&s,&st,(char*)ppt+8);
    printf("%p %p %p %p\n", ppt, &s, &st, ppt + 8);
    printf("%s\n", *(char**)(ppt+4));
    printf(" %d\n",*(int*)(ppt + 4+4));//当是X64就加8 X86就加4因为内存对齐规则
    return;
}

int main()
{
    char *p = "Hello world";
    test1("111","eee",45234,23);
    //test(2, 2.2, "Hello world");x
    void *s = &p;
    printf("%s", *(char**)s);
    return 0;
}

1556C 可变参数

一、可变参数

#include <stdio.h>

void debug_arg(unsigned int num, ...) 
{
    unsigned int i = 0;
    unsigned int *addr = &num;
    for (i = 0; i <= num; i++) 
    {
        /* *(addr + i) 从左往右依次取出传递进来的参数,类似于出栈过程 */
        printf("i=%d,value=%d\r\n", i, *(addr + i));
    }
}
int main(void)
{
    debug_arg(3, 66, 88, 666);
    return 0;
}

可变参数的工作原理,以32位机为例:

  • 1.函数参数的传递存储在栈中,从右至左压入栈中,压栈过程为递减。
  • 2.参数的传递以4字节对齐,float/double这里不讨论。

1555C 递归

采用递归方法来解决问题,必须符合以下三个条件:

1、可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。

说明:解决问题的方法相同,调用函数的参数每次不同(有规律的递增或递减),如果没有规律也就不能适用递归调用。

2、可以应用这个转化过程使问题得到解决。

说明:使用其他的办法比较麻烦或很难解决,而使用递归的方法可以很好地解决问题。

3、必定要有一个明确的结束递归的条件。

说明:一定要能够在适当的地方结束递归调用。不然可能导致系统崩溃。