优秀的编程知识分享平台

网站首页 > 技术文章 正文

C语言宏函数和字符串初始化、格式化的使用方式

nanyue 2024-08-06 18:06:16 技术文章 4 ℃

1.宏函数的使用方式

使用宏函数在一定程度上比普通函数效率高,普通函数会有入栈和出栈上的时间花费。

通常将比较频繁短小的函数写为宏函数

它的优点是以时间换空间

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

#define MYADD(x,y) ((x)+(y))
//宏函数要保证运算的完整性  添加括号
//宏函数在一定程度上会比普通函数效率高,而普通函数会有入栈和出栈的实际
//通常会将一些使用频繁、短小的函数封装为宏函数
//优点:以空间换时间

int myadd(int x, int y)
{
    return x + y;
}

static void test01()
{
    int a = 10;
    int b = 20;

    int ret = MYADD(a, b)*20;//((a) + (b))*20

    printf("%d\n", ret);
}

int main01()
{
    test01();

    return 0;
}

2..变量的传递作用域

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

int g_a = 122;//全局区数据,在main、func1、func2中都可使用
static void func2()
{

}
static void func1()
{
    int a = 10;//在main函数中不可使用,在func1和func2中都可使用

    func2();
}

int main02()
{
    int a = 10;//在main和func1、func2中都可使用
    func1(&a);

    return 0;
}

3.栈的生长方向和内存的存储方式

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>


//1、栈 生长方向
static void test01()
{
    int a = 10;//栈底  高地址
    int b = 20;
    int c = 30;
    int d = 40;//栈顶  低地址

    printf("%d\n", &a);
    printf("%d\n", &b);
    printf("%d\n", &c);
    printf("%d\n", &d);
}

//2、内存存储方式
static void test02()
{
    int a = 0xaabbccdd;

    unsigned char* p = &a;

    printf("%x", *p);//dd    低位字节数据放在低地址
    printf("%x", *(p+1));//cc   高位字节数据放在高地址

}
int main03()
{
    //test01();
    test02();

    return 0;
}

4.空指针和野指针

空指针:允许向NULL和非法地址拷贝内存

野指针:未初始化指针

malloc后free,但是没有将指针置空

指针操作超越变量作用域

需要注意的是:

空指针可以释放,但野指针不可以释放

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

//1、空指针  不允许向NULL和非法地址拷贝内存
static void test01()
{
    char* p = NULL;
    //给p指向的内存区域拷贝内容
    strcpy(p, "1111");//err

    char* q = 0x1122;
    //给q指向的内存区域拷贝内容
    strcpy(q, "2222");//err
}

int* dowork()
{
    int a = 10;
    int* p = &a;
    return p;
}

//2、野指针
static void test02()
{
    int* p;//未初始化指针

    printf("%d\n", *p);



    //malloc后free,但是指针没有置空
    int* q = malloc(sizeof(int));
    *q = 100;
    free(q);//野指针 未置空
    q = NULL;//将free后的指针要置空,防止野指针的出现
    printf("%d\n", *q);



    //指针操作超越变量作用域
    int* p2 = dowork();//p2是野指针
    printf("%d\n", *p2);
    printf("%d\n", *p2);

}

static void test03()
{
    int* p = NULL;

    free(p);//空指针可以释放不止一次
    free(p);
    free(p);

    int* p2 = malloc(4);
    free(p);
    free(p);//此处指针释放后没有置空,是野指针,野指针不可以释放

}
int main04()
{
    //test01();
   // test02();
    test03();

    return 0;
}

5..指针的步长

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<stddef.h>//offsetof的头文件



//指针的步长
//指针变量+1之后 跳跃的字节数

static void test01()
{
    char* p = NULL;
    printf("%d\n", p);
    printf("%d\n", p+1);

    double* p2 = NULL;
    printf("%d\n", p2);
    printf("%d\n", p2 + 1);


}

//2、解引用的时候,取的字节数
static void test02()
{
    char buf[1024] = { 0 };

    int a = 1000;

    memcpy(buf+1, &a, sizeof(int));

    char* p = buf;//通过p找到buf首地址

    printf("buf中的a=%d\n", *(int*)(p+1));
}

//3、自定义数据类型
struct person
{
    char a;//0~3
    int b;//4~7
    char buf[64];//8~71
    int d;//72~75
};

static void test03()
{
    struct person p1 = { 'a',10,"hello world",1000 };

    //计算结构体中属性的偏移

    printf("p1.d的偏移量为:%d", offsetof(struct person, d));//计算结构体中成员d的偏移量
    

    //打印d的值
    printf("p1.d的值为:%d\n", *(int*)((char*)(&p1) + offsetof(struct person, d)));
}

int main05()
{
    //test01();
    //test02();
    test03();
    return 0;
}

6.指针的间接赋值

条件:一个普通变量和一个指针变量(一个实参或一个形参)

建立关系

通过*进行赋值

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

static void changevalue(int* p)//a2实参 p形参  int*p=&a2
{
	*p = 1000;
}

//1、一个普通变量和一个指针变量(或者实参和形参)
//2、建立关系
//3、通过*进行赋值
static void test01()
{
    int a = 10;
    int* p = NULL;
     
    p = &a;

    *p = 100;

    printf("%d\n", a);


    int a2 = 10;
    changevalue(&a2);
    printf("%d\n", a2);


    printf("%d\n", &a2);
}
int main06()
{
    test01();

    return 0;
}

7.指针作为函数参数

输入特性:在主调函数中分配内存,被调函数使用

输出特性:在被调函数中分配内存,主调函数使用

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>


//1、输入特性:主调函数中分配内存,被调函数中使用内存


static void func(char* p)
{
    strcpy(p, "hello world");
}

static void test01()
{
    //分配到栈上
    char buf[1024] = { 0 };

    func(buf);

    printf("%s\n", buf);
}

static void printstring(char* str)
{
    printf("%s\b", str);
}

static void test02()
{
    //分配到堆区
    char* p = malloc(sizeof(char) * 64);

    memset(p, 0, 64);

    strcpy(p, "hello world");

    printstring(p);
}




//2、输出特性:被调函数分配内存,主调函数使用内存
static void allocatespace(char** pp)
{
	char* temp = malloc(sizeof(char) * 64);
    memset(temp, 0, 64);
    strcpy(temp, "hello world");
    *pp = temp;
}

static void test03()
{
    char* p = NULL;
    allocatespace(&p);

    printf("%s\n", p);
}
int main07()
{
    //test01();
    //test02();
    test03();

    return 0;
}

8..字符串的拷贝、逆置和初始化

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>


static void test01()
{
    //注意字符串需要有结束标志'\0'
    char str1[] = { 'h','e','l','l','o','\0' };//不加'\0'末尾有乱码
    printf("%s\n", str1);

   
    //初始化部分数组,剩余填写0
    char str2[100] = { 'h','e','l','l','o' };
    printf("%s\n", str2);

    
    //如果以字符串初始化,那么编译器默认会在字符串尾部添加'\0';
    char str3[] = "hello";
    printf("%s\n", str3);
    printf("sizeof str:%d\n", sizeof(str3));//sizeof统计\0
    printf("strlen str:%d\n", strlen(str3));//strlen不统计\0


    char str4[100] = "hello";
    printf("sizeof str:%d\n", sizeof(str4));//100
    printf("strlen str:%d\n", strlen(str4));//5


    char str5[] = "hello\0world";
    printf("%s\n", str5);//hello
    printf("sizeof str5:%d\n", sizeof(str5));//12
    printf("strlen str5:%d\n", strlen(str5));//5 统计到\0结束


    //8进制表示方式是在数字前加0
    char str6[] = "hello\012world";//  \012是8进制下的\10  \10在ASCII中是换行的意思
    printf("%s\n", str6);
    printf("sizeof str6:%d\n", sizeof(str6));//12
    printf("strlen str6:%d\n", strlen(str6));//11

}

//字符串拷贝
//参数1__目标字符串    参数2__源字符串
//将源字符串内容拷贝到源字符串
//方式1 利用[]方式拷贝
static void copystring01(char* dest, char* source)
{
    int len = strlen(source);
    for (int i = 0; i < len; i++)
    {
        dest[i] = source[i];
    }
    dest[len] = '\0';
}


//2、第二种利用字符串指针进行拷贝
static void copystring02(char*dest,char*source)
{
    int len = strlen(source);
    while (*source != '\0')
    {
        *dest = *source;
        dest++;
        source++;
    }
    *dest = '\0';
}
static void test02()
{
    char* str = "hello world";

    char buf[1024];

    //copystring01(buf, str);
    copystring02(buf,str);

    printf("%s\n", buf);
}

//方式3
static void copystring03(char* dest, char* source)
{
    while (*dest++ = *source++){}
}





//字符串逆置 方式1
static void reversestring01(char*str)
{
    int len = strlen(str);

    //起始位置下标
    int start = 0;

    //结束位置下表
    int end = len - 1;

    while (start < end)
    {
        char temp = str[start];
        str[start] = str[end];
        str[end] = temp;

        start++;
        end--;
    }

}

//方式2利用指针
static void reversestring02(char* str)
{
    int len = strlen(str);

    char* start = str;

    char* end = str + len - 1;

    while (start < end)
    {
        char* temp = *start;
        *start = *end;
        *end = temp;

        start++;
        end--;
    }
}

static void test03()
{
    char str[] = "abcedf";

    //reversestring01(str);
    reversestring02(str);
    
    printf("%s\n", str);

}

int main09()
{
    //test01();
    //test02();
    test03();

    return 0;
}

9.sprintf格式化字符串

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

//sprintf 格式化字符串
void test01()
{
    char buf[1024];
    memset(buf, 0, 1024);

    sprintf(buf, "%d年%d月%d日", 20121, 12, 13);

    printf("%s\n", buf);


    //字符串拼接
    memset(buf, 0, 1024);
    char str1[] = "hello";
    char str2[] = "world";
    int len = sprintf(buf, "%s%s", str1, str2);//sprintf返回值是字符串长度
    printf("buf:%s len%d\n", buf, len);


    //数组转换为字符串
    memset(buf, 0, 1024);
    int num = 100;
    sprintf(buf, "%d\n", num);
    printf("buf:%s\n", buf);

    //设置宽度  右对齐
    memset(buf, 0, 1024);
    sprintf(buf, "%8d", num);
    printf("buf:%s\n", buf);

    //设置宽度  左对齐
    memset(buf, 0, 1024);
    sprintf(buf, "%-8d", num);
    printf("buf:%s\n", buf);

    //转成16进制字符串  小写
    memset(buf, 0, 1024);
    sprintf(buf, "0x%x", num);
    printf("buf:%s\n", buf);

    //转成8进制字符串  
    memset(buf, 0, 1024);
    sprintf(buf, "0%o", num);
    printf("buf:%s\n", buf);

}

int main()
{
    test01();
    return 0;
}
最近发表
标签列表