一.复习以下C语言(指针、typedef、预处理)
(一)指针
指针是地址,这句话是错误的。
"指针是地址的类型"。具体的一个地址是指针类型的。
指针类型 *
整数类型 int
字符类型 char
数据类型:"访问内存的方式"
常量和变量,常量和变量在内存中都分配了一定的空间。
"常量"的内存空间里的值是"只读 r"的
"变量"的内存空间里的值是"可读可写 rw"的
变量四要素:名字、地址、值、数据类型
变量的内容是怎么访问到的?
通过变量的名字找到变量的地址,按照变量的类型再读取变量地址空间里的内容。
C语言本质就三句话:常量和变量,访问内存的方式,运算符的优先级
举例:代码参见 point.c
#include <stdio.h>
int main(void) {
int a = 258; [0000 0000][0000 0000][0000 0001][0000 0010]
char c = 'a';
char *p1;
p1 = (char *)&a;
printf("*p1 = %d\n", *p1); //2 最后一个字节
printf("*(p1+1) = %d\n", *(p1+1));//1 地址对应字节位+1,高位
int *p2;
p2 = (char *)&a;//gcc警告
printf("*p2 = %d\n", *p2);//257(a原值)
char *p3;
p3 = (int *)&c;//gcc警告
printf("*p3 = %d\n", *p3);//97(SCII码)
int *p4;
p4 = (int *)&c;
printf("*p4 = %d\n", *p4);//一个很大的不变的数
return 0;
}
sizeof(int) 测试int类型变量所占字节数
sizeof(char) 测试char类型变量所占字节数
char *var_p;
sizeof(var_p) 测试var_p指针在内存空间所占字节数
二级指针
int **var_q;
举例:代码参见 point2.c
#include <stdio.h>
int main(void) {
int var_b = 321;
int *var_p;
var_p = &var_b;
int **var_q = &var_p;
printf("var_p address %p\n", &var_p); //var_p的地址
printf("var_q content %p\n", var_q); //var_p的地址
printf("var_b address %p\n", &var_b); //var_b的地址
printf("*var_q %p\n", *var_q); //var_b的地址
printf("**var_q %d\n", **var_q); //var_b的值
return 0;
}
补充:
数据在内存里的存放方式,分为两种:
1. 高位字节在高地址,低位字节在低地址(小端)
2. 高位字节在低地址,低位字节在高地址(大端)
在普通的计算机里,大部分采用的是小端。
在网络通讯中采用大端。
作业:写一个程序,判断使用的机器是大端还是小端?
#include <stdio.h>
int main() {
int num = 321; //[0000 0000][0000 0000][0000 0001][0100 0001] = 321
int *p_num = #
printf("p_num address %p\n", p_num);//0x..94
printf("p_num+1 address %p\n", p_num+1);//0x..98
char *p_ch = (char *)#
printf("*p_ch %d\n", *p_ch);//65
printf("*(p_ch+1) %d\n", *(p_ch+1));//1
//printf("*(p_ch-1) %d\n", *(p_ch-1));//验证高位
if(p_num+1 > p_num && *(p_ch+1) == 1) {
printf("此电脑为小端\n");
}
else
printf("此电脑为小端\n");
return 0;
}
指针数组
int *arr[3]; "arr是常量"
//定义了一个数组,数组的名字是arr,数组里有3个元素,元素的类型是*类型。
字符串列表
int main(int argc, char *argv[]);
数组指针
int (*arr)[3]; "arr是一个指针类型的变量"
数据类型是 "int[3]" //12个字节
//一个 指向有3个整形数的数组 指针
一维数组
int arr[3];
定义了一个数组,这个数组的名字是arr,数组包含3个元素,元素是整数类型。
arr+1 正确
arr++ 错误(arr常量)
二维数组
int arr[2][3] = {{1,2,3},{4,5,6}};
arr 数组的名字,这个数组有2个元素,每个元素是"int[3]"类型
arr[0] 数组的名字,这个数组有3个元素,每个元素是 int 类型
arr[1] 数组的名字,这个数组有3个元素,每个元素是 int 类型
arr[1] == arr+1 == *(arr+1) ://第2个数组的首地址
arr[1][1] == *(arr[1]+1) == *(*(arr+1)+1)://第2个数组的元素5
arr[1][1] == *(*(arr + 2) - 2)://越界后指针减回来,还是元素5
int (*p)[3];
p = arr;
*(*(p + 1) + 1); //第2个数组的元素5 (p+1 等同于 p++)
函数
int *func(int, int);
这个函数的返回值是一个地址(即 int * 类型)。
函数指针(回调函数)
int (*func)(int, int);
func 是一个指针类型的变量,访问方式 int(int, int);
举例验证:指针和函数的综合运用
cp -r ../1128/tmath .
从上一层目录中靠文件夹tmath到当前目录,当前目录为一个点。
/*代码*/
#include <stdio.h>
#include "tmath.h" //昨天的头文件
int process(int(*p)(int, int), int x, int y) {
return p(x, y);
}
int main(void) {
int a = 6, b = 2;
int (*func)(int, int);
func = add;
printf("%d + %d = %d\n", a, b, func(a, b));
func = sub;
printf("%d - %d = %d\n", a, b, func(a, b));
printf("%d + %d = %d\n", a, b, process(add, a, b));
printf("%d - %d = %d\n", a, b, process(sub, a, b));
printf("%d * %d = %d\n", a, b, process(mul, a, b));
printf("%d / %d = %d\n", a, b, process(div, a, b));
return 0;
}
指针小结:
int p; //整形变量p
int *p; //一个指向整形数的指针p
int **p; //一个指向整形数的地址的二级指针p
int *p[3]; //一个包含有3个元素的指针数组p
int (*p)[3]; //一个指向具有3个整型元素的一维数组的指针p
int *func(int, int); //一个返回整形数地址的函数func,含2整形参
int (*func)(int, int); //一个指向函数地址的函数指针func,含2整形参
(二)数据类型别名的定义
int count_t; //定义了count_t这个变量,这个变量是整形类型
typedef int count_t;
count_t是 int 类型的别名
count_t var_a;
总结类型别名三步走:
1. 定义这个类型的一个变量
2. 在第一步的基础上加上 typedef ,这时候变量的名字就是数据类型的别名
3. 使用数据类型的别名定义变量
char *point_t;
typedef char *point_t;
point_t pt; //pt是指针类型的变量,但是读取pt内容里指向的内容的时候遵守char的访问方式(相当于 char *pt)
point_t pa, pb, pc; == char *pa, *pb, *pc;
struct stu {
int num;
char name[12];
struct stu *next; //指针类型的 struct stu结构体类型
};
typedef struct stu stu_t;
stu_t st_p; //st_p 结构体类型变量
stu_t *stu_p; //stu_p 结构体类型指针变量
typedef stu_t *stu_p;
stu_p p;
struct stu *p;
int (*func)(int, int);
func是一个指针类型的变量,是这种函数的访问方式
typedef int (*func_t)(int, int);
func_t 指针的类型,访问方式是 int (int, int) 函数的访问方式
func_t p; //指针类型的变量,函数的访问方式,可以把函数地址给p
示例:
func_t arr[4] = {add, sub, mul, div};
for(int i = 0; i < 4; i++) {
printf("%d\n", arr[i](a, b)); //替换名称,加入函数类型实参
}
int arr_t[3];
typedef int arr_t[3]; // arr_t == int[3]
arr_t st; //st是个数组,数组里面有3个元素
示例:
typedef int arr_t[3];
arr_t st = {1, 2, 3};
for(int i = 0; i < 3; i++) {
printf("%d ", st[i]); //st[i] == arr_t[i] 打印结果1 2 3
}
(三)头文件
在tmath文件夹下做改动,添加process.c文件,在该文件中实现process函数。类型的定义写在process.h头文件中。
#include <> 和 #include "" 的区别
<> //在系统指定的文件夹下找头文件
"" //在当前路径下找头文件,如果当前路径下没找到就到系统路径下找
系统指定的路径是哪些路径?怎么获取?
"-v" 在编译的时候加上该参数 //得到系统指定的多个路径
(一)指针
指针是地址,这句话是错误的。
"指针是地址的类型"。具体的一个地址是指针类型的。
指针类型 *
整数类型 int
字符类型 char
数据类型:"访问内存的方式"
常量和变量,常量和变量在内存中都分配了一定的空间。
"常量"的内存空间里的值是"只读 r"的
"变量"的内存空间里的值是"可读可写 rw"的
变量四要素:名字、地址、值、数据类型
变量的内容是怎么访问到的?
通过变量的名字找到变量的地址,按照变量的类型再读取变量地址空间里的内容。
C语言本质就三句话:常量和变量,访问内存的方式,运算符的优先级
举例:代码参见 point.c
#include <stdio.h>
int main(void) {
int a = 258; [0000 0000][0000 0000][0000 0001][0000 0010]
char c = 'a';
char *p1;
p1 = (char *)&a;
printf("*p1 = %d\n", *p1); //2 最后一个字节
printf("*(p1+1) = %d\n", *(p1+1));//1 地址对应字节位+1,高位
int *p2;
p2 = (char *)&a;//gcc警告
printf("*p2 = %d\n", *p2);//257(a原值)
char *p3;
p3 = (int *)&c;//gcc警告
printf("*p3 = %d\n", *p3);//97(SCII码)
int *p4;
p4 = (int *)&c;
printf("*p4 = %d\n", *p4);//一个很大的不变的数
return 0;
}
sizeof(int) 测试int类型变量所占字节数
sizeof(char) 测试char类型变量所占字节数
char *var_p;
sizeof(var_p) 测试var_p指针在内存空间所占字节数
二级指针
int **var_q;
举例:代码参见 point2.c
#include <stdio.h>
int main(void) {
int var_b = 321;
int *var_p;
var_p = &var_b;
int **var_q = &var_p;
printf("var_p address %p\n", &var_p); //var_p的地址
printf("var_q content %p\n", var_q); //var_p的地址
printf("var_b address %p\n", &var_b); //var_b的地址
printf("*var_q %p\n", *var_q); //var_b的地址
printf("**var_q %d\n", **var_q); //var_b的值
return 0;
}
补充:
数据在内存里的存放方式,分为两种:
1. 高位字节在高地址,低位字节在低地址(小端)
2. 高位字节在低地址,低位字节在高地址(大端)
在普通的计算机里,大部分采用的是小端。
在网络通讯中采用大端。
作业:写一个程序,判断使用的机器是大端还是小端?
#include <stdio.h>
int main() {
int num = 321; //[0000 0000][0000 0000][0000 0001][0100 0001] = 321
int *p_num = #
printf("p_num address %p\n", p_num);//0x..94
printf("p_num+1 address %p\n", p_num+1);//0x..98
char *p_ch = (char *)#
printf("*p_ch %d\n", *p_ch);//65
printf("*(p_ch+1) %d\n", *(p_ch+1));//1
//printf("*(p_ch-1) %d\n", *(p_ch-1));//验证高位
if(p_num+1 > p_num && *(p_ch+1) == 1) {
printf("此电脑为小端\n");
}
else
printf("此电脑为小端\n");
return 0;
}
指针数组
int *arr[3]; "arr是常量"
//定义了一个数组,数组的名字是arr,数组里有3个元素,元素的类型是*类型。
字符串列表
int main(int argc, char *argv[]);
数组指针
int (*arr)[3]; "arr是一个指针类型的变量"
数据类型是 "int[3]" //12个字节
//一个 指向有3个整形数的数组 指针
一维数组
int arr[3];
定义了一个数组,这个数组的名字是arr,数组包含3个元素,元素是整数类型。
arr+1 正确
arr++ 错误(arr常量)
二维数组
int arr[2][3] = {{1,2,3},{4,5,6}};
arr 数组的名字,这个数组有2个元素,每个元素是"int[3]"类型
arr[0] 数组的名字,这个数组有3个元素,每个元素是 int 类型
arr[1] 数组的名字,这个数组有3个元素,每个元素是 int 类型
arr[1] == arr+1 == *(arr+1) ://第2个数组的首地址
arr[1][1] == *(arr[1]+1) == *(*(arr+1)+1)://第2个数组的元素5
arr[1][1] == *(*(arr + 2) - 2)://越界后指针减回来,还是元素5
int (*p)[3];
p = arr;
*(*(p + 1) + 1); //第2个数组的元素5 (p+1 等同于 p++)
函数
int *func(int, int);
这个函数的返回值是一个地址(即 int * 类型)。
函数指针(回调函数)
int (*func)(int, int);
func 是一个指针类型的变量,访问方式 int(int, int);
举例验证:指针和函数的综合运用
cp -r ../1128/tmath .
从上一层目录中靠文件夹tmath到当前目录,当前目录为一个点。
/*代码*/
#include <stdio.h>
#include "tmath.h" //昨天的头文件
int process(int(*p)(int, int), int x, int y) {
return p(x, y);
}
int main(void) {
int a = 6, b = 2;
int (*func)(int, int);
func = add;
printf("%d + %d = %d\n", a, b, func(a, b));
func = sub;
printf("%d - %d = %d\n", a, b, func(a, b));
printf("%d + %d = %d\n", a, b, process(add, a, b));
printf("%d - %d = %d\n", a, b, process(sub, a, b));
printf("%d * %d = %d\n", a, b, process(mul, a, b));
printf("%d / %d = %d\n", a, b, process(div, a, b));
return 0;
}
指针小结:
int p; //整形变量p
int *p; //一个指向整形数的指针p
int **p; //一个指向整形数的地址的二级指针p
int *p[3]; //一个包含有3个元素的指针数组p
int (*p)[3]; //一个指向具有3个整型元素的一维数组的指针p
int *func(int, int); //一个返回整形数地址的函数func,含2整形参
int (*func)(int, int); //一个指向函数地址的函数指针func,含2整形参
(二)数据类型别名的定义
int count_t; //定义了count_t这个变量,这个变量是整形类型
typedef int count_t;
count_t是 int 类型的别名
count_t var_a;
总结类型别名三步走:
1. 定义这个类型的一个变量
2. 在第一步的基础上加上 typedef ,这时候变量的名字就是数据类型的别名
3. 使用数据类型的别名定义变量
char *point_t;
typedef char *point_t;
point_t pt; //pt是指针类型的变量,但是读取pt内容里指向的内容的时候遵守char的访问方式(相当于 char *pt)
point_t pa, pb, pc; == char *pa, *pb, *pc;
struct stu {
int num;
char name[12];
struct stu *next; //指针类型的 struct stu结构体类型
};
typedef struct stu stu_t;
stu_t st_p; //st_p 结构体类型变量
stu_t *stu_p; //stu_p 结构体类型指针变量
typedef stu_t *stu_p;
stu_p p;
struct stu *p;
int (*func)(int, int);
func是一个指针类型的变量,是这种函数的访问方式
typedef int (*func_t)(int, int);
func_t 指针的类型,访问方式是 int (int, int) 函数的访问方式
func_t p; //指针类型的变量,函数的访问方式,可以把函数地址给p
示例:
func_t arr[4] = {add, sub, mul, div};
for(int i = 0; i < 4; i++) {
printf("%d\n", arr[i](a, b)); //替换名称,加入函数类型实参
}
int arr_t[3];
typedef int arr_t[3]; // arr_t == int[3]
arr_t st; //st是个数组,数组里面有3个元素
示例:
typedef int arr_t[3];
arr_t st = {1, 2, 3};
for(int i = 0; i < 3; i++) {
printf("%d ", st[i]); //st[i] == arr_t[i] 打印结果1 2 3
}
(三)头文件
在tmath文件夹下做改动,添加process.c文件,在该文件中实现process函数。类型的定义写在process.h头文件中。
#include <> 和 #include "" 的区别
<> //在系统指定的文件夹下找头文件
"" //在当前路径下找头文件,如果当前路径下没找到就到系统路径下找
系统指定的路径是哪些路径?怎么获取?
"-v" 在编译的时候加上该参数 //得到系统指定的多个路径
终端:#include <...> 搜索从这里开始:(下面都是路径)