目录
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