C语言 11.使用小技巧

1.typedef使用

1.1起别名 - 简化struct关键字

1.2区分数据类型

1.3提高代码移植性


#define _CRT_SECURE_NO_WARNINGS    // VS下使用传统库函数会建议用_s更安全函数,如果不用会报错误  C4996
#include<stdio.h>   //标准   i input 输入   o  output  输出
#include<string.h>  //对字符串处理  strcat strstr strcmp strcpy
#include<stdlib.h>  //malloc(在堆区开放内存)   free   
/*       1.typddef使用            */
/*1.可以起别名*/ 
struct Person
{
    char name[64];
    int age;
};
//语法 typedef 原名  别名
typedef struct Person myPerson;
/*或者把前面的代码改成
typedef struct Person
{
    char name[64];
    int age;
}myPerson;
*/
void test01()
{
    struct Person p = { "aaaa",10 };       // 原来的调用方式
    myPerson p2 = { "bbbb",11 };           // 后来的调用方式
}
/* 2.区分数据类型,防止数据类型混淆*/
void test02()
{
    typedef char* PCHAR;
    PCHAR p1, p2;           // p1,p2是指针类型  
    //char* p1, p2;   // p1是指针,p2是char类型
    char* p3, * p4;    // 两个都是指针
}
/* 3.提高移植性,现在的用法比较少*/
typedef long long MYINT;
void test03()
{
    //long long a = 10; 
    //long long b = 100;
    // 早期编译器没有long long类型,可以把它改成MYINT类型
    MYINT a = 10;
    MYINT b = 100;
}
//main 函数 程序入口
int main() 
{
    //char buf[1024];
    //strcpy(buf, "hello world");
    //printf("%s\n", buf);
    system("pause");  //阻塞  请按任意键继续
    printf("aaaaaaaaaaaaaa");  // 有了上面那句下面这句会短暂的出现一次
    return EXIT_SUCCESS; //返回成功退出  0
}

VS创建多项目:

多项目

2.void使用

2.1不可以利用void创建变量 无法给无类型变量分配内存

2.2用途:限定函数返回值,函数参数

2.3void * 万能指针 可以不通过强制类型转换就转成其他类型指针



/* 1.void 无类型,不可以通过void创建变量,原因无法给void无类型变量分配内存   */
void test01()
{
    //void  a = 10;
}
/* 2.用途:限定函数返回值,限定函数参数 */
void test02()  
{
    return 10;
}
/* 3. (void * 万能指针  不管是几级指针,不管是什么类型的指针都是4个字节       //**/
void test03()
{
    printf("size of void* =%d\n", sizeof(void*));           // 4
    int * pInt = NULL;
    char * pChar = NULL;
    pChar = (char *)pInt;         // 没有强制转换会有警告
    void* p = NULL;
    pChar = p;                    // 没有警告   万能指针可以不通过强制类型转换就转成其他类型指针
}
//main 函数 程序入口
int main() 
{
    test02(10);     // 函数里没有参数,这样写会没警告没错误,函数里面有void就会出警告
    printf("%d\n", test02());  // test02函数前有void 这句就会报错,要是int或者没有就会报错
    test03();        // 不会报错      
    system("pause");  //阻塞  请按任意键继续
    printf("aaaaaaaaaaaaaa");  // 有了上面那句下面这句会短暂的出现一次
    return EXIT_SUCCESS; //返回成功退出  0
}

3.sizeof用法

3.1本质:不是一个函数,是一个操作符

3.2返回值类型 unsigned int无符号整型

3.3用途:可以统计数组长度


/*  1. sizeof本质 是不是一个函数? 不是函数,而是操作符(类似于加减乘除)       */
void test01()
{
    printf("size pf int = %d\n", sizeof(int));               //4  这里必须加括号
    double d = 3.14;
    printf("size of d = %d\n", sizeof d);                   // 8  这里括号可以去掉,如果是函数就必须加括号,这里可以不加可见不是函数
}
/*  2. sizeof返回值类型 无符号整型      */
void test02()
{
    //unsigned int a = 10;
     当unsigned int和int做运算,会转换成统一unsigned 数据类型,所以最终输出大于0
    //if (a - 20 > 0)
    //{
    //    printf("大于0\n");
    //}
    //else
    //{
    //    printf("小于0\n");
    //}
    // 输出大于0
    if (sizeof(int) - 5 > 0)
    {
        printf("大于0");
    }
    else
    {
        printf("小于0\n");
    }
}
/*  3. sizeof用途:统计数组长度,符号类型长度      */
// 当数组名做函数参数时,会退化为指针,指向数组中第一个元素的位置
void caclAray(int arr[])
{
    printf("array length  =%d\n", sizeof(arr));
}
void test03()
{
    int arr[] = { 1,2,3,4,5,6,7,8 };
    printf("array length  =%d\n", sizeof(arr));       // 32
    caclAray(arr);           //  4
}

4.变量的修改方式

4.1直接修改

4.2间接修改

4.3对自定义数据类型做练习


// 变量的修改方式
void test01()
{
    //1、直接修改
    int a = 10;
    a = 20;
    //2、间接修改 
    int* p = &a;
    *p = 30;
    printf("a = %d\n", a);
}
// 自定义数据类型的练习
struct Person
{
    char a;         // 0~3  因为
    int b;          //4
    char c;         //8
    int d;          //12
};
void test02()
{
    struct Person p = { 'a',10,'b',20 };
    // 直接修改 d 属性
    p.d = 1000;
    // 间接修改 d属性
    struct Person * pp = &p;
    /*pp->d = 1000;*/
    printf("%d\n", pp);
    printf("%d\n", pp + 1);      // 比上面多了16 
    char* pp = &p;
    *(int*)(pp + 12) = 2000;      // 通过指针的迁移来改变d属性
    *(int*)((int *)pp + 3) = 2000;  // 和上面相同
}

5.内存分区

5.1运行前

5.1.1代码区 共享 只读

5.1.2数据区 存放数据:全局变量 、静态变量、常量

5.1.2.1已初始化数据区 data

5.1.2.2未初始化数据区 bss

5.2运行后

5.2.1栈 符合先进后出数据结构,编译器自动管理分配和释放,有限容量

5.2.2堆 容量远远大于栈,不是无限。手动开辟 malloc 手动释放 free

6.栈区

6.1符合先进后出数据结构

6.2注意事项:不要返回局部变量的地址,局部变量在函数执行之后就被释放了,释放的内存就没有权限取操作了,如果操作结果未知



int* myFunc()
{
    int a = 10;        // 分配在栈上
    return &a;
}
void test01()
{
    // 局部变量a早已被释放,因此我们没有权限操作这块内存空间
    int* p = myFunc();     // 这里函数结束之后,内存就被释放了,所以无法获得内存信息
    printf("* p = %d\n", *p);       // 这里输出结果是对的,是因为做了优化
    printf("* p = %d\n", *p);
    /*
    * p = 10
    * p = -858993460*/
}
char* getString()
{
    char str[] = "hello world";
    return str;
}
void test02()
{
    char* p = NULL;
    p = getString(); 
    printf("%s\n", p);     // 结果未知 , 因为之前栈上的内存已经被被释放了
}

在这里插入图片描述

7.堆区

7.1利用malloc在堆区创建数据

7.2利用free释放堆区

7.3注意事项:主调函数没有分配内存,被调函数需要用更高级的指针去修饰低级指针,进行分配内存



int* getSpace()
{
    int * p = malloc(sizeof(int)*5);       
    if (p == NULL)
    {
        return; 
    }
    for (size_t i = 0; i < 5; i++)
    {
        p[i] = i + 100;
    }
    return p;
}
void test01()
{
    int* p = getSpace();
    for (size_t i = 0; i < 5; i++)
    {
        printf("%d\n", p[i]);     // 这里可以正确打印,因为这是在堆区开辟的内存,不会释放
    }
    // 手动开辟 手动释放
    free(p);
    p = NULL;
}
/***************************  注意事项  **************************/
void allocateSpace(char* pp)
{
    char* temp = malloc(100);
    memset(temp, 0, 100);            // 置空
    strcpy(temp, "hello world");
    pp = temp;
}
void test02()
{
    char* p = NULL;
    allocateSpace(p);
    printf("%s\n", p);   // (null)
}
void allocateSpace2(char** pp)
{
    char* temp = malloc(100);
    memset(temp, 0, 100);            // 置空
    strcpy(temp, "hello world");
    *pp = temp;
}
void test022()
{
    char* p = NULL;
    allocateSpace2(&p);
    printf("%s\n", p);   // 输出正确结果
}

8.static 和extern 区别

8.1特点:在运行前分配内存,程序运行结束生命周期结束 ,在本文件内都可以使用静态变量

8.2extern 可以提高变量作用域

int g_a = 1000; //在C语言下,全局变量前都隐式加了关键字 extern


/*   
static 静态变量
特点:在运行前分配内存,程序运行结束生命周期结束,在本文件内都可以使用
*/
static int a = 10;        // 全局作用域
void test01()
{
    static int d = 20;           // 局部作用域           a和b生命周期相同,但是作用域不相同
}
// 如果别的代码是g_b,这里extern int g_a, 会出现“1个无法解析的外部命令错误”,是链接阶段出现的错误
    // 告诉编译器,下面代码中出现g_a不要报错,是外部链接属性,在其他文件中
extern int g_a;
printf("%d\n", g_a);

在这里插入图片描述
在这里插入图片描述

9.常量

9.1const修饰的变量

9.1.1全局变量

9.1.1.1直接修改 失败 ,间接修改 语法通过,运行失败,受到常量区保护

9.1.2局部变量

9.1.2.1直接修改 失败 , 间接修改成功,放在栈上

9.2字符串常量

9.2.1vs 将多个相同字符串常量看成一个

9.2.2不可以修改字符串常量

9.2.3ANSI并没有制定出字符串是否可以修改的标准,根据编译器不同,可能最终结果也是不同的



/*   
const 修饰的变量
在C语言中,const修饰的局部变量,称为伪常量,不可以初始化数组
*/
// 全局变量
const int a = 10;       // 常量区
void test01()
{
    //a = 100;            // 直接修改失败
    int * p = &a;
    *p = 100;
    printf("%d\n", a);     // 间接修改,语法通过,运行失败,原因,受到常量区的保护
}
void test02()
{
    const int b = 10;      //  存放在栈上
    //b = 20;           // 直接修改失败
    int* p = &b;
    *p = 20;
    printf("%d\n", b);  // 间接修改成功
}
/*
字符串常量
*/
void test03()
{
    char* p1 = "hello world";
    char* p2 = "hello world";
    char* p3 = "hello world";
    char* p4 = "hello world";
    printf("%d\n", p1);                   // 5个地址完全相同
    printf("%d\n", p2);
    printf("%d\n", p3);
    printf("%d\n", p4);
    printf("%d\n", "hello world");
}
void test04()
{
    char* str = "hello world";
    str[0] = 'x';                              // 这里会报错,字符串常量无法修改    有些可以修改,因为用的标准不一样
   // 尽量不要修改字符串常量
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值