如何写出好(易于调试)的代码
1. 使用assert
2. 尽量使用const
3. 养成良好的编码风格
4. 添加必要的注释
5. 避免编码的陷阱
实现strcpy函数
虽然功能实现了,但是还有需要优化的地方
上面的代码,还能再优化,如果有人传了个空指针,则达不到我们想要的效果
这个时候我们可以用,assert进行断言,此时程序会报错
当传arr2,此时未报错,但还能继续优化,当有人把这俩者弄反时,还会报错
我们的目的不是改变逗号后面的内容,目的是改变逗号前面的,我们给第二个加上const,若运行,则报错
const作用
//int main() //{ // //int num = 10; // //num = 20; // //printf("%d\n", num); // // const int num = 10; // //num = 20; // //const 修饰指针变量 // //1. const 放在*的左边 // //意思是:p指向的对象不能通过p来改变了,但是p变量本身的值是可以改变的 // //*p = 20;//err // //2. const 放在*的右边 // //意思是:p指向的对象是可以通过p来改变的,但是不能修改p变量本身的值 // // // //int* const p = # // 意思 // //*p = 0;//ok // //int n = 100; // //p = &n;//err // // //const int* p = # // //int n = 100; // // int const* p; // //*p = 20;//err // //p = &n;//ok // // printf("%d\n", num); // // return 0; //}
总结:const修饰常变量,const修饰谁,谁就不能被直接改变,如const修饰a,不能直接改变a,但可拿指针变量改变a
模拟实现strlen函数
int my_strlen(const char* str) { int count = 0; assert(str); while (*str != '\0') { count++; str++; } return count; } int main() { char arr[] = "hello bit"; int len = my_strlen(arr);//char* printf("%d\n", len); return 0; }
练习题
如有以下代码:
struct student { int num; char name[32]; float score; }stu;
则下面的叙述不正确的是:( )
A.struct 是结构体类型的关键字
B.struct student 是用户定义的结构体类型
C.num, score 都是结构体成员名
D.stu 是用户定义的结构体类型名
A:正确,在C语言中需要自定义类型时,要用到struct关键字
B:正确:在C语言中,用struct定义的结构体,定义结构体类型变量时,需要用struct student
C:正确:结构体中的变量名称,称之为结构体的成员
D:错误:stu是定义的结构体类型变量,不是名称,如果想要让stu为结构体类型名称时,必须在结构体定义时添加 typedef关键字
结构体访问成员的操作符不包含:( )
A.. 操作符
B.-> 操作符
C.* 解引用操作符
D.sizeof
A:正确,结构体类型变量访问结构体中成员时,使用.操作符
B:正确,指向结构体类型变量的指针访问结构体中成员时,使用->操作符
C:正确,指向结构体类型变量的指针也可以通过.方式访问成员,只不过要先通过*对该指针解引用
D:错误,sizeof是求结构体类型大小的
因此:选择D
关于二级指针描述描述正确的是:( )
A.二级指针也是指针,只不过比一级指针更大
B.二级指针也是指针,是用来保存一级指针的地址
C.二级指针是用来存放数组的地址
D.二级指针的大小是4个字节
A:错误,二级指针是指针,不能说起比一级指针大,只能说二级指针指向的空间中存储的也是一个地址
B:正确
C:错误,数组的地址一般用一级指针存储,或者用数组指针接收
D:二级指针是指针,但是否占4个字节不一定,要看具体的系统
因此:选择B
下面哪个是指针数组:( )
A. int* arr[10];
B.int * arr[];
C.int **arr;
D.int (*arr)[10];
指针数组是一个数组,该数组的每个元素是一个指针
A:正确,定义了一个数组,该数组中有10个元素,每个元素都是int*的指针类型
B:错误,编译失败,定义数组时,要给出空间的大小,如果没有给时,必须要给出初始化结果
C:错误,定义了一个二级指针
D:错误,*和arr先结合,说明arr不是数组。实际上arr是一个指针,一个指向数组的指针。
arr是一个数组指针,后续会更新这一块
因此:选择A
下面程序要求输出结构体中成员a的数据,以下不能填入横线处的内容是( )
#include < stdio.h > struct S { int a; int b; }; int main( ) { struct S a, *p=&a; a.a = 99; printf( "%d\n", __________); return 0; }
A.a.a
B.*p.a
C.p->a
D.(*p).a
结构体类型变量需要访问其成员时,用.运算符,如果是指向结构体变量的指针访问时,需要用->,或者先对指针解引用,取到指向的结构体类型的变量,再通过.访问,但是要注意优先级
.的优先级大于*,先形成p.a,然后再形成*p.a,因为p是指针,不能用.要用->,所以错误
下面程序的输出结果是:( )
struct stu { int num; char name[10]; int age; }; void fun(struct stu *p) { printf(“%s\n”,(*p).name); return; } int main() { struct stu students[3] = {{9801,”zhang”,20}, {9802,”wang”,19}, {9803,”zhao”,18} }; fun(students + 1); return 0; }
在main函数中先定义了一个stu结构体类型的数组students,students指向结构体的起始位置,students+1表示该数组中的第一个元素,因此fun的形参实际指向的是students数组中的第一个元素,故打印的是wang
喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以多少汽水(编程实现)。
#include<stdio.h> int main() { int money; scanf("%d", &money); int s = 0; int t = 0; int sum = money; while (money!=0) { if (money == 1) { s = money + t; t = 0; } sum = sum + s; if (money % 2 != 0&&money>1) { t++; } s=money = money / 2; } printf("%d", sum); return 0; }
#include<stdio.h> int main() { int money = 20;//总共有多钱 int empty = 20;//空瓶个数 int total = 20;//换了多少瓶 while(empty>=2) //空瓶大于等于俩个时,开始换 { total = total + empty/2;//总瓶数=拿钱买的+空瓶/2 empty = empty / 2 + empty % 2;//有时候会出现剩余一瓶的可能性,把这瓶加上 } printf("%d", total); return 0; }
Debug和Relase补充
这个是在Debug下运行的结果,会进入死循环,
而在relase版本下我们可以看到,程序运行完毕,这是因为 relase版本对代码进行了优化,i的地址不再和arr[12]的地址重复
release版本下的地址
debug下的地址
我们发现地址不同,这是因为release版本对代码进行了优化