#mark- 01-指针和数组
//问题:指针类型的用途是什么?
答:
第一个用途, 取值的时候, 会根据指针类型所占用的字节去取出对应字节的数据
第二个用途, 用于做加法运算, 指针+1, 其实是加上指针类型所占用的长度 , 如果当前指针类型是int, 那么+1本质上是加上4个字节
#mark- 02-指针和字符串
//问题1.通过数组保存字符串和通过指针保存字符串的区别?
答:
1)如果通过数组来保存字符串, 那么字符串是一个变量 str 可以修改
如果通过指针来保存字符串, 那么字符串是一个常量 str2 不能修该
2)数组保存的字符串存储在内存的栈中, 而通过指针保存的字符串存储在常量区
//问题2:字符串存储在栈中和存储在常量区中有什么区别?
答:
存储在栈中的变量有一个特点, 当作用域结束系统会自动释放该变量
存储在常量区中的值有一个特点, 不会被释放, 而且多个相同的值对应的地址相同
char str3[] = "lnj";
printf("str = %p\n", str);
printf("str3 = %p\n", str3);
char *str4 = "lmj";
printf("str2 = %p\n", str2);
printf("str4 = %p\n", str4);
//问题3:保存字符串的方式有哪些?
答:
char str[] = "lnj";
存储的位置: 栈
特点: 相同的字符串会重复的分配存储空间
字符串可以修改
char *str = "lnj"
存储的位置: 常量区
特点: 相同的字符串不会重复的分配存储空间
字符串不可以修改
#mark- 03-指针和字符串注意点
//问题:指针保存字符串有什么注意点?
答:
//1.指针没有被初始化不能随便使用, 野指针
int *p;
printf("*p = %i\n", *p);
//2.scanf不能用指针接收字符串,只能用数组接收(指针指向的字符串是一个常量,不能被修改)
char str[10];
scanf("%s", str);
printf("str = %s\n", str);
指针没有初始化, 是野指针
char *str;
scanf("%s", str);
printf("str = %s\n", str);
//注意: 用指针来保存字符串不可以被修改
// 指针没有初始化不能随便使用
#mark- 04-指针数组
//问题:指针数组的本质是什么?
答:指针数组中保存的每一个元素都是指针
//也叫字符串数组
// 定义数组的格式: 元素类型 数组名称[元素的个数]
char *names[4] =
{
"lnj",
"lmj",
"jjj",
"lk"
};
for (int i = 0; i < 4; i++) {
printf("names[%i] = %s\n", i , names[i]);
}
#mark- 05-指针变量的作用
//问题:数据类型可以做哪3件事?
//数组作为函数参数,专业做法用指针接收
1.可以用来定义变量
2.可以用来作为形参的类型
3.作为返回值
//问题2:指针保存字符串和数组字符串,作为函数返回值有什么区别?
char *name = demo();
printf("name = %s\n", name);
char *name2 = demo2();
printf("name2 = %s\n", name2);
char *demo2()
{
char name[]= "lnj";
return name;
}
char *demo()
{
char *name = "lnj";
return name;
}
#mark- 06-指针综合练习
//问题:这一节使用了哪些知识点?
答:
// 自己可以尝试实现strlen函数
函数运用的知识:
1.任何数值都有真假性
2.++ 在前,在后区别
3.指针+1, 其实是加上指针类型所占用的长度
4.循环语句省略大括号,只会执行最近的一行语句
char *name = "lnj";
int size =myStrlen3(name);
printf("size = %i\n", size);
int myStrlen3(char *str)
{
int count = 0;
// \0 ascii 0 // 0代表假 1真
while (*str++)count++;
return count;
}
#mark- 07-指向函数的指针基本概念
//问题:指向函数的指针的格式?指向函数的指针有几种类型?
//1.指向函数的指针的定义格式
void (*funtionP) ();
* : 代表是一个指针
funtionP : 代表指针变量的名称, 区分
(*funtionP) : 代表将来指向一个函数
void : 代表将来指向的函数没有返回值
() : 代表将来指向的函数没有参数
//2.指向函数的指针第一种方式
void (*funtionP) ();
funtionP = test; // 注意: 千万不能写成test()
test(); // 0x100000f00();
(*funtionP)(); // 0x100000f00()
// test == funtionP
funtionP();
//3.四种函数类型 的指针
//没有返回值没有参数
void test()
{
printf("哥被执行了\n");
}
//有返回值没有参数
int getAge()
{
return 30;
}
//没有返回值有参数
void sum(int v1, int v2)
{
int res = v1 + v2;
printf("res = %i\n", res);
}
//有返回值有参数
int sum2(int v1 , int v2)
{
int res = v1 + v2;
return res;
}
int main(int argc, const char * argv[]) {
printf("test = %p\n", test);
//1).指向 没有返回值没有参数的指针
void (*funtionP) ();
funtionP = test; // 注意: 千万不能写成test()
test(); // 0x100000f00();
(*funtionP)(); // 0x100000f00()
// test == funtionP
funtionP();
//2).指向 有返回值没有参数的指针
int (*ageP)();
ageP = getAge;
printf("age = %i\n", ageP());
//3).指向 没有返回值有参数的指针
// void (*sumP)(int v1, int v2);
void (*sumP)(int, int); //形参名可以省略
sumP = sum;
sumP(10, 20);
//4).指向 有返回值有参数的指针
int (*sumP2)(int , int);
sumP2 = sum2;
printf("sum = %i\n", sumP2(10, 20));
}
#mark- 08-指向函数的指针应用场景
//问题:指向函数的指针应用场景是什么?
答:通过指向函数的指针将函数作为函数的参数传递
// 定义一个方法, 给你两个数, 用户要求你做加法你就做加法, 用户要求你做减法, 那你就做减法
int sum(int v1, int v2)
{
return v1 + v2;
}
int minus(int v1, int v2)
{
return v1 - v2;
}
// 以后我们只需要给demo函数传递对应的指针, 那么函数内部就可以调用不同的函数
int demo3(int v1, int v2, int (*p)(int, int))
{
return p(v1, v2);
}
int main(int argc, const char * argv[]) {
printf("mins = %i\n", demo3(20, 10, minus));
printf("sum = %i\n", demo3(20, 10, sum));
return 0;
}
#mark- 09-指向函数的指针练习
void ts(char *temp, void (*funtionP)(char *))
{
// 1.先将第一个字母转换为大写
upperCase(temp);
// temp == &str[0]
while (*temp != '\0') { // h e l l o 空格 w
// 2.1取出当前的字符, 判断是否等于 空格, 如果等于空格就需要将下一个字符转换为大写
if ((*temp) == ' ') {
// 2.2将下一个字符转换为大写
funtionP(++temp);
}else
{
temp++; // temp == &str[7];
}
}
}
//小写转换为大写
void upperCase(char *p)
{
// 1.判断是否是小写字母
if (*p >= 'a' && *p <= 'z') {
// 2.将小写字母转换为大写字母
*p = *p - ('a' - 'A'); // 32 小写的ascii大于大写
}
}
#mark- 10-结构体基本概念
//问题:如何定义结构体变量?结构体的格式?如何给结构体的属性赋值?
答:
数组: 是用于保存一组相同类型的数据
结构体: 是用于保存一组不同类型的数据
//1.定义一个结构体变量
1).定义结构体类型
2).根据结构体类型, 定义结构体变量
定义结构体类型的格式:
struct 结构体类型名称
{
属性;
};
//2.可以使用 结构体变量名称.属性的方式给结构体变量赋值
// 1.定义结构体类型
struct Person
{
char *name;
int age;
double height;
};
// 2.定义结构体变量
struct Person p;
// 注意: 数组不能先定义再进行一次性的初始化, 所以结构体中字符串用指针接收
p.name = "lnj";
p.age = 30;
p.height = 1.75;
#mark- 11-结构体初始化
//问题:结构体初始化的几种方式?如何从结构体中获取数据?
答:
注意和数组的对比学习
struct Dog
{
char *name;
int age;
double height;
};
// 1.定义的同时初始化
struct Dog sd = {"wc", 13, 5.0};
// 2.先定义再初始化(逐个初始化)
struct Dog sd1;
sd1.name = "ww";
sd1.age = 5;
sd1.height = 10.9;
// 3.先定义再初始化(一次性初始化)
struct Dog sd2;
// 特别注意: 结构体和数组有一点区别, 数组不能先定义再进行一次性的初始化, 而结构体可以
// 只不过需要明确的告诉系统{}中是一个结构体
sd2 = (struct Dog){"xq", 8, 8.8}; // 数组? 结构体?
// 4.指定将数据赋值给指定的属性
struct Dog sd3 = {.height = 1.77, .name = "ww", .age = 33};
printf("name = %s, age = %i, height = %lf\n", sd3.name, sd3.age, sd3.height);
#mark- 12-结构体内存分配
//问题:结构体变量是如何分配内存空间的?
答:
1.定义结构体类型并不会分配存储空间
2.只有定义结构体变量才会真正的分配存储空间
3.结构体第0个属性的地址就是结构体的地址
4.和数组一样, 结构体内存寻址从大到小, 存储数组是从小到大(先存储第0个属性, 再一次存储其它属性)
//问题2.结构体如何开辟存储空间?
答:
结构体分配存储空间本质上并不是将所有属性占用的存储空间的总和加在一起后再分配,
而是会获取结构体类型中占用内存最大的属性的大小, 然后取该大小的倍数
特例:
如果剩余的存储空间"不够"存储将要存储的数据, 那么就会重新开辟8个字节的存储空间, 并且将需要存储的数据放到新开辟的存储空间中
如果剩余的存储空间"够"存储将要存储的数据, 那么就不会开辟了
#mark- 13-结构体类型定义方式
//问题:结构体类型有几种定义方式?
答:
// 1.先定义结构体类型, 再定义结构体变量
struct Person
{
int age;
char *name;
double height;
};
struct Person sp;
// 2.定义结构体类型的同时定义结构体变量
struct Person
{
int age;
char *name;
double height;
} sp;
// 数据类型 变量名称
sp.age = 30;
printf("age = %i\n", sp.age);
struct Person sp1;
sp1.name = "lnj";
printf("name = %s\n", sp1.name);
// 3.定义结构体类型的同时定义结构体变量, 并且省略结构体名称
// 如果在定义结构体类型的同时定义结构体变量, 那么可以省略结构体类型名称
// 弊端: 由于结构体类型没有名称, 所以以后就不能使用该结构体类型
// 优点: 如果结构体类型只需要使用一次, 那么可以使用该方式
struct
{
int age;
char *name;
double height;
} sp;
sp.age = 55;
printf("age = %i\n", sp.age);
#mark- 14-结构体类型作用域
//问题:结构体类型的作用域的注意点有哪些?
答:
1.在不同的作用域中可以有同名的局部变量, 如果访问采用就近原则
2.在不同的作用域中可以定义同名的结构体类型, 如果使用同名的结构体类型定义结构体变量, 采用就近原则
3.如果将结构体类型写在函数或者代码块外面, 那么结构体类型的作用域和全局变量一样, 从定义的那一行开始一直直到文件末尾
4.相同作用域不能有同名的结构体类型
#mark- 15-指向结构体的指针
//问题1.如何定义指向结构体变量的指针?
答:
1.拷贝结构体类型 和 结构体变量名称
2.在类型和名称中间加上一颗心
struct Person
{
int age;
char *name;
double height;
};
struct Person sp = {30, "lnj", 1.75};
sp.name = "lnj";
sp.age = 30;
sp.height = 1.75;
// 定义了一个指向结构体的指针
// *sip == sp
struct Person *sip;
sip = &sp;
//问题2.当指针指向结构体之后如何利用指针访问结构体?
答:
结构体变量名称.属性;
(*结构体指针变量名称).属性;
结构体指针变量名称->属性;
#mark- 16-结构体数组
//问题:结构体数组格式是什么?如何获取结构体数组中的属性?
答:
// 元素类型 数组名称[元素个数];
struct Bumen bumens[3] =
{
{"iOS", 20, 100.0}, // 0
{"Andoird", 10, 99.0},
{"php", 500, 88.0}
};
// bumens[0] == ios
bumens[0].name = "iOSv587";
bumens[0].count = 99;
bumens[0].kpi = 100.0;
printf("name = %s, count = %i, kpi = %lf\n", bumens[0].name, bumens[0].count, bumens[0].kpi);
#mark- 17-结构体嵌套
//问题:结构体如何进行嵌套?如何给结构体中嵌套的结构体赋值或者访问?
答:
//赋值
如果结构体类型中的属性又是一个结构体, 那么赋值时候通过{}赋值
//访问
如果结构体的属性又是一个结构体, 那么可以通过连续.的方式, 访问结构体属性中的属性
#mark- 18-结构体和函数
//问题:结构体作为函数的参数如何传递?结构体之间赋值是如果传递的?
答:
1.将结构体的属性传递给函数, 在函数中修改形参不会影响到实参
2.将结构体名称作为参数传递, 在函数中修改形参不会影响到实参
结构体之间赋值是值传递, 系统会将A结构体的值 拷贝一份到 B结构体中
#mark- 19-枚举基本概念
//问题:什么是枚举类型?枚举类型的格式?定义枚举的规范有哪些?
答:
//1.枚举就是专门用于表示几种固定类型的取值
枚举的本质就是基本数据类型, 就是整形
//2.枚举类型定义的格式
enum 枚举类型名称
{
取值,
};
//3.通过枚举类型定义枚举变量
定义枚举变量和定义结构体变量一样, 直接将数据类型拷贝过来, 空格之后写上变量名称即可
//4.注意点:
由于枚举类型的本质是整型, 所以枚举类型除了可以接收枚举的固定的取值以外, 还可以接收其它整型的值
也就是枚举类型的变量可以当做int类型的变量来使用
//5.定义枚举类型的规范
枚举类型的取值一般以k开头 ,后面跟上枚举类型的名称 , 跟上当前取值的含义
和结构体一样, 枚举类型的名称首字母大写