1. 基础概念
1.1. 安装环境:
安装Qt Creator 4.6.1 (Community), 自带gcc开发环境, 配置环境变量:
package\5.11.0\mingw53_32\bin
package\Tools\mingw530_32\bin
编译:gcc hello.c
1.2. C语言的编译过程:
预编译 汇编 编译 链接
.c -> 引入头文件、去掉注释、宏替换 -> 编译成汇编 -> 然后在编程成二进制文件 --->
加入c执行的库 -> 可执行程序
c编程可执行程序步骤:
预编译->编译->汇编->链接
1.预处理:去掉注释、加载头文件、代替宏定义、条件编译生成预编译文件.i结尾
gcc hello.c -E -o hello.i
2.编译: 对c语言进行语法检查,如果有语法错误,报错终止,没有,编译成c独有的汇编代码
gcc hello.i -S -o hello.s
3. 汇编:
gcc hello.s -c -o hello.o
把汇编通过汇编器生成二进制机器语言,给cpu执行文件,已经是二进制不是文本文件
4. 链接
1.比如一个程序1.c 2.c 3.c三个文件构成,汇编以后1.o 2.o 3.o 链接-》hello整体
2.加载静态库(比如从操作系统加载printf)和动态库
gcc hello.o -o hello(生成可执行程序)
1.3. 指令集类型:RISC[精简指令集]和 CISC CPU[复杂指令集]
arm、mips(国产龙芯) : 精简指令集,基于arm架构
amd、intel:复杂指令集,基于x86架构,linux基于x86 CISC
32 位 x86 汇编叫做 I386
20%的指令为常用指令,在一个程序中执行的时候调用比例达到80%
80%指令不常用指令,在一个程序执行的时候调用比例20%
精简指令集复杂功能通过软件实现
arm公司只是设计指令集架构, 苹果、三星、高通购买指令集以后改,然后自己生产cpu
32位系统最大内存为4G
内核模式1G
用户模式3G
32位系统可以安装64位cpu, 运算还是安装32cpu来计算,c代码在64系统编译不能在32位上跑,所以软件有32位和64位区分
64位系统无法安装在32位cpu中
long 32位系统,4个字节,64位 windows 4个字节 unix 8个
long long 32、64都是 8个字节
int 32、64都是4个字节
unsigned : 最高位不表示符号位
char b = 1; => (-128~127) ,用char 保持数值
unsigned char a = 129; => (0~255)
特殊字符: \t、\n、
c语言类型限定:
volatile:
比如: int i=20; i=i+20; i=i+10; 那么会编译器会智能的识别: int i=i+30,提高效率
如果加了valatile int i=20;那么只能一行一行的计算了
register: 直接放入cpu寄存器中,不用去内存中读了,寄存器中变量没有内存地址,不能取地址
大小端存储
大端存储法——高地址存低字节,低地址存高字节(高存低,低存高)
(IBM大型机/网络字节序)
小端存储法——高地址存高字节,低地址存低字节(高存高,低存低)
(intel/ARM)
120 =》 0000 0000 0111 1000
地址:
0x8000 1000
0x8001 0111
0x8002 0000
0x8003 0000 高地址(0x8003 )存储高字节(1000 ),这个就是大端存储
1.4. c的 system函数使用
b.c
#include <stdlib.h>
#include <stdio.h>
int main(){
return 200;
}
hello.c
#include <stdlib.h>
#include <stdio.h>
int main(){
printf("hello ... jakc--\n");
/**
* system 执行操作系统命令或者运行指定程序
* gcc -o b b.c
*/
// system("dir");
// windows
// int i=system("b.exe"); // 把执行b程序的返回值赋值给i
// printf("--windows--%d--\n",i);
// system("pause");
// linux must /0x100
// linux 实际的结果必须要 除以 16进制的100
int i=system("./b");
printf("--linxu--%d--\n",i/0x100);
// 0 表示成功 -1表示失败
return 0;
}
2. c基本使用,指针
#include "stdafx.h"
#include "string.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#pragma warning(disable:4996)struct Student{
int age;
};void show(){
printf("hello world\n");
}
int add(int a, int b)
{
return a + b;
}
int func1(int(*p)(int, int), int a, int b)//第一个参数是指向函数的指针
{
return p(a, b);//通过指向函数的指针调用一个函数
}
// 如果要打印 p 数组内容,那么必须要传递n,否则无法知道p的长度
// 如果是 char* , 以\0结尾 知道长度
void print_array(int *p, int n){
int i;
for (i = 0; i < n; i++){
printf("--print_array--p[%d]=%d\n", i, p[i]);
}
}
void print_str(char* ch, int length){
printf("\n");
for (int i = 0; i < length; i++){
printf("%c", ch[i]);
}
}
// 二维数组作为函数参数
// void print_two_arr(int a9[][1])
void print_two_arr(int (*a9)[1]){
printf("print_two_arr--二维数组:%d\n", *(*(a9 + 1)));}
int _tmain0009(int argc, _TCHAR* argv[])
{
/* argc 函数参数个数
* 第一个参数argv[0] 就是程序本身
* 为什么需要 argc , 否则无法计算 char* args参数个数
*/
const int a_c = 1000; // c中的const是假的
int* a_c_p = &a_c;
*a_c_p = 440404040;
printf("const---%d\n", *a_c_p);int a = 100;
printf("%#x\n", &a); // 16进制输出地址
printf("%p", &a); //打印变量a的地址
int* c = (int*)(&a);
// 指针都是4个字节
printf("c------->%d--size:%d\n", *c,sizeof(c));// void* p: 无类型指针
void* c3 = NULL; // 空指针 ,指向变量NULL的指针
// 野指针指定执行任何内存空间的指针
// pp1可能是系统内部保存硬件的地址,修改了导致系统奔溃
//int *pp1;
//*pp1 = 30;
//printf("野...%d", *pp1);
// 指针兼容性
// 指针 12 1个字节 34 1个字节 56 1个字节 78 1个字节
int a5 = 0x12345678;
char* c6 = &a5;
printf("char0.....%#x\n", *c6); // 0x78
printf("char1.....%#x\n", *(c6+1)); // 0x56
printf("char2.....%#x\n", *(c6+2)); // 0x34
printf("char3.....%#x\n", *(c6+3)); // 0x12 小端存储,高存高,证明小端存储char b6 = 2;
int *p6 = &b6;
printf("p6-----%#x\n", *p6); //0x cccccc02 错误访问内存,未知内存 int 访问4个字节// 案例: IP在传输的的时候用int传输 192.168.120.222 15个字节 一个int4个字节
int ip6;
unsigned char* ip66 = &ip6;
*ip66 = 192;
ip66++;
*ip66 = 168;
ip66++;
*ip66 = 120;
ip66++;
*ip66 = 222;// 指针和数组的关系,指针是有类型的
// p1==p2 : 判断2个指针是否执行同一位置
int a6[] = { 100, 200, 300 };
for (int i = 0; i < sizeof(a6) / sizeof(int); i++){
// 数组名就是数组的首地址
printf("%d", (*(a6 + i)));
}
int* a7[10]; // 指针数组,存储指针的数组
int a8 = 10;
int* a8_1 = &a8;
int** a8_2 = &a8_1; // 二级指针,存储指针地址的指针
printf("二级指针:%d\n", **a8_2);// 数组越界
int arr[5] = { 1, 2, 3, 4, 5 };
printf("%d\n", arr[0]);
printf("%d\n", arr[5]); // 错误数据,数组越界后果,访问不属于自己内存//a[i][j] <==> *( *(a+i)+j ) 行指针 (a) 列指针 *(a+j)
int a9[2][1] = { { 2 }, { 4 } };
int (*p9)[1]; // 定义一个指针,指向 int [1] 这种数据类型,列为1,指向二位数组的指针,这个就是二维数组的指针
p9 = a9; // p9二维数组的第一行 p9++ 第二行 移动 4个字节 [1]*4
printf("p9--->%d\n", *(*(p9 + 1)));
// a9 a9[0] &a9[0][0] p9 地址都是相同的
printf("二维数组:%d\n", *(*(a9+1)) );
// 二维数组做为函数参数
print_two_arr(a9);// 二维长度
int a[2][1] = { { 2 }, { 4 } };
printf("size---%d \n", sizeof(a)); // 1*2*4=8个字节,内存中// 定义函数必须要传递长度情况
print_array(a6,3);
char a111[] = "abcdef";
a111[1] = '\0';
printf("--a10--%s", a111); // 输入a, printf函数输出内容是以\0结束的
// 自定义打印函数 解决printf函数不住
print_str(a111, sizeof(a111));int c1 = show; //函数名就是函数的入口地址,函数就是一条一条指令
printf("\n%d\n", c1);
int * c11 = (int*)c1;
void(*p)() = c11;
p(); // 直接调用,编译器帮助你找地址
(*p)(); // 找地址
//void show(){
// printf("hello world\n");
//}// 一个函数指针, 函数的回调
void(*px)();
px = show;
px();
int a20=func1(add,1000,2000); // 函数回调
printf("---回调---%d\n", a20);
// 定义函数类型
typedef void(FUN_TYPE)();
FUN_TYPE *p_fun = show;
p_fun();// 定义函数指针
typedef void(*FUN_TYPE_P)();
FUN_TYPE_P fun_type_p = show;
fun_type_p();// 把指针转化为函数指针类型写法
int c5= show;
void(*pFun3)() = (void(*)())(c5); //把地址转化为函数指针类型
pFun3();printf("pFun3的size:%d", sizeof(pFun3)); //指针4个字节
// 内存操作函数
int a30[10] = { 0 };
a30[0] = 22;
int a31[10] = { 0 };
// 把 a30 内容拷贝到 a31 , 第三个参数拷贝字节大小
//memcpy(a31, a30, sizeof(a30));
// memmove 和 memcpy 差不多, ,memcpy(&buf[3],&buf[5],15] 5后面是重叠部分,memcpy使用要避免内存重叠,可能会出问题,使用memmove
printf("\n a31....%d \n", a31[0]);
// 如果想把a30再次初始化为0
// buf 地址 要设置的值 内存大小,单位字节
memset(a30, 0, sizeof(a30));
getchar();
return 0;
}
3. 输入输出、字符串
/*
* 问题1: 如果utf-8如何解决
问题2: 如果有汉字有字母如何解决
*/
void chineseReverse(){
char str3[] = "中";
// gbk 一个中文2个字节, 01 【中】 23【国】
// 2和0调位 3和1调位 23 01 结果
int len = 0;
while (str3[len]){
len++;
}
printf("chineseReverse-size--%d \n", len);
int min = 0;
int max = len - 1; //3
while (min < max){
char tmp = str3[min]; // 0
str3[min] = str3[max-1]; // 0 <= 2
str3[max - 1] = tmp; // 0 => 2
tmp = str3[min+1]; // 1
str3[min+1] = str3[max ]; // 1 <= 3
str3[max] = tmp; // 3 => 1
min = min + 2;
max = max - 2;
}
printf("chineseReverse-result--%s \n", str3);
}
void getChinaAndEnglishLength(){
// 有汉字 有 字母 获取长度
char str4[] = "中1国";
int len4 = 0;
while (str4[len4]){
if (str4[len4] < 0){ //汉字的一个字节ascii码一定是负数
len4 = len4 + 2;
}
else{ //英文
len4 = len4 + 1;
}
}
printf("len4....%d\n", len4);
}
/*
* 去掉字符串右边空格
*/
void stringTrimRight(){
char str[] = "stringTrimLeft ";
int len = strlen(str);
printf("(%s)\n---length--%d-\n", str,strlen(str));
for (int i = len-1; i >= 0; i--){
if (str[i] != ' '){
str[i + 1] = '\0';
break;
}
}
printf("finally---(%s)\n", str);
}
/*
* 去掉字符串左边空格
*/
void stringTrimLeft(){
char str[] = " stringTrimLeft ";
// 把abc往前推,知道有几个空格,将非空格字符往前移动
int len = strlen(str); //9
int spaceLength = 0;
while (str[spaceLength++] == ' '); //4
spaceLength--; //3
int i = spaceLength;
while (str[i]){
str[i - spaceLength] = str[i];
i++;
}
str[len-spaceLength] = '\0';
printf("(%s)---length--%d--i--%d\n", str, strlen(str), spaceLength);
}
/*
* rand
*/
void srandFun(){
// 随机数种子,否则rand每次数据都是一样的
srand(time(NULL));
for (int i = 0; i < 10; i++){
int j = rand();
printf("--%d\n", j);
}
}
int _tmain00009(int argc, _TCHAR* argv[])
{
/*
int a = 0;
int b = 0;
scanf("%d",&a);
scanf("%d", &b);
printf("a+b=%d\n", a + b);*/
char getsstr[10] = { 0 };
//1. scanf是回车键、空格作为输入结束的标识,但是回车键本身并不作为字符串的一部分
// 如果输入内容大于数组长度,那么scanf就会缓冲区溢出,导致程序奔溃
//scanf("%s", getsstr);
// 2. gets认为回车是结束标识,空格不是, scanf、gets都会出现内存溢出风险,gets可以接收空隔
//gets(getsstr);
// 3. fgets // 只是读取sizeof(getsstr)个字符,其他的不要
//fgets(getsstr, sizeof(getsstr), stdin);
//puts(getsstr); // 自动加\n
// 字符串
char str1[] = { 'a', 'b', 'c' ,'\0'};
char str2[] = "abcdef"; // 自动加\0
printf("str1------%s \n", str1);
printf("str2------%s---length --%d \n", str2,strlen(str2));
printf("reverse str2 ---%s\n", strrev(str2));
chineseReverse(); // 汉字逆序
getChinaAndEnglishLength(); // 获取有文字英文字母字符串长度
stringTrimRight(); // 去掉右边空格
stringTrimLeft(); // 去掉左边空隔
srandFun(); // 随机数// 字符串函数c语言
puts("--------------------str function----------------------");
char ss1[100] = "abc";
char ss2[] = "ac";
// 把ss2内容合并到ss1, ss1要足够大,存在缓冲区溢出问题
//1.strcat(ss1, ss2);
// 安全替代函数
strncat(ss1, ss2, 1); // 可以指定追加多少个字符
puts(ss1);
// 2. strcmp
// 长度
// 长度不一样,根据逐个字符的ascII码
// 相同返回0
if (strcmp(ss1, ss2) == 0){
puts("同1");
}
else{
puts("不同1");
}
if (strncmp(ss1, ss2,1) == 0){ // 只比较第一个字符
puts("同2");
}
else{
puts("不同2");
}
// 3. strcpy, 把ss2内容覆盖拷贝ss1
char sss1[100] = "abc";
char sss2[100] = "ac";
strcpy(sss1, sss2);
puts(sss1);
// strncpy
//strncpy(sss1, sss2, 2);
//puts(sss1);//4. sprinf 格式化字符串
sprintf(sss1, "hello springf --%d", 100);
puts(sss1);//5. sscanf取出
int w = 0;
char *sscanfstr = "abc-4-abc";
sscanf(sscanfstr, "abc-%d-abc", &w);
printf("---w---%d\n", w);//6. strchar: 是否包含
// 找到返回 后面部分 4-abc 没有返回null
char* buf6 = strchr(sscanfstr, '4');
printf("strchr--%s\n", buf6);
buf6 = strstr(sscanfstr, "-4");
printf("%s\n", buf6);//7. strtok: 截取
char bufarr[] = "abc_def_efl";
// 第一次传递字符串,第二次传递NULL,没有找到返回NULl
//char* buf7 = strtok(bufarr, "_");
//printf("strtok1=%s\n", buf7);
//buf7 = strtok(NULL, "_");
//printf("strtok2=%s\n", buf7);
//buf7 = strtok(NULL, "_");
//printf("strtok3=%s\n", buf7);
char* buf7 = strtok(bufarr, "_");
while (buf7){
printf("strtok=%s\n", buf7);
buf7=strtok(NULL, "_");
}// 8. atoi、atof、atol (int,float,long)
char* str8 = "33r3";
int i8 = atoi(str8);
printf("i8=%d\n", i8);// char - > int
// int -> char
// char[]->int 逐个转化 【高位10^n乘以】
// int -> char[] 取出逐个位 /10 %10
char c9 = '5';
int i9 = c9 - '0';
c9 = 5 + '0';
printf("i9=%d,c9=%c\n", i9,c9);// 头文件定义,避免多次引入头文件
#ifndef __AA__ // 头文件名称
#define __AA__#endif
system("pause");
return 0;
}
4. 递归:
// 先序递归和后序递归
void digui1(int n){
if (n > 0){
n--;
printf("先序n=%d\n", n); // 在函数调用过程调用
digui1(n);
printf("后序n=%d\n", n); // 由后面往前调用
}
}
// 递归解决问题,年龄问题、斐波拉
int digui_age(int n){
if (n == 1){
return 10;
}
else{
return digui_age(n - 1) + 2;
}
}// 10 进制转化为2进制
// 9%2 1 9/2=4
// 4%2 0 4/2=2
// 2%2 0 2/2=1
// 1%2 1 1/2=0
// 1001
// 10 进制转化为16进制同理
int digui_num(int n){
int d = n % 2;
//printf("%d", d); // 如何把它倒叙输出,使用后续递归
if (n > 0){
n= n / 2;
digui_num(n);
printf("%d", d);
}
}
//int _tmain9999999(int argc, _TCHAR* argv[])
{digui1(3);
int s1=digui_age(10);
printf("%d\n",s1);
// 递归缺点,多个函数,消耗计算机资源
// 优点: 思路清晰
digui_num(9);
system("pause");
return 0;
}递归调用流程图:
5. 内存理解:
内存四区:
1.代码区:
程序被操作系统加载到内存的时候,所有可执行代码都被加载到代码区,不可以修改
2.静态区:
全局变量|静态变量
3. 栈区:
函数成员都保存在栈区,还是在执行过程中入栈,执行完毕以后出栈,后进先出
每个线程都有自己的栈
函数参数入栈从右往左
3.1 栈溢出
当栈空间以满,但是还往栈里压变量,栈溢出
比如 char array[1024*1024*100]={0} // 溢出
4. 堆
系统中内存管理是按照 页的方法管理,一页4K,不是按照字节
int *pp= malloc(sizeof(1)); // 实际分配4K内存
test.c
#include <stdlib.h>
#include <stdio.h>
int a0 = 100;
void print_test(){
printf("hell--test.c");
}
main.c
#include "stdafx.h"
#include "string.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#pragma warning(disable:4996)extern int a0; // 全局变量,必须使用extern修饰,表示有一个变量a已经在其他文件中定义了
//局部变量: 函数执行完毕,那么释放内存,没有了
//static: 静态变量, 只初始化一次,程序运行期间,静态变量一直存在,static修饰变量、函数只能在当前c文件中使用
void mystatic(){
static int a1 = 0; // 第一次调用mystatic()函数的时候a1初始化,后面调用mystatic()函数这句话不执行了
printf("static---%d\n", a1);
a1++;
}
int* geta(){
int a = 10; // 函数执行入栈,函数执行完毕以后出栈a,那么a不存在了,不能这么写
return &a;
}
int *geta1(){
int * p = malloc(sizeof(int)* 10);
return p;
}
// 这里p=0 ,这里传递是值传递
// getHeap执行完毕以后,p就消失了,p是栈中变量
// 解决方法: 使用二级指针
void getHeap(int *p){
printf("--getHeap-%p---\n", &p);
p = malloc(sizeof(int)* 10);
printf("--malloc-%p---\n", &p);
}
void getHeap2(int **p){
printf("--getHeap-%p---\n", &p);
*p = malloc(sizeof(int)* 10);
printf("--malloc-%p---\n", &p);
}
int main001()
{
printf("a0----%d\n", a0);
for (int i = 0; i<10; i++){
// mystatic();
}
// 函数可以直接调用 test.c中的函数
// print_test();// 堆内存,可以通过函数的返回值返回一个堆地址
//int * p1= malloc(sizeof(int)*10);
// int *p1= geta1();
// 里面可能有垃圾值,需要调用memset清理
// memset(p1,0,sizeof(int)*10);
// for(int i=0;i<10;i++){
// *(p1+i)=i;
// }
// for(int i=0;i<10;i++){
// printf("malloc--%d\n",*(p1+i));
// }
// free(p1);
int * p2 = NULL;
// 错误 一级指针
// printf("--start--%p--p2-%p---\n",p2,&p2);
// getHeap(p2);
// printf("--finally--p2-%p---\n",&p2);
// 二级指针解决
// getHeap2(&p2);
// calloc = malloc+memset
p2 = calloc(10, sizeof(int));
for (int i = 0; i<10; i++){
*(p2 + i) = i;
}
// 在原来的堆p中后面继续申请,保证内存连续
// 在原有内存的基础上增加5个内存
// 如果原来内存后面的内存没有被使用,那么直接在后面分配
// 如果原来内存后面的内存被使用,那么重新分配一块新内存
// 直接打印 p2和p3地址,可以判断是否重新分配
int *p3 = realloc(p2, sizeof(int)* 15);
for (int i = 10; i<15; i++){
*(p3 + i) = i;
}for (int i = 0; i<15; i++){
printf("%d\n", *(p3 + i));
}
free(p2);while (1){
malloc(sizeof(int)* 1000000);
}return 0;
}5. 结构体:
1. 结构体定义初始化
2. 结构体字节对齐
3. 结构体在嵌入式、如何解决内存
4. 结构体数组、结构体嵌套
struct student{
int id;
char name[256];
};/**
* 对齐原则:
* 1. 每一个需要对齐的变量位置都是以2的倍数对齐
* 2. 结构体大小最大字节整数倍
* 首先按照Int对齐,首先a 1个字节 [00位] b 2个字节,b不会放在[01位] 而是放在 [02位] ,[01]空出来 一行
* e 放下
* 只有不同数据类型存在才有这个问题中间空下来,如果都是相同数据类型,那么直接相加就行了
* 调整顺序,可以解决对齐问题而带来的内存浪费
*/
struct C{
char a; // 1
short b; // 2
char e; // 1 按照int对齐 4个字节一行
int d; // 按照long long 对齐 4个字节一行
long long c; // 8 个字节
// 一共24个字节
};// 结构体中变量可以定义为位
// a 为 1 位,1个bit a2为 1 个bit
// 结构体大小为8个 字节 sizeof(struct D), 计算机最小单位为字节
// 在嵌入式中用 下面来控制灯光 a1灯、a2灯、a3、a4、a5
struct D{
char a1 : 1;
char a2 : 1;
char a3 : 1;
char a4 : 1;
char a5 : 1;
// 1个字节
int a6 : 3;
// 4个字节
};struct CDD{ // 对齐 44 个字节
int name[10];
char a;
}//经典案例: 如何把浪费的内存使用起来
struct E
{
char a; // 8个字节,这里浪费了3个字节
int b;
};struct F1{
char a;
char b;
};
struct F{
char c; // 1
struct F1 f; //2 个 一行 中间不要
int b; // 4
// 一共8个字节
};struct J{
int id;
char* name;
};union K{
int a;
char b;
char* p;
};
// 默认REA=0,BLACK=1,YELLOW=2
// REA=5, BLACK, YELLOW 后面5,6,7 自动赋值
enum W{
REA=5, BLACK, YELLOW
};int _tmain(int argc, _TCHAR* argv[])
{
struct student stu1 = { 0 };
// memset(&stu,0,sizeof(stu));
// 初始化方式1
stu1.id = 100;
strcpy(stu1.name, "xiaojiejie");
printf("--id=%d--%s\n", stu1.id, stu1.name);
// 方式2
struct student stu = { 1, "xiaogege" };
printf("--id=%d--%s\n", stu.id, stu.name);printf("c---%d\n", sizeof(struct C));
struct D sd;
sd.a1 = 1;
sd.a2 = 1;
printf("struct D---%d\n", sizeof(struct D));// 如果把这 三个字节综合 使用
struct E a = { 'a', 2 };
char* p = &a; // 这样就可以把 浪费的内存也使用起来,c++不行
printf("%p\n", p);
p++;
*p = 3;
p++;
*p = 5;
p++;
*p = 8;// 结构体数组
struct student stu_arr[] = { { 1, "x1" }, { 2, "x2" } };
for (int i = 0; i<2; i++){
printf("--name---%s\n", stu_arr[i].name);
}// 结构体嵌套字节计算
printf("---%d\n", sizeof(struct F));// 结构体赋值
struct E e1; // 结构体的赋值,就是简单的内存拷贝
e1 = a;
printf("e1---a=%d->b=%d\n", e1.a, e1.b);// 结构体指针
struct E* e2 = &a;
printf("e2--a=%d->b=%d\n", e2->a, e2->b);// 堆数组使用注意,指针移动
struct E* e_array = malloc((sizeof(struct E) * 10));
struct E* p10 = e_array;
e_array->a = 100;
e_array++;
e_array->a = 200;
// 错误e_array++ 以后,位置偏移了,释放长度为10,释放了不属于自己的内存,
//free(e_array);
// 正确解决
free(p10);
// 结构体变量中指针
struct J j = { 100, "xiaoming" };
// printf("j..name..%s\n", j.name); // “"xiaoing" 保存在代码区// j.id = 100;
j.name = malloc(100); // 解决:必须要首先分配内存在堆中
strcpy(j.name, "xiaohei"); // 直接strcpy错误,j.name指着NULL,没有分配内存
printf("j..name..%s\n", j.name);
free(j.name);// 结构在堆中分配
struct J* j10 = malloc(sizeof(struct J));
j10->name = malloc(100);
strcpy(j10->name, "xiaoming");
// 释放顺序,必须要先释放j10->name 才释放 j10
printf("j10...%s\n", j10->name);
free(j10->name);
free(j10);
// 结构体作为参数
// 如果直接传递结构体,那么是值传递
// 传递引用,需要传递结构体指针// 联合体
union K k;
// 联合体大小,为数据类型的最大值
printf("union...%d\n", sizeof(union K));
k.a = 'a';
k.b = 100;
// 两者地址是相同的
printf("--a--%p--b--%p\n", &k.a, &k.b);
// a 和 b 都是 100, a和b同一块内存,b覆盖a
printf("--a--%d--b--%d\n", k.a, k.b);
// 如何联合体中有指针
// k.p = malloc(100);
// k.b = 10;
// free(k.p);
// 那么k.p=10, free(k.p)无法释放,
// 解决在下次修改赋值之前释放
// 枚举
enum W w = REA;
switch (w){
case REA:
puts("红色");
break;
case BLACK:
puts("黑色");
break;
}
getchar();
return 0;}