C语言基础

it2024-12-28  18

一. 关于函数

自定义函数 (1)在函数使用之前定义函数 (2)先声明这个函数,然后使用,使用的代码后面定义这个函数 ```c #include <stdio.h> void syahello(){ println("helloo"); }

int main(){ sayhello(); // 调用的函数要提前写出来,如果要写在后面,需要先在前面声明 }


void syahello(); //提前声明,没有实现 int main(){ sayhello(); // 调用的函数要提前写出来,如果要写在后面,需要先在前面声明 } void syahello(){ println("helloo"); }


mytest1(); //如果函数没有明确标明函数的返回值类型,则默认为返回int(不要这样写) print("%d\n",mytest1()); ```

参数传递 (1)形参和实参是值传递,是单向传递,只能由实参传递给形参,不能由形参传递给实参 (2)如果函数的参数是数组,则可以通过形参修改实参的值

void testarr(char s[]){ s[0] = 'a'; s[1]='b'; } int main(){ char chs[100] = "hello world" testarr(chs); printf("%d",chs); // 数组变为abllo world } ------------------------------ void mergearr(char arr1[],char arr2[]){ int len1 = 0; while(arr1[len1++]); len1--; // 记录数组一的长度 int len2=0; while(arrp[len2+1]); len2--; int i; for(i=0;i<len2;i++){ arr1[len1+i] = arr2[i]; } } int main(){ char arr1[100]="asdad"; char arr2[100]="1111"; mergearr(arr1,arr2); printf("%s\n",arr1); // 发现数组2已经合并了 printf("%s\n",arr2); }

二. 多文件编译

文件位置指示 (1) <stdio.h> : 尖括号表示文件在系统路径中 (2) "abc/myhead.h":表示abc目录下的myhead.h ```c myhead.h: #define MAX 300 // #define就是简单的字符串替换

main.c: #include <stdio.h> #include "abc/myhead.h"

int main(){ int c = MAX; printf("%d",c) } ```

三. 指针

计算机内存的最小单位使BYTE(字节) (1)每个BYTE的内存都有一个唯一的内存编号,这个编号就是内存地址 (2)编号在32位系统下是一个32位的整数,编号在64位系统下是一个64位的整数 (3)用指针型变量来表示这个内存地址号。

*的作用: Typename * variableName:声明一个指针变量。变量名为variableName,该指针地址处的内容类型为Typename

&:得到变量对应的指针变量(内存地址) ```c void main{ int a=3; int p; // p是int型变量,p是指针型变量 p = &a; // 指针型变量就是内存号,所以&符号取得a的内存号 int b = p; printf("%d",b);p = 100; printf("%d",a) // 通过内存号修改变量值

char chs[10]; printf("%u,%u,%u,%u",chs,chs[0],chs[1],chs[2]) // %u:unsigned无符号整形,数组首地址,第一个元素的地址,第二个元素的地址 int ints[10]; printf("%u,%u,%u,%u",ints,ints[0],ints[1],ints[2]) // %u:unsigned无符号整形,数组首地址,第一个元素 } ```

无类型指针: (1)void p:说明p只是一个指针变量,而不去指向任何一个变量 (2)空指针的写法: int *p = NULL ```c #include <stdio.h> void main(){ int a = 100; int p; // int p是声明写法:整体代表一个地址号,这个地址号上存着整数。单独一个p:定义写法,也是内存号 int q; p = &a; //int p = 1; // p指向的地址号是1(声明+定义写在了一起) printf("%u",p); // 3852910012 printf("%u",p); // 100 printf("%d\n",sizeof(p)); //8:指针在64位系统下就是一个64位的整数,所以占8字节。无论指针指向什么类型的数据,64位下都占8字节 printf("%d\n",sizeof(a)); //4:int型占4字节

int *p; //声明指针,没有指向,就是野指针。野指针使导致程序崩溃的主要原因 *p = 1; // 为地址上赋值1,会出现错误 [1] 6285 segmentation fault (core dumped) a.out int *p = NULL; // 空指针使合法的,野指针是非法的

}


/** int p 与p一致 / #include <stdio.h> void main(){ int a =100; int p = &a; int q; q = &a; printf("p:%u,q:%u\n",p,q); // p:2373644580,q:2373644580 printf("p:%d,q:%d",p,q);// p:100,q:100 } ```

指针的兼容性 ```c #include <stdio.h> void main(){ int a = 0X1013; char b = 13;

int *p; p=&a; printf("%x",*p); // 1013 p=&b; // 10130d 用int指针指向char类型变量,因为int指针指向了连续4字节的地址,所以除了1013外后面还有3个字节的数据当做一体来指向 printf("%x",*p); int *q; char buf[] = {0x12,0x34,0x56,0x78,0x90,6,7,8,9,10}; q = buf; printf("%x\n",*q); //指针不兼容的情况 float i = 3.14; int *p = &i; printf("%d",*p); // 此时并不能打印出3,这就是指针类型不兼容 } ```

指向常量的指针和指针常量 ```c void main(){ int a = 100; const int p = &a; // 指向常量的指针只能读内存,但可以改变指向 printf("%d\n",p); // 100

int *const q = &a; // 常量指针可以读写指向的内存,但不能改变指向 *q = 200; printf("%d\n",*q); //200 printf("%d\n",a); //200

} ```

数组与指针 (1)数组名也是一个指针变量,指向数组第一个元素。通过数组名+1,来指向数组中的第二个元素。 (2)指针运算不是简单地数值加减法,而是指针指向的数据类型在内存中所占字节数为倍数的运算。 ```c void printarr(char s[]){ int i; for(i=0;i<10;i++){ printf("s[%d]\n", %d); } } void main(){ char buf[10] = {1,2,3,4,5,6,7,8,9,0}; char p = buf; char p1 = &buf[0]; char *p2 = &buf[1];

printf("%d\n", *p); //1 printf("%d\n", *p1); //1 printf("%d\n", *p2); //2 p2 ++; // 指针是地址号,p+1 *p2 = 100; // 更改为100 printarr(buf);

}(3)ip地址在网络中不是以字符串传输,字符串太长,而是以DWORD传输(4字节),int类型也占4字节。c #include<stdio.h> void change2char(int i){ unsigned char p = &i; printf("%d.%d.%d.%d",p,(p+1),(p+2),(p+3)); } void main(){ int a = 0; unsigned char p1 = &a; // 无符号使得最大值为255p1 = 192; // (p+1) = 168; // (p+2) = 0; // (p+3) = 1; p1++;p1 = 168; p1++;p1=0; p1++;p1=1; printf("ip:%d",ip); // a已经被改变为一个ip地址 change2char(a); } ------------------ int s2ip(char s[]){ int a = 0; int b = 0; int c = 0; int d = 0; sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d); printf("a=%d,b=%d,c=%d,d=%d\n", a,b,c,d); int ip=0; char p = &ip; p++;p = a; p++;p=b; p++;p=c; p++;p=d; } void main(){ char s[] = "192.168.0.1"; printf("%d\n", s); }c eg:用指针合并数组 void main(){ char s1[] = "hello"; char s2[]="world"; char p1 = &s1; char p2 = s2; while(p1){ //p1指向的元素为0时跳出循环 p1 ++; } while(p2){p1++ = *p2++; // 先指向,后指针移位 }

}(3)二维数组的指针 void main(){ int buf[2][3] arr = {{1,2,3},{4,5,6}}; // int p[3] ; //定义一个指针数组 int (p)[3]; // 定义了一个指针,指向int[3]这种数据类型,也叫作指向二维数组的指针 printf("%d\n",sizeof(p)) ; //8 因为指针在64位系统都是占8位 printf("%d,%d\n",p,p+1); //相差12字节

p = buf; //p指向了二维数组的第一行 p++; //p指向了二维数组的第二行 // 指针遍历 int i,j; for(i=0;i<2;i++){ for(j=0;j<3;j++){ //printf("%d\n", *(*(p+i))); //打印结果1,1,1,4,4,4 printf("%d\n", *(*(p+i)+j); // 遍历每个元素 printf("%d\n", p[i][j]); // 每个元素,与上面相等 } }

}

指针作为函数参数 void test(int n){ n++; } void test2(int p){ (p)++; } void main(){ int i =100; test(i); //c语言是值传递,所以普通函数参数的改变是不能传回给参数的,但如果参数是地址,改变地址上的值 printf("%d\n", i);

test2(&i); printf("%d\n", i);

} -------------------------------------------------- 一维数组作为参数的指针 void swap(int a,int b){ int tmp = a;a = b;b = tmp; } // 当数组名作为参数传递时,数组名解析为数组首地址指针,所以多写为void setarray(int buf) void setarray(int buf[]){ printf("占%d位\n", sizeof(buf)); //8:64位系统指针占8字节 buf[0] = 100; buf[1]=200; int i; for(i=0;i<10;i++){ printf("%d\n", buf[i]); } } void setarray(int buf,int n){ // n为数组的长度 buf[0] = 100; buf[1]=200; int i; for(i=0;i<n;i++){ printf("%d\n", buf[i]); } } void main(){ int a = 10; int b = 20; swap(&a,&b); printf("%d,%d\n", a,b);

// 因为数组名是数组的首地址,所以数组名作为行参传递的时候,就相当于传递的指针 int buf[]= {1,2,3,4,5,6,7,8,9,0}; printf("%d\n", sizeof(buf)); //40 setarray(buf,sizeof(buf)/sizeof(int)); // 数组长度写成sizeof(buf)/sizeof(type)

} -------------------------- 二维数组的指针作为函数参数 void printarr2(const int (*p)[4],int m,int n){//mn分别是二维数组的第一个和第二个角标。通过const标识,保护函数不会改变数组中的值 int i,j; for(i=0;i<m;i++){ for(j=0;j<n;j++){ printf("p[%d][%d] = %d\n", i,j,p[i][j]); } } } int main{ int buf[][4] = {{1,2,3,4},{5,6,7,8}}; printarr2(buf,sizeof(buf)/sizeof(buf[0]),sizeof(buf[0])/sizeof(int)); return 1; } 33 min 18 s --------------计算二维数组每一列的平均值---------------- #include<stdio.h> void main(){ int buf[2][4] = {{1,2,3,4},{5,6,7,8}}; for(int i=0;i<4;i++){ int sum = 0; for (int j = 0; j < 2;j++) { sum += buf[j][i]; } printf("%d\n", sum); } } ```

函数指针 (1)函数也有地址,这个地址存放的是代码 (2)函数名同数组名一样,都是即表示本身,又表示指向自身开头的指针 (3)函数指针可以单独声明:ReturnType (*func)(p1Type,p2Type) (4)函数指针是指向函数的指针变量void *p(int,char*) : 定义一个函数,函数名为p,参数为int和char类型,返回值为void类型void (*p)(int,char*):定义一个指针,该指针指向函数。返回值为void, 参数为int和char类型c int getmax(int x, int y){ // 也可以写为int *getmax(int x, int y) 意思一样 return x>y?x:y; } int getmin(int x, int y){ return x<y?x:y; } int main(){ int (*p)(int,int) ; //定义一个指针p指向函数,该函数反获知是int,2个输入参数是(int,int) int fun = 0; scanf("%d",&fun); if(fun==1){ p = getmax; //函数名就是指针 }else{ p = getmin; //函数名就是指针 } int i = p(2,4); // 通过函数指针调用函数 printf("%d\n", i); } (5)将一块内存初始化为0最常见的方法:     a. memset(buf,值,size(buf)):要设置的内存地址,设置的值,内存大小(单位字节)     b. memcpy(buf2,buf1,sizeof(buf1)):将buf1的内容全部拷贝到buf2     c. memmove(buf2,buf1,sizeof(buf1)); ```c #include <string.h> #include <stdio.h> void main(){ int buf[10] = {1}; memset (buf,0,sizeof(buf));

int buf2[] = {1,2,3,4,5,6,7,8,9,0}; int buf3[]; memcpy(buf3,buf2,sizeof(buf2)); for (int i = 0; i < 10; i++) { printf("%d\n", buf2[i]); }

} ```

指针数组与多级指针 (1)一个经典的错误模型:c void getheap(int *p){ p = malloc(sizeof(int)*10); } void main(){ int *p = NULL; getheap(p); // 此处是错误的,因为函数的参数是值传递,虽然语义是getheap(p),但是mallo的内存地址只是付给了函数的形参,函数退出后,形参的值并不能传递给实参(指针的值也一样不能传递),导致p还是空指针,下面的p[0]=1发生空指针异常 p[0] = 1; printf("%d\n", p[0]); free(p); } (2)经典的正确模型:二级指针作为形参,对上面错误模型的改写c void getheap(int **p){ *p = malloc(sizeof(int)*10); // 所致地址号的内容,就是指以及指针的内容 } void main(){ int *p = NULL; getheap(&p); p[0] = 1; printf("%d\n", p[0]); free(p); }

main函数的一些解释: (1)c语言main函数是有2个输入参数的,第一个表示输入参数的个数,第二个是一个指针数组,每个指针指向char类型 (2)输入参数个数永远大于1,因为程序名本身作为一个输入参数 (3)指针数组:这个指针数组的长度是第一个参数值,每个元素指向一个输入参数

int main(int argc,char *args[]){ if(argc!=4){ printf("请输入完整的数学表达式,以空格分开" ); return 0; } int a = atoi(args[1]); int b = atoi(args[3]); switch(args[2][0]){ case '+': printf("%d\n", a+b); break; case '-': printf("%d\n", a-b); break; case '*': printf("%d\n", a*b); break; case '/': if(b){ printf("%d\n", a/b); } break; } return 0; }

四. 内存管理

作用域 (1)文件作用域:全局变量,定义在函数外面的变量

int a=10; void main(){ int a = 20; // 函数内部可以再次定义和全局变量同名的变量,定以后a为20,没有定义为10 printf("%d\n", a); } (2)extern关键字c #include "c.h" // 在这个文件中定义了age=10 extern int age ; // 声明该变量在其他文件中定义了,连接时使用就可以了。这文件需要是.c文件,不能是.h文件。gcc编译时后面加上两个文件名 setage(int n){ age = n; } getage(){ printf("%d\n", age); } void main(){ setage(11); getage(); // 11 } (3)auto自动变量:不写auto,就默认为auto变量 (4)register 变量:建议变量放到空闲的寄存器中:寄存器变量不能取地址操作 (5)static 变量:只初始化一次,程序运行期间一直存在 。static只是增大了变量的存在时间,却没增大变量的作用域 一旦全局变量被static修饰,则这个变量的作用域被限制在该文件内部,其他文件不能用extern使用c void mystatic(){ static int a = 0; // 不加static,循环打印每次打印都是0,加上static每次打印+1 printf("%d\n", a); a++; } void main(){ for (int i = 0; i < 10; ++i) { mystatic(); } }

c语言中,函数都是全局的。 (1)如果函数前面加了static,则函数被限制在文件内部使用 (2)函数前面加不加extern一样。而变量前面的extern是可以省略的,比如:int a; 如果其他文件中定义了int a=10,则此处的变量是声明,如果其他文件中没有定义a,则此处是定义 (此处也是说明c语言并不严谨)

c语言内存管理 程序在内存中分为4个区域: (1)栈:不会很大,栈空间是操作系统为每个程序固定分配的大小以k为单位。栈空间是操作系统为每个程序固定分配的大。此部分的内存表现为先进后出。所有自动变量,函数行参都有编译器自动放到栈中。当一个自动变量超出其作用域时,自动从栈中弹出。先进入栈中的变量放到大内存号中 每个线程都有自己的栈

int main(){ char array[1024*1024*1024] = {0}; //定义一个超大的数组就会栈溢出 return 0; }

(2)堆:堆没有大小限制,只是无力限制。但是堆内存不能由编译器自动释放 ```c #include <stdlib.h> int main(){ int p = malloc(sizeof(int)10); // 在堆中申请了10个int的大小 char p2 = malloc(sizeof(char)10);

memset(p,0,sizeof(int)*10); // malloc申请的堆内存 ,在代码的操作上相当于数组 for(int i=0;i<10;i++){ p[i] = i; // 操纵数组 } free(p) ; // 释放通过malloc申请的堆内存 free(p2);

}(3)静态区:所有的全局变量,以及程序中的静态变量都存储在静态区 (4)代码区:程序在被操作系统加载到内存中时,所有可执行代码加载到代码段,这块内存不能再程序运行期间修改c int c= 0; void main(){ int a=1; int b=2; static int d = 3; printf("%d,%d,%d,%d,%d\n", &a,&b,&c,&d,main); // 1321929276,1321929272,6295612,6295604,4195632 } 上述程序的a和b在栈区,所以地址号紧挨着:1321929276,1321929272 发现a所在的内存号在高位。 c和d在静态区:地址号紧挨着 main在代码段 ```

堆栈的一些问题 (1)不能将一个栈变量的地址作为函数的返回值,因为函数结束后,栈变量被释放c eg: int *geta(){ int a = 10; return &a; } (2)但是可以把堆变量的地址作为函数返回值返回 ```c int geta(){ int p = malloc(sizeof(int)); return p; }

int main(){ int p = geta(); malloc(p); }(3)不能用变量声明一个数组长度,eg:`int arr[i]`, 如果申请内存的大小靠程序运行期间决定,那么就要使用 `malloc(sizeof(int)*i)`,要记得free (4)代码区和静态区的大小都是固定的,静态区是编译器根据代码提前申请的。堆和栈所占大小是动态,但是栈有一个操作系统规定的栈上线shell cd /proc # 每个进程号都是一个文件 cd pid cat maps # 查看内存映射,这里列出了代码段,堆,栈,静态区的使用情况(栈的预设范围)(5)堆的分配 操作系统分配内存的单位不是字节,而是页。cat smps之后,发现堆有个项是内存页大小:4k。页太大,浪费的内存空间太大,但操作系统维护起来很简单,因为不用频繁的申请释放内存<br></br> (6)malloc申请的堆内存上会存在旧数据,所以申请后需要用memset清空 :` memset(p,p,sizeof(int)*10)` 这两句话的简写就是calloc,这样内存上全部是0c int main(){ char p = calloc(10,sizeof(char)); // 申请大小是10sizeof(char) }(7)relloc用于扩展已经申请的堆内存,在原有内存上,增加连续的内存,如果没有连续空间可扩展,则会新分配一个连续空间,将原有内存拷贝到新空间,然后释放原有内存。但是relloc只申请,不打扫(内存中有旧数据)c // rello(p,新的内存大小),.如果p为NULL,则realloc等于malloc,不指定分配空间的起始位置 void main(){ char p1 = calloc(10,sizeof(char)); char *p2 = realloc(p1,5); // p1指向的地址变为5 // realloc(NULL,5) = malloc(5) } ```

五. 二进制文件读写

fscanf:读文件时,可以根据固定格式读取 fprintf:写文件,和printf一样,可以定义输出格式,只是输出到文件中 ```c #include <stdio.h> #include <string.h> #include <stdlib.h> void main(){ FILE *p = fopen("a.txt","r"); while(!feof(p)){ int a,b; fscanf(p,"%d + %d =",&a,&b); //文本格式为a + b =,从中截取a,b printf("%d,%d",a,b); } }

void main(){ FILE *p = fopen("a.txt","w"); char buf[100] = "hello world fuck "; int a=1,b=2; fprintf(p,"%s,%d,%d",buf,a,b); fclose(p); } ```

fread与fwrite读写二进制文件

上面的函数只能操作字符文件,字符文件每次只能读一行 void main(){ FILE *p = fopen("a.txt","rb"); // 以二进制方式读取 char buf[100] = {0}; fread(buf,sizeof(char),1,p); // 读取字节的缓冲区,读取单位,一次读取几个单位,文件指针 printf("%s\n", buf); fclose(p); }

六. 结构体

结构体的声明 ```c #include <string.h> struct student{ char name[100]; int age; int sex; }; // 声明一个结构体

void main(){ struct student st; // 定义了一个结构体的变量,名字叫st //struct student st2 = {.name="周杰伦",.sex=0}; 结构体中通过.来给属性赋值 struct student st3 = {0}; // 定义结构体变量,并把结构体变量的所有属性设置为0 struct student st4; memset(&st,0,sizeof(st)); // 结构体变量初始化 st.age = 25; st.sex = 1; strcpy(st.name,"刘德华");

printf("%d\n", st.age ); } ```

结构体的内存对齐模式: 结构体在内存中是一个矩形,而不是一个不规则形状,以结构体中占地最大的元素对齐。而且不同类型的属性要跨偶数个字节 ```c struct A { int a; // 4字节 char b; // 以4字节对齐 }; // 总共8字节

struct B { char a; // 1个字节 short b; // short不同于char类型,所以要夸2字节,从第3个字节开始对齐 , 占2个字节 char c; // 占1个字节 int d; // 第二个8字节对齐行 long long e; // 8个字节,占地最多的属性,所以其他属性以8字节对齐; };

printf("%d\n", sizeof(B)); ```

结构体成员的位字段 ```c struct A { char a:2; // char占1个字节,但是这样声明的结构体中的a字段,只有前2bit能通过结构体使用,但值得注意的是此时整个结构体仍然占1个字节而不是2bit }; void main(){ struct A a; a.a = 3; //二进制11,有符号char,所以会打印-1 printf("%d\n", ); }

struct B { char a:2; unsigned char b:4; char c:1; };

printf("%d\n", sizeof(struct B)); ```

结构体模仿数组(用字段地址连续的结构体成员) ```c struct D { char a; char b; char c; };

void main(){ struct G g; char *p = &g; p[0] = 'a'; p[1] = 'b'; p[2] = 'c'; printf("%d,%d,%d\n", g.a,g.b,g.c); } ```

结构体数组 ```c struct People { int age; char name[10]; };

void main(){ struct People p[3] = {{11,"aa"},{22,"bb"},{33,"cc"}}; for (int i = 0; i < 3; ++i) { printf("%d,%s\n", p[i].age,p[i].name); } } ```

结构体可以变相实现数组的赋值 ```c struct A { char arr[10]; };

void main(){ struct A a1 = {"hello"}; struct A a2= {0}; a2 = a1; printf("%s\n", a2.arr); } ```

结构体的嵌套c struct A{ char a; char b; } struct{ struct A a; int b; } // 一共占8字节

结构体与指针 (1)结构体赋值,其实就是结构体之间内存的拷贝

struct strss { char s[100]; }; void main(){ struct strss s1,s2; strcpy(s1.s,"hello"); // s2 =s1; 这句话等同于下面的memcpy,结构体是变量,所以可以赋值 memcpy(&s2,&s1,sizeof(s1)); printf("%s\n", s2.s); }

(2)指向结构体的指针操作结构体c void main(){ struct A a; struct A *p = &a; // (*p).a = 10; 这样写是对的,但是太麻烦,没人这么写 p -> a = 10; // 用->代表结构体的一个成员 } (3)指向结构体数组的指针 ```c #include <string.h> #include <stdlib.h> void main(){ struct A *p ;

/* struct A array[10] = {0}; p = array; // 指针指向数组首地址 */ //也可以在堆中创建结构体 p = malloc(sizeof(struct A) * 10); memset(p,0,sizeof(struct A) * 10) struct A *array = p; p->a = 10; p->b = 11; p++; // p已经变化了,所以free时要从头free,就是array。如果free(p),就从当前p的位置往下数10个字节,这样就free了不属于结构体数组的内存,运行报错 p->a = 3 p->b = 4; for (int i = 0; i < 10; ++i) { printf("%d,%d\n", array[i].a,array[i].b); } free(array);

}(4)结构体作为函数参数时,尽量使用指针传递参数,不要直接使用结构体作为形参 因为结构体和int等变量一样,作为参数传递时是值拷贝传递,如果传递指针,则函数只需要拷贝8字节的地址号,而且如果函数想要操作结构体的话,也只能通过指针进行参数操作c void setname(struct student a,int age){a->age = age; } ```

四. 联合体

联合体所占内存的大小等于联合体中最长属性所占内存的大小,联合体的所有属性公用一块内存地址c union A{ int a; char b; } void main(){ union A a; a.a = 0X12345678; printf("%d\n", sizeof(union A)); // 4 ,最长的int属性,栈4字节 printf("%p,%p\n", &a.a,&a.b); // 2个地址一样 printf("%d,%d\n", a.a,a.b); // 12345678, 78 }

一个经典的错误写法c union A{ int a; char *b; } void main(){ union A a; a.b = malloc(100); a.a = 100; // 因a,b属性用同一块内存,此时改变了a的值就是改变了b的值,下面free的地址就不再是malloc来的地址,会报错 free(b) } 所以:如果一个联合体中包含了含有指针变量,那么一定要在使用完这个指针后,病free掉它,才能使用联合体的其它成员

五. 枚举

枚举是一个int常亮,不能改变他的值

枚举中的每个量都相当于#define x 整数

//#define red 0 相当于下面的枚举定义 //#define black 1 //#define yellow 2 enum A{ red,black,yellow }; void main(){ printf("%f\n", red); // 打印0 }

六。typedef

typedef char BYTE; 自定义一个BYTE类型,作为char类型相当于 #define BYTE char

typedef 通常用于简化结构体变量的名字为一个简短的名字

struct A{ int a; } typedef struct abc A; // 本来声明变量时需要struct A a, 现在只需要abc A即可

typedef定义结构体的简写形式 ```c typedef struct A{ int a; }abc; // 张扬相当于上面2句话的简写, A也可以不写形成第三种写法,如下

typedef struct { int a; }abc; ```

typedef定义函数指针 ```c #include <string.h> #include <stdio.h> void mystrcat (char s1,char s2){ strcat(s1,s2); } // 定义一个函数,有三个参数,函数指针,形参1,形参2 void test(void (p)(char ,char ),char s1,char s2){//函数指针p,返回值是void ,形参有两个,都是char *类型 p(s1,s2); }

// 上面的test函数太复杂,因为参数里包含一个函数指针 typedef void (MYSTRCAT)(char ,char ) // 把一个函数指针定义为MYSTRCAT类型

void main(){ MYSTRCAT aa[10]; char s1[100] = "hello"; char s2[5] = "world"; test(mystrcat,s1,s2); printf("%s\n", s1); } ```

转载于:https://www.cnblogs.com/moonlord/p/6979327.html

最新回复(0)