c语言中总结

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;

}

发布了60 篇原创文章 · 获赞 57 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/dreams_deng/article/details/104266017