嵌入式C语言基础知识查漏补缺 -- 复合类型&内存&等等

目录

1. 生成随机数

2. 传值和传地址

3. 返回变量的地址:

4. 返回堆区的地址:

5. 结构体数组

6. 结构体指针

7. 结构体套指针

8. 结构体数组作为函数的形参

9. 枚举:

10. 内存存储方式:大小端

 11. 习题

12. typedef的作用

13. sizeof

14. 内存分区

(1)程序运行前

(2)程序运行后

 15. 栈的生长方向


 

1. 生成随机数

srand():在调用rand()函数之前使用,是一个设置随机数种子的函数。

rand():产生随机数的函数。

time():time(NULL)这个函数的返回值是作为srand()函数的参数的。意思是以现在的系统时间作为随机数的种子来产生随机数。参数为NULL,用来获取系统时间。

int main()
{
    unsigned long n;
    srand((unsigned)time(NULL));
    for (int i = 0; i < 10; i++)
    {
        //n = rand() % 26 + 'a';
        n = rand();
        printf("%d\n", n);
    }

    return 0;
}

2. 传值和传地址

通过函数的值传递,不能改变实参的值。但是传实参的地址,可以在调用函数时改变实参的值。

3. 返回变量的地址:

只有普通局部变量的地址不可以返回,因为普通局部变量在所在函数结束之后就被释放。

eg:

int a = 10;
int* fun()
{
    static int a =10;
    a *= 10;
    return &a;
}

int main()
{
    int *p = fun();
    *p = 2000;            // p 所指向的空间没有被释放(静态局部变量),则可以操作这个地址
    printf("%d\n", *p);

    return 0;
}

4. 返回堆区的地址:

堆区的地址可以被返回,只要没有被释放。

5. 结构体数组

是一个数组,数组的每一个元素都是结构体。

struct stu
{
    int id;
    int age;
    char name[24];
};

int main()
{
    struct stu num[3] = { {1,10,"paul"},{2,20,"rose"},{3,30,"kobe"}};
    for (int i = 0; i < sizeof(num) / sizeof(num[0]); i++)
    {
        printf("%d %d %s\n", num[i].id, num[i].age, num[i].name);
    }

    return 0;
}

6. 结构体指针

struct STUDENT
{
    int id;
    int age;
    char name[24];
};

int main()
{
    struct STUDENT stu;         //定义一个结构体变量stu
    struct STUDENT* p = NULL;   //定义一个指向struct STUDENT结构体类型的指针变量 p
    p = &stu;
    strcpy(p->name, "wo");
    p->id = 1;
    p->age = 18;
    printf("%d %d %s ", p->id, p->age, p->name);

    return 0;
}

7. 结构体套指针

首先来看一个指针的问题:

(1)对于 p = "hello"; 表示把字符串hello的地址赋给指针变量 p,没毛病;

(2)对于 strcpy(q, "world"); 必须要先给指针变量 q 申请一段空间,并将malloc申请的空间地址赋给 q,然后再执行copy动作。否则的话,q 就是个野指针。

int main()
{
    char* p;
    p = "hello";

    char* q;
    q = (char*)malloc(128);
    strcpy(q, "world");

    return 0;
}

再看下面这个:

struct t
{
    int a;
    char b;
};

struct tea
{
    int id;
    char* p;
    struct t* q;
};

int main()
{
    // tmp自身在定义的时候就有空间了,但是tmp的指向空间是在malloc后才有的;p、q同理。
    struct tea* tmp = (struct tea*)malloc(sizeof(struct tea));   //定义结构体指针变量 tmp 并为其开辟空间
    tmp->id = 1; 
    tmp->p = (char*)malloc(100);                     // p 是个野指针,要先开辟空间才能赋值
    strcpy(tmp->p, "hello");

    tmp->q = (struct t*)malloc(sizeof(struct t));   // q 是个野指针,要先开辟空间才能赋值
    tmp->q->a = 100;

    free(tmp->p);
    free(tmp->q);
    free(tmp);        //malloc了几次,就要释放几次;释放的时候从里往外进行

    return 0;
}

8. 结构体数组作为函数的形参

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include<string.h>

struct tea
{
    int id;
    char name[128];
};

void set_num(struct tea* p, int n)
{
    for (int i = 0; i < n; i++)
    {
        p[i].id = i + 10;
        char buf[128] = "";
        sprintf(buf, "%d%d%d", i, i, i);
        strcpy(p[i].name, buf);
    }
}

int main()
{
    struct tea num[5];
    memset(num, 0, sizeof(num));
    set_num(num, sizeof(num) / sizeof(num[0]));
    for (int i = 0; i < sizeof(num) / sizeof(num[0]); i++)
    {
        pritnf("%d %s\n", num[i].id, num[i].name);
    }

    return 0;
}

输出为:

10 000
11 111
12 222
13 333
14 444

9. 枚举:

枚举 {} 里面列举的常量的值默认从0开始。

10. 内存存储方式:大小端

 一般家用的电脑都是用的小端存储方式。如下例子解释,

int main()
{
	int a = 0x11223344;
	char* p = &a;

	printf("%x\n", *p);         //低位字节,低地址
	printf("%x\n", *(p + 1));
	printf("%x\n", *(p + 2));
	printf("%x\n", *(p + 3));   //高位字节,高地址
 
	system("pause");
	return 0;
}

输出为:

44
33
22
11

 11. 习题

(1)

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include<string.h>

typedef struct student
{
    char name[50];
    unsigned int age;
    int score;
}student;

void print_stu(student* stu, int n)
{
    int i = 0;
    printf("name\tage\tscore\n");
    for (i = 0; i < n; i++)
    {
        printf("%s\t%d\t%d\n", stu[i].name, stu[i].age, stu[i].score);
    }
}

void sort_stu(student* stu, int n)
{
    int i = 0;
    int j = 0;
    student tmp;

    for (i = 0; i < n - 1; i++)
    {
        for (j = 0; j < n - 1 - i; j++)
        {
            if (stu[j].score < stu[j + 1].score)
            {
                tmp = stu[j];
                stu[j] = stu[j + 1];
                stu[j + 1] = tmp;
            }
            else if (stu[j].score == stu[j + 1].score)
            {
                if (stu[j].age > stu[j + 1].age)
                {
                    tmp = stu[j];
                    stu[j] = stu[j + 1];
                    stu[j + 1] = tmp;
                }
            }
        }
    }
}

int main()
{
    student stu[5] =
    {
        {"paul", 18, 90},
        {"rose", 19, 80},
        {"kobe", 19, 90},
        {"jack", 20, 60},
        {"pete", 19, 70}
    };
    int n = sizeof(stu) / sizeof(stu[0]);
    printf("排序前:\n");
    print_stu(stu, n);
    sort_stu(stu, n);

    printf("排序后:\n");
    print_stu(stu, n);

    return 0;
}

(2)fflush(stdout);    //刷新标准输出缓冲区,把输出缓冲区的东西打印到标准输出设备上,接在printf后面可提高打印效率

12. typedef的作用

(1)简化结构体创建变量

struct MY_WORLD
{
	int x;
	char y;
};
typedef MY_WORLD myworld;
等价于
typedef struct MY_WORLD
{
	int x;
	char y;
};

(2)区分数据类型

char* x, y;
则x的数据类型是 char*;y的数据类型是 char。

typedef char* pchar;
pchar a,b;
则a的数据类型是 char*;b的数据类型是 char*。
等价于:char *a, *b;

(3)提高移植性

13. sizeof

(1)sizeof() 不是函数,是个运算符。它返回的是一个unsigned int类型的值。

(2)统计数组占用内存空间大小

另外需注意,当数组名传入到函数中,会被退化为指针,该指针指向数组中第一个元素的地址。所以第二个 printf 输出为4,cal_Array( int arr[ ] )中的int arr[ ]等价于int* arr。

void cal_Array(int arr[])
{
	printf("传给函数后size of arr = %d\n",sizeof(arr));
}

int main()
{
	int arr[] = { 1,2,3,4.5 };
	printf("size of arr = %d\n",sizeof(arr));
	cal_Array(arr);

	system("pause");
	return 0;
}

输出为:

size of arr = 16
传给函数后size of arr = 4

14. 内存分区

(1)程序运行前

即程序没有加载到内存前,可执行程序内部已经分好了3段信息,分别为代码区(text)、数据区(data)、未初始化数据区(bss)。

  • text:代码区。共享的、只读的;
  • data:已初始化的全局变量、静态变量、常量;
  • bss:未初始化的全局变量、静态变量、常量;该区内的数据在程序开始执行前辈内核初始化为0或者空(NULL);

(2)程序运行后

        程序在加载到内存前,data区和bss区的大小就是固定的,程序运行期间不能改变。

        然后,运行可执行程序,操作系统把物理硬盘程序load到内存,除了根据可执行程序的信息分出text、data、bss外,还额外增加了栈区、堆区。

  • text:所有的可执行代码都加载到代码区,这块内存在运行期间不可修改;
  • bss和data:分别加载的是可执行文件的BSS段和数据段,这两个区的数据生存周期是整个程序运行过程。
  • 栈区:先进后出;存放函数的参数值、返回值、局部变量等。
  • 堆区:容量远大于栈,用于动态内存分配;在内存中位于BSS区和栈区之间。

注意事项:

① 局部变量的地址无法返回;

② 堆区的注意事项:如果在主调函数中没有给指针分配内存,那么被调函数中需要利用高级指针给主调函数汇总的指针分配内存。

如下两张图片所示,第一张中,指针 p并未被赋值,输出为NULL;第二张中,指针 p 被赋值为"helloworld"的地址。

 

 15. 栈的生长方向

栈底:高地址;栈顶:低地址。

 

int main()
{
	int a = 10;    //栈底,高地址
	int b = 20;
	int c = 30;    //栈顶,低地址

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

	system("pause");
	return 0;
}

输出为:

17825456
17825444
17825432

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值