Day One

一、typedef

1.可以起别名: typedef+原名+别名

struct person 
{
    char name[64];
    int age;
};
​
typedef struct Person myPerson;
void test01()
{
    struct Person p = {"aaa",10};
    myPerson p2 = {"bbb",20};
}

2.区分数据类型

#include <stdio.h>
#include <iostream>
#include <typeinfo>
using namespace std;
​
int main()
{
    char *p1, p2;
    int p3 = 0;
    printf("p1数据类型是:%s\n", typeid(p1).name()); //char*
    printf("p2数据类型是: %s\n", typeid(p2).name()); //char
    printf("p3数据类型是: %s\n", typeid(p3).name());
    system("pause");
    return 0;
}
​
//typedef char * pchar
//pchar p1,p2  两个都是char*

3.提高移植性

typedef int myint;
void test()
{
    myint a = 10;
}
//换类型就好,保证兼容性

二、void

1.修饰函数参数和返回

2.不可使用创建变量

3.void * 指针

//(1)由于void 指针没有特定的类型,因此可以指向任何类型的数据。因此他可以指向任何类型的数据。也就是说,任何类型的指针都可以直接赋值给 void 指针,而无需进行其他相关的强制类型转换。
    double d = 1.54;
    void * p = & d;
//(2)虽然在 C 语言中,将 void* 指针赋值给其他指针类型是合法的,但在 C++ 中,需要进行显式的类型转换。这是为了确保类型安全性,避免隐藏的错误。可以使用 static_cast 进行显式的类型转换。
    #include <stdio.h>
    #include <windows.h>
    int main()
    {
        void *p = NULL;
        printf("void * = %d \n", sizeof(void *)); 
        //32位机器是4位,64位机器是8位
        int *pInt = NULL;
        char *pChar = NULL;
        pChar = static_cast<char *>(p);
        //显式的类型转换
        system("pause");
        return 0;
    }
//(3)避免对 void 指针进行算术操作
 

三、sizeof

不是一个函数,而是一个操作符

#include <stdio.h>
#include <windows.h>
​
int main()
{
    char *a;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof(*a));
​
    int b, c = 1;
    printf("%d %d %d\n", sizeof(++b), 4, sizeof(int));
​
    printf("%u\n %u\n", 0xFFFFFFFF, -1);
    printf("%d\n %d\n", 0xFFFFFFFF, -1);
    printf("%x\n %x\n", 0xFFFFFFFF, -1);
    printf("%o\n %o\n", 0xFFFFFFFF, -1);
    system("pause");
    return 0;
}

四、变量的修改

1.间接修改

//(1)比较两个结构体之间的位置
#include <stdio.h>
#include <windows.h>
struct Person
{
    char a;
    int b;
    char c;
    int d;
};
int main()
{
    struct Person p = {'a', 10, 'b', 20};
    struct Person *pp = &p;
    printf("%d\n", pp);
    printf("%d\n", pp + 1);
    system("pause");
    return 0;
}
//输出结果的值相差16的原因:据默认的对齐规则,结构体变量中成员对齐,所以即使char本身是1个字节,但其后会自动补充一些字节来保证成员之间的对齐
​
//(2)修改变量值
    int a = 100;
    int *pInt = &a;
    *pInt = 61;
    printf("%d\n", a);
​
//(3)修改结构体成员变量的值
    struct Person p = {'a', 10, 'b', 20};
    struct Person *pp = &p;
    printf("%d\n", pp);
    printf("%d\n", pp + 1);
​
    printf("%d\n", p.d); // 20
    char *pst = (char *)pp;
    *(int *)(pst + 12) = 2000;
    printf("%d\n", p.d); // 2000
​
//char *pst = (char *)pp;
// *(int *)(pst + 12) = 2000;
//其中这两行代码先进行了一个指针的类型转换,而后再通过间接方式完成了对值的修改
//pst+12找到了最后一个int的d,后将原本char*的pst换成int*类型,再解引用修改值

五、内存分区

1.栈区(stack):存放函数形参和局部变量,由编译器自动分配和释放,先进后出,有限空间。

void add (int a,int b) //形参a,b
{
    int c = 0; // 局部变量c
    return a+b+c;
}

2.堆区(heap):该区由程序员申请后使用,需要手动释放否则会造成内存泄露。如果程序员没有手动释放,那么程序结束时可能由操作系统回收。先进先出,一般是malloc。

3.全局/静态存储区:存放全局变量和静态变量(包括静态全局变量与静态局部变量),

  • 初始化的全局变量与静态局部变量放在一起(data段)

    int a = 0;
    static int b = 10;

  • 未初始化的放在一起(bss段)

    int a;
    char *p;

4.文字常量区:常量在统一运行时被创建,常量区的内存是只读的,程序结束后由系统释放。

5.程序代码区: 存放程序的二进制代码,内存由系统管理。(4、5为text段)

  • text 和 data 段都在可执行文件中,由系统从可执行文件中加载;

  • 而 bss 段不在可执行文件中,由系统初始化。

六、栈区

#define _CRY_SECURE_NO_WARNINGS	//告诉编译器忽略与安全相关的警告信息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

int *myFunc()
{
    static int a = 10;  
    //原本使用int a = 10 时,会报错。因为此时的a是一个局部变量,它的生命周期仅限于该函数的执行过程。当函数返回时,其局部变量和栈帧都会被销毁。
    //静态变量的生命周期会持续到程序的整个运行期间,并且在每次调用函数时,该变量的值都会保留
    return &a;
}
void test()
{
    int *p = myFunc();
    printf("%d \n", *p);
}
int main()
{
    test();
    system("pause");
    return 0;
} 

//当函数返回时,其局部变量和栈帧都会被销毁。所以无法打印hello world
char *getString()
{
    static char str[] = "hello world";
    return str;
}
void test2()
{
    char *p = NULL;
    p = getString();
    printf("%s\n", p);
}


//修改。考虑将 getString() 函数改为使用动态内存分配来存储字符串,并返回相应的指针
char *getString()
{
    // static char str[] = "hello world";
    // return str;
    char *str = (char *)malloc(sizeof("hello world"));
    strcpy(str, "hello world");
    return str;
}

完整代码

#define _CRY_SECURE_NO_WARNINGS // 告诉编译器忽略与安全相关的警告信息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

int *myFunc()
{
    static int a = 10;
    return &a;
}
char *getString()
{
    // static char str[] = "hello world";
    // return str;
    char *str = (char *)malloc(sizeof("hello world"));
    strcpy(str, "hello world");
    return str;
}
void test()
{
    int *p = myFunc();
    printf("%d \n", *p);
}

void test2()
{
    char *p = NULL;
    p = getString();
    printf("%s\n", p);
}

int main()
{
    test();
    test2();
    system("pause");
    return 0;
}

七、堆区

一般有malloc

#define _CRY_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

int *getSpace()
{
    int *p = (int *)malloc(sizeof(int) * 5);  
    //此处的malloc函数的返回值其实是一个void* 需要转换指针类型
    if (p == NULL)
    {
        return NULL;
    }
    for (int i = 0; i < 5; i++)
    {
        p[i] = i + 100;
    }
    return p;
}

void test()
{
    int *p = getSpace();
    //此处调用函数不会出现问题,因为有了malloc,堆中的数据会直到手动回收后消失
    for (int i = 0; i < 5; i++)
    {
        printf("%d\n", p[i]);
    }
}

int main()
{
    test();
    system("pause");
    return 0;
}

八、堆栈注意

1.空指针赋值(错误示范及示意图)

void allocateSpace(char *pp)
{
    char *temp = (char *)malloc(100);
    memset(temp, 0, 100);
    strcpy(temp, "hello world");
    pp = temp;
}

void test2()
{
    char *p = NULL;
    allocateSpace(p);
    printf("%s \n", p); //NULL
}

2.成功代码(二级指针)

void allocateSpace(char **pp)
{
    char *temp = (char *)malloc(100);
    memset(temp, 0, 100);
    strcpy(temp, "hello world");
    *pp = temp;
}

void test2()
{
    char *p = NULL;
    allocateSpace(&p);
    printf("%s \n", p);
}

补充:memset()函数

void *memset(void *s, int c, size_t n)函数

  • s指向要填充的内存块。

  • c是要被设置的值。

  • n是要被设置该值的字符数。

  • 返回类型是一个指向存储区s的指针。

九、Static and Extern

1.static 静态变量

特点:在运行前分配内存(活)。程序运行结束则生命周期结束。本文件中都可以使用。

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

static int a = 10;//全局静态

void test1()
{
    //局部静态
    static int b = 20; 
    //与a的生命周期相同,但作用域不同
}
int main()
{
    printf("%d\n",a);
    system("pause");
    return 0;
}

2.extern 外部变量

extern int g_a; //告诉编译器不要报错,g_a是一个外部链接属性

十、常量

1.用const修饰的不能修改

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

const int a = 10;

void test1()
{
    int *p = &a; //受系统保护无法直接用&a取
    *p = 100;
    printf("%d\n",a);
}
void test2()
{
    const int b =20;
    int *p = &b; // 受系统保护无法直接用&b取
    *p = 30;
    printf("%d\n",b);
}
int main()
{
    test1();
    system("pause");
    return 0;
}

2.字符串常量

void test3()//指向同一地址
{
    char *p1 = "aaa";
    char *p2 = "aaa";
    char *p3 = "aaa";
    printf("%d\n%d\n%d\n", p1, p2, p3);
}

//不能够直接修改字符,且尽量不要直接修改字符串常量
void test3()
{
    char *p1 = "aaa";
    char *p2 = "aaa";
    char *p3 = "aaa";
    char *str = "acd";
    // str[0] = "x";
    printf("%c\n", str[0]);
    printf("%d\n%d\n%d\n", p1, p2, p3);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值