当前位置: 代码迷 >> 综合 >> 【Day6 C语言语法学习-5】
  详细解决方案

【Day6 C语言语法学习-5】

热度:41   发布时间:2023-11-30 18:17:46.0

Day6 C语言语法学习-5

  • 一、函数指针
  • 二、回调函数
    • 练习:编写函数,定义一个无符号4字节的整数,然后获取每一个字节的内容,然后返回相加之和
    • 面试题:define和typedef的区别?
  • 三、存储类型
  • 四、内存管理
    • 思考题1
  • 思考题2
  • 五、结构体
    • 5.1 结构体的使用
      • 5.1.1 结构体的定义和赋值
      • 5.1.2 定义结构体同时赋值
      • 5.1.3 结构体数组
    • 5.2 结构体定义的方式
      • 5.2.1 无名结构体
      • 5.2.2 有名结构体
      • 5.2.3 struct的特殊赋值方式
    • 5.3 结构体占用字节大小
      • 5.3.1 结构体的位域
  • 六、共用体
    • 6.1 基本用法
    • 6.2 大小端的判断
    • 6.3结构体中定义联合体
  • 七、枚举类型
    • 练习:
      • 实例一:
      • 实例二

一、函数指针

函数指针:本质是一个指针,指向一个函数;
我们定义的函数名其实就是一个指针,保存当前函数代码区的首地址;
函数指针学习的目的就是想解决能不能定义一个指针,可以像函数名一样对当前函数进行调用。

函数指针定义的格式:
返回值类型(*变量名)(参数);

函数数值真的作用:将一个函数作为参数传递给另一个函数是需要定义成函数指针,也称之为回调函数

#include <stdio.h>
typedef unsigned char uchar;   //用uchar表示 unsigned char
typedef int (*T)(int,int);   //声明T为函数指针类型
void f1()
{printf("hello world!\n");
}
int f2(int x,int y)
{printf("this is f2\n");return 0;
}
int main(int argc, const char *argv[])
{void (*p1)();  //定义函数指针p1 = f1;//不能写成p1 = f1();   ---->调用f1函数,将返回值赋值给p1p1();//	p1 = f2;   //类型不兼容int (*p2)(int,int);p2 = f2;p2(1,2);T p3;  //等价于int(*p3)(int,int)p3 = f2;p3(1,2);return 0;
}

二、回调函数

#include <stdio.h>
int less(int x,int y)
{return (x < y) ? 1 : 0;
}
int greater(int x,int y)
{return (x > y) ? 1 : 0;
}
void Sort(int a[],int length,int (*p)(int,int))
{int i,j;for(i = 0 ; i < length - 1;i++){for(j = 0 ; j < length - 1 - i;j++){if(p(a[j],a[j+1])){
#if 0int t = a[j];a[j] = a[j+1];a[j+1] = t;
#endifa[j] = a[j] + a[j + 1];a[j + 1] = a[j] - a[j + 1];a[j] = a[j] - a[j + 1];}}}}
void Print(int a[],int length)
{int i;for(i = 0 ; i < length;i++){printf("%d ",a[i]);}putchar(10);}
int main(int argc, const char *argv[])
{int a[10] = {0};printf("请输入10个数字:\n");int i;int length = sizeof(a)/sizeof(a[0]);for(i = 0 ; i < length;i++){scanf("%d",&a[i]);}Sort(a,length,less);Print(a,length);return 0;
}

练习:编写函数,定义一个无符号4字节的整数,然后获取每一个字节的内容,然后返回相加之和

要求:这个整数需要传参,返回每一个字节之和
458963212 —>?0001 1011 0101 1011 0011 1001 0000 1100?
0000 1100? —>12
0011 1001 —> 57
0101 1011 —>91
0001 1011 —> 27
12+57+91+27 = 187

#include <stdio.h>
int GetByteSum(unsigned int num)
{int sum = 0;
#if 0//将每8位拿出来int byte1,byte2,byte3,byte4;byte1 = num & 0xff;byte2 = (num >> 8) & 0xff;byte3 = (num >> 16) & 0xff;byte4 = (num >> 24) & 0xff;sum = byte1 + byte2 + byte3 + byte4;
#endifunsigned char *p = (unsigned char)&num;int i;for(i = 0 ; i < 4; i++){sum += *p;p++;}p = NULL;return sum;}
int main(int argc, const char *argv[])
{unsigned int num = 458963212;int n = GetByteSum(num);printf("n = %d\n",n);return 0;
}

面试题:define和typedef的区别?

1.#define是一个预处理指令,在预处理阶段进行字符串的简单替换,并不参与到编译阶段,typedef是类型的重定义,在编译的时候进行过类型的重定义;
2.当使用#define和typedef定义多个指针的时候,define定义的是第一个指针,后面全是变量,而typedef定义的全是指针;
3.define一般用于表示常量,还有函数的替换工作,typedef用于类型重定义。

#include <stdio.h>
#include <stdlib.h>
// 方法1:({})
// 方法2:do{}while(0)
int min;
#define MAX(a,b) ({int max;if(a > b)max=a;else max=b;max;})  //({})里面可以有多条语句,最后一句就是宏的返回值
#define MIN(a,b) do{if(a < b) min = a;else min = b;}while(0)#define PRINT_MSG(msg) do{printf("%s\n",msg);return -1;}while(0)
#define S(n) #n   //#代表字符串化
int main(int argc, const char *argv[])
{int *p  = NULL;int hello = 100;printf("max = %d\n",MAX(100,200));MIN(100,200);printf("min = %d\n",min);p = (int *)malloc(4);if(p == NULL){PRINT_MSG("malloc failed!\n");}printf("%s\n",S(10));  //字符串化return 0;
}

三、存储类型

auto:
register:定义变量在寄存器上,运行速率快,但是芯片的寄存器的个数有限制,不能无限使用;寄存器变量不能取地址;
extern: 使用的变量或者文件在其它文件中定义;
static:
1.限定作用域:(修饰的变量或者函数只能在本文件中使用)
2.延长生命周期
volatile:保证数据每次都从内存中取值,而不从缓存中取数据,防止编译器对代码进行优化
1.多线程访问同一个变量的时候
2.使用C语言操作硬件地址的时候

#include <stdio.h>int a = 100;
void fun()
{static int i = 0; printf("i = %d\n",i++);printf("a = %d\n",a);
}
int main(int argc, const char *argv[])
{int a = 200;printf("a = %d\n",a);{int a = 300;printf("a = %d\n",a);}printf("i = %d\n",i);fun();fun();fun();return 0;
}

四、内存管理

----------------------------
栈区:局部变量,正在调用的函数,形参都在栈栈区由于在释放时不会清0,所以在定义变量的时候不初始化,他们都是随机值,栈区空间由系统自动申请,自动释放
----------------------------
堆区:使用malloc分配的内存都在堆区,手动申请,手动释放
----------------------------
静态区|.bss:使用static修饰的未初始化的变量和全局的未初始化的变量在.bss段|----------------------------|.data:使用static修饰的已初始化的变量和全局的已初始化的变量在.data段|----------------------------|.text:代码段,文本段|----------------------------|.ro: 只读数据段|const int a;|char *p = "helloworld";#include <stdio.h>
int a; //.bss段
static int w = 4; //.data
int b = 1234;   //.data
const int g = 789; //.ro
void func(void)
{const int y = 1234; //.roint *p = &y;  //栈区
}
int main(int argc, const char *argv[])
{char ch; //.stackchar ch1 = 'a';  //.stackchar *p = "aaaa"; //P .stack  "aaaa":.roreturn 0;
}

思考题1

#include <stdio.h>int main(int argc, const char *argv[])
{char *p = NULL;char *tmp = "helloworld";p = (tmp + 4);//p[-1] = ? p[-5] = ?printf("p[-1] = %c,p[-5] = %c\n",p[-1],p[-5]);return 0;
}

思考题2

#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{char *data = "12345678";short *tmp = NULL;char p[6] = {0};tmp = (short *)&p[2];*tmp = atoi(&data[4]);printf("*tmp = %d\n",*tmp);//p[0],p[1],p[2],p[3],p[4],p[5]?int i = 0 ; for(i = 0 ; i < 5;i++){printf("p[%d] = %#x\n",i,p[i]);}return 0;
}jsetc@linux:~/jsetc/208/day6$ ./8-思考题2 
*tmp = 5678
p[0] = 0
p[1] = 0
p[2] = 0x2e
p[3] = 0x16
p[4] = 0

五、结构体

结构体是一个构造类型,结构体的成员在内存上是连续的,
但是结构体的成员的类型可以是不相同的。
结构体的关键词用struct来标注。

格式:

struct 类型名{成员1;成员2;。。。
};

【注意事项】
(1)结构体使用struct来标识
(2)struct后面跟的是结构体的类型名,而非变量名
(3)结构体的成员是被{}所包括着
(4){}后面要加;
(5)结构体的成员可以不尽相同
(6)结构体成员之间用分号隔开
(7)定义结构体的时候不能赋值
(8)在结构体里面不能写函数,因为函数占用的字节不固定,但是可以写函数指针
(9)结构体访问成员的方式 “变量.成员”
(10) 结构体指针访问成员的方式“变量->成员”

5.1 结构体的使用

5.1.1 结构体的定义和赋值

#include <stdio.h>
struct person{char name[32];char sex;int age;int high;float weight;
};
int main(int argc, const char *argv[])
{struct person p1;strcpy(p1.name,"张三");p1.sex = 'm';p1.age = 18;p1.high = 189;p1.weight = 80;struct person *p2 = NULL;p2 = (struct person *)malloc(sizeof(*p2));if(p2 == NULL){printf("malloc failure");}strcpy(p2->name,"张三");p2->sex = 'm';p2->age = 18;p2->high = 189;p2->weight = 80;return 0;
}

5.1.2 定义结构体同时赋值

#include <stdio.h>
struct person{char name[32];char sex;int age;int high;float weight;
};
int main(int argc, const char *argv[])
{struct person p1 = {"zhangsan",'m',20,186,80}; //给所有成员初始化struct person p2 = {             //选择成员初始化.name = "zhangsan",.weight = 80};return 0;
}

5.1.3 结构体数组

struct person p1[3]; //这个就是结构体数组,数组中有三个strcut person

5.2 结构体定义的方式

5.2.1 无名结构体

struct{char name[32];int age;float weight;  
}变量1,变量2;
struct 变量1;----->这个是错误的写法typedef struct{char name[32];int age;float weight;
}student_t;  //类型名
student_t 变量1,变量2;

【注意】结构体可以整体赋值,数组在定义后不允许整体赋值

5.2.2 有名结构体

typedef struct student{char name[32];int age;float weight;
}student_t;  //类型名
student_t 变量1,变量2;

5.2.3 struct的特殊赋值方式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct person{char name[32];char sex;int age;int high;float weight;
}person;
int main(int argc, const char *argv[])
{person p;p = (person){"张安",'m',20,189,80};printf("name = %s,sex = %c age = %d high = %d weight = %f\n",p.name,p.sex,p.age,p.high,p.weight);
//结构体数组赋值
//方法1person p1[2] = {{.name = "zhangsan",.age = 19},{.name = "zhangsi",.age = 20}};
//方法2:
person p2[3] = {[0] = {.name = "zhangsan",.age = 19},[2] = {.name = "zhangsi",.age = 20}};return 0;
}
//方法3:
person p3[3];p3[0].name

5.3 结构体占用字节大小

规则:
结构体占用字节大小的口诀是:
1.如果结构体内部的变量类型最大成员小于等于4字节,就按照最大的类型进行内存对齐
2.如果结构体内部变量类型大于四字节,就按照四字节对齐
3.结构体最终大小和结构体中成员最大类型对齐(32位和4字节对齐,64位和8字节对齐)
在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
struct aa{char a;  //1个字节
};
struct bb{short a;  //2个字节
};
struct cc{int a;  //4个字节
};
struct dd{char a;  // 2个字节char b;
};
struct ee{int a;  // 8个字节char b;
};
struct ff{char a;  // 6个字节short b;char c;
};
struct gg{char a;  // 8个字节short b;int c;
};
struct hh{short a;  // 8个字节char b;int c;
};
struct ii{char a[3];  // 12个字节int b;char c;short d;
};
struct jj{double a;  // 24个字节char b;int c;short d;float e;
};
int main(int argc, const char *argv[])
{struct ff a;struct gg b;struct jj c;struct ii d;printf("aa = %ld\n",sizeof(struct aa));printf("bb = %ld\n",sizeof(struct bb));printf("cc = %ld\n",sizeof(struct cc));printf("dd = %ld\n",sizeof(struct dd));printf("ee = %ld\n",sizeof(struct ee));printf("ff = %ld\n",sizeof(struct ff));printf("gg = %ld\n",sizeof(struct gg));printf("hh = %ld\n",sizeof(struct hh));printf("ii = %ld\n",sizeof(struct ii));printf("jj = %ld\n",sizeof(struct jj));printf("ff.a = %p\nff.b = %p\nff.c = %p\n",&a.a,&a.b,&a.c);printf("gg.a = %p\ngg.b = %p\ngg.c = %p\n",&b.a,&b.b,&b.c);printf("jj.a = %p\njj.b = %p\njj.c = %p\njj.d = %p\njj.e = %p\n\n",&c.a,&c.b,&c.c,&c.d,&c.e);printf("ii.a = %p\nii.b = %p\nii.c = %p\nii.d = %p\n",d.a,&d.b,&d.c,&d.d);return 0;
}

5.3.1 结构体的位域

压缩结构体大小的一种方式

#include <stdio.h>
struct aa{char a:1;char b:5;char c:3;
};
int main(int argc, const char *argv[])
{printf("sizeof(aa) = %ld\n",sizeof(struct aa));return 0;
}

【注意】
1.在结构体内部进行位域操作的时候,是从内存最低位置开始站位,如果这个变量是有
符号的,那么它的最高位就是符号位,如果这个位域只有一位,它既是符号位又是数据位
2.结构体位域的成员不能够取地址

六、共用体

6.1 基本用法

union内部的成员公用同一块内存空间,共用体的大小就是内部最大成员的内存空间
不管union有多少个成员,union的内存空间都是这个最大成员的内存空间

格式:

union 类型名{char a;short b;int c;
};

6.2 大小端的判断

字节序:
大端序:高字节存放在低地址,低字节存放在高地址(网络序)
小端序:高字节存放在高地址,低字节存放在低地址
在这里插入图片描述

#include <stdio.h>
typedef union aa{unsigned char a;int c;
}aa_t;
int main(int argc, const char *argv[])
{aa_t a;a.c = 0x12345678;if(a.a == 0x12){printf("这是大端机器\n");}else if(a.a == 0x78){printf("这是小端机器\n");}return 0;
}

在这里插入图片描述

6.3结构体中定义联合体

作用:让结构体的通用性更强

#include <stdio.h>
struct aa
{char a;union{int b;char c;};
};
int main(int argc, const char *argv[])
{struct aa a;a.a = 10;a.b = 0xaabb;printf("size = %ld\n",sizeof(a));printf("c = %#x\n",a.c);return 0;
}

七、枚举类型

枚举就是有限数据的罗列,比如一周有七天,一年有12个月,
这些都是有限的罗列,适合用枚举
格式:

enum 类型名{成员1,成员2,   成员3,    成员4 = 10,成员5,.。。成员n,   
};

【注意】
(1)枚举类型的关键是enum
(2)枚举的成员没有变量类型
(3)成员间通过逗号来区分
(4)枚举的成员的值依次向下递增1
(5)枚举中的成员访问不是通过.或者->来访问,直接拿成员使用
(6)枚举类型的大小是4个字节
(7)枚举类型如果不赋初值,默认从0开始

练习:

实例一:

#include <stdio.h>
enum week{MON = 1,TUS,WED,THU,FRI,SAT,SUN
};
int main(int argc, const char *argv[])
{enum week day;printf("day size = %ld\n",sizeof(day));day = TUS;printf("day  = %d\n",day);day = 10;   //虽然编译器不会报错,但是不能这样赋值//给枚举类型变量赋值的时候,只能赋值枚举常量printf("day  = %d\n",day);return 0;
}

实例二

#include <stdio.h>
#define S(n) case n: return #n
typedef enum leds{RED,GREEN,BLUE,
}leds;
char *led_type_to_str(enum leds led)
{switch(led){S(RED);S(GREEN);S(BLUE);default: return "不存在的";}}leds led_open(leds led)
{printf("打开%s灯\n",led_type_to_str(led));return led;
}int main(int argc, const char *argv[])
{
#if 0char *p = NULL;enum leds led;led = led_open(RED);p = led_type_to_str(led);printf("打开%s-ok!\n",p);
#endifprintf("打开%s-ok!\n",led_type_to_str(led_open(RED)));return 0;
}