一 函数指针
函数指针:本质是一个指针,指向一个函数
我们定义的函数名其实就是一个指针,保存当前函数代码区的首地址
函数指针学习的目的就是想解决能不能定义一个指针,可以像函数名一样对当前函数进行调用
函数指针定义的格式:
返回值类型(*变量名)(参数);
函数数值真的作用:将一个函数作为参数传递给另一个函数是需要定义成函数指针,也称之为回调函数
#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函数,将返回值赋值给p1 p1(); // 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 0 int t = a[j]; a[j] = a[j+1]; a[j+1] = t; #endif a[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; #endif unsigned char *p = (unsigned char)# 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; //.ro int *p = &y; //栈区 } int main(int argc, const char *argv[]) { char ch; //.stack char ch1 = 'a'; //.stack char *p = "aaaa"; //P .stack "aaaa":.ro return 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);//“5678”,atoi一直到'\0'结束 //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;
注意:结构体可以整体赋值,数组在定义后不允许整体赋值