新能源网站建设哪家好东莞搜索引擎推广
22 数组指针变量
-
指向整个数组的指针
-
int(*p)[5]
其中(*p)说明p是一个指针变量,[5]说明p指向的是一整个数组,5代表元素个数,int 代表指向这个数组里边存放的元素的数据类型 -
int arr[6] = { 1,2,3,4,5,6 }; int (*p)[6] = &arr; for (int i = 0; i < 6; i++) {printf("%d\n", (*p)[i]); }
-
对于数组指针,如果想访问数组里的每一个元素,需要像上述代码一样(即*p相当与数组名)
23 二维数组传参
-
对于二维数组本质的理解:二维数组的每一行其实都是一个一维数组,也就是说二维数组可以理解成是一个一维数组的数组,二维数组的每一个元素都是一个一维数组,二维数组的数组名是首元素的地址,也就是第一行的地址(就是这个一维数组的地址)
void my_print2(int(*arr)[5], int l, int r) {for (int i = 0; i < l; i++) {for (int j = 0; j < r; j++) {printf("%d ", arr[i][j]);}printf("\n");} }int main() {int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };my_print2(arr, 3, 5);return 0; }
-
对于
p[i][j]
来说,相当于*( *(p+i)+j )
24 函数指针变量
- 指向函数的指针,存放的是函数的地址。(函数也是有地址的,函数名就是函数的地址,并且&Add与Add没有任何区别,都是函数的地址)
- 基本格式
int (*p)(int, int) = &Add
(*p)说明是指针,(int,int)是函数参数,说明指向的是一个函数,int是函数的返回值类型 - 基本用法
int res = (*p)(1, 3)
对指针解引用就相当于得到了函数名。int res = p(1, 5);
其实也可以,也就是可以直接用函数地址来进行调用,但推荐使用第一种,易于理解 (* ( void(*) ( ) ) 0 ) ()
对于这个代码的理解:void(*)()
是一种函数指针类型,它指向的函数没有参数,返回值为void(void(*)())0
在括号里边放类型是强制类型转化,也就是将0这个int类型的数据转变为一个函数指针类型(也就是将0看作一个地址)*(void (*)())0
相当于对一个函数指针解引用,得到的是一个函数名(* ( void(*) ( ) ) 0 ) ()
相当于调用这个函数 整个表达式的目的就是调用地址为0处的函数
25 typedef关键字
- 可以给类型重命名
- 格式:
typedef long long int LL;
- 在连续创建多个指针变量时,应写为
int* p1,*p2
,每个指针变量都有自己的*。 - 对于函数指针重命名的时候,应该
typedef void (*Pt)(int) ;
只能把重命名的名字(Pt)写到里边
26 函数指针数组
-
一个数组里面存放的元素都是函数指针。
-
格式:
int (*arr[4])(int,int)={Add,Sub};
其中Add,Sub都是函数名称。其实写作int (*)(int,int) arr[4] ={Add,Sub};
更容易接受,但语法规定 -
注意,只有多个函数有相同的返回值类型,以及相同的参数类型的时候,多个函数名才能放入同一个函数指针数组里面。
-
应用:转移表(可以大大简化代码,可以起到跳转的作用)
void menu() {printf("********************\n");printf("1.add 2.sub\n");printf("3.mul 4.div\n");printf(" 0.exit\n");printf("********************\n");
}
int add(int x,int y) {return x+y;
}
int sub(int x, int y) {return x -y;
}
int mul(int x, int y) {return x*y;
}
int div(int x, int y) {return x/y;
}int main() {int input = 0;int (*parr[5])(int, int) = { NULL,add,sub,mul,div };do {menu();printf("请选择一个数字\n");scanf("%d", &input);if (input == 0) {printf("退出程序\n");}else if (input > 0 && input < 5) {printf("请输入两个操作数:");int a, b;scanf("%d %d", &a, &b);int res=(parr[input])(a, b);printf("res=%d\n", res);}else {printf("请重新输入\n");}} while (input);return 0;
}
27 回调函数
-
回调函数是指通过函数指针而被间接调用的函数。相当于将某一个函数作为参数在函数间进行传递。(在某些时候可以大大简化代码)
-
qsort函数的使用技巧(回调函数的例子,可以对任何数据类型进行排序)
-
-
void qsort(void *base, size_t num, size_t size, int (*cmp)(const void *, const void *));
其中base
指向待排序数组,num
待排序数组元素个数,size
待排序数组中每个元素所占内存的大小(单位字节),cmp
是一个函数指针,指向一个函数,这个函数可以用来比较base数组里任意两个元素的大小。 -
比较两个整数 int cmp_int(const void* p1, const void* p2) {return *(int*)p1 - *(int*)p2; } //因为void*不能直接解引用,所以必须先进行强制类型转化 int main() {int arr[] = { 6, 3, 9, 8, 5, 2, 4, 7, 1, 0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, 4, cmp_int);for (int i = 0; i < 10; i++) {printf("%d ", arr[i]);}return 0; }
-
-
-
比较两个结构体 struct Stu {char name[20];int age; }; int cmp_struct(const void*p1,const void*p2){return strcmp((*(struct Stu*)p1).name, (*(struct Stu*)p2).name); } //因为void*不能直接解引用,所以必须先进行强制类型转化int main() {struct Stu arr[3] = { {"bbb",70},{"ccc",18},{"aaa",100} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_struct);for (int i = 0; i < 3; i++) {printf("%s ", arr[i].name);}return 0; }
-
qsort函数的模拟实现 (核心在于无论什么类型的数据,都可以一个一个字节的交换)
int cmp_int(const void* p1, const void* p2) {return *(int*)p1 - *(int*)p2;//return *(int*)p2 - *(int*)p1 ; }void my_swap(char* p1,char* p2,size_t n) {for (int i = 0; i < n; i++) {char tem = *p1;*p1 = *p2;*p2 = tem;p1++;p2++;}}void bubble_qsort(void * base,size_t sz,size_t width,int(*cmp)(const void *,const void *)) {for (int i = 0; i < sz - 1; i++) {for (int j = 0; j < sz - 1; j++) {if (cmp(((char*)base + j * width), ((char*)base + (j+1)*width)) > 0) {my_swap(((char*)base + j * width), ((char*)base + (j + 1) * width),width);}}}} int main() {int arr[] = { 6, 3, 9, 8, 5, 2, 4, 7, 1, 0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_qsort(arr, sz, 4, cmp_int);for (int i = 0; i < 10; i++) {printf("%d ", arr[i]);}return 0; }
-
28 sizeof 和 strlen
- sizeof是一种操作符,它是根据字符串所占内存的大小来计算字节个数的(因为char是一个字节,所以也是字符个数),所以无论有没有
\0
,计算结果没有区别。并且结果的个数是包含\0
在内的个数 - strlen是一种函数根据
\0
来进行计数的,它会返回\0
之前所有字符的个数,不包含\0
。如果没有\0
,则会一直向后查找,直到遇到\0
为止。所以有可能造成越界访问
29 关于指针的易错题
-
对于数组名,当
sizeof(数组名)
只有数组名时,代表整个数组的大小。但如果数组名和其他元素或数字一起放入()里,则表示首元素的地址,不在是那种特例了 -
arr[4]={1,2,3,4}; sizeof(arr);//结果是16 sizeof(arr+0);//结果是4/8 因为arr不是单独在里面,所以只能表示首元素地址(地址的大小取决与x86/x64)
-
对于srtlen(arr)来说,它接受的参数必须是一个指针,所以无论传进去什么,都会被当作指针对待,在函数内部会对它进行解引用操作。所以不能传入一个int等类型所以
strlen(*arr)
是一个错误语句。 -
int a[3]][4]={0};sizeof(a[0])a[0]相当于第一行首元素的地址,现在单独放在了sizeof()里边,代表的是整个第一行,所以答案是4*4sizeof(a[0]+1) 现在a[0]不是单独放在sizeof()里面,所以a[0]只表示第一行第一个元素的地址,+1之后变成a[0][1]的地址sizeof(a+1)a不是单独放在sizeof()内部,所以表示的是首元素的地址,即第一行的地址,所以a+1表示第二行的地址,所以结果是4/8(因为其仍然是一个地址)sizeof()这个操作符在计算大小的时候,是根据类型推断的,不直接去访问所传入的空间,所以哪怕传入了一个野指针,也不造成越界访问,仅仅根据类型判断。
-
在x86条件下,该结构体大小是20个字节struct test{int a;char* b;short c;short d[4];char e[2]; }* p=(struct test*)0x100000;1.p+0x1 2.(unsigned int*)p + 0x1 3.(unsigned long)p + 0x11.因为P是一个结构体指针类型,所以当+1之后跳过一个结构体的大小(即20个字节,答案为0x100014(16进制)) 2.因为p被强转成了int*类型,所以p+1之后跳过一个整型的大小,答案为0x100004(16进制)) 3.p被强转成long 类型之后,p就从一个指针变量变成了一个long型的变量,+1就相当与两个数字之间进行运算,所以答案为0x100001(16进制))
-
int arr[3][2]={(0,1),(2,3),(4,5)} int* p; p = a[0]; printf("%d\n",p);对于数组arr来说,(0,1)其实是一个逗号表达式,它的结果是最后一个元素的值,即(0,1)==1。所以其实在数组arr里只放入了{1,3,5}.所以p是arr第一行第一个元素的地址。
1 字符分类函数
-
都在头文件< ctype.h > 里面
2 字符转换函数
- 都在头文件< ctype.h > 里面
- tolower(),toupper();大小写转化函数
3 strlen 函数的使用和模拟实现
-
strlen的返回值是size_t,是一种无符号整数。(对于无符号整数来说,是没有负数的,如果一个负数被转变为无符号整数,会直接将这个负数的补码看作一个无符号整数,也就是会变成一个很大的正数(因为负数首位符号位是1))
-
模拟实现(不创建临时变量)(使用递归)size_t my_strlen(const char* str) {if (*str != '\0') {return 1 + my_strlen(str + 1);}else {return 0;} } int main() {char arr[] = "abcdefaaa";size_t len = my_strlen(arr);printf("%zd ", len);return 0; }
4 strcpy 的使用和模拟实现
-
strcpy(arr2, arr1)
将arr1的内容放入arr2里 -
- 在拷贝时,arr1里面必须包含
\0
- 再拷贝时
\0
也会被拷贝进入arr2 - 如果arr2里面原本有元素,那么arr2的元素会被arr1的元素从头开始覆盖,为覆盖的元素保持不变
- strcpy有返回值是一个指针,指向arr2的起始地址
- 在拷贝时,arr1里面必须包含
-
模拟实现 版本一: void my_strcpy(char* dest,char* src) {while (*src != '\0') {*dest = *src;dest++;src++;}*dest = *src; }版本二: char* my_strcpy(char* dest, char* src) {char* res=dest;assert(dest&&src);while (*dest++ = *src++) {;}return res; }int main() {char arr1[] = "asjdflks";char arr2[20];my_strcpy(arr2, arr1);printf("%s", arr2);return 0; }
5 strcat使用及其模拟实现
-
使用:在给定字符串后面追加另一个字符串
-
- 注意:目标和源头字符串都要包含‘\0’,
strcat(char * destination,const char* source)
-
模拟实现
-
char* my_strcat(char * dest,const char* src) {char* res = dest;while (*dest != '\0') {dest++;}while (*dest++ = *src++) {;}return res; } int main() {char arr[20] = "hello ";char arrr[] = "world";my_strcat(arr, arrr);printf("%s", arr);return 0; }//(注意,不可以追加两个相同的字符串,不然会因为‘\0’的丢失造成死循环)
6 strcmp的使用与模拟实现
-
比较的不是长度,是对应字符的ASCII码的大小
-
在传递时,如果传递的是“abc”,直接传递这样的常量字符串,也是可以使用char*的指针接收的。
-
模拟实现
-
int my_strcmp(const char* str1,const char*str2) {assert(str1 && str2);while (*str1 == *str2) {if (*str1 == '\0') {return 0;}str1++;str2++;}return (*str1 - *str2); } int main() {int res = my_strcmp("ax", "ade");printf("%d", res);return 0; }
7 strncmp strncpy strncat可以指定长度的字符串函数
8 strstr的使用和模拟实现
-
在字符串里查找一个字串第一次出现的地方
-
const char* strstr(const char* st1,const char* str2)
在str1里找str2 -
模拟实现
-
char* my_strstr(const char* str1,const char*str2) {char* s1 = NULL;char* s2 = NULL;char* cur = str1;while (*cur) {s1 = cur;s2 = str2;while (*s1 && *s2 && *s1 == *s2) {s1++;s2++;}if (*s2=='\0') {return cur;}cur++;}return NULL; } int main() {char arr[] = "abcsdfhdf";char arrr[] = "sd";char* p = my_strstr(arr, arrr);printf("%s", p);return 0; }
9 strtok函数的使用
-
用于分割字符串(以所提供的字符为分隔符)
-
char* strtok(char* str,char* sep)
sep指向一个字符串,包含所有用于分割字符串的字符 -
如果待切割的字符串里有多个分割符,则调用一次该函数,会将从左边数第一个分隔符变成’\0’,并且每一次调用该函数会存下来这次操作到的位置,下一次在调用的时候,会直接从这个位置开始向后寻找下一个分隔符。除了第一次调用,其他时候调用时,str只需要传入NULL即可
-
示例: int main() {char arr[] = "zpw@bitedu.net";char a[] = "@.";for (char* r = strtok(arr, a); r != NULL;r=strtok(NULL,a)) {printf("%s\n", r);}//用于分割字符串return 0; }
-
应该是内部实现的时候有一个static修饰的变量(只在第一次调用函数时初始化,之后再次调用该函数时,不会再次初始化,而是保留上一次调用结束时的值。)
10 strerror的使用
-
每⼀个错误码(库函数的错误码)都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
-
char* strerror()
可以用于查看错误信息,在errno变量里 -
for (int i = 0; i < 20; i++) {printf("%d:%s\n", i, strerror(i)); }
11 perror函数
void perror(char* str)
它这个函数会自动打印错误信息,这个str参数是一个提示用的信息,自己输入的perror(“错误信息是:”)
打印结果就是:错误信息是:No such file or directory